summaryrefslogtreecommitdiff
path: root/lib/Archive
diff options
context:
space:
mode:
authorReid Spencer <rspencer@reidspencer.com>2004-11-06 08:51:45 +0000
committerReid Spencer <rspencer@reidspencer.com>2004-11-06 08:51:45 +0000
commit362cbf0d747154f2617f2cabe20187235dcaba60 (patch)
tree8a9eece69846f269562360e75f8f19c8612ab107 /lib/Archive
parent07adb2836b8aa7a3872e33c285958f5937662b50 (diff)
First kinda/sorta working version of the Archive library. Reading is not
yet supported but writing works. Way too early to review this. More to come git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@17499 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'lib/Archive')
-rw-r--r--lib/Archive/Archive.cpp24
-rw-r--r--lib/Archive/ArchiveInternals.h158
-rw-r--r--lib/Archive/ArchiveWriter.cpp284
3 files changed, 466 insertions, 0 deletions
diff --git a/lib/Archive/Archive.cpp b/lib/Archive/Archive.cpp
new file mode 100644
index 00000000000..e9fcd2ebe16
--- /dev/null
+++ b/lib/Archive/Archive.cpp
@@ -0,0 +1,24 @@
+//===-- Archive.cpp - Generic LLVM archive functions ------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file was developed by Reid Spencer and is distributed under the
+// University of Illinois Open Source License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Builds up standard unix archive files (.a) containing LLVM bytecode.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ArchiveInternals.h"
+
+using namespace llvm;
+
+Archive::Archive() {
+}
+
+Archive::~Archive() {
+}
+
+// vim: sw=2 ai
diff --git a/lib/Archive/ArchiveInternals.h b/lib/Archive/ArchiveInternals.h
new file mode 100644
index 00000000000..dde83583c9a
--- /dev/null
+++ b/lib/Archive/ArchiveInternals.h
@@ -0,0 +1,158 @@
+//===-- lib/Bytecode/ArchiveInternals.h -------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file was developed by Reid Spencer and is distributed under the
+// University of Illinois Open Source License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Internal implementation header for LLVM Archive files.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LIB_BYTECODE_ARCHIVEINTERNALS_H
+#define LIB_BYTECODE_ARCHIVEINTERNALS_H
+
+#include "llvm/Bytecode/Archive.h"
+#include "llvm/System/TimeValue.h"
+
+#define ARFILE_MAGIC "!<arch>\n" ///< magic string
+#define ARFILE_MAGIC_LEN (sizeof(ARFILE_MAGIC)-1) ///< length of magic string
+#define ARFILE_SYMTAB_NAME "/" ///< name of symtab entry
+#define ARFILE_STRTAB_NAME "//" ///< name of strtab entry
+#define ARFILE_PAD '\n' ///< inter-file align padding
+
+namespace llvm {
+
+ /// The ArchiveMemberHeader structure is used internally for bytecode archives.
+ /// The header precedes each file member in the archive. This structure is
+ /// defined using character arrays for direct and correct interpretation
+ /// regardless of the endianess of the machine that produced it.
+ /// @brief Archive File Member Header
+ class ArchiveMemberHeader {
+ public:
+ void init() {
+ memset(name,' ',16);
+ memset(date,' ',12);
+ memset(uid,' ',6);
+ memset(gid,' ',6);
+ memset(mode,' ',8);
+ memset(size,' ',10);
+ fmag[0] = '`';
+ fmag[1] = '\n';
+ }
+ void setDate( int secondsSinceEpoch = 0 ) {
+ if (secondsSinceEpoch == 0) {
+ sys::TimeValue tv = sys::TimeValue::now();
+ uint64_t secs; uint32_t nanos;
+ tv.GetTimespecTime(secs,nanos);
+ secondsSinceEpoch = (int) secs;
+ }
+ char buffer[20];
+ sprintf(buffer,"%d", secondsSinceEpoch);
+ memcpy(date,buffer,strlen(buffer));
+ }
+
+ void setSize(size_t sz) {
+ char buffer[20];
+ sprintf(buffer, "%u", (unsigned)sz);
+ memcpy(size,buffer,strlen(buffer));
+ }
+
+ void setMode(int m) {
+ char buffer[20];
+ sprintf(buffer, "%o", m);
+ memcpy(mode,buffer,strlen(buffer));
+ }
+
+ void setUid(unsigned u) {
+ char buffer[20];
+ sprintf(buffer, "%u", u);
+ memcpy(uid,buffer,strlen(buffer));
+ }
+
+ void setGid(unsigned g) {
+ char buffer[20];
+ sprintf(buffer, "%u", g);
+ memcpy(gid,buffer,strlen(buffer));
+ }
+
+ bool setName(const std::string& nm) {
+ if (nm.length() > 0 && nm.length() <= 16) {
+ memcpy(name,nm.c_str(),nm.length());
+ for (int i = nm.length()+1; i < 16; i++ ) name[i] = ' ';
+ return true;
+ }
+ return false;
+ }
+
+ private:
+ char name[16]; ///< Name of the file member. The filename is terminated with '/'
+ ///< and blanks. The empty name (/ and 15 blanks) is for the
+ ///< symbol table. The special name "//" and 15 blanks is for
+ ///< the string table, used for long file names. It must be
+ ///< first in the archive.
+ char date[12]; ///< File date, decimal seconds since Epoch
+ char uid[6]; ///< user id in ASCII decimal
+ char gid[6]; ///< group id in ASCII decimal
+ char mode[8]; ///< file mode in ASCII octal
+ char size[10]; ///< file size in ASCII decimal
+ char fmag[2]; ///< Always contains ARFILE_MAGIC_TERMINATOR
+
+ };
+
+ /// The ArchiveInternals class is used to hold the content of the archive
+ /// while it is in memory. It also provides the bulk of the implementation for
+ /// the llvm:Archive class's interface.
+ class Archive::ArchiveInternals {
+ /// @name Types
+ /// @{
+ public:
+ typedef std::vector<std::string> StrTab;
+
+ /// This structure holds information for one member in the archive. It is
+ /// used temporarily while the contents of the archive are being
+ /// determined.
+ struct MemberInfo {
+ MemberInfo() {}
+ sys::Path path;
+ std::string name;
+ sys::Path::StatusInfo status;
+ StrTab symbols;
+ unsigned offset;
+ };
+
+ /// @}
+ /// @name Methods
+ /// @{
+ public:
+ /// @brief Add a file member to the archive.
+ void addFileMember(
+ const sys::Path& path, ///< The path to the file to be added
+ const std::string& name, ///< The name for the member
+ const StrTab* syms = 0 ///< The symbol table of the member
+ );
+
+ /// @brief Write the accumulated archive information to an archive file
+ void writeArchive();
+ void writeMember(const MemberInfo& member,std::ofstream& ARFile);
+ void writeSymbolTable(std::ofstream& ARFile);
+ void writeInteger(int num, std::ofstream& ARFile);
+
+ /// @}
+ /// @name Data
+ /// @{
+ private:
+ friend class Archive; ///< Parent class is a friend
+ sys::Path fname; ///< Path to the archive file
+ std::vector<MemberInfo> members; ///< Info about member files
+ Archive::SymTab* symtab; ///< User's symbol table
+
+ /// @}
+ };
+}
+
+#endif
+
+// vim: sw=2 ai
diff --git a/lib/Archive/ArchiveWriter.cpp b/lib/Archive/ArchiveWriter.cpp
new file mode 100644
index 00000000000..e3e2df4aee0
--- /dev/null
+++ b/lib/Archive/ArchiveWriter.cpp
@@ -0,0 +1,284 @@
+//===-- ArchiveWriter.cpp - LLVM archive writing --------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file was developed by Reid Spencerand is distributed under the
+// University of Illinois Open Source License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Builds up standard unix archive files (.a) containing LLVM bytecode.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ArchiveInternals.h"
+#include "llvm/Module.h"
+#include "llvm/Bytecode/Reader.h"
+#include "llvm/Support/FileUtilities.h"
+#include "llvm/ADT/StringExtras.h"
+#include "llvm/System/MappedFile.h"
+#include <fstream>
+#include <iostream>
+
+using namespace llvm;
+
+Archive*
+Archive::CreateEmpty(const sys::Path& Filename) {
+ Archive* result = new Archive;
+ Archive::ArchiveInternals* impl = result->impl = new Archive::ArchiveInternals;
+ impl->fname = Filename;
+ return result;
+}
+
+Archive*
+Archive::CreateFromFiles(
+ const sys::Path& Filename,
+ const PathList& Files,
+ const std::string& StripName
+) {
+ Archive* result = new Archive;
+ Archive::ArchiveInternals* impl = result->impl = new Archive::ArchiveInternals;
+ impl->fname = Filename;
+
+ try {
+ size_t strip_len = StripName.length();
+ for (PathList::const_iterator P = Files.begin(), E = Files.end(); P != E ;++P)
+ {
+ if (P->readable()) {
+ std::string name(P->get());
+ if (strip_len > 0 && StripName == name.substr(0,strip_len)) {
+ name.erase(0,strip_len);
+ }
+ if (P->isBytecodeFile()) {
+ std::vector<std::string> syms;
+ if (!GetBytecodeSymbols(*P, syms))
+ throw std::string("Can not get symbols from: ") + P->get();
+ impl->addFileMember(*P, name, &syms);
+ } else {
+ impl->addFileMember(*P, name);
+ }
+ }
+ else
+ throw std::string("Can not read: ") + P->get();
+ }
+
+ // Now that we've collected everything, write the archive
+ impl->writeArchive();
+
+ } catch(...) {
+ delete impl;
+ result->impl = 0;
+ delete result;
+ throw;
+ }
+
+ return result;
+}
+
+void
+Archive::ArchiveInternals::addFileMember(
+ const sys::Path& filePath,
+ const std::string& memberName,
+ const StrTab* symbols
+) {
+ MemberInfo info;
+ info.path = filePath;
+ info.name = memberName;
+ filePath.getStatusInfo(info.status);
+ if (symbols)
+ info.symbols = *symbols;
+ info.offset = 0;
+ members.push_back(info);
+}
+
+void
+Archive::ArchiveInternals::writeInteger(int num, std::ofstream& ARFile) {
+ char buff[4];
+ buff[0] = (num >> 24) & 255;
+ buff[1] = (num >> 16) & 255;
+ buff[2] = (num >> 8) & 255;
+ buff[3] = num & 255;
+ ARFile.write(buff, sizeof(buff));
+}
+
+void
+Archive::ArchiveInternals::writeSymbolTable( std::ofstream& ARFile ) {
+
+ // Compute the number of symbols in the symbol table and the
+ // total byte size of the string pool. While we're traversing,
+ // build the string pool for supporting long file names. Also,
+ // build the table of file offsets for the symbol table and
+ // the
+ typedef std::map<std::string,unsigned> SymbolMap;
+ StrTab stringPool;
+ SymbolMap symbolTable;
+ std::vector<unsigned> fileOffsets;
+ std::string symTabStrings;
+ unsigned fileOffset = 0;
+ unsigned spOffset = 0;
+ unsigned numSymbols = 0;
+ unsigned numSymBytes = 0;
+ for (unsigned i = 0; i < members.size(); i++ ) {
+ MemberInfo& mi = members[i];
+ StrTab& syms = mi.symbols;
+ size_t numSym = syms.size();
+ numSymbols += numSym;
+ for (unsigned j = 0; j < numSym; j++ ) {
+ numSymBytes += syms[j].size() + 1;
+ symbolTable[syms[i]] = i;
+ }
+ if (mi.name.length() > 15 || std::string::npos != mi.name.find('/')) {
+ stringPool.push_back(mi.name + "/\n");
+ mi.name = std::string("/") + utostr(spOffset);
+ spOffset += mi.name.length() + 2;
+ } else if (mi.name[mi.name.length()-1] != '/') {
+ mi.name += "/";
+ }
+ fileOffsets.push_back(fileOffset);
+ fileOffset += sizeof(ArchiveMemberHeader) + mi.status.fileSize;
+ }
+
+
+ // Compute the size of the symbol table file member
+ unsigned symTabSize = 0;
+ if (numSymbols != 0)
+ symTabSize =
+ sizeof(ArchiveMemberHeader) + // Size of the file header
+ 4 + // Size of "number of entries"
+ (4 * numSymbols) + // Size of member file indices
+ numSymBytes; // Size of the string table
+
+ // Compute the size of the string pool
+ unsigned strPoolSize = 0;
+ if (spOffset != 0 )
+ strPoolSize =
+ sizeof(ArchiveMemberHeader) + // Size of the file header
+ spOffset; // Number of bytes in the string pool
+
+ // Compute the byte index offset created by symbol table and string pool
+ unsigned firstFileOffset = symTabSize + strPoolSize;
+
+ // Create header for symbol table. This must be first if there is
+ // a symbol table and must have a special name.
+ if ( symTabSize > 0 ) {
+ ArchiveMemberHeader Hdr;
+ Hdr.init();
+
+ // Name of symbol table is '/ ' but "" is passed in
+ // because the setName method always terminates with a /
+ Hdr.setName(ARFILE_SYMTAB_NAME);
+ Hdr.setDate();
+ Hdr.setSize(symTabSize - sizeof(ArchiveMemberHeader));
+ Hdr.setMode(0);
+ Hdr.setUid(0);
+ Hdr.setGid(0);
+
+ // Write header to archive file
+ ARFile.write((char*)&Hdr, sizeof(Hdr));
+
+ // Write the number of entries in the symbol table
+ this->writeInteger(numSymbols, ARFile);
+
+ // Write the file offset indices for each symbol and build the
+ // symbol table string pool
+ std::string symTabStrPool;
+ symTabStrPool.reserve(256 * 1024); // Reserve 256KBytes for symbols
+ for (SymbolMap::iterator I = symbolTable.begin(), E = symbolTable.end();
+ I != E; ++I ) {
+ this->writeInteger(firstFileOffset + fileOffsets[I->second], ARFile);
+ symTabStrPool += I->first;
+ symTabStrPool += "\0";
+ }
+
+ // Write the symbol table's string pool
+ ARFile.write(symTabStrPool.data(), symTabStrPool.size());
+ }
+
+ //============== DONE WITH SYMBOL TABLE
+
+ if (strPoolSize > 0) {
+ // Initialize the header for the string pool
+ ArchiveMemberHeader Hdr;
+ Hdr.init();
+ Hdr.setName(ARFILE_STRTAB_NAME);
+ Hdr.setDate();
+ Hdr.setSize(spOffset);
+ Hdr.setMode(0);
+ Hdr.setUid(0);
+ Hdr.setGid(0);
+
+ // Write the string pool header
+ ARFile.write((char*)&Hdr, sizeof(Hdr));
+
+ // Write the string pool
+ for (unsigned i = 0; i < stringPool.size(); i++) {
+ ARFile.write(stringPool[i].data(), stringPool[i].size());
+ }
+ }
+}
+
+void
+Archive::ArchiveInternals::writeMember(
+ const MemberInfo& member,
+ std::ofstream& ARFile
+) {
+
+ // Map the file into memory. We do this early for two reasons. First,
+ // if there's any kind of error, we want to know about it. Second, we
+ // want to ensure we're using the most recent size for this file.
+ sys::MappedFile mFile(member.path);
+ mFile.map();
+
+ // Header for the archive member
+ ArchiveMemberHeader Hdr;
+ Hdr.init();
+
+ // Set the name. If its longer than 15 chars, it will have already
+ // been reduced by the writeSymbolTable.
+ Hdr.setName(member.name);
+
+ // Set the other header members
+ Hdr.setSize( mFile.size() );
+ Hdr.setMode( member.status.mode);
+ Hdr.setUid ( member.status.user);
+ Hdr.setGid ( member.status.group);
+ Hdr.setDate( member.status.modTime.ToPosixTime() );
+
+ // Write header to archive file
+ ARFile.write((char*)&Hdr, sizeof(Hdr));
+
+ //write to archive file
+ ARFile.write(mFile.charBase(),mFile.size());
+
+ mFile.unmap();
+}
+
+void
+Archive::ArchiveInternals::writeArchive() {
+
+ // Create archive file for output.
+ std::ofstream ArchiveFile(fname.get().c_str());
+
+ // Check for errors opening or creating archive file.
+ if ( !ArchiveFile.is_open() || ArchiveFile.bad() ) {
+ throw std::string("Error opening archive file: ") + fname.get();
+ }
+
+ // Write magic string to archive.
+ ArchiveFile << ARFILE_MAGIC;
+
+ // Write the symbol table and string pool
+ writeSymbolTable(ArchiveFile);
+
+ //Loop over all member files, and add to the archive.
+ for ( unsigned i = 0; i < members.size(); ++i) {
+ if(ArchiveFile.tellp() % 2 != 0)
+ ArchiveFile << ARFILE_PAD;
+ writeMember(members[i],ArchiveFile);
+ }
+
+ //Close archive file.
+ ArchiveFile.close();
+}
+
+// vim: sw=2 ai