Skip to content

Commit 4415fd8

Browse files
committed
[llvm-ar][Object][COFF] Add support for EC symbols to llvm-ar.
Make writeArchive IsEC argument optional and use EC symbol map when indicated by input object files.
1 parent fd09d51 commit 4415fd8

File tree

4 files changed

+165
-12
lines changed

4 files changed

+165
-12
lines changed

llvm/include/llvm/Object/ArchiveWriter.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ Error writeArchive(StringRef ArcName, ArrayRef<NewArchiveMember> NewMembers,
5252
SymtabWritingMode WriteSymtab, object::Archive::Kind Kind,
5353
bool Deterministic, bool Thin,
5454
std::unique_ptr<MemoryBuffer> OldArchiveBuf = nullptr,
55-
bool IsEC = false);
55+
std::optional<bool> IsEC = std::nullopt);
5656

5757
// writeArchiveToBuffer is similar to writeArchive but returns the Archive in a
5858
// buffer instead of writing it out to a file.

llvm/lib/Object/ArchiveWriter.cpp

+54-11
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ using namespace llvm;
4848
using namespace llvm::object;
4949

5050
struct SymMap {
51-
bool UseECMap;
51+
bool UseECMap = false;
5252
std::map<std::string, uint16_t> Map;
5353
std::map<std::string, uint16_t> ECMap;
5454
};
@@ -678,6 +678,25 @@ static bool isECObject(object::SymbolicFile &Obj) {
678678
return false;
679679
}
680680

