diff --git a/llvm/docs/CommandGuide/llvm-ar.rst b/llvm/docs/CommandGuide/llvm-ar.rst index 03d5b9e41ada3..63b3a519550bc 100644 --- a/llvm/docs/CommandGuide/llvm-ar.rst +++ b/llvm/docs/CommandGuide/llvm-ar.rst @@ -261,7 +261,7 @@ Other .. option:: --format= - This option allows for default, gnu, darwin or bsd ```` to be selected. + This option allows for default, gnu, darwin, bsd or coff ```` to be selected. When creating an ``archive`` with the default ````, :program:``llvm-ar`` will attempt to infer it from the input files and fallback to the default toolchain target if unable to do so. diff --git a/llvm/docs/ReleaseNotes.rst b/llvm/docs/ReleaseNotes.rst index b34a5f31c5eb0..7be51730663bd 100644 --- a/llvm/docs/ReleaseNotes.rst +++ b/llvm/docs/ReleaseNotes.rst @@ -153,6 +153,9 @@ Changes to the LLVM tools if it's not specified with the ``--format`` argument and cannot be inferred from input files. +* llvm-ar now allows specifying COFF archive format with ``--format`` argument + and uses it by default for COFF targets. + * llvm-objcopy now supports ``--set-symbol-visibility`` and ``--set-symbols-visibility`` options for ELF input to change the visibility of symbols. diff --git a/llvm/include/llvm/Object/Archive.h b/llvm/include/llvm/Object/Archive.h index f71630054dc63..a3165c3235e0e 100644 --- a/llvm/include/llvm/Object/Archive.h +++ b/llvm/include/llvm/Object/Archive.h @@ -339,6 +339,7 @@ class Archive : public Binary { Kind kind() const { return (Kind)Format; } bool isThin() const { return IsThin; } static object::Archive::Kind getDefaultKind(); + static object::Archive::Kind getDefaultKindForTriple(Triple &T); child_iterator child_begin(Error &Err, bool SkipInternal = true) const; child_iterator child_end() const; diff --git a/llvm/lib/Object/Archive.cpp b/llvm/lib/Object/Archive.cpp index 9000e9aa81ff4..6139d9996bdad 100644 --- a/llvm/lib/Object/Archive.cpp +++ b/llvm/lib/Object/Archive.cpp @@ -969,12 +969,19 @@ Archive::Archive(MemoryBufferRef Source, Error &Err) Err = Error::success(); } +object::Archive::Kind Archive::getDefaultKindForTriple(Triple &T) { + if (T.isOSDarwin()) + return object::Archive::K_DARWIN; + if (T.isOSAIX()) + return object::Archive::K_AIXBIG; + if (T.isOSWindows()) + return object::Archive::K_COFF; + return object::Archive::K_GNU; +} + object::Archive::Kind Archive::getDefaultKind() { Triple HostTriple(sys::getDefaultTargetTriple()); - return HostTriple.isOSDarwin() - ? object::Archive::K_DARWIN - : (HostTriple.isOSAIX() ? object::Archive::K_AIXBIG - : object::Archive::K_GNU); + return getDefaultKindForTriple(HostTriple); } Archive::child_iterator Archive::child_begin(Error &Err, diff --git a/llvm/lib/Object/ArchiveWriter.cpp b/llvm/lib/Object/ArchiveWriter.cpp index e0629747b40cd..aa57e55de70c8 100644 --- a/llvm/lib/Object/ArchiveWriter.cpp +++ b/llvm/lib/Object/ArchiveWriter.cpp @@ -62,12 +62,16 @@ object::Archive::Kind NewArchiveMember::detectKindFromObject() const { Expected> OptionalObject = object::ObjectFile::createObjectFile(MemBufferRef); - if (OptionalObject) - return isa(**OptionalObject) - ? object::Archive::K_DARWIN - : (isa(**OptionalObject) - ? object::Archive::K_AIXBIG - : object::Archive::K_GNU); + if (OptionalObject) { + if (isa(**OptionalObject)) + return object::Archive::K_DARWIN; + if (isa(**OptionalObject)) + return object::Archive::K_AIXBIG; + if (isa(**OptionalObject) || + isa(**OptionalObject)) + return object::Archive::K_COFF; + return object::Archive::K_GNU; + } // Squelch the error in case we had a non-object file. consumeError(OptionalObject.takeError()); @@ -80,10 +84,7 @@ object::Archive::Kind NewArchiveMember::detectKindFromObject() const { MemBufferRef, file_magic::bitcode, &Context)) { auto &IRObject = cast(**ObjOrErr); auto TargetTriple = Triple(IRObject.getTargetTriple()); - return TargetTriple.isOSDarwin() - ? object::Archive::K_DARWIN - : (TargetTriple.isOSAIX() ? object::Archive::K_AIXBIG - : object::Archive::K_GNU); + return object::Archive::getDefaultKindForTriple(TargetTriple); } else { // Squelch the error in case this was not a SymbolicFile. consumeError(ObjOrErr.takeError()); @@ -976,10 +977,12 @@ static Error writeArchiveToStream(raw_ostream &Out, SmallString<0> StringTableBuf; raw_svector_ostream StringTable(StringTableBuf); SymMap SymMap; + bool ShouldWriteSymtab = WriteSymtab != SymtabWritingMode::NoSymtab; // COFF symbol map uses 16-bit indexes, so we can't use it if there are too - // many members. - if (isCOFFArchive(Kind) && NewMembers.size() > 0xfffe) + // many members. COFF format also requires symbol table presence, so use + // GNU format when NoSymtab is requested. + if (isCOFFArchive(Kind) && (NewMembers.size() > 0xfffe || !ShouldWriteSymtab)) Kind = object::Archive::K_GNU; // In the scenario when LLVMContext is populated SymbolicFile will contain a @@ -1008,7 +1011,6 @@ static Error writeArchiveToStream(raw_ostream &Out, uint64_t LastMemberHeaderOffset = 0; uint64_t NumSyms = 0; uint64_t NumSyms32 = 0; // Store symbol number of 32-bit member files. - bool ShouldWriteSymtab = WriteSymtab != SymtabWritingMode::NoSymtab; for (const auto &M : Data) { // Record the start of the member's offset diff --git a/llvm/test/tools/llvm-ar/coff-symtab.test b/llvm/test/tools/llvm-ar/coff-symtab.test new file mode 100644 index 0000000000000..4f7270d9e2c6e --- /dev/null +++ b/llvm/test/tools/llvm-ar/coff-symtab.test @@ -0,0 +1,91 @@ +Verify that llvm-ar uses COFF archive format by ensuring that archive map is sorted. + +RUN: rm -rf %t.dir && split-file %s %t.dir && cd %t.dir + +RUN: yaml2obj coff-symtab.yaml -o coff-symtab.obj +RUN: llvm-ar crs out.a coff-symtab.obj +RUN: llvm-nm --print-armap out.a | FileCheck %s + +RUN: llvm-as coff-symtab.ll -o coff-symtab.bc +RUN: llvm-ar crs out2.a coff-symtab.bc +RUN: llvm-nm --print-armap out2.a | FileCheck %s + +RUN: yaml2obj elf.yaml -o coff-symtab.o +RUN: llvm-ar crs --format coff out3.a coff-symtab.o +RUN: llvm-nm --print-armap out3.a | FileCheck %s + +Create an empty archive with no symbol map, add a COFF file to it and check that the output archive is a COFF archive. + +RUN: llvm-ar rcS out4.a +RUN: llvm-ar rs out4.a coff-symtab.obj +RUN: llvm-nm --print-armap out4.a | FileCheck %s + +CHECK: Archive map +CHECK-NEXT: a in coff-symtab +CHECK-NEXT: b in coff-symtab +CHECK-NEXT: c in coff-symtab +CHECK-EMPTY: + +#--- coff-symtab.yaml +--- !COFF +header: + Machine: IMAGE_FILE_MACHINE_UNKNOWN + Characteristics: [ ] +sections: + - Name: .text + Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ] + Alignment: 4 + SectionData: '' +symbols: + - Name: b + Value: 0 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_FUNCTION + StorageClass: IMAGE_SYM_CLASS_EXTERNAL + - Name: c + Value: 0 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_FUNCTION + StorageClass: IMAGE_SYM_CLASS_EXTERNAL + - Name: a + Value: 0 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_FUNCTION + StorageClass: IMAGE_SYM_CLASS_EXTERNAL +... + + +#--- coff-symtab.ll +target triple = "x86_64-unknown-windows-msvc" + +define void @b() { ret void } +define void @c() { ret void } +define void @a() { ret void } + +#--- elf.yaml +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data : ELFDATA2LSB + Type: ET_REL + Machine: EM_X86_64 +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x0000000000000004 + Content: '' +Symbols: + - Name: b + Binding: STB_GLOBAL + Section: .text + - Name: c + Binding: STB_GLOBAL + Section: .text + - Name: a + Binding: STB_GLOBAL + Section: .text +... diff --git a/llvm/test/tools/llvm-ar/no-symtab.yaml b/llvm/test/tools/llvm-ar/no-symtab.yaml new file mode 100644 index 0000000000000..7370c9b323552 --- /dev/null +++ b/llvm/test/tools/llvm-ar/no-symtab.yaml @@ -0,0 +1,32 @@ +## Create archives with no symtab in various formats and check that we can read them. + +# RUN: yaml2obj %s -o %t.o +# RUN: rm -f %t.*.a + +# RUN: llvm-ar --format=gnu rcS %t.gnu.a %t.o +# RUN: llvm-ar --format=coff rcS %t.coff.a %t.o +# RUN: llvm-ar --format=darwin rcS %t.darwin.a %t.o +# RUN: llvm-ar --format=bsd rcS %t.bsd.a %t.o +# RUN: llvm-ar --format=bigarchive rcS %t.bigarchive.a %t.o + +# RUN: llvm-nm --print-armap %t.gnu.a | FileCheck %s +# RUN: llvm-nm --print-armap %t.coff.a | FileCheck %s +# RUN: llvm-nm --print-armap %t.darwin.a | FileCheck %s +# RUN: llvm-nm --print-armap %t.bsd.a | FileCheck %s +# RUN: llvm-nm --print-armap %t.bigarchive.a | FileCheck %s + +# CHECK-NOT: Archive map + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_X86_64 +Sections: + - Name: .text + Type: SHT_PROGBITS +Symbols: + - Name: symbol + Binding: STB_GLOBAL + Section: .text diff --git a/llvm/tools/llvm-ar/llvm-ar.cpp b/llvm/tools/llvm-ar/llvm-ar.cpp index 81cb2a21daf1f..294b8531b08f1 100644 --- a/llvm/tools/llvm-ar/llvm-ar.cpp +++ b/llvm/tools/llvm-ar/llvm-ar.cpp @@ -82,6 +82,7 @@ static void printArHelp(StringRef ToolName) { =darwin - darwin =bsd - bsd =bigarchive - big archive (AIX OS) + =coff - coff --plugin= - ignored for compatibility -h --help - display this help and exit --output - the directory to extract archive members to @@ -193,7 +194,7 @@ static SmallVector PositionalArgs; static bool MRI; namespace { -enum Format { Default, GNU, BSD, DARWIN, BIGARCHIVE, Unknown }; +enum Format { Default, GNU, COFF, BSD, DARWIN, BIGARCHIVE, Unknown }; } static Format FormatType = Default; @@ -1025,14 +1026,21 @@ static void performWriteOperation(ArchiveOperation Operation, Kind = object::Archive::K_GNU; else if (OldArchive) { Kind = OldArchive->kind(); - if (Kind == object::Archive::K_BSD) { - auto InferredKind = object::Archive::K_BSD; + std::optional AltKind; + if (Kind == object::Archive::K_BSD) + AltKind = object::Archive::K_DARWIN; + else if (Kind == object::Archive::K_GNU && !OldArchive->hasSymbolTable()) + // If there is no symbol table, we can't tell GNU from COFF format + // from the old archive type. + AltKind = object::Archive::K_COFF; + if (AltKind) { + auto InferredKind = Kind; if (NewMembersP && !NewMembersP->empty()) InferredKind = NewMembersP->front().detectKindFromObject(); else if (!NewMembers.empty()) InferredKind = NewMembers.front().detectKindFromObject(); - if (InferredKind == object::Archive::K_DARWIN) - Kind = object::Archive::K_DARWIN; + if (InferredKind == AltKind) + Kind = *AltKind; } } else if (NewMembersP) Kind = !NewMembersP->empty() ? NewMembersP->front().detectKindFromObject() @@ -1044,6 +1052,9 @@ static void performWriteOperation(ArchiveOperation Operation, case GNU: Kind = object::Archive::K_GNU; break; + case COFF: + Kind = object::Archive::K_COFF; + break; case BSD: if (Thin) fail("only the gnu format has a thin mode"); @@ -1376,6 +1387,7 @@ static int ar_main(int argc, char **argv) { .Case("darwin", DARWIN) .Case("bsd", BSD) .Case("bigarchive", BIGARCHIVE) + .Case("coff", COFF) .Default(Unknown); if (FormatType == Unknown) fail(std::string("Invalid format ") + Match);