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) { @@ -141,14 +141,22 @@ llvm_unreachable("not supported for writting"); } -static void print32(raw_ostream &Out, object::Archive::Kind Kind, - uint32_t Val) { +template +static void print(raw_ostream &Out, object::Archive::Kind Kind, 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) + print(Out, Kind, Val); + else + print(Out, Kind, Val); +} + static void printRestOfMemberHeader( raw_fd_ostream &Out, const sys::TimePoint &ModTime, unsigned UID, unsigned GID, unsigned Perms, unsigned Size) { @@ -280,13 +288,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 +327,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 + print(Out, Kind, NameOffset); + printNBits(Out, Kind, 0, Is64Bit); // member offset } } @@ -344,13 +362,14 @@ StringRef StringTable = NameOS.str(); if (isBSDLike(Kind)) - print32(Out, Kind, StringTable.size()); // byte count of the string table + print(Out, Kind, + StringTable.size()); // byte count of the string table Out << StringTable; // If there are no symbols, emit an empty symbol table, to satisfy Solaris // tools, older versions of which expect a symbol table in a non-empty // archive, regardless of whether there are any symbols in it. if (StringTable.size() == 0) - print32(Out, Kind, 0); + print(Out, Kind, 0); // ld64 requires the next member header to start at an offset that is // 4 bytes aligned. @@ -359,7 +378,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); @@ -368,12 +387,12 @@ Out.seek(BodyStartOffset); unsigned NumSyms = MemberOffsetRefs.size(); if (isBSDLike(Kind)) - print32(Out, Kind, NumSyms * 8); + print(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,10 +417,54 @@ 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(); @@ -412,12 +475,12 @@ writeStringTable(Out, ArcName, NewMembers, StringMapIndexes, Thin); std::vector::iterator StringMapIndexIter = StringMapIndexes.begin(); - std::vector MemberOffset; + 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 @@ -445,7 +508,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,27 @@ +# RUN: yaml2obj %s > %t +# RUN: rm -f %t.lib +# RUN: llvm-ar cr %t.lib %t +# RUN: mv %t %t2 +# RUN: llvm-ar cr %t.lib %t2 %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: 2147483648 + +# CHECK: archive-SYM64-write.test.tmp: +# CHECK: archive-SYM64-write.test.tmp2: +# CHECK: trivial-object-test.elf-x86-64: +# CHECK-NEXT: U SomeOtherFunction +# CHECK-NEXT: 0000000000000000 T main +# CHECK-NEXT: U puts