diff options
Diffstat (limited to 'lib/Object/Archive.cpp')
-rw-r--r-- | lib/Object/Archive.cpp | 172 |
1 files changed, 172 insertions, 0 deletions
diff --git a/lib/Object/Archive.cpp b/lib/Object/Archive.cpp new file mode 100644 index 0000000..e2eaff5 --- /dev/null +++ b/lib/Object/Archive.cpp @@ -0,0 +1,172 @@ +//===- Archive.cpp - ar File Format implementation --------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines the ArchiveObjectFile class. +// +//===----------------------------------------------------------------------===// + +#include "llvm/Object/Archive.h" +#include "llvm/ADT/APInt.h" +#include "llvm/Support/MemoryBuffer.h" + +using namespace llvm; +using namespace object; + +namespace { +const StringRef Magic = "!<arch>\n"; + +struct ArchiveMemberHeader { + char Name[16]; + char LastModified[12]; + char UID[6]; + char GID[6]; + char AccessMode[8]; + char Size[10]; //< Size of data, not including header or padding. + char Terminator[2]; + + ///! Get the name without looking up long names. + StringRef getName() const { + char EndCond = Name[0] == '/' ? ' ' : '/'; + StringRef::size_type end = StringRef(Name, sizeof(Name)).find(EndCond); + if (end == StringRef::npos) + end = sizeof(Name); + assert(end <= sizeof(Name) && end > 0); + // Don't include the EndCond if there is one. + return StringRef(Name, end); + } + + uint64_t getSize() const { + APInt ret; + StringRef(Size, sizeof(Size)).getAsInteger(10, ret); + return ret.getZExtValue(); + } +}; + +const ArchiveMemberHeader *ToHeader(const char *base) { + return reinterpret_cast<const ArchiveMemberHeader *>(base); +} +} + +Archive::Child Archive::Child::getNext() const { + size_t SpaceToSkip = sizeof(ArchiveMemberHeader) + + ToHeader(Data.data())->getSize(); + // If it's odd, add 1 to make it even. + if (SpaceToSkip & 1) + ++SpaceToSkip; + + const char *NextLoc = Data.data() + SpaceToSkip; + + // Check to see if this is past the end of the archive. + if (NextLoc >= Parent->Data->getBufferEnd()) + return Child(Parent, StringRef(0, 0)); + + size_t NextSize = sizeof(ArchiveMemberHeader) + + ToHeader(NextLoc)->getSize(); + + return Child(Parent, StringRef(NextLoc, NextSize)); +} + +error_code Archive::Child::getName(StringRef &Result) const { + StringRef name = ToHeader(Data.data())->getName(); + // Check if it's a special name. + if (name[0] == '/') { + if (name.size() == 1) { // Linker member. + Result = name; + return object_error::success; + } + if (name.size() == 2 && name[1] == '/') { // String table. + Result = name; + return object_error::success; + } + // It's a long name. + // Get the offset. + APInt offset; + name.substr(1).getAsInteger(10, offset); + const char *addr = Parent->StringTable->Data.begin() + + sizeof(ArchiveMemberHeader) + + offset.getZExtValue(); + // Verify it. + if (Parent->StringTable == Parent->end_children() + || addr < (Parent->StringTable->Data.begin() + + sizeof(ArchiveMemberHeader)) + || addr > (Parent->StringTable->Data.begin() + + sizeof(ArchiveMemberHeader) + + Parent->StringTable->getSize())) + return object_error::parse_failed; + Result = addr; + return object_error::success; + } + // It's a simple name. + if (name[name.size() - 1] == '/') + Result = name.substr(0, name.size() - 1); + else + Result = name; + return object_error::success; +} + +uint64_t Archive::Child::getSize() const { + return ToHeader(Data.data())->getSize(); +} + +MemoryBuffer *Archive::Child::getBuffer() const { + StringRef name; + if (getName(name)) return NULL; + return MemoryBuffer::getMemBuffer(Data.substr(sizeof(ArchiveMemberHeader), + getSize()), + name, + false); +} + +error_code Archive::Child::getAsBinary(OwningPtr<Binary> &Result) const { + OwningPtr<Binary> ret; + if (error_code ec = + createBinary(getBuffer(), ret)) + return ec; + Result.swap(ret); + return object_error::success; +} + +Archive::Archive(MemoryBuffer *source, error_code &ec) + : Binary(Binary::isArchive, source) + , StringTable(Child(this, StringRef(0, 0))) { + // Check for sufficient magic. + if (!source || source->getBufferSize() + < (8 + sizeof(ArchiveMemberHeader) + 2) // Smallest archive. + || StringRef(source->getBufferStart(), 8) != Magic) { + ec = object_error::invalid_file_type; + return; + } + + // Get the string table. It's the 3rd member. + child_iterator StrTable = begin_children(); + child_iterator e = end_children(); + for (int i = 0; StrTable != e && i < 2; ++StrTable, ++i) {} + + // Check to see if there were 3 members, or the 3rd member wasn't named "//". + StringRef name; + if (StrTable != e && !StrTable->getName(name) && name == "//") + StringTable = StrTable; + + ec = object_error::success; +} + +Archive::child_iterator Archive::begin_children() const { + const char *Loc = Data->getBufferStart() + Magic.size(); + size_t Size = sizeof(ArchiveMemberHeader) + + ToHeader(Loc)->getSize(); + return Child(this, StringRef(Loc, Size)); +} + +Archive::child_iterator Archive::end_children() const { + return Child(this, StringRef(0, 0)); +} + +namespace llvm { + +} // end namespace llvm |