From c0b28d55a7f578db6023c8d8856d98293ea76ce4 Mon Sep 17 00:00:00 2001 From: James Y Knight Date: Wed, 10 Oct 2018 21:07:02 +0000 Subject: [PATCH] llvm-ar: Darwin archive format fixes. * Support writing the DARWIN64 symbol table format. * In darwin archives, emit a symbol table whenever requested, even when there are no members, as the apple linker will abort if given an archive without a symbol table. Added tests for same, and also simplified and moved the GNU 64-bit symbol table test into archive-symtab.test. llvm-svn: 344183 --- llvm/lib/Object/ArchiveWriter.cpp | 61 ++++++++++++++++++------------- llvm/test/Object/archive-GNU64-write.test | 40 -------------------- llvm/test/Object/archive-format.test | 2 +- llvm/test/Object/archive-symtab.test | 28 ++++++++++++++ 4 files changed, 64 insertions(+), 67 deletions(-) delete mode 100644 llvm/test/Object/archive-GNU64-write.test diff --git a/llvm/lib/Object/ArchiveWriter.cpp b/llvm/lib/Object/ArchiveWriter.cpp index c6c0bef..7672053 100644 --- a/llvm/lib/Object/ArchiveWriter.cpp +++ b/llvm/lib/Object/ArchiveWriter.cpp @@ -121,6 +121,11 @@ static void printWithSpacePadding(raw_ostream &OS, T Data, unsigned Size) { OS.indent(Size - SizeSoFar); } +static bool isDarwin(object::Archive::Kind Kind) { + return Kind == object::Archive::K_DARWIN || + Kind == object::Archive::K_DARWIN64; +} + static bool isBSDLike(object::Archive::Kind Kind) { switch (Kind) { case object::Archive::K_GNU: @@ -128,8 +133,8 @@ static bool isBSDLike(object::Archive::Kind Kind) { return false; case object::Archive::K_BSD: case object::Archive::K_DARWIN: - return true; case object::Archive::K_DARWIN64: + return true; case object::Archive::K_COFF: break; } @@ -314,7 +319,9 @@ static void printNBits(raw_ostream &Out, object::Archive::Kind Kind, static void writeSymbolTable(raw_ostream &Out, object::Archive::Kind Kind, bool Deterministic, ArrayRef Members, StringRef StringTable) { - if (StringTable.empty()) + // We don't write a symbol table on an archive with no members -- except on + // Darwin, where the linker will abort unless the archive has a symbol table. + if (StringTable.empty() && !isDarwin(Kind)) return; unsigned NumSyms = 0; @@ -322,15 +329,15 @@ static void writeSymbolTable(raw_ostream &Out, object::Archive::Kind Kind, NumSyms += M.Symbols.size(); unsigned Size = 0; - Size += is64BitKind(Kind) ? 8 : 4; // Number of entries + unsigned OffsetSize = is64BitKind(Kind) ? sizeof(uint64_t) : sizeof(uint32_t); + + Size += OffsetSize; // Number of entries if (isBSDLike(Kind)) - Size += NumSyms * 8; // Table - else if (is64BitKind(Kind)) - Size += NumSyms * 8; // Table + Size += NumSyms * OffsetSize * 2; // Table else - Size += NumSyms * 4; // Table + Size += NumSyms * OffsetSize; // Table if (isBSDLike(Kind)) - Size += 4; // byte count + Size += OffsetSize; // byte count Size += StringTable.size(); // ld64 expects the members to be 8-byte aligned for 64-bit content and at // least 4-byte aligned for 32-bit content. Opt for the larger encoding @@ -340,25 +347,26 @@ static void writeSymbolTable(raw_ostream &Out, object::Archive::Kind Kind, unsigned Pad = OffsetToAlignment(Size, Alignment); Size += Pad; - 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); + if (isBSDLike(Kind)) { + const char *Name = is64BitKind(Kind) ? "__.SYMDEF_64" : "__.SYMDEF"; + printBSDMemberHeader(Out, Out.tell(), Name, now(Deterministic), 0, 0, 0, + Size); + } else { + const char *Name = is64BitKind(Kind) ? "/SYM64" : ""; + printGNUSmallMemberHeader(Out, Name, now(Deterministic), 0, 0, 0, Size); + } uint64_t Pos = Out.tell() + Size; if (isBSDLike(Kind)) - print(Out, Kind, NumSyms * 8); + printNBits(Out, Kind, NumSyms * 2 * OffsetSize); else printNBits(Out, Kind, NumSyms); for (const MemberData &M : Members) { for (unsigned StringOffset : M.Symbols) { if (isBSDLike(Kind)) - print(Out, Kind, StringOffset); + printNBits(Out, Kind, StringOffset); printNBits(Out, Kind, Pos); // member offset } Pos += M.Header.size() + M.Data.size() + M.Padding.size(); @@ -366,7 +374,7 @@ static void writeSymbolTable(raw_ostream &Out, object::Archive::Kind Kind, if (isBSDLike(Kind)) // byte count of the string table - print(Out, Kind, StringTable.size()); + printNBits(Out, Kind, StringTable.size()); Out << StringTable; while (Pad--) @@ -466,9 +474,7 @@ computeMemberData(raw_ostream &StringTable, raw_ostream &SymNames, // See also the functions that handle the lookup: // in lldb: ObjectContainerBSDArchive::Archive::FindObject() // in llvm/tools/dsymutil: BinaryHolder::GetArchiveMemberBuffers(). - bool UniqueTimestamps = - Deterministic && (Kind == object::Archive::K_DARWIN || - Kind == object::Archive::K_DARWIN64); + bool UniqueTimestamps = Deterministic && isDarwin(Kind); std::map FilenameCount; if (UniqueTimestamps) { for (const NewArchiveMember &M : NewMembers) @@ -488,9 +494,8 @@ computeMemberData(raw_ostream &StringTable, raw_ostream &SymNames, // least 4-byte aligned for 32-bit content. Opt for the larger encoding // uniformly. This matches the behaviour with cctools and ensures that ld64 // is happy with archives that we generate. - unsigned MemberPadding = Kind == object::Archive::K_DARWIN - ? OffsetToAlignment(Data.size(), 8) - : 0; + unsigned MemberPadding = + isDarwin(Kind) ? OffsetToAlignment(Data.size(), 8) : 0; unsigned TailPadding = OffsetToAlignment(Data.size() + MemberPadding, 2); StringRef Padding = StringRef(PaddingData, MemberPadding + TailPadding); @@ -569,8 +574,12 @@ Error llvm::writeArchive(StringRef ArcName, // 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 >= (1ULL << Sym64Threshold)) - Kind = object::Archive::K_GNU64; + if (LastOffset >= (1ULL << Sym64Threshold)) { + if (Kind == object::Archive::K_DARWIN) + Kind = object::Archive::K_DARWIN64; + else + Kind = object::Archive::K_GNU64; + } } Expected Temp = diff --git a/llvm/test/Object/archive-GNU64-write.test b/llvm/test/Object/archive-GNU64-write.test deleted file mode 100644 index 0bfb7c8..0000000 --- a/llvm/test/Object/archive-GNU64-write.test +++ /dev/null @@ -1,40 +0,0 @@ -# REQUIRES: llvm-64-bits -# REQUIRES: system-linux -# REQUIRES: shell - -# RUN: yaml2obj %s > %t -# RUN: dd if=%t of=%t bs=1 count=0 seek=1M -# RUN: rm -f %t.lib -# RUN: cp %t %t2 -# RUN: SYM64_THRESHOLD=19 llvm-ar cr %t.lib %t %t2 %p/Inputs/trivial-object-test.elf-x86-64 -# RUN: llvm-nm --print-armap %t.lib | FileCheck %s -# RUN: grep SYM64 %t.lib - -# Delete temp files. They are too large. -# RUN: rm -f %t %t2 %t.lib - -!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: 32 - -# CHECK: Archive map -# CHECK-NEXT: main in trivial-object-test.elf-x86-64 - -# CHECK: archive-GNU64-write.test.tmp: - -# CHECK: archive-GNU64-write.test.tmp2: - -# CHECK: trivial-object-test.elf-x86-64: -# CHECK-NEXT: U SomeOtherFunction -# CHECK-NEXT: 0000000000000000 T main -# CHECK-NEXT: U puts diff --git a/llvm/test/Object/archive-format.test b/llvm/test/Object/archive-format.test index 219fc7f..b1ae411 100644 --- a/llvm/test/Object/archive-format.test +++ b/llvm/test/Object/archive-format.test @@ -38,7 +38,7 @@ BSD-SAME: #1/16 0 0 0 644 20 ` BSD-NEXT: 0123456789abcdefzed. RUN: rm -f %t.a -RUN: llvm-ar --format=darwin rc %t.a 0123456789abcde 0123456789abcdef +RUN: llvm-ar --format=darwin rcS %t.a 0123456789abcde 0123456789abcdef RUN: cat %t.a | FileCheck -strict-whitespace --check-prefix=DARWIN %s DARWIN: ! diff --git a/llvm/test/Object/archive-symtab.test b/llvm/test/Object/archive-symtab.test index 2979707..96f4813 100644 --- a/llvm/test/Object/archive-symtab.test +++ b/llvm/test/Object/archive-symtab.test @@ -2,6 +2,11 @@ RUN: rm -f %t.a RUN: llvm-ar rcsU %t.a %p/Inputs/trivial-object-test.elf-x86-64 %p/Inputs/trivial-object-test2.elf-x86-64 RUN: llvm-nm -M %t.a | FileCheck %s +RUN: rm -f %t.a +RUN: env SYM64_THRESHOLD=1 llvm-ar rcsU %t.a %p/Inputs/trivial-object-test.elf-x86-64 %p/Inputs/trivial-object-test2.elf-x86-64 +RUN: llvm-nm -M %t.a | FileCheck %s +RUXX: grep SYM64 %t.a + CHECK: Archive map CHECK-NEXT: main in trivial-object-test.elf-x86-64 CHECK-NEXT: foo in trivial-object-test2.elf-x86-64 @@ -82,6 +87,11 @@ RUN: rm -f %t.a RUN: llvm-ar --format=bsd rcsU %t.a %p/Inputs/trivial-object-test.macho-x86-64 %p/Inputs/trivial-object-test2.macho-x86-64 RUN: llvm-nm -M %t.a | FileCheck --check-prefix=MACHO %s +RUN: rm -f %t.a +RUN: env SYM64_THRESHOLD=1 llvm-ar --format=darwin rcsU %t.a %p/Inputs/trivial-object-test.macho-x86-64 %p/Inputs/trivial-object-test2.macho-x86-64 +RUN: llvm-nm -M %t.a | FileCheck --check-prefix=MACHO %s +RUN: grep '__\.SYMDEF_64' %t.a + MACHO: Archive map MACHO-NEXT: _main in trivial-object-test.macho-x86-64 MACHO-NEXT: _foo in trivial-object-test2.macho-x86-64 @@ -138,3 +148,21 @@ RUN: llvm-ar --format=gnu rcsD %t.a %p/Inputs/trivial-object-test.macho-x86-64 RUN: FileCheck --check-prefix=GNU-SYMTAB-ALIGN %s < %t.a GNU-SYMTAB-ALIGN: ! GNU-SYMTAB-ALIGN-NEXT: / 0 0 0 0 14 ` + + +** Test the behavior of an empty archive: + +No symbol table emitted for GNU archives +RUN: rm -f %t.a +RUN: llvm-ar rcs --format=gnu %t.a +RUN: not grep -q '/ ' %t.a + +No symbol table for BSD archives +RUN: rm -f %t.a +RUN: llvm-ar rcs --format=bsd %t.a +RUN: not grep -q '__\.SYMDEF' %t.a + +And we do emit a symbol table for DARWIN archives +RUN: rm -f %t.a +RUN: llvm-ar rcs --format=darwin %t.a +RUN: grep -q '__\.SYMDEF' %t.a -- 2.7.4