Skip to content

Commit 0dda8e4

Browse files
committed
[llvm-ar] Use COFF archive format for COFF targets.
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.
1 parent 99be387 commit 0dda8e4

File tree

8 files changed

+171
-23
lines changed

8 files changed

+171
-23
lines changed

llvm/docs/CommandGuide/llvm-ar.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -261,7 +261,7 @@ Other
261261

262262
.. option:: --format=<type>
263263

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

llvm/docs/ReleaseNotes.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,9 @@ Changes to the LLVM tools
153153
if it's not specified with the ``--format`` argument and cannot be inferred from
154154
input files.
155155

156+
* llvm-ar now allows specifying COFF archive format with ``--format`` argument
157+
and uses it by default for COFF targets.
158+
156159
* llvm-objcopy now supports ``--set-symbol-visibility`` and
157160
``--set-symbols-visibility`` options for ELF input to change the
158161
visibility of symbols.

llvm/include/llvm/Object/Archive.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -339,6 +339,7 @@ class Archive : public Binary {
339339
Kind kind() const { return (Kind)Format; }
340340
bool isThin() const { return IsThin; }
341341
static object::Archive::Kind getDefaultKind();
342+
static object::Archive::Kind getDefaultKindForTriple(Triple &T);
342343

343344
child_iterator child_begin(Error &Err, bool SkipInternal = true) const;
344345
child_iterator child_end() const;

llvm/lib/Object/Archive.cpp

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -969,12 +969,19 @@ Archive::Archive(MemoryBufferRef Source, Error &Err)
969969
Err = Error::success();
970970
}
971971

972+
object::Archive::Kind Archive::getDefaultKindForTriple(Triple &T) {
973+
if (T.isOSDarwin())
974+
return object::Archive::K_DARWIN;
975+
if (T.isOSAIX())
976+
return object::Archive::K_AIXBIG;
977+
if (T.isOSWindows())
978+
return object::Archive::K_COFF;
979+
return object::Archive::K_GNU;
980+
}
981+
972982
object::Archive::Kind Archive::getDefaultKind() {
973983
Triple HostTriple(sys::getDefaultTargetTriple());
974-
return HostTriple.isOSDarwin()
975-
? object::Archive::K_DARWIN
976-
: (HostTriple.isOSAIX() ? object::Archive::K_AIXBIG
977-
: object::Archive::K_GNU);
984+
return getDefaultKindForTriple(HostTriple);
978985
}
979986

