[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:
parent
c18e1215c4
commit
2037577768
@ -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.
|
||||
|
@ -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.
|
||||
|
@ -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;
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
91
llvm/test/tools/llvm-ar/coff-symtab.test
Normal file
91
llvm/test/tools/llvm-ar/coff-symtab.test
Normal 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
|
||||
...
|
32
llvm/test/tools/llvm-ar/no-symtab.yaml
Normal file
32
llvm/test/tools/llvm-ar/no-symtab.yaml
Normal 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
|
@ -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);
|
||||
|
Loading…
x
Reference in New Issue
Block a user