Mercurial > hg > CbC > CbC_llvm
diff lib/Object/Archive.cpp @ 120:1172e4bd9c6f
update 4.0.0
author | mir3636 |
---|---|
date | Fri, 25 Nov 2016 19:14:25 +0900 |
parents | 7d135dc70f03 |
children | 803732b1fca8 |
line wrap: on
line diff
--- a/lib/Object/Archive.cpp Tue Jan 26 22:56:36 2016 +0900 +++ b/lib/Object/Archive.cpp Fri Nov 25 19:14:25 2016 +0900 @@ -12,7 +12,6 @@ //===----------------------------------------------------------------------===// #include "llvm/Object/Archive.h" -#include "llvm/ADT/APInt.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/Twine.h" #include "llvm/Support/Endian.h" @@ -28,135 +27,413 @@ void Archive::anchor() { } -StringRef ArchiveMemberHeader::getName() const { +static Error +malformedError(Twine Msg) { + std::string StringMsg = "truncated or malformed archive (" + Msg.str() + ")"; + return make_error<GenericBinaryError>(std::move(StringMsg), + object_error::parse_failed); +} + +ArchiveMemberHeader::ArchiveMemberHeader(const Archive *Parent, + const char *RawHeaderPtr, + uint64_t Size, Error *Err) + : Parent(Parent), + ArMemHdr(reinterpret_cast<const ArMemHdrType *>(RawHeaderPtr)) { + if (RawHeaderPtr == nullptr) + return; + ErrorAsOutParameter ErrAsOutParam(Err); + + if (Size < sizeof(ArMemHdrType)) { + if (Err) { + std::string Msg("remaining size of archive too small for next archive " + "member header "); + Expected<StringRef> NameOrErr = getName(Size); + if (!NameOrErr) { + consumeError(NameOrErr.takeError()); + uint64_t Offset = RawHeaderPtr - Parent->getData().data(); + *Err = malformedError(Msg + "at offset " + Twine(Offset)); + } else + *Err = malformedError(Msg + "for " + NameOrErr.get()); + } + return; + } + if (ArMemHdr->Terminator[0] != '`' || ArMemHdr->Terminator[1] != '\n') { + if (Err) { + std::string Buf; + raw_string_ostream OS(Buf); + OS.write_escaped(llvm::StringRef(ArMemHdr->Terminator, + sizeof(ArMemHdr->Terminator))); + OS.flush(); + std::string Msg("terminator characters in archive member \"" + Buf + + "\" not the correct \"`\\n\" values for the archive " + "member header "); + Expected<StringRef> NameOrErr = getName(Size); + if (!NameOrErr) { + consumeError(NameOrErr.takeError()); + uint64_t Offset = RawHeaderPtr - Parent->getData().data(); + *Err = malformedError(Msg + "at offset " + Twine(Offset)); + } else + *Err = malformedError(Msg + "for " + NameOrErr.get()); + } + return; + } +} + +// This gets the raw name from the ArMemHdr->Name field and checks that it is +// valid for the kind of archive. If it is not valid it returns an Error. +Expected<StringRef> ArchiveMemberHeader::getRawName() const { char EndCond; - if (Name[0] == '/' || Name[0] == '#') + auto Kind = Parent->kind(); + if (Kind == Archive::K_BSD || Kind == Archive::K_DARWIN64) { + if (ArMemHdr->Name[0] == ' ') { + uint64_t Offset = reinterpret_cast<const char *>(ArMemHdr) - + Parent->getData().data(); + return malformedError("name contains a leading space for archive member " + "header at offset " + Twine(Offset)); + } + EndCond = ' '; + } + else if (ArMemHdr->Name[0] == '/' || ArMemHdr->Name[0] == '#') EndCond = ' '; else EndCond = '/'; llvm::StringRef::size_type end = - llvm::StringRef(Name, sizeof(Name)).find(EndCond); + llvm::StringRef(ArMemHdr->Name, sizeof(ArMemHdr->Name)).find(EndCond); if (end == llvm::StringRef::npos) - end = sizeof(Name); - assert(end <= sizeof(Name) && end > 0); + end = sizeof(ArMemHdr->Name); + assert(end <= sizeof(ArMemHdr->Name) && end > 0); // Don't include the EndCond if there is one. - return llvm::StringRef(Name, end); + return llvm::StringRef(ArMemHdr->Name, end); } -ErrorOr<uint32_t> ArchiveMemberHeader::getSize() const { +// This gets the name looking up long names. Size is the size of the archive +// member including the header, so the size of any name following the header +// is checked to make sure it does not overflow. +Expected<StringRef> ArchiveMemberHeader::getName(uint64_t Size) const { + + // This can be called from the ArchiveMemberHeader constructor when the + // archive header is truncated to produce an error message with the name. + // Make sure the name field is not truncated. + if (Size < offsetof(ArMemHdrType, Name) + sizeof(ArMemHdr->Name)) { + uint64_t ArchiveOffset = reinterpret_cast<const char *>(ArMemHdr) - + Parent->getData().data(); + return malformedError("archive header truncated before the name field " + "for archive member header at offset " + + Twine(ArchiveOffset)); + } + + // The raw name itself can be invalid. + Expected<StringRef> NameOrErr = getRawName(); + if (!NameOrErr) + return NameOrErr.takeError(); + StringRef Name = NameOrErr.get(); + + // Check if it's a special name. + if (Name[0] == '/') { + if (Name.size() == 1) // Linker member. + return Name; + if (Name.size() == 2 && Name[1] == '/') // String table. + return Name; + // It's a long name. + // Get the string table offset. + std::size_t StringOffset; + if (Name.substr(1).rtrim(' ').getAsInteger(10, StringOffset)) { + std::string Buf; + raw_string_ostream OS(Buf); + OS.write_escaped(Name.substr(1).rtrim(' ')); + OS.flush(); + uint64_t ArchiveOffset = reinterpret_cast<const char *>(ArMemHdr) - + Parent->getData().data(); + return malformedError("long name offset characters after the '/' are " + "not all decimal numbers: '" + Buf + "' for " + "archive member header at offset " + + Twine(ArchiveOffset)); + } + + // Verify it. + if (StringOffset >= Parent->getStringTable().size()) { + uint64_t ArchiveOffset = reinterpret_cast<const char *>(ArMemHdr) - + Parent->getData().data(); + return malformedError("long name offset " + Twine(StringOffset) + " past " + "the end of the string table for archive member " + "header at offset " + Twine(ArchiveOffset)); + } + const char *addr = Parent->getStringTable().begin() + StringOffset; + + // GNU long file names end with a "/\n". + if (Parent->kind() == Archive::K_GNU || + Parent->kind() == Archive::K_MIPS64) { + StringRef::size_type End = StringRef(addr).find('\n'); + return StringRef(addr, End - 1); + } + return addr; + } + + if (Name.startswith("#1/")) { + uint64_t NameLength; + if (Name.substr(3).rtrim(' ').getAsInteger(10, NameLength)) { + std::string Buf; + raw_string_ostream OS(Buf); + OS.write_escaped(Name.substr(3).rtrim(' ')); + OS.flush(); + uint64_t ArchiveOffset = reinterpret_cast<const char *>(ArMemHdr) - + Parent->getData().data(); + return malformedError("long name length characters after the #1/ are " + "not all decimal numbers: '" + Buf + "' for " + "archive member header at offset " + + Twine(ArchiveOffset)); + } + if (getSizeOf() + NameLength > Size) { + uint64_t ArchiveOffset = reinterpret_cast<const char *>(ArMemHdr) - + Parent->getData().data(); + return malformedError("long name length: " + Twine(NameLength) + + " extends past the end of the member or archive " + "for archive member header at offset " + + Twine(ArchiveOffset)); + } + return StringRef(reinterpret_cast<const char *>(ArMemHdr) + getSizeOf(), + NameLength).rtrim('\0'); + } + + // It is not a long name so trim the blanks at the end of the name. + if (Name[Name.size() - 1] != '/') + return Name.rtrim(' '); + + // It's a simple name. + return Name.drop_back(1); +} + +Expected<uint32_t> ArchiveMemberHeader::getSize() const { uint32_t Ret; - if (llvm::StringRef(Size, sizeof(Size)).rtrim(" ").getAsInteger(10, Ret)) - return object_error::parse_failed; // Size is not a decimal number. + if (llvm::StringRef(ArMemHdr->Size, + sizeof(ArMemHdr->Size)).rtrim(" ").getAsInteger(10, Ret)) { + std::string Buf; + raw_string_ostream OS(Buf); + OS.write_escaped(llvm::StringRef(ArMemHdr->Size, + sizeof(ArMemHdr->Size)).rtrim(" ")); + OS.flush(); + uint64_t Offset = reinterpret_cast<const char *>(ArMemHdr) - + Parent->getData().data(); + return malformedError("characters in size field in archive header are not " + "all decimal numbers: '" + Buf + "' for archive " + "member header at offset " + Twine(Offset)); + } return Ret; } -sys::fs::perms ArchiveMemberHeader::getAccessMode() const { +Expected<sys::fs::perms> ArchiveMemberHeader::getAccessMode() const { unsigned Ret; - if (StringRef(AccessMode, sizeof(AccessMode)).rtrim(" ").getAsInteger(8, Ret)) - llvm_unreachable("Access mode is not an octal number."); + if (StringRef(ArMemHdr->AccessMode, + sizeof(ArMemHdr->AccessMode)).rtrim(' ').getAsInteger(8, Ret)) { + std::string Buf; + raw_string_ostream OS(Buf); + OS.write_escaped(llvm::StringRef(ArMemHdr->AccessMode, + sizeof(ArMemHdr->AccessMode)).rtrim(" ")); + OS.flush(); + uint64_t Offset = reinterpret_cast<const char *>(ArMemHdr) - + Parent->getData().data(); + return malformedError("characters in AccessMode field in archive header " + "are not all decimal numbers: '" + Buf + "' for the " + "archive member header at offset " + Twine(Offset)); + } return static_cast<sys::fs::perms>(Ret); } -sys::TimeValue ArchiveMemberHeader::getLastModified() const { +Expected<sys::TimePoint<std::chrono::seconds>> +ArchiveMemberHeader::getLastModified() const { unsigned Seconds; - if (StringRef(LastModified, sizeof(LastModified)).rtrim(" ") - .getAsInteger(10, Seconds)) - llvm_unreachable("Last modified time not a decimal number."); + if (StringRef(ArMemHdr->LastModified, + sizeof(ArMemHdr->LastModified)).rtrim(' ') + .getAsInteger(10, Seconds)) { + std::string Buf; + raw_string_ostream OS(Buf); + OS.write_escaped(llvm::StringRef(ArMemHdr->LastModified, + sizeof(ArMemHdr->LastModified)).rtrim(" ")); + OS.flush(); + uint64_t Offset = reinterpret_cast<const char *>(ArMemHdr) - + Parent->getData().data(); + return malformedError("characters in LastModified field in archive header " + "are not all decimal numbers: '" + Buf + "' for the " + "archive member header at offset " + Twine(Offset)); + } - sys::TimeValue Ret; - Ret.fromEpochTime(Seconds); + return sys::toTimePoint(Seconds); +} + +Expected<unsigned> ArchiveMemberHeader::getUID() const { + unsigned Ret; + StringRef User = StringRef(ArMemHdr->UID, sizeof(ArMemHdr->UID)).rtrim(' '); + if (User.empty()) + return 0; + if (User.getAsInteger(10, Ret)) { + std::string Buf; + raw_string_ostream OS(Buf); + OS.write_escaped(User); + OS.flush(); + uint64_t Offset = reinterpret_cast<const char *>(ArMemHdr) - + Parent->getData().data(); + return malformedError("characters in UID field in archive header " + "are not all decimal numbers: '" + Buf + "' for the " + "archive member header at offset " + Twine(Offset)); + } return Ret; } -unsigned ArchiveMemberHeader::getUID() const { +Expected<unsigned> ArchiveMemberHeader::getGID() const { unsigned Ret; - if (StringRef(UID, sizeof(UID)).rtrim(" ").getAsInteger(10, Ret)) - llvm_unreachable("UID time not a decimal number."); - return Ret; -} - -unsigned ArchiveMemberHeader::getGID() const { - unsigned Ret; - if (StringRef(GID, sizeof(GID)).rtrim(" ").getAsInteger(10, Ret)) - llvm_unreachable("GID time not a decimal number."); + StringRef Group = StringRef(ArMemHdr->GID, sizeof(ArMemHdr->GID)).rtrim(' '); + if (Group.empty()) + return 0; + if (Group.getAsInteger(10, Ret)) { + std::string Buf; + raw_string_ostream OS(Buf); + OS.write_escaped(Group); + OS.flush(); + uint64_t Offset = reinterpret_cast<const char *>(ArMemHdr) - + Parent->getData().data(); + return malformedError("characters in GID field in archive header " + "are not all decimal numbers: '" + Buf + "' for the " + "archive member header at offset " + Twine(Offset)); + } return Ret; } Archive::Child::Child(const Archive *Parent, StringRef Data, uint16_t StartOfFile) - : Parent(Parent), Data(Data), StartOfFile(StartOfFile) {} + : Parent(Parent), Header(Parent, Data.data(), Data.size(), nullptr), + Data(Data), StartOfFile(StartOfFile) { +} -Archive::Child::Child(const Archive *Parent, const char *Start, - std::error_code *EC) - : Parent(Parent) { +Archive::Child::Child(const Archive *Parent, const char *Start, Error *Err) + : Parent(Parent), + Header(Parent, Start, + Parent + ? Parent->getData().size() - (Start - Parent->getData().data()) + : 0, Err) { if (!Start) return; - uint64_t Size = sizeof(ArchiveMemberHeader); + // If we are pointed to real data, Start is not a nullptr, then there must be + // a non-null Err pointer available to report malformed data on. Only in + // the case sentinel value is being constructed is Err is permitted to be a + // nullptr. + assert(Err && "Err can't be nullptr if Start is not a nullptr"); + + ErrorAsOutParameter ErrAsOutParam(Err); + + // If there was an error in the construction of the Header + // then just return with the error now set. + if (*Err) + return; + + uint64_t Size = Header.getSizeOf(); Data = StringRef(Start, Size); - if (!isThinMember()) { - ErrorOr<uint64_t> MemberSize = getRawSize(); - if ((*EC = MemberSize.getError())) + Expected<bool> isThinOrErr = isThinMember(); + if (!isThinOrErr) { + *Err = isThinOrErr.takeError(); + return; + } + bool isThin = isThinOrErr.get(); + if (!isThin) { + Expected<uint64_t> MemberSize = getRawSize(); + if (!MemberSize) { + *Err = MemberSize.takeError(); return; + } Size += MemberSize.get(); Data = StringRef(Start, Size); } // Setup StartOfFile and PaddingBytes. - StartOfFile = sizeof(ArchiveMemberHeader); + StartOfFile = Header.getSizeOf(); // Don't include attached name. - StringRef Name = getRawName(); + Expected<StringRef> NameOrErr = getRawName(); + if (!NameOrErr){ + *Err = NameOrErr.takeError(); + return; + } + StringRef Name = NameOrErr.get(); if (Name.startswith("#1/")) { uint64_t NameSize; - if (Name.substr(3).rtrim(" ").getAsInteger(10, NameSize)) - llvm_unreachable("Long name length is not an integer"); + if (Name.substr(3).rtrim(' ').getAsInteger(10, NameSize)) { + std::string Buf; + raw_string_ostream OS(Buf); + OS.write_escaped(Name.substr(3).rtrim(' ')); + OS.flush(); + uint64_t Offset = Start - Parent->getData().data(); + *Err = malformedError("long name length characters after the #1/ are " + "not all decimal numbers: '" + Buf + "' for " + "archive member header at offset " + + Twine(Offset)); + return; + } StartOfFile += NameSize; } } -ErrorOr<uint64_t> Archive::Child::getSize() const { +Expected<uint64_t> Archive::Child::getSize() const { if (Parent->IsThin) { - ErrorOr<uint32_t> Size = getHeader()->getSize(); - if (std::error_code EC = Size.getError()) - return EC; + Expected<uint32_t> Size = Header.getSize(); + if (!Size) + return Size.takeError(); return Size.get(); } return Data.size() - StartOfFile; } -ErrorOr<uint64_t> Archive::Child::getRawSize() const { - ErrorOr<uint32_t> Size = getHeader()->getSize(); - if (std::error_code EC = Size.getError()) - return EC; - return Size.get(); +Expected<uint64_t> Archive::Child::getRawSize() const { + return Header.getSize(); } -bool Archive::Child::isThinMember() const { - StringRef Name = getHeader()->getName(); +Expected<bool> Archive::Child::isThinMember() const { + Expected<StringRef> NameOrErr = Header.getRawName(); + if (!NameOrErr) + return NameOrErr.takeError(); + StringRef Name = NameOrErr.get(); return Parent->IsThin && Name != "/" && Name != "//"; } -ErrorOr<StringRef> Archive::Child::getBuffer() const { - if (!isThinMember()) { - ErrorOr<uint32_t> Size = getSize(); - if (std::error_code EC = Size.getError()) - return EC; +Expected<std::string> Archive::Child::getFullName() const { + Expected<bool> isThin = isThinMember(); + if (!isThin) + return isThin.takeError(); + assert(isThin.get()); + Expected<StringRef> NameOrErr = getName(); + if (!NameOrErr) + return NameOrErr.takeError(); + StringRef Name = *NameOrErr; + if (sys::path::is_absolute(Name)) + return Name; + + SmallString<128> FullName = sys::path::parent_path( + Parent->getMemoryBufferRef().getBufferIdentifier()); + sys::path::append(FullName, Name); + return StringRef(FullName); +} + +Expected<StringRef> Archive::Child::getBuffer() const { + Expected<bool> isThinOrErr = isThinMember(); + if (!isThinOrErr) + return isThinOrErr.takeError(); + bool isThin = isThinOrErr.get(); + if (!isThin) { + Expected<uint32_t> Size = getSize(); + if (!Size) + return Size.takeError(); return StringRef(Data.data() + StartOfFile, Size.get()); } - ErrorOr<StringRef> Name = getName(); - if (std::error_code EC = Name.getError()) - return EC; - SmallString<128> FullName = sys::path::parent_path( - Parent->getMemoryBufferRef().getBufferIdentifier()); - sys::path::append(FullName, *Name); + Expected<std::string> FullNameOrErr = getFullName(); + if (!FullNameOrErr) + return FullNameOrErr.takeError(); + const std::string &FullName = *FullNameOrErr; ErrorOr<std::unique_ptr<MemoryBuffer>> Buf = MemoryBuffer::getFile(FullName); if (std::error_code EC = Buf.getError()) - return EC; + return errorCodeToError(EC); Parent->ThinBuffers.push_back(std::move(*Buf)); return Parent->ThinBuffers.back()->getBuffer(); } -ErrorOr<Archive::Child> Archive::Child::getNext() const { +Expected<Archive::Child> Archive::Child::getNext() const { size_t SpaceToSkip = Data.size(); // If it's odd, add 1 to make it even. if (SpaceToSkip & 1) @@ -166,16 +443,25 @@ // Check to see if this is at the end of the archive. if (NextLoc == Parent->Data.getBufferEnd()) - return Child(Parent, nullptr, nullptr); + return Child(nullptr, nullptr, nullptr); // Check to see if this is past the end of the archive. - if (NextLoc > Parent->Data.getBufferEnd()) - return object_error::parse_failed; + if (NextLoc > Parent->Data.getBufferEnd()) { + std::string Msg("offset to next archive member past the end of the archive " + "after member "); + Expected<StringRef> NameOrErr = getName(); + if (!NameOrErr) { + consumeError(NameOrErr.takeError()); + uint64_t Offset = Data.data() - Parent->getData().data(); + return malformedError(Msg + "at offset " + Twine(Offset)); + } else + return malformedError(Msg + NameOrErr.get()); + } - std::error_code EC; - Child Ret(Parent, NextLoc, &EC); - if (EC) - return EC; + Error Err = Error::success(); + Child Ret(Parent, NextLoc, &Err); + if (Err) + return std::move(Err); return Ret; } @@ -186,69 +472,46 @@ return offset; } -ErrorOr<StringRef> Archive::Child::getName() const { - StringRef name = getRawName(); - // Check if it's a special name. - if (name[0] == '/') { - if (name.size() == 1) // Linker member. - return name; - if (name.size() == 2 && name[1] == '/') // String table. - return name; - // It's a long name. - // Get the offset. - std::size_t offset; - if (name.substr(1).rtrim(" ").getAsInteger(10, offset)) - llvm_unreachable("Long name offset is not an integer"); - - // Verify it. - if (offset >= Parent->StringTable.size()) - return object_error::parse_failed; - const char *addr = Parent->StringTable.begin() + offset; - - // GNU long file names end with a "/\n". - if (Parent->kind() == K_GNU || Parent->kind() == K_MIPS64) { - StringRef::size_type End = StringRef(addr).find('\n'); - return StringRef(addr, End - 1); - } - return StringRef(addr); - } else if (name.startswith("#1/")) { - uint64_t name_size; - if (name.substr(3).rtrim(" ").getAsInteger(10, name_size)) - llvm_unreachable("Long name length is not an ingeter"); - return Data.substr(sizeof(ArchiveMemberHeader), name_size) - .rtrim(StringRef("\0", 1)); - } - // It's a simple name. - if (name[name.size() - 1] == '/') - return name.substr(0, name.size() - 1); - return name; +Expected<StringRef> Archive::Child::getName() const { + Expected<uint64_t> RawSizeOrErr = getRawSize(); + if (!RawSizeOrErr) + return RawSizeOrErr.takeError(); + uint64_t RawSize = RawSizeOrErr.get(); + Expected<StringRef> NameOrErr = Header.getName(Header.getSizeOf() + RawSize); + if (!NameOrErr) + return NameOrErr.takeError(); + StringRef Name = NameOrErr.get(); + return Name; } -ErrorOr<MemoryBufferRef> Archive::Child::getMemoryBufferRef() const { - ErrorOr<StringRef> NameOrErr = getName(); - if (std::error_code EC = NameOrErr.getError()) - return EC; +Expected<MemoryBufferRef> Archive::Child::getMemoryBufferRef() const { + Expected<StringRef> NameOrErr = getName(); + if (!NameOrErr) + return NameOrErr.takeError(); StringRef Name = NameOrErr.get(); - ErrorOr<StringRef> Buf = getBuffer(); - if (std::error_code EC = Buf.getError()) - return EC; + Expected<StringRef> Buf = getBuffer(); + if (!Buf) + return Buf.takeError(); return MemoryBufferRef(*Buf, Name); } -ErrorOr<std::unique_ptr<Binary>> +Expected<std::unique_ptr<Binary>> Archive::Child::getAsBinary(LLVMContext *Context) const { - ErrorOr<MemoryBufferRef> BuffOrErr = getMemoryBufferRef(); - if (std::error_code EC = BuffOrErr.getError()) - return EC; + Expected<MemoryBufferRef> BuffOrErr = getMemoryBufferRef(); + if (!BuffOrErr) + return BuffOrErr.takeError(); - return createBinary(BuffOrErr.get(), Context); + auto BinaryOrErr = createBinary(BuffOrErr.get(), Context); + if (BinaryOrErr) + return std::move(*BinaryOrErr); + return BinaryOrErr.takeError(); } -ErrorOr<std::unique_ptr<Archive>> Archive::create(MemoryBufferRef Source) { - std::error_code EC; - std::unique_ptr<Archive> Ret(new Archive(Source, EC)); - if (EC) - return EC; +Expected<std::unique_ptr<Archive>> Archive::create(MemoryBufferRef Source) { + Error Err = Error::success(); + std::unique_ptr<Archive> Ret(new Archive(Source, Err)); + if (Err) + return std::move(Err); return std::move(Ret); } @@ -257,8 +520,9 @@ FirstRegularStartOfFile = C.StartOfFile; } -Archive::Archive(MemoryBufferRef Source, std::error_code &ec) +Archive::Archive(MemoryBufferRef Source, Error &Err) : Binary(Binary::ID_Archive, Source) { + ErrorAsOutParameter ErrAsOutParam(&Err); StringRef Buffer = Data.getBuffer(); // Check for sufficient magic. if (Buffer.startswith(ThinMagic)) { @@ -266,31 +530,45 @@ } else if (Buffer.startswith(Magic)) { IsThin = false; } else { - ec = object_error::invalid_file_type; + Err = make_error<GenericBinaryError>("File too small to be an archive", + object_error::invalid_file_type); return; } + // Make sure Format is initialized before any call to + // ArchiveMemberHeader::getName() is made. This could be a valid empty + // archive which is the same in all formats. So claiming it to be gnu to is + // fine if not totally correct before we look for a string table or table of + // contents. + Format = K_GNU; + // Get the special members. - child_iterator I = child_begin(false); - if ((ec = I->getError())) + child_iterator I = child_begin(Err, false); + if (Err) return; child_iterator E = child_end(); + // See if this is a valid empty archive and if so return. if (I == E) { - ec = std::error_code(); + Err = Error::success(); return; } - const Child *C = &**I; + const Child *C = &*I; auto Increment = [&]() { ++I; - if ((ec = I->getError())) + if (Err) return true; - C = &**I; + C = &*I; return false; }; - StringRef Name = C->getRawName(); + Expected<StringRef> NameOrErr = C->getRawName(); + if (!NameOrErr) { + Err = NameOrErr.takeError(); + return; + } + StringRef Name = NameOrErr.get(); // Below is the pattern that is used to figure out the archive format // GNU archive format @@ -311,31 +589,58 @@ // seem to create the third member if there's no member whose filename // exceeds 15 characters. So the third member is optional. - if (Name == "__.SYMDEF") { - Format = K_BSD; - // We know that the symbol table is not an external file, so we just assert - // there is no error. - SymbolTable = *C->getBuffer(); + if (Name == "__.SYMDEF" || Name == "__.SYMDEF_64") { + if (Name == "__.SYMDEF") + Format = K_BSD; + else // Name == "__.SYMDEF_64" + Format = K_DARWIN64; + // We know that the symbol table is not an external file, but we still must + // check any Expected<> return value. + Expected<StringRef> BufOrErr = C->getBuffer(); + if (!BufOrErr) { + Err = BufOrErr.takeError(); + return; + } + SymbolTable = BufOrErr.get(); if (Increment()) return; setFirstRegular(*C); - ec = std::error_code(); + Err = Error::success(); return; } if (Name.startswith("#1/")) { Format = K_BSD; // We know this is BSD, so getName will work since there is no string table. - ErrorOr<StringRef> NameOrErr = C->getName(); - ec = NameOrErr.getError(); - if (ec) + Expected<StringRef> NameOrErr = C->getName(); + if (!NameOrErr) { + Err = NameOrErr.takeError(); return; + } Name = NameOrErr.get(); if (Name == "__.SYMDEF SORTED" || Name == "__.SYMDEF") { - // We know that the symbol table is not an external file, so we just - // assert there is no error. - SymbolTable = *C->getBuffer(); + // We know that the symbol table is not an external file, but we still + // must check any Expected<> return value. + Expected<StringRef> BufOrErr = C->getBuffer(); + if (!BufOrErr) { + Err = BufOrErr.takeError(); + return; + } + SymbolTable = BufOrErr.get(); + if (Increment()) + return; + } + else if (Name == "__.SYMDEF_64 SORTED" || Name == "__.SYMDEF_64") { + Format = K_DARWIN64; + // We know that the symbol table is not an external file, but we still + // must check any Expected<> return value. + Expected<StringRef> BufOrErr = C->getBuffer(); + if (!BufOrErr) { + Err = BufOrErr.takeError(); + return; + } + SymbolTable = BufOrErr.get(); if (Increment()) return; } @@ -350,100 +655,132 @@ bool has64SymTable = false; if (Name == "/" || Name == "/SYM64/") { - // We know that the symbol table is not an external file, so we just assert - // there is no error. - SymbolTable = *C->getBuffer(); + // We know that the symbol table is not an external file, but we still + // must check any Expected<> return value. + Expected<StringRef> BufOrErr = C->getBuffer(); + if (!BufOrErr) { + Err = BufOrErr.takeError(); + return; + } + SymbolTable = BufOrErr.get(); if (Name == "/SYM64/") has64SymTable = true; if (Increment()) return; if (I == E) { - ec = std::error_code(); + Err = Error::success(); return; } - Name = C->getRawName(); + Expected<StringRef> NameOrErr = C->getRawName(); + if (!NameOrErr) { + Err = NameOrErr.takeError(); + return; + } + Name = NameOrErr.get(); } if (Name == "//") { Format = has64SymTable ? K_MIPS64 : K_GNU; - // The string table is never an external member, so we just assert on the - // ErrorOr. - StringTable = *C->getBuffer(); + // The string table is never an external member, but we still + // must check any Expected<> return value. + Expected<StringRef> BufOrErr = C->getBuffer(); + if (!BufOrErr) { + Err = BufOrErr.takeError(); + return; + } + StringTable = BufOrErr.get(); if (Increment()) return; setFirstRegular(*C); - ec = std::error_code(); + Err = Error::success(); return; } if (Name[0] != '/') { Format = has64SymTable ? K_MIPS64 : K_GNU; setFirstRegular(*C); - ec = std::error_code(); + Err = Error::success(); return; } if (Name != "/") { - ec = object_error::parse_failed; + Err = errorCodeToError(object_error::parse_failed); return; } Format = K_COFF; - // We know that the symbol table is not an external file, so we just assert - // there is no error. - SymbolTable = *C->getBuffer(); + // We know that the symbol table is not an external file, but we still + // must check any Expected<> return value. + Expected<StringRef> BufOrErr = C->getBuffer(); + if (!BufOrErr) { + Err = BufOrErr.takeError(); + return; + } + SymbolTable = BufOrErr.get(); if (Increment()) return; if (I == E) { setFirstRegular(*C); - ec = std::error_code(); + Err = Error::success(); return; } - Name = C->getRawName(); + NameOrErr = C->getRawName(); + if (!NameOrErr) { + Err = NameOrErr.takeError(); + return; + } + Name = NameOrErr.get(); if (Name == "//") { - // The string table is never an external member, so we just assert on the - // ErrorOr. - StringTable = *C->getBuffer(); + // The string table is never an external member, but we still + // must check any Expected<> return value. + Expected<StringRef> BufOrErr = C->getBuffer(); + if (!BufOrErr) { + Err = BufOrErr.takeError(); + return; + } + StringTable = BufOrErr.get(); if (Increment()) return; } setFirstRegular(*C); - ec = std::error_code(); + Err = Error::success(); } -Archive::child_iterator Archive::child_begin(bool SkipInternal) const { - if (Data.getBufferSize() == 8) // empty archive. +Archive::child_iterator Archive::child_begin(Error &Err, + bool SkipInternal) const { + if (isEmpty()) return child_end(); if (SkipInternal) - return Child(this, FirstRegularData, FirstRegularStartOfFile); + return child_iterator(Child(this, FirstRegularData, + FirstRegularStartOfFile), + &Err); const char *Loc = Data.getBufferStart() + strlen(Magic); - std::error_code EC; - Child c(this, Loc, &EC); - if (EC) - return child_iterator(EC); - return child_iterator(c); + Child C(this, Loc, &Err); + if (Err) + return child_end(); + return child_iterator(C, &Err); } Archive::child_iterator Archive::child_end() const { - return Child(this, nullptr, nullptr); + return child_iterator(Child(nullptr, nullptr, nullptr), nullptr); } StringRef Archive::Symbol::getName() const { return Parent->getSymbolTable().begin() + StringIndex; } -ErrorOr<Archive::Child> Archive::Symbol::getMember() const { +Expected<Archive::Child> Archive::Symbol::getMember() const { const char *Buf = Parent->getSymbolTable().begin(); const char *Offsets = Buf; - if (Parent->kind() == K_MIPS64) + if (Parent->kind() == K_MIPS64 || Parent->kind() == K_DARWIN64) Offsets += sizeof(uint64_t); else Offsets += sizeof(uint32_t); @@ -460,6 +797,14 @@ // the archive of the member that defines the symbol. Which is what // is needed here. Offset = read32le(Offsets + SymbolIndex * 8 + 4); + } else if (Parent->kind() == K_DARWIN64) { + // The SymbolIndex is an index into the ranlib_64 structs that start at + // Offsets (the first uint64_t is the number of bytes of the ranlib_64 + // structs). The ranlib_64 structs are a pair of uint64_t's the first + // being a string table offset and the second being the offset into + // the archive of the member that defines the symbol. Which is what + // is needed here. + Offset = read64le(Offsets + SymbolIndex * 16 + 8); } else { // Skip offsets. uint32_t MemberCount = read32le(Buf); @@ -467,7 +812,7 @@ uint32_t SymbolCount = read32le(Buf); if (SymbolIndex >= SymbolCount) - return object_error::parse_failed; + return errorCodeToError(object_error::parse_failed); // Skip SymbolCount to get to the indices table. const char *Indices = Buf + 4; @@ -479,16 +824,16 @@ --OffsetIndex; if (OffsetIndex >= MemberCount) - return object_error::parse_failed; + return errorCodeToError(object_error::parse_failed); Offset = read32le(Offsets + OffsetIndex * 4); } const char *Loc = Parent->getData().begin() + Offset; - std::error_code EC; - Child C(Parent, Loc, &EC); - if (EC) - return EC; + Error Err = Error::success(); + Child C(Parent, Loc, &Err); + if (Err) + return std::move(Err); return C; } @@ -559,6 +904,22 @@ // Skip the byte count of the string table. buf += sizeof(uint32_t); buf += ran_strx; + } else if (kind() == K_DARWIN64) { + // The __.SYMDEF_64 or "__.SYMDEF_64 SORTED" member starts with a uint64_t + // which is the number of bytes of ranlib_64 structs that follow. The + // ranlib_64 structs are a pair of uint64_t's the first being a string + // table offset and the second being the offset into the archive of the + // member that define the symbol. After that the next uint64_t is the byte + // count of the string table followed by the string table. + uint64_t ranlib_count = 0; + ranlib_count = read64le(buf) / 16; + const char *ranlibs = buf + 8; + uint64_t ran_strx = 0; + ran_strx = read64le(ranlibs); + buf += sizeof(uint64_t) + (ranlib_count * (2 * (sizeof(uint64_t)))); + // Skip the byte count of the string table. + buf += sizeof(uint64_t); + buf += ran_strx; } else { uint32_t member_count = 0; uint32_t symbol_count = 0; @@ -585,27 +946,31 @@ return read64be(buf); if (kind() == K_BSD) return read32le(buf) / 8; + if (kind() == K_DARWIN64) + return read64le(buf) / 16; uint32_t member_count = 0; member_count = read32le(buf); buf += 4 + (member_count * 4); // Skip offsets. return read32le(buf); } -Archive::child_iterator Archive::findSym(StringRef name) const { +Expected<Optional<Archive::Child>> Archive::findSym(StringRef name) const { Archive::symbol_iterator bs = symbol_begin(); Archive::symbol_iterator es = symbol_end(); for (; bs != es; ++bs) { StringRef SymName = bs->getName(); if (SymName == name) { - ErrorOr<Archive::child_iterator> ResultOrErr = bs->getMember(); - // FIXME: Should we really eat the error? - if (ResultOrErr.getError()) - return child_end(); - return ResultOrErr.get(); + if (auto MemberOrErr = bs->getMember()) + return Child(*MemberOrErr); + else + return MemberOrErr.takeError(); } } - return child_end(); + return Optional<Child>(); } +// Returns true if archive file contains no member file. +bool Archive::isEmpty() const { return Data.getBufferSize() == 8; } + bool Archive::hasSymbolTable() const { return !SymbolTable.empty(); }