681+
static bool isAnyArm64COFF(object::SymbolicFile &Obj) {
682+
if (Obj.isCOFF())
683+
return COFF::isAnyArm64(cast<COFFObjectFile>(&Obj)->getMachine());
684+
685+
if (Obj.isCOFFImportFile())
686+
return COFF::isAnyArm64(cast<COFFImportFile>(&Obj)->getMachine());
687+
688+
if (Obj.isIR()) {
689+
Expected<std::string> TripleStr =
690+
getBitcodeTargetTriple(Obj.getMemoryBufferRef());
691+
if (!TripleStr)
692+
return false;
693+
Triple T(*TripleStr);
694+
return T.isOSWindows() && T.getArch() == Triple::aarch64;
695+
}
696+
697+
return false;
698+
}
699+
681700
bool isImportDescriptor(StringRef Name) {
682701
return Name.starts_with(ImportDescriptorPrefix) ||
683702
Name == StringRef{NullImportDescriptorSymbolName} ||
@@ -731,7 +750,8 @@ static Expected<std::vector<MemberData>>
731750
computeMemberData(raw_ostream &StringTable, raw_ostream &SymNames,
732751
object::Archive::Kind Kind, bool Thin, bool Deterministic,
733752
SymtabWritingMode NeedSymbols, SymMap *SymMap,
734-
LLVMContext &Context, ArrayRef<NewArchiveMember> NewMembers) {
753+
LLVMContext &Context, ArrayRef<NewArchiveMember> NewMembers,
754+
std::optional<bool> IsEC) {
735755
static char PaddingData[8] = {'\n', '\n', '\n', '\n', '\n', '\n', '\n', '\n'};
736756
uint64_t MemHeadPadSize = 0;
737757
uint64_t Pos =
@@ -807,6 +827,30 @@ computeMemberData(raw_ostream &StringTable, raw_ostream &SymNames,
807827
}
808828
}
809829

830+
if (SymMap) {
831+
if (IsEC) {
832+
SymMap->UseECMap = *IsEC;
833+
} else {
834+
// When IsEC is not specified by the caller, use it when we have both
835+
// any ARM64 object (ARM64 or ARM64EC) and any EC object (ARM64EC or
836+
// AMD64). This may be a single ARM64EC object, but may also be separated
837+
// ARM64 and AMD64 objects.
838+
bool HaveArm64 = false, HaveEC = false;
839+
for (std::unique_ptr<SymbolicFile> &SymFile : SymFiles) {
840+
if (!SymFile)
841+
continue;
842+
if (!HaveArm64)
843+
HaveArm64 = isAnyArm64COFF(*SymFile);
844+
if (!HaveEC)
845+
HaveEC = isECObject(*SymFile);
846+
if (HaveArm64 && HaveEC) {
847+
SymMap->UseECMap = true;
848+
break;
849+
}
850+
}
851+
}
852+
}
853+
810854
// The big archive format needs to know the offset of the previous member
811855
// header.
812856
uint64_t PrevOffset = 0;
@@ -953,11 +997,10 @@ Expected<std::string> computeArchiveRelativePath(StringRef From, StringRef To) {
953997
return std::string(Relative);
954998
}
955999

956-
static Error writeArchiveToStream(raw_ostream &Out,
957-
ArrayRef<NewArchiveMember> NewMembers,
958-
SymtabWritingMode WriteSymtab,
959-
object::Archive::Kind Kind,
960-
bool Deterministic, bool Thin, bool IsEC) {
1000+
static Error
1001+
writeArchiveToStream(raw_ostream &Out, ArrayRef<NewArchiveMember> NewMembers,
1002+
SymtabWritingMode WriteSymtab, object::Archive::Kind Kind,
1003+
bool Deterministic, bool Thin, std::optional<bool> IsEC) {
9611004
assert((!Thin || !isBSDLike(Kind)) && "Only the gnu format has a thin mode");
9621005

9631006
SmallString<0> SymNamesBuf;
@@ -977,10 +1020,9 @@ static Error writeArchiveToStream(raw_ostream &Out,
9771020
// reference to it, thus SymbolicFile should be destroyed first.
9781021
LLVMContext Context;
9791022

980-
SymMap.UseECMap = IsEC;
9811023
Expected<std::vector<MemberData>> DataOrErr = computeMemberData(
9821024
StringTable, SymNames, Kind, Thin, Deterministic, WriteSymtab,
983-
isCOFFArchive(Kind) ? &SymMap : nullptr, Context, NewMembers);
1025+
isCOFFArchive(Kind) ? &SymMap : nullptr, Context, NewMembers, IsEC);
9841026
if (Error E = DataOrErr.takeError())
9851027
return E;
9861028
std::vector<MemberData> &Data = *DataOrErr;
@@ -1226,7 +1268,8 @@ static Error writeArchiveToStream(raw_ostream &Out,
12261268
Error writeArchive(StringRef ArcName, ArrayRef<NewArchiveMember> NewMembers,
12271269
SymtabWritingMode WriteSymtab, object::Archive::Kind Kind,
12281270
bool Deterministic, bool Thin,
1229-
std::unique_ptr<MemoryBuffer> OldArchiveBuf, bool IsEC) {
1271+
std::unique_ptr<MemoryBuffer> OldArchiveBuf,
1272+
std::optional<bool> IsEC) {
12301273
Expected<sys::fs::TempFile> Temp =
12311274
sys::fs::TempFile::create(ArcName + ".temp-archive-%%%%%%%.a");
12321275
if (!Temp)
@@ -1263,7 +1306,7 @@ writeArchiveToBuffer(ArrayRef<NewArchiveMember> NewMembers,
12631306
raw_svector_ostream ArchiveStream(ArchiveBufferVector);
12641307

12651308
if (Error E = writeArchiveToStream(ArchiveStream, NewMembers, WriteSymtab,
1266-
Kind, Deterministic, Thin, false))
1309+
Kind, Deterministic, Thin, std::nullopt))
12671310
return std::move(E);
12681311

12691312
return std::make_unique<SmallVectorMemoryBuffer>(
+83
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
## Test that ECSYMBOLS section is created when ARM64EC is used.
2+
3+
# RUN: yaml2obj %s -o %t.arm64ec.obj -DMACHINE=IMAGE_FILE_MACHINE_ARM64EC
4+
# RUN: yaml2obj %s -o %t.arm64.obj -DMACHINE=IMAGE_FILE_MACHINE_ARM64
5+
# RUN: yaml2obj %s -o %t.amd64.obj -DMACHINE=IMAGE_FILE_MACHINE_AMD64
6+
7+
## Create ARM64EC archive
8+
# RUN: rm -f %t.a
9+
# RUN: llvm-ar crs %t.a %t.arm64ec.obj
10+
# RUN: llvm-nm --print-armap %t.a | FileCheck --check-prefixes=NOMAP,ECMAP %s
11+
12+
## Add ARM64 object to the archive
13+
# RUN: llvm-ar rs %t.a %t.arm64.obj
14+
# RUN: llvm-nm --print-armap %t.a | FileCheck --check-prefixes=MAP,ECMAP %s
15+
16+
## Create ARM64 archive
17+
# RUN: rm -f %t.a
18+
# RUN: llvm-ar crs %t.a %t.arm64.obj
19+
# RUN: llvm-nm --print-armap %t.a | FileCheck --check-prefixes=MAP,NOECMAP %s
20+
21+
# Add ARM64EC object to the archive
22+
# RUN: llvm-ar rs %t.a %t.arm64ec.obj
23+
# RUN: llvm-nm --print-armap %t.a | FileCheck --check-prefixes=MAP,ECMAP %s
24+
25+
## Create mixed archive
26+
# RUN: rm -f %t.a
27+
# RUN: llvm-ar crs %t.a %t.arm64ec.obj %t.arm64.obj
28+
# RUN: llvm-nm --print-armap %t.a | FileCheck --check-prefixes=MAP,ECMAP %s
29+
30+
## Create mixed archive
31+
# RUN: rm -f %t.a
32+
# RUN: llvm-ar crs %t.a %t.amd64.obj %t.arm64.obj
33+
# RUN: llvm-nm --print-armap %t.a | FileCheck --check-prefixes=MAP,AMDECMAP %s
34+
35+
# MAP: Archive map
36+
# MAP-NEXT: a in ecsymbols.yaml.tmp.arm64.obj
37+
# MAP-NEXT: b in ecsymbols.yaml.tmp.arm64.obj
38+
# MAP-NEXT: c in ecsymbols.yaml.tmp.arm64.obj
39+
# MAP-EMPTY:
40+
# NOMAP-NOT: Archive map
41+
42+
# ECMAP: Archive EC map
43+
# ECMAP-NEXT: a in ecsymbols.yaml.tmp.arm64ec.obj
44+
# ECMAP-NEXT: b in ecsymbols.yaml.tmp.arm64ec.obj
45+
# ECMAP-NEXT: c in ecsymbols.yaml.tmp.arm64ec.obj
46+
# ECMAP-EMPTY:
47+
# NOECMAP-NOT: Archive EC map
48+
49+
# AMDECMAP: Archive EC map
50+
# AMDECMAP-NEXT: a in ecsymbols.yaml.tmp.amd64.obj
51+
# AMDECMAP-NEXT: b in ecsymbols.yaml.tmp.amd64.obj
52+
# AMDECMAP-NEXT: c in ecsymbols.yaml.tmp.amd64.obj
53+
# ECMAP-EMPTY:
54+
55+
--- !COFF
56+
header:
57+
Machine: [[MACHINE]]
58+
Characteristics: [ ]
59+
sections:
60+
- Name: .text
61+
Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ]
62+
Alignment: 4
63+
SectionData: ''
64+
symbols:
65+
- Name: b
66+
Value: 0
67+
SectionNumber: 1
68+
SimpleType: IMAGE_SYM_TYPE_NULL
69+
ComplexType: IMAGE_SYM_DTYPE_FUNCTION
70+
StorageClass: IMAGE_SYM_CLASS_EXTERNAL
71+
- Name: c
72+
Value: 0
73+
SectionNumber: 1
74+
SimpleType: IMAGE_SYM_TYPE_NULL
75+
ComplexType: IMAGE_SYM_DTYPE_FUNCTION
76+
StorageClass: IMAGE_SYM_CLASS_EXTERNAL
77+
- Name: a
78+
Value: 0
79+
SectionNumber: 1
80+
SimpleType: IMAGE_SYM_TYPE_NULL
81+
ComplexType: IMAGE_SYM_DTYPE_FUNCTION
82+
StorageClass: IMAGE_SYM_CLASS_EXTERNAL
83+
...

llvm/test/tools/llvm-lib/empty.test

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
Create import libraries with empty exports and make sure that archive symbols
2+
are properly populated.
3+
4+
RUN: split-file %s %t.dir && cd %t.dir
5+
6+
RUN: llvm-lib -machine:arm64 -out:arm64.lib -def:test.def
7+
RUN: llvm-nm --print-armap arm64.lib | FileCheck --check-prefixes=CHECK,NOECMAP %s
8+
9+
RUN: llvm-lib -machine:arm64ec -out:arm64ec.lib -def:test.def
10+
RUN: llvm-nm --print-armap arm64ec.lib | FileCheck --check-prefixes=CHECK,ECMAP %s
11+
12+
CHECK: Archive map
13+
CHECK-NEXT: __IMPORT_DESCRIPTOR_test in test.dll
14+
CHECK-NEXT: __NULL_IMPORT_DESCRIPTOR in test.dll
15+
CHECK-NEXT: test_NULL_THUNK_DATA in test.dll
16+
CHECK-EMPTY:
17+
18+
ECMAP: Archive EC map
19+
ECMAP-NEXT: __IMPORT_DESCRIPTOR_test in test.dll
20+
ECMAP-NEXT: __NULL_IMPORT_DESCRIPTOR in test.dll
21+
ECMAP-NEXT: test_NULL_THUNK_DATA in test.dll
22+
23+
NOECMAP-NOT: Archive EC map
24+
25+
#--- test.def
26+
LIBRARY test.dll
27+
EXPORTS

0 commit comments

Comments
 (0)