2003-08-28 15:22:38 +00:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// LLVM 'ar' UTILITY
|
|
|
|
//
|
|
|
|
// This utility may be invoked in the following manner:
|
|
|
|
// llvm-ar archivename files..
|
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "Support/CommandLine.h"
|
|
|
|
#include "llvm/Bytecode/Reader.h"
|
|
|
|
#include "llvm/Module.h"
|
|
|
|
|
|
|
|
#include <string>
|
|
|
|
#include <iostream>
|
|
|
|
#include <fstream>
|
|
|
|
#include <vector>
|
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <fcntl.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <sys/mman.h>
|
|
|
|
|
|
|
|
|
|
|
|
using std::string;
|
|
|
|
using std::vector;
|
|
|
|
using std::cout;
|
|
|
|
|
|
|
|
|
|
|
|
#define ARFMAG "\n" /* header trailer string */
|
|
|
|
#define ARMAG "!<arch>\n" /* magic string */
|
|
|
|
#define SARMAG 8 /* length of magic string */
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
|
|
|
// Each file member is preceded by a file member header. Which is
|
|
|
|
// of the following format:
|
|
|
|
//
|
|
|
|
// char ar_name[16] - '/' terminated file member name.
|
|
|
|
// If the file name does not fit, a dummy name is used.
|
|
|
|
// char ar_date[12] - file date in decimal
|
|
|
|
// char ar_uid[6] - User id of file owner in decimal.
|
|
|
|
// char ar_gid[6] - Group ID file belongs to in decimal.
|
|
|
|
// char ar_mode[8] - File mode in octal.
|
|
|
|
// char ar_size[10] - Size of file in decimal.
|
|
|
|
// char ar_fmag[2] - Trailer of header file, a newline.
|
|
|
|
struct ar_hdr {
|
|
|
|
char name[16];
|
|
|
|
char date[12];
|
|
|
|
char uid[6];
|
|
|
|
char gid[6];
|
|
|
|
char mode[8];
|
|
|
|
char size[10];
|
|
|
|
char fmag[2];
|
|
|
|
void init() {
|
|
|
|
memset(name,' ',16);
|
|
|
|
memset(date,' ',12);
|
|
|
|
memset(uid,' ',6);
|
|
|
|
memset(gid,' ',6);
|
|
|
|
memset(mode,' ',8);
|
|
|
|
memset(size,' ',10);
|
|
|
|
memset(fmag,' ',2);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
//Option to generate symbol table or not
|
|
|
|
//running llvm-ar -s is the same as ranlib
|
|
|
|
cl::opt<bool> SymbolTable ("s", cl::desc("Generate an archive symbol table"));
|
|
|
|
|
|
|
|
//Archive name
|
|
|
|
cl::opt<string> Archive (cl::Positional, cl::desc("<archive file>"),
|
|
|
|
cl::Required);
|
|
|
|
|
|
|
|
//For now we require one or more member files, this should change so
|
|
|
|
//we can just run llvm-ar -s on an archive to generate the symbol
|
|
|
|
//table
|
|
|
|
cl::list<string> Members(cl::ConsumeAfter, cl::desc("<archive members>..."));
|
|
|
|
|
|
|
|
|
|
|
|
static inline bool Error(std::string *ErrorStr, const char *Message) {
|
|
|
|
if (ErrorStr) *ErrorStr = Message;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// WriteSymbolTable - Writes symbol table to ArchiveFile, return false
|
|
|
|
// on errors. Also returns by reference size of symbol table.
|
|
|
|
//
|
|
|
|
// Overview of method:
|
|
|
|
// 1) Generate the header for the symbol table. This is a normal
|
|
|
|
// archive member header, but it has a zero length name.
|
|
|
|
// 2) For each archive member file, stat the file and parse the bytecode
|
|
|
|
// Store cummulative offset (file size + header size).
|
|
|
|
// 3) Loop over all the symbols for the current member file,
|
|
|
|
// add offset entry to offset vector, and add symbol name to its vector.
|
|
|
|
// Note: The symbol name vector is a vector of chars to speed up calculating
|
|
|
|
// the total size of the symbol table.
|
|
|
|
// 4) Update offset vector once we know the total size of symbol table. This is
|
|
|
|
// because the symbol table appears before all archive member file contents.
|
|
|
|
// We add the size of magic string, and size of symbol table to each offset.
|
|
|
|
// 5) If the new updated offset it not even, we add 1 byte to offset because
|
|
|
|
// a newline will be inserted when writing member files. This adjustment is
|
|
|
|
// cummulative (ie. each time we have an odd offset we add 1 to total adjustment).
|
|
|
|
// 6) Lastly, write symbol table to file.
|
|
|
|
//
|
|
|
|
bool WriteSymbolTable(std::ofstream &ArchiveFile) {
|
|
|
|
|
|
|
|
//Create header for symbol table. This is essentially an empty header with the
|
|
|
|
//name set to a '/' to indicate its a symbol table.
|
|
|
|
ar_hdr Hdr;
|
|
|
|
Hdr.init();
|
|
|
|
|
|
|
|
//Name of symbol table is '/'
|
|
|
|
Hdr.name[0] = '/';
|
2003-09-13 03:18:56 +00:00
|
|
|
Hdr.name[1] = '\0';
|
2003-08-28 15:22:38 +00:00
|
|
|
|
|
|
|
//Set the header trailer to a newline
|
|
|
|
memcpy(Hdr.fmag,ARFMAG,sizeof(ARFMAG));
|
|
|
|
|
|
|
|
|
|
|
|
//Write header to archive file
|
|
|
|
ArchiveFile.write((char*)&Hdr, sizeof(Hdr));
|
|
|
|
|
|
|
|
|
|
|
|
unsigned memoff = 0; //Keep Track of total size of files added to archive
|
|
|
|
vector<unsigned> offsets; //Vector of offsets into archive file
|
|
|
|
vector<char> names; //Vector of characters that are the symbol names.
|
|
|
|
|
|
|
|
//Loop over archive member files, parse bytecode, and generate symbol table.
|
|
|
|
for(unsigned i=0; i<Members.size(); ++i) {
|
|
|
|
|
|
|
|
//Open Member file for reading and copy to buffer
|
|
|
|
int FD = open(Members[i].c_str(),O_RDONLY);
|
|
|
|
|
|
|
|
//Check for errors opening the file.
|
|
|
|
if (FD == -1) {
|
|
|
|
std::cerr << "Error opening file!\n";
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
//Stat the file to get its size.
|
|
|
|
struct stat StatBuf;
|
|
|
|
if (stat(Members[i].c_str(), &StatBuf) == -1 || StatBuf.st_size == 0) {
|
|
|
|
std::cerr << "Error stating file\n";
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
//Size of file
|
|
|
|
unsigned Length = StatBuf.st_size;
|
|
|
|
|
|
|
|
//Read in file into a buffer.
|
|
|
|
unsigned char *buf = (unsigned char*)mmap(0, Length,PROT_READ,
|
|
|
|
MAP_PRIVATE, FD, 0);
|
|
|
|
|
|
|
|
//Check if mmap failed.
|
|
|
|
if (buf == (unsigned char*)MAP_FAILED) {
|
|
|
|
std::cerr << "Error mmapping file!\n";
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
//Parse the bytecode file and get all the symbols.
|
|
|
|
string ErrorStr;
|
|
|
|
Module *M = ParseBytecodeBuffer(buf,Length,Members[i],&ErrorStr);
|
|
|
|
|
|
|
|
//Check for errors parsing bytecode.
|
|
|
|
//if(ErrorStr) {
|
|
|
|
//std::cerr << "Error Parsing Bytecode\n";
|
|
|
|
//return false;
|
|
|
|
//}
|
|
|
|
|
|
|
|
//Loop over function names and global vars and add to symbol maps
|
|
|
|
for(Module::iterator I = M->begin(), E=M->end(); I != E; ++I) {
|
|
|
|
|
|
|
|
//get function name
|
|
|
|
string NM = ((Function*)I)->getName();
|
|
|
|
|
|
|
|
//Loop over the characters in the name and add to symbol name vector
|
|
|
|
for(unsigned i=0; i<NM.size(); ++i)
|
|
|
|
names.push_back(NM[i]);
|
|
|
|
|
|
|
|
//Each symbol is null terminated.
|
|
|
|
names.push_back('\0');
|
|
|
|
|
|
|
|
//Add offset to vector of offsets
|
|
|
|
offsets.push_back(memoff);
|
|
|
|
}
|
|
|
|
|
|
|
|
memoff += Length + sizeof(Hdr);
|
|
|
|
}
|
|
|
|
|
|
|
|
//Determine how large our symbol table is.
|
|
|
|
unsigned symbolTableSize = sizeof(Hdr) + 4 + 4*(offsets.size()) + names.size();
|
|
|
|
cout << "Symbol Table Size: " << symbolTableSize << "\n";
|
|
|
|
|
|
|
|
//Number of symbols should be in network byte order as well
|
|
|
|
char num[4];
|
|
|
|
unsigned temp = offsets.size();
|
|
|
|
num[0] = (temp >> 24) & 255;
|
|
|
|
num[1] = (temp >> 16) & 255;
|
|
|
|
num[2] = (temp >> 8) & 255;
|
|
|
|
num[3] = temp & 255;
|
|
|
|
|
|
|
|
//Write number of symbols to archive file
|
|
|
|
ArchiveFile.write(num,4);
|
|
|
|
|
|
|
|
//Adjustment to offset to start files on even byte boundaries
|
|
|
|
unsigned adjust = 0;
|
|
|
|
|
|
|
|
//Update offsets write symbol tabel to archive.
|
|
|
|
for(unsigned i=0; i<offsets.size(); ++i) {
|
|
|
|
char output[4];
|
|
|
|
offsets[i] = offsets[i] + symbolTableSize + SARMAG;
|
|
|
|
offsets[i] += adjust;
|
|
|
|
if((offsets[i] % 2 != 0)) {
|
|
|
|
adjust++;
|
|
|
|
offsets[i] += adjust;
|
|
|
|
}
|
|
|
|
|
|
|
|
cout << "Offset: " << offsets[i] << "\n";
|
|
|
|
output[0] = (offsets[i] >> 24) & 255;
|
|
|
|
output[1] = (offsets[i] >> 16) & 255;
|
|
|
|
output[2] = (offsets[i] >> 8) & 255;
|
|
|
|
output[3] = offsets[i] & 255;
|
|
|
|
ArchiveFile.write(output,4);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//Write out symbol name vector.
|
|
|
|
for(unsigned i=0; i<names.size(); ++i)
|
|
|
|
ArchiveFile << names[i];
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// AddMemberToArchive - Writes member file to archive. Returns false on errors.
|
|
|
|
//
|
|
|
|
// Overview of method:
|
|
|
|
// 1) Open file, and stat it.
|
|
|
|
// 2) Fill out header using stat information. If name is longer then 15
|
|
|
|
// characters, use "dummy" name.
|
|
|
|
// 3) Write header and file contents to disk.
|
|
|
|
// 4) Keep track of total offset into file, and insert a newline if it is odd.
|
|
|
|
//
|
|
|
|
bool AddMemberToArchive(string Member, std::ofstream &ArchiveFile) {
|
|
|
|
|
|
|
|
ar_hdr Hdr; //Header for archive member file.
|
|
|
|
|
|
|
|
//stat the file to get info
|
|
|
|
struct stat StatBuf;
|
|
|
|
if (stat(Member.c_str(), &StatBuf) == -1 || StatBuf.st_size == 0)
|
|
|
|
cout << "ERROR\n";
|
|
|
|
|
|
|
|
//fill in header
|
|
|
|
|
|
|
|
//set name to white spaces
|
|
|
|
memset(Hdr.name,' ', sizeof(Hdr.name));
|
|
|
|
|
|
|
|
//check the size of the name, if less than 15, we can copy it directly
|
|
|
|
//otherwise we give it a dummy name for now
|
|
|
|
if(Member.length() < 16)
|
|
|
|
memcpy(Hdr.name,Member.c_str(),Member.length());
|
|
|
|
else
|
|
|
|
memcpy(Hdr.name, "Dummy", 5);
|
|
|
|
|
|
|
|
//terminate name with forward slash
|
|
|
|
Hdr.name[15] = '/';
|
|
|
|
|
|
|
|
//file member size in decimal
|
|
|
|
unsigned Length = StatBuf.st_size;
|
|
|
|
sprintf(Hdr.size,"%d", Length);
|
|
|
|
cout << "Size: " << Length << "\n";
|
|
|
|
|
|
|
|
//file member user id in decimal
|
|
|
|
sprintf(Hdr.uid, "%d", StatBuf.st_uid);
|
|
|
|
|
|
|
|
//file member group id in decimal
|
|
|
|
sprintf(Hdr.gid, "%d", StatBuf.st_gid);
|
|
|
|
|
|
|
|
//file member date in decimal
|
2003-09-23 17:27:02 +00:00
|
|
|
sprintf(Hdr.date,"%d", (int)StatBuf.st_mtime);
|
2003-08-28 15:22:38 +00:00
|
|
|
|
|
|
|
//file member mode in OCTAL
|
|
|
|
sprintf(Hdr.mode,"%d", StatBuf.st_mode);
|
|
|
|
|
|
|
|
//add our header trailer
|
|
|
|
memcpy(Hdr.fmag,ARFMAG,sizeof(ARFMAG));
|
|
|
|
|
|
|
|
//write header to archive file
|
|
|
|
ArchiveFile.write((char*)&Hdr, sizeof(Hdr));
|
|
|
|
|
|
|
|
//open Member file for reading and copy to buffer
|
|
|
|
int FD = open(Member.c_str(),O_RDONLY);
|
|
|
|
if (FD == -1) {
|
|
|
|
std::cerr << "Error opening file!\n";
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
unsigned char *buf = (unsigned char*)mmap(0, Length,PROT_READ,
|
|
|
|
MAP_PRIVATE, FD, 0);
|
|
|
|
|
|
|
|
//check if mmap failed
|
|
|
|
if (buf == (unsigned char*)MAP_FAILED) {
|
|
|
|
std::cerr << "Error mmapping file!\n";
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
//write to archive file
|
|
|
|
ArchiveFile.write((char*)buf,Length);
|
|
|
|
|
|
|
|
// Unmmap the memberfile
|
|
|
|
munmap((char*)buf, Length);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// CreateArchive - Generates archive with or without symbol table.
|
|
|
|
//
|
|
|
|
void CreateArchive() {
|
|
|
|
|
|
|
|
//Create archive file for output.
|
|
|
|
std::ofstream ArchiveFile(Archive.c_str());
|
|
|
|
|
|
|
|
//Check for errors opening or creating archive file.
|
|
|
|
if(!ArchiveFile.is_open() || ArchiveFile.bad() ) {
|
|
|
|
std::cerr << "Error opening Archive File\n";
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
//Write magic string to archive.
|
|
|
|
ArchiveFile << ARMAG;
|
|
|
|
|
|
|
|
//If the '-s' option was specified, generate symbol table.
|
|
|
|
if(SymbolTable) {
|
|
|
|
cout << "Symbol Table Start: " << ArchiveFile.tellp() << "\n";
|
|
|
|
if(!WriteSymbolTable(ArchiveFile)) {
|
|
|
|
std::cerr << "Error creating symbol table. Exiting program.";
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
cout << "Symbol Table End: " << ArchiveFile.tellp() << "\n";
|
|
|
|
}
|
|
|
|
//Loop over all member files, and add to the archive.
|
|
|
|
for(unsigned i=0; i<Members.size(); ++i) {
|
|
|
|
if(ArchiveFile.tellp() % 2 != 0)
|
|
|
|
ArchiveFile << ARFMAG;
|
|
|
|
|
|
|
|
cout << "Member File Start: " << ArchiveFile.tellp() << "\n";
|
|
|
|
|
|
|
|
if(AddMemberToArchive(Members[i],ArchiveFile) != true) {
|
|
|
|
std::cerr << "Error adding file to archive. Exiting program.";
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
cout << "Member File End: " << ArchiveFile.tellp() << "\n";
|
|
|
|
}
|
|
|
|
|
|
|
|
//Close archive file.
|
|
|
|
ArchiveFile.close();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int main(int argc, char **argv) {
|
|
|
|
|
|
|
|
//Parse Command line options
|
|
|
|
cl::ParseCommandLineOptions(argc, argv, " llvm-ar\n");
|
|
|
|
|
|
|
|
//Create archive!
|
|
|
|
CreateArchive();
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|