Index: include/llvm/Object/Archive.h =================================================================== --- include/llvm/Object/Archive.h +++ include/llvm/Object/Archive.h @@ -49,7 +49,7 @@ Expected getName(uint64_t Size) const; /// Members are not larger than 4GB. - Expected getSize() const; + Expected getSize() const; Expected getAccessMode() const; Expected> getLastModified() const; Index: lib/Object/Archive.cpp =================================================================== --- lib/Object/Archive.cpp +++ lib/Object/Archive.cpp @@ -220,8 +220,8 @@ return Name.drop_back(1); } -Expected ArchiveMemberHeader::getSize() const { - uint32_t Ret; +Expected ArchiveMemberHeader::getSize() const { + uint64_t Ret; if (StringRef(ArMemHdr->Size, sizeof(ArMemHdr->Size)).rtrim(" ").getAsInteger(10, Ret)) { std::string Buf; @@ -338,7 +338,7 @@ ErrorAsOutParameter ErrAsOutParam(Err); - // If there was an error in the construction of the Header + // If there was an error in the construction of the Header // then just return with the error now set. if (*Err) return; @@ -434,7 +434,7 @@ return isThinOrErr.takeError(); bool isThin = isThinOrErr.get(); if (!isThin) { - Expected Size = getSize(); + Expected Size = getSize(); if (!Size) return Size.takeError(); return StringRef(Data.data() + StartOfFile, Size.get()); Index: lib/Object/ArchiveWriter.cpp =================================================================== --- lib/Object/ArchiveWriter.cpp +++ lib/Object/ArchiveWriter.cpp @@ -115,7 +115,7 @@ bool MayTruncate = false) { uint64_t OldPos = OS.tell(); OS << Data; - unsigned SizeSoFar = OS.tell() - OldPos; + uint64_t SizeSoFar = OS.tell() - OldPos; if (Size > SizeSoFar) { OS.indent(Size - SizeSoFar); } else if (Size < SizeSoFar) { @@ -149,9 +149,25 @@ support::endian::Writer(Out).write(Val); } +static void print64(raw_ostream &Out, object::Archive::Kind Kind, + uint64_t Val) { + if (isBSDLike(Kind)) + support::endian::Writer(Out).write(Val); + else + support::endian::Writer(Out).write(Val); +} + +static void printNBits(raw_ostream &Out, object::Archive::Kind Kind, + uint64_t Val, bool Is64Bit) { + if (Is64Bit) + print64(Out, Kind, Val); + else + print32(Out, Kind, Val); +} + static void printRestOfMemberHeader( raw_fd_ostream &Out, const sys::TimePoint &ModTime, - unsigned UID, unsigned GID, unsigned Perms, unsigned Size) { + unsigned UID, unsigned GID, unsigned Perms, uint64_t Size) { printWithSpacePadding(Out, sys::toTimeT(ModTime), 12); printWithSpacePadding(Out, UID, 6, true); printWithSpacePadding(Out, GID, 6, true); @@ -164,7 +180,7 @@ printGNUSmallMemberHeader(raw_fd_ostream &Out, StringRef Name, const sys::TimePoint &ModTime, unsigned UID, unsigned GID, unsigned Perms, - unsigned Size) { + uint64_t Size) { printWithSpacePadding(Out, Twine(Name) + "/", 16); printRestOfMemberHeader(Out, ModTime, UID, GID, Perms, Size); } @@ -173,7 +189,7 @@ printBSDMemberHeader(raw_fd_ostream &Out, StringRef Name, const sys::TimePoint &ModTime, unsigned UID, unsigned GID, unsigned Perms, - unsigned Size) { + uint64_t Size) { uint64_t PosAfterHeader = Out.tell() + 60 + Name.size(); // Pad so that even 64 bit object files are aligned. unsigned Pad = OffsetToAlignment(PosAfterHeader, 8); @@ -194,9 +210,9 @@ static void printMemberHeader(raw_fd_ostream &Out, object::Archive::Kind Kind, bool Thin, StringRef Name, - std::vector::iterator &StringMapIndexIter, + std::vector::iterator &StringMapIndexIter, const sys::TimePoint &ModTime, - unsigned UID, unsigned GID, unsigned Perms, unsigned Size) { + unsigned UID, unsigned GID, unsigned Perms, uint64_t Size) { if (isBSDLike(Kind)) return printBSDMemberHeader(Out, Name, ModTime, UID, GID, Perms, Size); if (!useStringTable(Thin, Name)) @@ -237,9 +253,9 @@ static void writeStringTable(raw_fd_ostream &Out, StringRef ArcName, ArrayRef Members, - std::vector &StringMapIndexes, + std::vector &StringMapIndexes, bool Thin) { - unsigned StartOffset = 0; + uint64_t StartOffset = 0; for (const NewArchiveMember &M : Members) { StringRef Path = M.Buf->getBufferIdentifier(); StringRef Name = M.MemberName; @@ -266,7 +282,7 @@ return; if (Out.tell() % 2) Out << '\n'; - int Pos = Out.tell(); + uint64_t Pos = Out.tell(); Out.seek(StartOffset - 12); printWithSpacePadding(Out, Pos - StartOffset, 10); Out.seek(Pos); @@ -280,13 +296,26 @@ return sys::TimePoint(); } +bool isArchiveSymbol(const object::BasicSymbolRef &S) { + uint32_t Symflags = S.getFlags(); + if (Symflags & object::SymbolRef::SF_FormatSpecific) + return false; + if (!(Symflags & object::SymbolRef::SF_Global)) + return false; + if (Symflags & object::SymbolRef::SF_Undefined && + !(Symflags & object::SymbolRef::SF_Indirect)) + return false; + return true; +} + // Returns the offset of the first reference to a member offset. -static ErrorOr +static ErrorOr writeSymbolTable(raw_fd_ostream &Out, object::Archive::Kind Kind, ArrayRef Members, - std::vector &MemberOffsetRefs, bool Deterministic) { - unsigned HeaderStartOffset = 0; - unsigned BodyStartOffset = 0; + std::vector &MemberOffsetRefs, bool Deterministic, + bool Is64Bit) { + uint64_t HeaderStartOffset = 0; + uint64_t BodyStartOffset = 0; SmallString<128> NameBuf; raw_svector_ostream NameOS(NameBuf); LLVMContext Context; @@ -306,30 +335,27 @@ HeaderStartOffset = Out.tell(); if (isBSDLike(Kind)) printBSDMemberHeader(Out, "__.SYMDEF", now(Deterministic), 0, 0, 0, 0); + else if (Is64Bit) + printGNUSmallMemberHeader(Out, "/SYM64", now(Deterministic), 0, 0, 0, + 0); else printGNUSmallMemberHeader(Out, "", now(Deterministic), 0, 0, 0, 0); BodyStartOffset = Out.tell(); - print32(Out, Kind, 0); // number of entries or bytes + printNBits(Out, Kind, 0, Is64Bit); // number of entries or bytes } for (const object::BasicSymbolRef &S : Obj.symbols()) { - uint32_t Symflags = S.getFlags(); - if (Symflags & object::SymbolRef::SF_FormatSpecific) - continue; - if (!(Symflags & object::SymbolRef::SF_Global)) - continue; - if (Symflags & object::SymbolRef::SF_Undefined && - !(Symflags & object::SymbolRef::SF_Indirect)) + if (!isArchiveSymbol(S)) continue; - unsigned NameOffset = NameOS.tell(); + uint64_t NameOffset = NameOS.tell(); if (auto EC = S.printName(NameOS)) return EC; NameOS << '\0'; MemberOffsetRefs.push_back(MemberNum); if (isBSDLike(Kind)) print32(Out, Kind, NameOffset); - print32(Out, Kind, 0); // member offset + printNBits(Out, Kind, 0, Is64Bit); // member offset } } @@ -359,7 +385,7 @@ Out.write(uint8_t(0)); // Patch up the size of the symbol table now that we know how big it is. - unsigned Pos = Out.tell(); + uint64_t Pos = Out.tell(); const unsigned MemberHeaderSize = 60; Out.seek(HeaderStartOffset + 48); // offset of the size field. printWithSpacePadding(Out, Pos - MemberHeaderSize - HeaderStartOffset, 10); @@ -370,10 +396,10 @@ if (isBSDLike(Kind)) print32(Out, Kind, NumSyms * 8); else - print32(Out, Kind, NumSyms); + printNBits(Out, Kind, NumSyms, Is64Bit); Out.seek(Pos); - return BodyStartOffset + 4; + return BodyStartOffset + (Is64Bit ? 8 : 4); } std::pair @@ -398,26 +424,70 @@ std::vector MemberOffsetRefs; - unsigned MemberReferenceOffset = 0; + uint64_t MemberReferenceOffset = 0; + bool Is64BitSymTab = false; if (WriteSymtab) { - ErrorOr MemberReferenceOffsetOrErr = writeSymbolTable( - Out, Kind, NewMembers, MemberOffsetRefs, Deterministic); + // check if we need to switch to 64-bit by computing size from here + uint64_t MaxOffset = MemberReferenceOffset; + uint64_t LastOffset = 0; + LLVMContext Context; + // Compute the size of each member plus the size to be added to the symbols + for (const auto &M : NewMembers) { + MemoryBufferRef MemberBuffer = M.Buf->getMemBufferRef(); + // The offset of this is the previous + LastOffset = MaxOffset; + // Add size of header + MaxOffset += 60; + // Add the size of the buffer + MaxOffset += M.Buf->getBufferSize(); + // Add the size of padding + if (Kind == object::Archive::K_DARWIN) + MaxOffset += OffsetToAlignment(M.Buf->getBufferSize(), 8); + // Add the size added at the end in order to get an even location + MaxOffset += MaxOffset & 1; + // Now we need to add space for each symbol + Expected> ObjOrErr = + object::SymbolicFile::createSymbolicFile( + MemberBuffer, llvm::file_magic::unknown, &Context); + if (!ObjOrErr) { + // FIXME: check only for "not an object file" errors. + consumeError(ObjOrErr.takeError()); + continue; + } + object::SymbolicFile &Obj = *ObjOrErr.get(); + for (const object::BasicSymbolRef &S : Obj.symbols()) { + if(!isArchiveSymbol(S)) + continue; + // Add space for the symbol offset + MaxOffset += 4; + // Add space for the string in the string table + SmallString<128> NameBuf; + raw_svector_ostream NameLengthBuf(NameBuf); + S.printName(NameLengthBuf); + MaxOffset += NameLengthBuf.tell(); + } + } + if (LastOffset >> 32 != 0) + Is64BitSymTab = true; + // Store the location in case we have to do this all over again + ErrorOr MemberReferenceOffsetOrErr = writeSymbolTable( + Out, Kind, NewMembers, MemberOffsetRefs, Deterministic, Is64BitSymTab); if (auto EC = MemberReferenceOffsetOrErr.getError()) return std::make_pair(ArcName, EC); MemberReferenceOffset = MemberReferenceOffsetOrErr.get(); } - std::vector StringMapIndexes; + std::vector StringMapIndexes; if (!isBSDLike(Kind)) writeStringTable(Out, ArcName, NewMembers, StringMapIndexes, Thin); - std::vector::iterator StringMapIndexIter = StringMapIndexes.begin(); - std::vector MemberOffset; + std::vector::iterator StringMapIndexIter = StringMapIndexes.begin(); + std::vector MemberOffset; for (const NewArchiveMember &M : NewMembers) { MemoryBufferRef File = M.Buf->getMemBufferRef(); - unsigned Padding = 0; + uint64_t Padding = 0; - unsigned Pos = Out.tell(); + uint64_t Pos = Out.tell(); MemberOffset.push_back(Pos); // ld64 expects the members to be 8-byte aligned for 64-bit content and at @@ -429,7 +499,7 @@ printMemberHeader(Out, Kind, Thin, M.MemberName, StringMapIndexIter, M.ModTime, M.UID, M.GID, M.Perms, - M.Buf->getBufferSize() + Padding); + (uint64_t)M.Buf->getBufferSize() + Padding); if (!Thin) Out << File.getBuffer(); @@ -445,7 +515,7 @@ for (unsigned MemberNum : MemberOffsetRefs) { if (isBSDLike(Kind)) Out.seek(Out.tell() + 4); // skip over the string offset - print32(Out, Kind, MemberOffset[MemberNum]); + printNBits(Out, Kind, MemberOffset[MemberNum], Is64BitSymTab); } } Index: test/Object/archive-SYM64-write.test =================================================================== --- /dev/null +++ test/Object/archive-SYM64-write.test @@ -0,0 +1,24 @@ +# RUN: yaml2obj %s > %t +# RUN: rm -f %t.lib +# RUN: llvm-ar cr %t.lib %t %p/Inputs/trivial-object-test.elf-x86-64 +# RUN: llvm-nm %t.lib | FileCheck %s + +!ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_EXEC + Machine: EM_X86_64 +Sections: + - Name: .data + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC ] + AddressAlign: 0x0000000000000001 + Content: "00" + Size: 4294967296 + +# CHECK: archive-SYM64-write.test.tmp: +# CHECK: trivial-object-test.elf-x86-64: +# CHECK-NEXT: U SomeOtherFunction +# CHECK-NEXT: 0000000000000000 T main +# CHECK-NEXT: U puts