#include "ArchiveInternals.h"
#include "llvm/Module.h"
#include "llvm/ADT/OwningPtr.h"
#include "llvm/Bitcode/ReaderWriter.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/Process.h"
#include "llvm/Support/Signals.h"
#include "llvm/Support/system_error.h"
#include <fstream>
#include <ostream>
#include <iomanip>
using namespace llvm;
static inline void writeInteger(unsigned num, std::ofstream& ARFile) {
while (1) {
if (num < 0x80) { ARFile << (unsigned char)num;
return;
}
ARFile << (unsigned char)(0x80 | ((unsigned char)num & 0x7F));
num >>= 7; }
}
static inline unsigned numVbrBytes(unsigned num) {
if (num < 1<<14) {
if (num < 1<<7)
return 1;
else
return 2;
}
if (num < 1<<21)
return 3;
if (num < 1<<28)
return 4;
return 5; }
Archive* Archive::CreateEmpty(const sys::Path& FilePath, LLVMContext& C) {
Archive* result = new Archive(FilePath, C);
return result;
}
bool
Archive::fillHeader(const ArchiveMember &mbr, ArchiveMemberHeader& hdr,
int sz, bool TruncateNames) const {
hdr.init();
char buffer[32];
sprintf(buffer, "%-8o", mbr.getMode());
memcpy(hdr.mode,buffer,8);
sprintf(buffer, "%-6u", mbr.getUser());
memcpy(hdr.uid,buffer,6);
sprintf(buffer, "%-6u", mbr.getGroup());
memcpy(hdr.gid,buffer,6);
uint64_t secondsSinceEpoch = mbr.getModTime().toEpochTime();
sprintf(buffer,"%-12u", unsigned(secondsSinceEpoch));
memcpy(hdr.date,buffer,12);
std::string mbrPath = mbr.getPath().str();
size_t mbrLen = mbrPath.length();
while (mbrLen > 0 && mbrPath[mbrLen-1] == ' ') {
mbrPath.erase(mbrLen-1,1);
mbrLen--;
}
bool writeLongName = false;
if (mbr.isStringTable()) {
memcpy(hdr.name,ARFILE_STRTAB_NAME,16);
} else if (mbr.isSVR4SymbolTable()) {
memcpy(hdr.name,ARFILE_SVR4_SYMTAB_NAME,16);
} else if (mbr.isBSD4SymbolTable()) {
memcpy(hdr.name,ARFILE_BSD4_SYMTAB_NAME,16);
} else if (mbr.isLLVMSymbolTable()) {
memcpy(hdr.name,ARFILE_LLVM_SYMTAB_NAME,16);
} else if (TruncateNames) {
const char* nm = mbrPath.c_str();
unsigned len = mbrPath.length();
size_t slashpos = mbrPath.rfind('/');
if (slashpos != std::string::npos) {
nm += slashpos + 1;
len -= slashpos +1;
}
if (len > 15)
len = 15;
memcpy(hdr.name,nm,len);
hdr.name[len] = '/';
} else if (mbrPath.length() < 16 && mbrPath.find('/') == std::string::npos) {
memcpy(hdr.name,mbrPath.c_str(),mbrPath.length());
hdr.name[mbrPath.length()] = '/';
} else {
std::string nm = "#1/";
nm += utostr(mbrPath.length());
memcpy(hdr.name,nm.data(),nm.length());
if (sz < 0)
sz -= mbrPath.length();
else
sz += mbrPath.length();
writeLongName = true;
}
if (sz < 0) {
buffer[0] = '-';
sprintf(&buffer[1],"%-9u",(unsigned)-sz);
} else {
sprintf(buffer, "%-10u", (unsigned)sz);
}
memcpy(hdr.size,buffer,10);
return writeLongName;
}
bool
Archive::addFileBefore(const sys::Path& filePath, iterator where,
std::string* ErrMsg) {
bool Exists;
if (sys::fs::exists(filePath.str(), Exists) || !Exists) {
if (ErrMsg)
*ErrMsg = "Can not add a non-existent file to archive";
return true;
}
ArchiveMember* mbr = new ArchiveMember(this);
mbr->data = 0;
mbr->path = filePath;
const sys::FileStatus *FSInfo = mbr->path.getFileStatus(false, ErrMsg);
if (!FSInfo) {
delete mbr;
return true;
}
mbr->info = *FSInfo;
unsigned flags = 0;
bool hasSlash = filePath.str().find('/') != std::string::npos;
if (hasSlash)
flags |= ArchiveMember::HasPathFlag;
if (hasSlash || filePath.str().length() > 15)
flags |= ArchiveMember::HasLongFilenameFlag;
sys::fs::file_magic type;
if (sys::fs::identify_magic(mbr->path.str(), type))
type = sys::fs::file_magic::unknown;
switch (type) {
case sys::fs::file_magic::bitcode:
flags |= ArchiveMember::BitcodeFlag;
break;
default:
break;
}
mbr->flags = flags;
members.insert(where,mbr);
return false;
}
bool
Archive::writeMember(
const ArchiveMember& member,
std::ofstream& ARFile,
bool CreateSymbolTable,
bool TruncateNames,
bool ShouldCompress,
std::string* ErrMsg
) {
unsigned filepos = ARFile.tellp();
filepos -= 8;
size_t fSize = member.getSize();
const char *data = (const char*)member.getData();
MemoryBuffer *mFile = 0;
if (!data) {
OwningPtr<MemoryBuffer> File;
if (error_code ec = MemoryBuffer::getFile(member.getPath().c_str(), File)) {
if (ErrMsg)
*ErrMsg = ec.message();
return true;
}
mFile = File.take();
data = mFile->getBufferStart();
fSize = mFile->getBufferSize();
}
if (CreateSymbolTable && member.isBitcode()) {
std::vector<std::string> symbols;
std::string FullMemberName = archPath.str() + "(" + member.getPath().str()
+ ")";
Module* M =
GetBitcodeSymbols(data, fSize, FullMemberName, Context, symbols, ErrMsg);
if ( M ) {
for (std::vector<std::string>::iterator SI = symbols.begin(),
SE = symbols.end(); SI != SE; ++SI) {
std::pair<SymTabType::iterator,bool> Res =
symTab.insert(std::make_pair(*SI,filepos));
if (Res.second) {
symTabSize += SI->length() +
numVbrBytes(SI->length()) +
numVbrBytes(filepos);
}
}
delete M;
} else {
delete mFile;
if (ErrMsg)
*ErrMsg = "Can't parse bitcode member: " + member.getPath().str()
+ ": " + *ErrMsg;
return true;
}
}
int hdrSize = fSize;
ArchiveMemberHeader Hdr;
bool writeLongName = fillHeader(member,Hdr,hdrSize,TruncateNames);
ARFile.write((char*)&Hdr, sizeof(Hdr));
if (writeLongName) {
ARFile.write(member.getPath().str().data(),
member.getPath().str().length());
}
ARFile.write(data,fSize);
if ((ARFile.tellp() & 1) == 1)
ARFile << ARFILE_PAD;
delete mFile;
return false;
}
void
Archive::writeSymbolTable(std::ofstream& ARFile) {
ArchiveMemberHeader Hdr;
Hdr.init();
memcpy(Hdr.name,ARFILE_LLVM_SYMTAB_NAME,16);
uint64_t secondsSinceEpoch = sys::TimeValue::now().toEpochTime();
char buffer[32];
sprintf(buffer, "%-8o", 0644);
memcpy(Hdr.mode,buffer,8);
sprintf(buffer, "%-6u", sys::Process::GetCurrentUserId());
memcpy(Hdr.uid,buffer,6);
sprintf(buffer, "%-6u", sys::Process::GetCurrentGroupId());
memcpy(Hdr.gid,buffer,6);
sprintf(buffer,"%-12u", unsigned(secondsSinceEpoch));
memcpy(Hdr.date,buffer,12);
sprintf(buffer,"%-10u",symTabSize);
memcpy(Hdr.size,buffer,10);
ARFile.write((char*)&Hdr, sizeof(Hdr));
#ifndef NDEBUG
unsigned startpos = ARFile.tellp();
#endif
for ( Archive::SymTabType::iterator I = symTab.begin(), E = symTab.end();
I != E; ++I)
{
writeInteger(I->second, ARFile);
writeInteger(I->first.length(), ARFile);
ARFile.write(I->first.data(), I->first.length());
}
#ifndef NDEBUG
unsigned endpos = ARFile.tellp();
#endif
assert(endpos - startpos == symTabSize && "Invalid symTabSize computation");
if (symTabSize % 2 != 0 )
ARFile << ARFILE_PAD;
}
bool
Archive::writeToDisk(bool CreateSymbolTable, bool TruncateNames, bool Compress,
std::string* ErrMsg)
{
if (members.empty() && mapfile && mapfile->getBufferSize() > 8) {
if (ErrMsg)
*ErrMsg = "Can't write an archive not opened for writing";
return true;
}
sys::Path TmpArchive = archPath;
if (TmpArchive.createTemporaryFileOnDisk(ErrMsg))
return true;
sys::RemoveFileOnSignal(TmpArchive);
std::ios::openmode io_mode = std::ios::out | std::ios::trunc |
std::ios::binary;
std::ofstream ArchiveFile(TmpArchive.c_str(), io_mode);
if (!ArchiveFile.is_open() || ArchiveFile.bad()) {
TmpArchive.eraseFromDisk();
if (ErrMsg)
*ErrMsg = "Error opening archive file: " + archPath.str();
return true;
}
if (CreateSymbolTable) {
symTabSize = 0;
symTab.clear();
}
ArchiveFile << ARFILE_MAGIC;
for (MembersList::iterator I = begin(), E = end(); I != E; ++I) {
if (writeMember(*I, ArchiveFile, CreateSymbolTable,
TruncateNames, Compress, ErrMsg)) {
TmpArchive.eraseFromDisk();
ArchiveFile.close();
return true;
}
}
ArchiveFile.close();
if (CreateSymbolTable) {
sys::Path FinalFilePath = archPath;
{
OwningPtr<MemoryBuffer> arch;
if (error_code ec = MemoryBuffer::getFile(TmpArchive.c_str(), arch)) {
if (ErrMsg)
*ErrMsg = ec.message();
return true;
}
const char* base = arch->getBufferStart();
if (FinalFilePath.createTemporaryFileOnDisk(ErrMsg))
return true;
sys::RemoveFileOnSignal(FinalFilePath);
std::ofstream FinalFile(FinalFilePath.c_str(), io_mode);
if (!FinalFile.is_open() || FinalFile.bad()) {
TmpArchive.eraseFromDisk();
if (ErrMsg)
*ErrMsg = "Error opening archive file: " + FinalFilePath.str();
return true;
}
FinalFile << ARFILE_MAGIC;
if (foreignST) {
if (writeMember(*foreignST, FinalFile, false, false, false, ErrMsg)) {
FinalFile.close();
TmpArchive.eraseFromDisk();
return true;
}
}
writeSymbolTable(FinalFile);
FinalFile.write(base + sizeof(ARFILE_MAGIC)-1,
arch->getBufferSize()-sizeof(ARFILE_MAGIC)+1);
FinalFile.close();
}
if (FinalFilePath.renamePathOnDisk(TmpArchive, ErrMsg))
return true;
}
cleanUpMemory();
if (TmpArchive.renamePathOnDisk(archPath, ErrMsg))
return true;
if (archPath.makeReadableOnDisk(ErrMsg))
return true;
if (archPath.makeWriteableOnDisk(ErrMsg))
return true;
return false;
}