[llvm-ar] Use COFF archive format for COFF targets. (#82898)

Detect COFF files by default and allow specifying it with --format
argument.

This is important for ARM64EC, which uses a separated symbol map for EC
symbols. Since K_COFF is mostly compatible with K_GNU, this shouldn't
really make a difference for other targets.

This originally landed as #82642, but was reverted due to test failures
in tests using no symbol table. Since COFF symbol can't express it,
fallback to GNU format in that case.
This commit is contained in:
Jacek Caban 2024-03-13 13:27:20 +01:00 committed by GitHub
parent c18e1215c4
commit 2037577768
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 171 additions and 23 deletions

View File

@ -261,7 +261,7 @@ Other
.. option:: --format=<type>
This option allows for default, gnu, darwin or bsd ``<type>`` to be selected.
This option allows for default, gnu, darwin, bsd or coff ``<type>`` to be selected.
When creating an ``archive`` with the default ``<type>``, :program:``llvm-ar``
will attempt to infer it from the input files and fallback to the default
toolchain target if unable to do so.

View File

@ -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.

View File

@ -339,6 +339,7 @@ public:
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;

View File

@ -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,

View File

@ -62,12 +62,16 @@ object::Archive::Kind NewArchiveMember::detectKindFromObject() const {
Expected<std::unique_ptr<object::ObjectFile>> OptionalObject =
object::ObjectFile::createObjectFile(MemBufferRef);
if (OptionalObject)
return isa<object::MachOObjectFile>(**OptionalObject)
? object::Archive::K_DARWIN
: (isa<object::XCOFFObjectFile>(**OptionalObject)
? object::Archive::K_AIXBIG
: object::Archive::K_GNU);
if (OptionalObject) {
if (isa<object::MachOObjectFile>(**OptionalObject))
return object::Archive::K_DARWIN;
if (isa<object::XCOFFObjectFile>(**OptionalObject))
return object::Archive::K_AIXBIG;
if (isa<object::COFFObjectFile>(**OptionalObject) ||
isa<object::COFFImportFile>(**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<object::IRObjectFile>(**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

View File

@ -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
...

View File

@ -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

View File

@ -82,6 +82,7 @@ static void printArHelp(StringRef ToolName) {
=darwin - darwin
=bsd - bsd
=bigarchive - big archive (AIX OS)
=coff - coff
--plugin=<string> - ignored for compatibility
-h --help - display this help and exit
--output - the directory to extract archive members to
@ -193,7 +194,7 @@ static SmallVector<const char *, 256> 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<object::Archive::Kind> 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);