From b3d6348d2e6c9d063473882638fe4edbcb7e159c Mon Sep 17 00:00:00 2001 From: Jake Ehrlich Date: Fri, 27 Oct 2017 22:26:37 +0000 Subject: [PATCH] Add support for writing 64-bit symbol tables for archives when offsets become too large for 32-bit This should fix https://bugs.llvm.org//show_bug.cgi?id=34189 This change makes it so that if writing a K_GNU style archive, you need to output a > 32-bit offset it should output in K_GNU64 style instead. Differential Revision: https://reviews.llvm.org/D36812 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@316805 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Object/ArchiveWriter.cpp | 64 ++++++++++++++++++++++++---- test/Object/archive-SYM64-write.test | 31 ++++++++++++++ 2 files changed, 86 insertions(+), 9 deletions(-) create mode 100644 test/Object/archive-SYM64-write.test diff --git a/lib/Object/ArchiveWriter.cpp b/lib/Object/ArchiveWriter.cpp index 919e2676802..63f5082c29d 100644 --- a/lib/Object/ArchiveWriter.cpp +++ b/lib/Object/ArchiveWriter.cpp @@ -122,11 +122,11 @@ static void printWithSpacePadding(raw_ostream &OS, T Data, unsigned Size) { static bool isBSDLike(object::Archive::Kind Kind) { switch (Kind) { case object::Archive::K_GNU: + case object::Archive::K_GNU64: return false; case object::Archive::K_BSD: case object::Archive::K_DARWIN: return true; - case object::Archive::K_GNU64: case object::Archive::K_DARWIN64: case object::Archive::K_COFF: break; @@ -134,8 +134,8 @@ static bool isBSDLike(object::Archive::Kind Kind) { 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 @@ -216,6 +216,20 @@ static std::string computeRelativePath(StringRef From, StringRef To) { return Relative.str(); } +static bool is64BitKind(object::Archive::Kind Kind) { + switch (Kind) { + case object::Archive::K_GNU: + case object::Archive::K_BSD: + case object::Archive::K_DARWIN: + case object::Archive::K_COFF: + return false; + case object::Archive::K_DARWIN64: + case object::Archive::K_GNU64: + return true; + } + llvm_unreachable("not supported for writting"); +} + static void addToStringTable(raw_ostream &Out, StringRef ArcName, const NewArchiveMember &M, bool Thin) { StringRef ID = M.Buf->getBufferIdentifier(); @@ -288,6 +302,14 @@ static bool isArchiveSymbol(const object::BasicSymbolRef &S) { return true; } +static void printNBits(raw_ostream &Out, object::Archive::Kind Kind, + uint64_t Val) { + if (is64BitKind(Kind)) + print(Out, Kind, Val); + else + print(Out, Kind, Val); +} + static void writeSymbolTable(raw_ostream &Out, object::Archive::Kind Kind, bool Deterministic, ArrayRef Members, StringRef StringTable) { @@ -299,9 +321,11 @@ static void writeSymbolTable(raw_ostream &Out, object::Archive::Kind Kind, NumSyms += M.Symbols.size(); unsigned Size = 0; - Size += 4; // Number of entries + Size += is64BitKind(Kind) ? 8 : 4; // Number of entries if (isBSDLike(Kind)) Size += NumSyms * 8; // Table + else if (is64BitKind(Kind)) + Size += NumSyms * 8; // Table else Size += NumSyms * 4; // Table if (isBSDLike(Kind)) @@ -318,27 +342,30 @@ static void writeSymbolTable(raw_ostream &Out, object::Archive::Kind Kind, if (isBSDLike(Kind)) printBSDMemberHeader(Out, Out.tell(), "__.SYMDEF", now(Deterministic), 0, 0, 0, Size); + else if (is64BitKind(Kind)) + printGNUSmallMemberHeader(Out, "/SYM64", now(Deterministic), 0, 0, 0, Size); else printGNUSmallMemberHeader(Out, "", now(Deterministic), 0, 0, 0, Size); uint64_t Pos = Out.tell() + Size; if (isBSDLike(Kind)) - print32(Out, Kind, NumSyms * 8); + print(Out, Kind, NumSyms * 8); else - print32(Out, Kind, NumSyms); + printNBits(Out, Kind, NumSyms); for (const MemberData &M : Members) { for (unsigned StringOffset : M.Symbols) { if (isBSDLike(Kind)) - print32(Out, Kind, StringOffset); - print32(Out, Kind, Pos); // member offset + print(Out, Kind, StringOffset); + printNBits(Out, Kind, Pos); // member offset } Pos += M.Header.size() + M.Data.size() + M.Padding.size(); } if (isBSDLike(Kind)) - print32(Out, Kind, StringTable.size()); // byte count of the string table + // byte count of the string table + print(Out, Kind, StringTable.size()); Out << StringTable; while (Pad--) @@ -442,6 +469,25 @@ Error llvm::writeArchive(StringRef ArcName, if (!StringTableBuf.empty()) Data.insert(Data.begin(), computeStringTable(StringTableBuf)); + // We would like to detect if we need to switch to a 64-bit symbol table. + if (WriteSymtab) { + uint64_t MaxOffset = 0; + uint64_t LastOffset = MaxOffset; + for (const auto& M : Data) { + // Record the start of the member's offset + LastOffset = MaxOffset; + // Account for the size of each part associated with the member. + MaxOffset += M.Header.size() + M.Data.size() + M.Padding.size(); + // We assume 32-bit symbols to see if 32-bit symbols are possible or not. + MaxOffset += M.Symbols.size() * 4; + } + // If LastOffset isn't going to fit in a 32-bit varible we need to switch + // to 64-bit. Note that the file can be larger than 4GB as long as the last + // member starts before the 4GB offset. + if (LastOffset >> 32 != 0) + Kind = object::Archive::K_GNU64; + } + SmallString<128> TmpArchive; int TmpArchiveFD; if (auto EC = sys::fs::createUniqueFile(ArcName + ".temp-archive-%%%%%%%.a", diff --git a/test/Object/archive-SYM64-write.test b/test/Object/archive-SYM64-write.test new file mode 100644 index 00000000000..1cccc1d06ba --- /dev/null +++ b/test/Object/archive-SYM64-write.test @@ -0,0 +1,31 @@ +# RUN: yaml2obj %s > %t +# RUN: rm -f %t.lib +# RUN: cp %t %t2 +# RUN: llvm-ar cr %t.lib %t %t2 %p/Inputs/trivial-object-test.elf-x86-64 +# RUN: llvm-nm --print-armap %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 map +# CHECK-NEXT: main in trivial-object-test.elf-x86-64 + +# 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