diff options
Diffstat (limited to 'lib/Object/Archive.cpp')
-rw-r--r-- | lib/Object/Archive.cpp | 161 |
1 files changed, 134 insertions, 27 deletions
diff --git a/lib/Object/Archive.cpp b/lib/Object/Archive.cpp index 2a5951ada5..5b1e007e85 100644 --- a/lib/Object/Archive.cpp +++ b/lib/Object/Archive.cpp @@ -13,6 +13,7 @@ #include "llvm/Object/Archive.h" #include "llvm/ADT/APInt.h" +#include "llvm/Support/ErrorHandling.h" #include "llvm/Support/Endian.h" #include "llvm/Support/MemoryBuffer.h" @@ -122,7 +123,14 @@ error_code Archive::Child::getName(StringRef &Result) const { + sizeof(ArchiveMemberHeader) + Parent->StringTable->getSize())) return object_error::parse_failed; - Result = addr; + + // GNU long file names end with a /. + if (Parent->kind() == K_GNU) { + StringRef::size_type End = StringRef(addr).find('/'); + Result = StringRef(addr, End); + } else { + Result = addr; + } return object_error::success; } else if (name.startswith("#1/")) { APInt name_size; @@ -187,15 +195,52 @@ Archive::Archive(MemoryBuffer *source, error_code &ec) child_iterator i = begin_children(false); child_iterator e = end_children(); - if (i != e) ++i; // Nobody cares about the first member. - if (i != e) { - SymbolTable = i; - ++i; - } - if (i != e) { - StringTable = i; - } + StringRef name; + if ((ec = i->getName(name))) + return; + // Below is the pattern that is used to figure out the archive format + // GNU archive format + // First member : / (points to the symbol table ) + // Second member : // (may exist, if it exists, points to the string table) + // Note : The string table is used if the filename exceeds 15 characters + // BSD archive format + // First member : __.SYMDEF (points to the symbol table) + // There is no string table, if the filename exceeds 15 characters or has a + // embedded space, the filename has #1/<size>, The size represents the size + // of the filename that needs to be read after the archive header + // COFF archive format + // First member : / + // Second member : / (provides a directory of symbols) + // Third member : // contains the string table, this is present even if the + // string table is empty + if (name == "/") { + SymbolTable = i; + StringTable = e; + if (i != e) ++i; + if ((ec = i->getName(name))) + return; + if (name[0] != '/') { + Format = K_GNU; + } else if ((name.size() > 1) && (name == "//")) { + Format = K_GNU; + StringTable = i; + ++i; + } else { + Format = K_COFF; + if (i != e) { + SymbolTable = i; + ++i; + } + if (i != e) { + StringTable = i; + } + } + } else if (name == "__.SYMDEF") { + Format = K_BSD; + SymbolTable = i; + StringTable = e; + } ec = object_error::success; } @@ -221,20 +266,45 @@ error_code Archive::Symbol::getName(StringRef &Result) const { } error_code Archive::Symbol::getMember(child_iterator &Result) const { - const char *buf = Parent->SymbolTable->getBuffer()->getBufferStart(); - uint32_t member_count = *reinterpret_cast<const support::ulittle32_t*>(buf); - const char *offsets = buf + 4; - buf += 4 + (member_count * 4); // Skip offsets. - const char *indicies = buf + 4; + const char *Buf = Parent->SymbolTable->getBuffer()->getBufferStart(); + const char *Offsets = Buf + 4; + uint32_t Offset = 0; + if (Parent->kind() == K_GNU) { + Offset = *(reinterpret_cast<const support::ubig32_t*>(Offsets) + + SymbolIndex); + } else if (Parent->kind() == K_BSD) { + llvm_unreachable("BSD format is not supported"); + } else { + uint32_t MemberCount = *reinterpret_cast<const support::ulittle32_t*>(Buf); + + // Skip offsets. + Buf += sizeof(support::ulittle32_t) + + (MemberCount * sizeof(support::ulittle32_t)); + + uint32_t SymbolCount = *reinterpret_cast<const support::ulittle32_t*>(Buf); + + if (SymbolIndex >= SymbolCount) + return object_error::parse_failed; - uint16_t offsetindex = - *(reinterpret_cast<const support::ulittle16_t*>(indicies) - + SymbolIndex); + // Skip SymbolCount to get to the indices table. + const char *Indices = Buf + sizeof(support::ulittle32_t); - uint32_t offset = *(reinterpret_cast<const support::ulittle32_t*>(offsets) - + (offsetindex - 1)); + // Get the index of the offset in the file member offset table for this + // symbol. + uint16_t OffsetIndex = + *(reinterpret_cast<const support::ulittle16_t*>(Indices) + + SymbolIndex); + // Subtract 1 since OffsetIndex is 1 based. + --OffsetIndex; - const char *Loc = Parent->getData().begin() + offset; + if (OffsetIndex >= MemberCount) + return object_error::parse_failed; + + Offset = *(reinterpret_cast<const support::ulittle32_t*>(Offsets) + + OffsetIndex); + } + + const char *Loc = Parent->getData().begin() + Offset; size_t Size = sizeof(ArchiveMemberHeader) + ToHeader(Loc)->getSize(); Result = Child(Parent, StringRef(Loc, Size)); @@ -253,10 +323,20 @@ Archive::Symbol Archive::Symbol::getNext() const { Archive::symbol_iterator Archive::begin_symbols() const { const char *buf = SymbolTable->getBuffer()->getBufferStart(); - uint32_t member_count = *reinterpret_cast<const support::ulittle32_t*>(buf); - buf += 4 + (member_count * 4); // Skip offsets. - uint32_t symbol_count = *reinterpret_cast<const support::ulittle32_t*>(buf); - buf += 4 + (symbol_count * 2); // Skip indices. + if (kind() == K_GNU) { + uint32_t symbol_count = 0; + symbol_count = *reinterpret_cast<const support::ubig32_t*>(buf); + buf += sizeof(uint32_t) + (symbol_count * (sizeof(uint32_t))); + } else if (kind() == K_BSD) { + llvm_unreachable("BSD archive format is not supported"); + } else { + uint32_t member_count = 0; + uint32_t symbol_count = 0; + member_count = *reinterpret_cast<const support::ulittle32_t*>(buf); + buf += 4 + (member_count * 4); // Skip offsets. + symbol_count = *reinterpret_cast<const support::ulittle32_t*>(buf); + buf += 4 + (symbol_count * 2); // Skip indices. + } uint32_t string_start_offset = buf - SymbolTable->getBuffer()->getBufferStart(); return symbol_iterator(Symbol(this, 0, string_start_offset)); @@ -264,9 +344,36 @@ Archive::symbol_iterator Archive::begin_symbols() const { Archive::symbol_iterator Archive::end_symbols() const { const char *buf = SymbolTable->getBuffer()->getBufferStart(); - uint32_t member_count = *reinterpret_cast<const support::ulittle32_t*>(buf); - buf += 4 + (member_count * 4); // Skip offsets. - uint32_t symbol_count = *reinterpret_cast<const support::ulittle32_t*>(buf); + uint32_t symbol_count = 0; + if (kind() == K_GNU) { + symbol_count = *reinterpret_cast<const support::ubig32_t*>(buf); + buf += sizeof(uint32_t) + (symbol_count * (sizeof(uint32_t))); + } else if (kind() == K_BSD) { + llvm_unreachable("BSD archive format is not supported"); + } else { + uint32_t member_count = 0; + member_count = *reinterpret_cast<const support::ulittle32_t*>(buf); + buf += 4 + (member_count * 4); // Skip offsets. + symbol_count = *reinterpret_cast<const support::ulittle32_t*>(buf); + } return symbol_iterator( Symbol(this, symbol_count, 0)); } + +Archive::child_iterator Archive::findSym(StringRef name) const { + Archive::symbol_iterator bs = begin_symbols(); + Archive::symbol_iterator es = end_symbols(); + Archive::child_iterator result; + + StringRef symname; + for (; bs != es; ++bs) { + if (bs->getName(symname)) + return end_children(); + if (symname == name) { + if (bs->getMember(result)) + return end_children(); + return result; + } + } + return end_children(); +} |