980987
Archive::child_iterator Archive::child_begin(Error &Err,

llvm/lib/Object/ArchiveWriter.cpp

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -62,12 +62,16 @@ object::Archive::Kind NewArchiveMember::detectKindFromObject() const {
6262
Expected<std::unique_ptr<object::ObjectFile>> OptionalObject =
6363
object::ObjectFile::createObjectFile(MemBufferRef);
6464

65-
if (OptionalObject)
66-
return isa<object::MachOObjectFile>(**OptionalObject)
67-
? object::Archive::K_DARWIN
68-
: (isa<object::XCOFFObjectFile>(**OptionalObject)
69-
? object::Archive::K_AIXBIG
70-
: object::Archive::K_GNU);
65+
if (OptionalObject) {
66+
if (isa<object::MachOObjectFile>(**OptionalObject))
67+
return object::Archive::K_DARWIN;
68+
if (isa<object::XCOFFObjectFile>(**OptionalObject))
69+
return object::Archive::K_AIXBIG;
70+
if (isa<object::COFFObjectFile>(**OptionalObject) ||
71+
isa<object::COFFImportFile>(**OptionalObject))
72+
return object::Archive::K_COFF;
73+
return object::Archive::K_GNU;
74+
}
7175

7276
// Squelch the error in case we had a non-object file.
7377
consumeError(OptionalObject.takeError());
@@ -80,10 +84,7 @@ object::Archive::Kind NewArchiveMember::detectKindFromObject() const {
8084
MemBufferRef, file_magic::bitcode, &Context)) {
8185
auto &IRObject = cast<object::IRObjectFile>(**ObjOrErr);
8286
auto TargetTriple = Triple(IRObject.getTargetTriple());
83-
return TargetTriple.isOSDarwin()
84-
? object::Archive::K_DARWIN
85-
: (TargetTriple.isOSAIX() ? object::Archive::K_AIXBIG
86-
: object::Archive::K_GNU);
87+
return object::Archive::getDefaultKindForTriple(TargetTriple);
8788
} else {
8889
// Squelch the error in case this was not a SymbolicFile.
8990
consumeError(ObjOrErr.takeError());
@@ -976,10 +977,12 @@ static Error writeArchiveToStream(raw_ostream &Out,
976977
SmallString<0> StringTableBuf;
977978
raw_svector_ostream StringTable(StringTableBuf);
978979
SymMap SymMap;
980+
bool ShouldWriteSymtab = WriteSymtab != SymtabWritingMode::NoSymtab;
979981

980982
// COFF symbol map uses 16-bit indexes, so we can't use it if there are too
981-
// many members.
982-
if (isCOFFArchive(Kind) && NewMembers.size() > 0xfffe)
983+
// many members. COFF format also requires symbol table presence, so use
984+
// GNU format when NoSymtab is requested.
985+
if (isCOFFArchive(Kind) && (NewMembers.size() > 0xfffe || !ShouldWriteSymtab))
983986
Kind = object::Archive::K_GNU;
984987

985988
// In the scenario when LLVMContext is populated SymbolicFile will contain a
@@ -1008,7 +1011,6 @@ static Error writeArchiveToStream(raw_ostream &Out,
10081011
uint64_t LastMemberHeaderOffset = 0;
10091012
uint64_t NumSyms = 0;
10101013
uint64_t NumSyms32 = 0; // Store symbol number of 32-bit member files.
1011-
bool ShouldWriteSymtab = WriteSymtab != SymtabWritingMode::NoSymtab;
10121014

10131015
for (const auto &M : Data) {
10141016
// Record the start of the member's offset
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
Verify that llvm-ar uses COFF archive format by ensuring that archive map is sorted.
2+
3+
RUN: rm -rf %t.dir && split-file %s %t.dir && cd %t.dir
4+
5+
RUN: yaml2obj coff-symtab.yaml -o coff-symtab.obj
6+
RUN: llvm-ar crs out.a coff-symtab.obj
7+
RUN: llvm-nm --print-armap out.a | FileCheck %s
8+
9+
RUN: llvm-as coff-symtab.ll -o coff-symtab.bc
10+
RUN: llvm-ar crs out2.a coff-symtab.bc
11+
RUN: llvm-nm --print-armap out2.a | FileCheck %s
12+
13+
RUN: yaml2obj elf.yaml -o coff-symtab.o
14+
RUN: llvm-ar crs --format coff out3.a coff-symtab.o
15+
RUN: llvm-nm --print-armap out3.a | FileCheck %s
16+
17+
Create an empty archive with no symbol map, add a COFF file to it and check that the output archive is a COFF archive.
18+
19+
RUN: llvm-ar rcS out4.a
20+
RUN: llvm-ar rs out4.a coff-symtab.obj
21+
RUN: llvm-nm --print-armap out4.a | FileCheck %s
22+
23+
CHECK: Archive map
24+
CHECK-NEXT: a in coff-symtab
25+
CHECK-NEXT: b in coff-symtab
26+
CHECK-NEXT: c in coff-symtab
27+
CHECK-EMPTY:
28+
29+
#--- coff-symtab.yaml
30+
--- !COFF
31+
header:
32+
Machine: IMAGE_FILE_MACHINE_UNKNOWN
33+
Characteristics: [ ]
34+
sections:
35+
- Name: .text
36+
Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ]
37+
Alignment: 4
38+
SectionData: ''
39+
symbols:
40+
- Name: b
41+
Value: 0
42+
SectionNumber: 1
43+
SimpleType: IMAGE_SYM_TYPE_NULL
44+
ComplexType: IMAGE_SYM_DTYPE_FUNCTION
45+
StorageClass: IMAGE_SYM_CLASS_EXTERNAL
46+
- Name: c
47+
Value: 0
48+
SectionNumber: 1
49+
SimpleType: IMAGE_SYM_TYPE_NULL
50+
ComplexType: IMAGE_SYM_DTYPE_FUNCTION
51+
StorageClass: IMAGE_SYM_CLASS_EXTERNAL
52+
- Name: a
53+
Value: 0
54+
SectionNumber: 1
55+
SimpleType: IMAGE_SYM_TYPE_NULL
56+
ComplexType: IMAGE_SYM_DTYPE_FUNCTION
57+
StorageClass: IMAGE_SYM_CLASS_EXTERNAL
58+
...
59+
60+
61+
#--- coff-symtab.ll
62+
target triple = "x86_64-unknown-windows-msvc"
63+
64+
define void @b() { ret void }
65+
define void @c() { ret void }
66+
define void @a() { ret void }
67+
68+
#--- elf.yaml
69+
--- !ELF
70+
FileHeader:
71+
Class: ELFCLASS64
72+
Data : ELFDATA2LSB
73+
Type: ET_REL
74+
Machine: EM_X86_64
75+
Sections:
76+
- Name: .text
77+
Type: SHT_PROGBITS
78+
Flags: [ SHF_ALLOC, SHF_EXECINSTR ]
79+
AddressAlign: 0x0000000000000004
80+
Content: ''
81+
Symbols:
82+
- Name: b
83+
Binding: STB_GLOBAL
84+
Section: .text
85+
- Name: c
86+
Binding: STB_GLOBAL
87+
Section: .text
88+
- Name: a
89+
Binding: STB_GLOBAL
90+
Section: .text
91+
...
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
## Create archives with no symtab in various formats and check that we can read them.
2+
3+
# RUN: yaml2obj %s -o %t.o
4+
# RUN: rm -f %t.*.a
5+
6+
# RUN: llvm-ar --format=gnu rcS %t.gnu.a %t.o
7+
# RUN: llvm-ar --format=coff rcS %t.coff.a %t.o
8+
# RUN: llvm-ar --format=darwin rcS %t.darwin.a %t.o
9+
# RUN: llvm-ar --format=bsd rcS %t.bsd.a %t.o
10+
# RUN: llvm-ar --format=bigarchive rcS %t.bigarchive.a %t.o
11+
12+
# RUN: llvm-nm --print-armap %t.gnu.a | FileCheck %s
13+
# RUN: llvm-nm --print-armap %t.coff.a | FileCheck %s
14+
# RUN: llvm-nm --print-armap %t.darwin.a | FileCheck %s
15+
# RUN: llvm-nm --print-armap %t.bsd.a | FileCheck %s
16+
# RUN: llvm-nm --print-armap %t.bigarchive.a | FileCheck %s
17+
18+
# CHECK-NOT: Archive map
19+
20+
--- !ELF
21+
FileHeader:
22+
Class: ELFCLASS64
23+
Data: ELFDATA2LSB
24+
Type: ET_REL
25+
Machine: EM_X86_64
26+
Sections:
27+
- Name: .text
28+
Type: SHT_PROGBITS
29+
Symbols:
30+
- Name: symbol
31+
Binding: STB_GLOBAL
32+
Section: .text

llvm/tools/llvm-ar/llvm-ar.cpp

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ static void printArHelp(StringRef ToolName) {
8282
=darwin - darwin
8383
=bsd - bsd
8484
=bigarchive - big archive (AIX OS)
85+
=coff - coff
8586
--plugin=<string> - ignored for compatibility
8687
-h --help - display this help and exit
8788
--output - the directory to extract archive members to
@@ -193,7 +194,7 @@ static SmallVector<const char *, 256> PositionalArgs;
193194
static bool MRI;
194195

195196
namespace {
196-
enum Format { Default, GNU, BSD, DARWIN, BIGARCHIVE, Unknown };
197+
enum Format { Default, GNU, COFF, BSD, DARWIN, BIGARCHIVE, Unknown };
197198
}
198199

199200
static Format FormatType = Default;
@@ -1025,14 +1026,21 @@ static void performWriteOperation(ArchiveOperation Operation,
10251026
Kind = object::Archive::K_GNU;
10261027
else if (OldArchive) {
10271028
Kind = OldArchive->kind();
1028-
if (Kind == object::Archive::K_BSD) {
1029-
auto InferredKind = object::Archive::K_BSD;
1029+
std::optional<object::Archive::Kind> AltKind;
1030+
if (Kind == object::Archive::K_BSD)
1031+
AltKind = object::Archive::K_DARWIN;
1032+
else if (Kind == object::Archive::K_GNU && !OldArchive->hasSymbolTable())
1033+
// If there is no symbol table, we can't tell GNU from COFF format
1034+
// from the old archive type.
1035+
AltKind = object::Archive::K_COFF;
1036+
if (AltKind) {
1037+
auto InferredKind = Kind;
10301038
if (NewMembersP && !NewMembersP->empty())
10311039
InferredKind = NewMembersP->front().detectKindFromObject();
10321040
else if (!NewMembers.empty())
10331041
InferredKind = NewMembers.front().detectKindFromObject();
1034-
if (InferredKind == object::Archive::K_DARWIN)
1035-
Kind = object::Archive::K_DARWIN;
1042+
if (InferredKind == AltKind)
1043+
Kind = *AltKind;
10361044
}
10371045
} else if (NewMembersP)
10381046
Kind = !NewMembersP->empty() ? NewMembersP->front().detectKindFromObject()
@@ -1044,6 +1052,9 @@ static void performWriteOperation(ArchiveOperation Operation,
10441052
case GNU:
10451053
Kind = object::Archive::K_GNU;
10461054
break;
1055+
case COFF:
1056+
Kind = object::Archive::K_COFF;
1057+
break;
10471058
case BSD:
10481059
if (Thin)
10491060
fail("only the gnu format has a thin mode");
@@ -1376,6 +1387,7 @@ static int ar_main(int argc, char **argv) {
13761387
.Case("darwin", DARWIN)
13771388
.Case("bsd", BSD)
13781389
.Case("bigarchive", BIGARCHIVE)
1390+
.Case("coff", COFF)
13791391
.Default(Unknown);
13801392
if (FormatType == Unknown)
13811393
fail(std::string("Invalid format ") + Match);

0 commit comments

Comments
 (0)