Skip to content

Commit 33c6b77

Browse files
authored
[llvm-lib][Object] Add support for EC importlib symbols. (#81059)
ARM64EC import libraries expose two additional symbols: mangled thunk symbol (like `#func`) and auxiliary import symbol (like`__imp_aux_func`). The main functional change with this patch is that those symbols are properly added to static library ECSYMBOLS.
1 parent 8884ba4 commit 33c6b77

File tree

7 files changed

+225
-32
lines changed

7 files changed

+225
-32
lines changed

llvm/include/llvm/Object/COFF.h

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1362,6 +1362,47 @@ class SectionStrippedError
13621362
SectionStrippedError() { setErrorCode(object_error::section_stripped); }
13631363
};
13641364

1365+
inline std::optional<std::string>
1366+
getArm64ECMangledFunctionName(StringRef Name) {
1367+
bool IsCppFn = Name[0] == '?';
1368+
if (IsCppFn && Name.find("$$h") != std::string::npos)
1369+
return std::nullopt;
1370+
if (!IsCppFn && Name[0] == '#')
1371+
return std::nullopt;
1372+
1373+
StringRef Prefix = "$$h";
1374+
size_t InsertIdx = 0;
1375+
if (IsCppFn) {
1376+
InsertIdx = Name.find("@@");
1377+
size_t ThreeAtSignsIdx = Name.find("@@@");
1378+
if (InsertIdx != std::string::npos && InsertIdx != ThreeAtSignsIdx) {
1379+
InsertIdx += 2;
1380+
} else {
1381+
InsertIdx = Name.find("@");
1382+
if (InsertIdx != std::string::npos)
1383+
InsertIdx++;
1384+
}
1385+
} else {
1386+
Prefix = "#";
1387+
}
1388+
1389+
return std::optional<std::string>(
1390+
(Name.substr(0, InsertIdx) + Prefix + Name.substr(InsertIdx)).str());
1391+
}
1392+
1393+
inline std::optional<std::string>
1394+
getArm64ECDemangledFunctionName(StringRef Name) {
1395+
if (Name[0] == '#')
1396+
return std::string(Name.substr(1));
1397+
if (Name[0] != '?')
1398+
return std::nullopt;
1399+
1400+
std::pair<StringRef, StringRef> Pair = Name.split("$$h");
1401+
if (Pair.second.empty())
1402+
return std::nullopt;
1403+
return (Pair.first + Pair.second).str();
1404+
}
1405+
13651406
} // end namespace object
13661407

13671408
} // end namespace llvm

llvm/include/llvm/Object/COFFImportFile.h

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@ namespace llvm {
2727
namespace object {
2828

2929
class COFFImportFile : public SymbolicFile {
30+
private:
31+
enum SymbolIndex { ImpSymbol, ThunkSymbol, ECAuxSymbol, ECThunkSymbol };
32+
3033
public:
3134
COFFImportFile(MemoryBufferRef Source)
3235
: SymbolicFile(ID_COFFImportFile, Source) {}
@@ -36,9 +39,23 @@ class COFFImportFile : public SymbolicFile {
3639
void moveSymbolNext(DataRefImpl &Symb) const override { ++Symb.p; }
3740

3841
Error printSymbolName(raw_ostream &OS, DataRefImpl Symb) const override {
39-
if (Symb.p == 0)
42+
switch (Symb.p) {
43+
case ImpSymbol:
4044
OS << "__imp_";
41-
OS << StringRef(Data.getBufferStart() + sizeof(coff_import_header));
45+
break;
46+
case ECAuxSymbol:
47+
OS << "__imp_aux_";
48+
break;
49+
}
50+
const char *Name = Data.getBufferStart() + sizeof(coff_import_header);
51+
if (Symb.p != ECThunkSymbol && COFF::isArm64EC(getMachine())) {
52+
if (std::optional<std::string> DemangledName =
53+
getArm64ECDemangledFunctionName(Name)) {
54+
OS << StringRef(*DemangledName);
55+
return Error::success();
56+
}
57+
}
58+
OS << StringRef(Name);
4259
return Error::success();
4360
}
4461

@@ -52,7 +69,12 @@ class COFFImportFile : public SymbolicFile {
5269

5370
basic_symbol_iterator symbol_end() const override {
5471
DataRefImpl Symb;
55-
Symb.p = isData() ? 1 : 2;
72+
if (isData())
73+
Symb.p = ImpSymbol + 1;
74+
else if (COFF::isArm64EC(getMachine()))
75+
Symb.p = ECThunkSymbol + 1;
76+
else
77+
Symb.p = ThunkSymbol + 1;
5678
return BasicSymbolRef(Symb, this);
5779
}
5880

llvm/lib/Object/COFFImportFile.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -684,6 +684,21 @@ Error writeImportLibrary(StringRef ImportName, StringRef Path,
684684
NameType = getNameType(SymbolName, E.Name, Machine, MinGW);
685685
}
686686

687+
// On ARM64EC, use EXPORTAS to import demangled name for mangled symbols.
688+
if (ImportType == IMPORT_CODE && isArm64EC(Machine)) {
689+
if (std::optional<std::string> MangledName =
690+
getArm64ECMangledFunctionName(Name)) {
691+
if (ExportName.empty()) {
692+
NameType = IMPORT_NAME_EXPORTAS;
693+
ExportName.swap(Name);
694+
}
695+
Name = std::move(*MangledName);
696+
} else if (ExportName.empty()) {
697+
NameType = IMPORT_NAME_EXPORTAS;
698+
ExportName = std::move(*getArm64ECDemangledFunctionName(Name));
699+
}
700+
}
701+
687702
Members.push_back(OF.createShortImport(Name, E.Ordinal, ImportType,
688703
NameType, ExportName, Machine));
689704
}

llvm/lib/Target/AArch64/AArch64Arm64ECCallLowering.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,13 @@
2424
#include "llvm/IR/IRBuilder.h"
2525
#include "llvm/IR/Instruction.h"
2626
#include "llvm/InitializePasses.h"
27+
#include "llvm/Object/COFF.h"
2728
#include "llvm/Pass.h"
2829
#include "llvm/Support/CommandLine.h"
2930
#include "llvm/TargetParser/Triple.h"
3031

3132
using namespace llvm;
33+
using namespace llvm::object;
3234

3335
using OperandBundleDef = OperandBundleDefT<Value *>;
3436

llvm/lib/Target/AArch64/AArch64MCInstLower.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,13 @@
2323
#include "llvm/MC/MCExpr.h"
2424
#include "llvm/MC/MCInst.h"
2525
#include "llvm/MC/MCStreamer.h"
26+
#include "llvm/Object/COFF.h"
2627
#include "llvm/Support/CodeGen.h"
2728
#include "llvm/Support/CommandLine.h"
2829
#include "llvm/Target/TargetLoweringObjectFile.h"
2930
#include "llvm/Target/TargetMachine.h"
3031
using namespace llvm;
32+
using namespace llvm::object;
3133

3234
extern cl::opt<bool> EnableAArch64ELFLocalDynamicTLSGeneration;
3335

llvm/lib/Target/AArch64/Utils/AArch64BaseInfo.h

Lines changed: 0 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -248,34 +248,6 @@ static inline bool atomicBarrierDroppedOnZero(unsigned Opcode) {
248248
return false;
249249
}
250250

251-
static inline std::optional<std::string>
252-
getArm64ECMangledFunctionName(std::string Name) {
253-
bool IsCppFn = Name[0] == '?';
254-
if (IsCppFn && Name.find("$$h") != std::string::npos)
255-
return std::nullopt;
256-
if (!IsCppFn && Name[0] == '#')
257-
return std::nullopt;
258-
259-
StringRef Prefix = "$$h";
260-
size_t InsertIdx = 0;
261-
if (IsCppFn) {
262-
InsertIdx = Name.find("@@");
263-
size_t ThreeAtSignsIdx = Name.find("@@@");
264-
if (InsertIdx != std::string::npos && InsertIdx != ThreeAtSignsIdx) {
265-
InsertIdx += 2;
266-
} else {
267-
InsertIdx = Name.find("@");
268-
if (InsertIdx != std::string::npos)
269-
InsertIdx++;
270-
}
271-
} else {
272-
Prefix = "#";
273-
}
274-
275-
Name.insert(Name.begin() + InsertIdx, Prefix.begin(), Prefix.end());
276-
return std::optional<std::string>(Name);
277-
}
278-
279251
namespace AArch64CC {
280252

281253
// The CondCodes constants map directly to the 4-bit encoding of the condition

llvm/test/tools/llvm-lib/arm64ec-implib.test

Lines changed: 140 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,23 @@ ARMAP-NEXT: __NULL_IMPORT_DESCRIPTOR in test.dll
1111
ARMAP-NEXT: test_NULL_THUNK_DATA in test.dll
1212
ARMAP-EMPTY:
1313
ARMAP-NEXT: Archive EC map
14+
ARMAP-NEXT: #expname in test.dll
15+
ARMAP-NEXT: #funcexp in test.dll
16+
ARMAP-NEXT: #mangledfunc in test.dll
17+
ARMAP-NEXT: ?test_cpp_func@@$$hYAHPEAX@Z in test.dll
18+
ARMAP-NEXT: ?test_cpp_func@@YAHPEAX@Z in test.dll
19+
ARMAP-NEXT: __imp_?test_cpp_func@@YAHPEAX@Z in test.dll
20+
ARMAP-NEXT: __imp_aux_?test_cpp_func@@YAHPEAX@Z in test.dll
21+
ARMAP-NEXT: __imp_aux_expname in test.dll
22+
ARMAP-NEXT: __imp_aux_funcexp in test.dll
23+
ARMAP-NEXT: __imp_aux_mangledfunc in test.dll
1424
ARMAP-NEXT: __imp_dataexp in test.dll
25+
ARMAP-NEXT: __imp_expname in test.dll
1526
ARMAP-NEXT: __imp_funcexp in test.dll
27+
ARMAP-NEXT: __imp_mangledfunc in test.dll
28+
ARMAP-NEXT: expname in test.dll
1629
ARMAP-NEXT: funcexp in test.dll
30+
ARMAP-NEXT: mangledfunc in test.dll
1731

1832
RUN: llvm-readobj test.lib | FileCheck -check-prefix=READOBJ %s
1933

@@ -35,10 +49,42 @@ READOBJ-EMPTY:
3549
READOBJ-NEXT: File: test.dll
3650
READOBJ-NEXT: Format: COFF-import-file-ARM64EC
3751
READOBJ-NEXT: Type: code
38-
READOBJ-NEXT: Name type: name
52+
READOBJ-NEXT: Name type: export as
3953
READOBJ-NEXT: Export name: funcexp
4054
READOBJ-NEXT: Symbol: __imp_funcexp
4155
READOBJ-NEXT: Symbol: funcexp
56+
READOBJ-NEXT: Symbol: __imp_aux_funcexp
57+
READOBJ-NEXT: Symbol: #funcexp
58+
READOBJ-EMPTY:
59+
READOBJ-NEXT: File: test.dll
60+
READOBJ-NEXT: Format: COFF-import-file-ARM64EC
61+
READOBJ-NEXT: Type: code
62+
READOBJ-NEXT: Name type: export as
63+
READOBJ-NEXT: Export name: mangledfunc
64+
READOBJ-NEXT: Symbol: __imp_mangledfunc
65+
READOBJ-NEXT: Symbol: mangledfunc
66+
READOBJ-NEXT: Symbol: __imp_aux_mangledfunc
67+
READOBJ-NEXT: Symbol: #mangledfunc
68+
READOBJ-EMPTY:
69+
READOBJ-NEXT: File: test.dll
70+
READOBJ-NEXT: Format: COFF-import-file-ARM64EC
71+
READOBJ-NEXT: Type: code
72+
READOBJ-NEXT: Name type: export as
73+
READOBJ-NEXT: Export name: ?test_cpp_func@@YAHPEAX@Z
74+
READOBJ-NEXT: Symbol: __imp_?test_cpp_func@@YAHPEAX@Z
75+
READOBJ-NEXT: Symbol: ?test_cpp_func@@YAHPEAX@Z
76+
READOBJ-NEXT: Symbol: __imp_aux_?test_cpp_func@@YAHPEAX@Z
77+
READOBJ-NEXT: Symbol: ?test_cpp_func@@$$hYAHPEAX@Z
78+
READOBJ-EMPTY:
79+
READOBJ-NEXT: File: test.dll
80+
READOBJ-NEXT: Format: COFF-import-file-ARM64EC
81+
READOBJ-NEXT: Type: code
82+
READOBJ-NEXT: Name type: export as
83+
READOBJ-NEXT: Export name: expname
84+
READOBJ-NEXT: Symbol: __imp_expname
85+
READOBJ-NEXT: Symbol: expname
86+
READOBJ-NEXT: Symbol: __imp_aux_expname
87+
READOBJ-NEXT: Symbol: #expname
4288
READOBJ-EMPTY:
4389
READOBJ-NEXT: File: test.dll
4490
READOBJ-NEXT: Format: COFF-import-file-ARM64EC
@@ -51,8 +97,101 @@ Creating a new lib containing the existing lib:
5197
RUN: llvm-lib -machine:arm64ec test.lib -out:test2.lib
5298
RUN: llvm-nm --print-armap test2.lib | FileCheck -check-prefix=ARMAP %s
5399

100+
101+
RUN: llvm-lib -machine:arm64ec -def:exportas.def -out:exportas.lib
102+
RUN: llvm-nm --print-armap exportas.lib | FileCheck -check-prefix=EXPAS-ARMAP %s
103+
RUN: llvm-readobj exportas.lib | FileCheck -check-prefix=EXPAS-READOBJ %s
104+
105+
EXPAS-ARMAP: Archive EC map
106+
EXPAS-ARMAP-NEXT: #func1 in test.dll
107+
EXPAS-ARMAP-NEXT: #func2 in test.dll
108+
EXPAS-ARMAP-NEXT: #func3 in test.dll
109+
EXPAS-ARMAP-NEXT: #func4 in test.dll
110+
EXPAS-ARMAP-NEXT: __imp_aux_func1 in test.dll
111+
EXPAS-ARMAP-NEXT: __imp_aux_func2 in test.dll
112+
EXPAS-ARMAP-NEXT: __imp_aux_func3 in test.dll
113+
EXPAS-ARMAP-NEXT: __imp_aux_func4 in test.dll
114+
EXPAS-ARMAP-NEXT: __imp_data1 in test.dll
115+
EXPAS-ARMAP-NEXT: __imp_data2 in test.dll
116+
EXPAS-ARMAP-NEXT: __imp_func1 in test.dll
117+
EXPAS-ARMAP-NEXT: __imp_func2 in test.dll
118+
EXPAS-ARMAP-NEXT: __imp_func3 in test.dll
119+
EXPAS-ARMAP-NEXT: __imp_func4 in test.dll
120+
EXPAS-ARMAP-NEXT: func1 in test.dll
121+
EXPAS-ARMAP-NEXT: func2 in test.dll
122+
EXPAS-ARMAP-NEXT: func3 in test.dll
123+
EXPAS-ARMAP-NEXT: func4 in test.dll
124+
125+
EXPAS-READOBJ: File: test.dll
126+
EXPAS-READOBJ-NEXT: Format: COFF-import-file-ARM64EC
127+
EXPAS-READOBJ-NEXT: Type: code
128+
EXPAS-READOBJ-NEXT: Name type: export as
129+
EXPAS-READOBJ-NEXT: Export name: func1
130+
EXPAS-READOBJ-NEXT: Symbol: __imp_func1
131+
EXPAS-READOBJ-NEXT: Symbol: func1
132+
EXPAS-READOBJ-NEXT: Symbol: __imp_aux_func1
133+
EXPAS-READOBJ-NEXT: Symbol: #func1
134+
EXPAS-READOBJ-EMPTY:
135+
EXPAS-READOBJ-NEXT: File: test.dll
136+
EXPAS-READOBJ-NEXT: Format: COFF-import-file-ARM64EC
137+
EXPAS-READOBJ-NEXT: Type: code
138+
EXPAS-READOBJ-NEXT: Name type: export as
139+
EXPAS-READOBJ-NEXT: Export name: func2
140+
EXPAS-READOBJ-NEXT: Symbol: __imp_func2
141+
EXPAS-READOBJ-NEXT: Symbol: func2
142+
EXPAS-READOBJ-NEXT: Symbol: __imp_aux_func2
143+
EXPAS-READOBJ-NEXT: Symbol: #func2
144+
EXPAS-READOBJ-EMPTY:
145+
EXPAS-READOBJ-NEXT: File: test.dll
146+
EXPAS-READOBJ-NEXT: Format: COFF-import-file-ARM64EC
147+
EXPAS-READOBJ-NEXT: Type: code
148+
EXPAS-READOBJ-NEXT: Name type: export as
149+
EXPAS-READOBJ-NEXT: Export name: #func3
150+
EXPAS-READOBJ-NEXT: Symbol: __imp_func3
151+
EXPAS-READOBJ-NEXT: Symbol: func3
152+
EXPAS-READOBJ-NEXT: Symbol: __imp_aux_func3
153+
EXPAS-READOBJ-NEXT: Symbol: #func3
154+
EXPAS-READOBJ-EMPTY:
155+
EXPAS-READOBJ-NEXT: File: test.dll
156+
EXPAS-READOBJ-NEXT: Format: COFF-import-file-ARM64EC
157+
EXPAS-READOBJ-NEXT: Type: code
158+
EXPAS-READOBJ-NEXT: Name type: export as
159+
EXPAS-READOBJ-NEXT: Export name: #func4
160+
EXPAS-READOBJ-NEXT: Symbol: __imp_func4
161+
EXPAS-READOBJ-NEXT: Symbol: func4
162+
EXPAS-READOBJ-NEXT: Symbol: __imp_aux_func4
163+
EXPAS-READOBJ-NEXT: Symbol: #func4
164+
EXPAS-READOBJ-EMPTY:
165+
EXPAS-READOBJ-NEXT: File: test.dll
166+
EXPAS-READOBJ-NEXT: Format: COFF-import-file-ARM64EC
167+
EXPAS-READOBJ-NEXT: Type: data
168+
EXPAS-READOBJ-NEXT: Name type: export as
169+
EXPAS-READOBJ-NEXT: Export name: #data1
170+
EXPAS-READOBJ-NEXT: Symbol: __imp_data1
171+
EXPAS-READOBJ-EMPTY:
172+
EXPAS-READOBJ-NEXT: File: test.dll
173+
EXPAS-READOBJ-NEXT: Format: COFF-import-file-ARM64EC
174+
EXPAS-READOBJ-NEXT: Type: data
175+
EXPAS-READOBJ-NEXT: Name type: export as
176+
EXPAS-READOBJ-NEXT: Export name: data2
177+
EXPAS-READOBJ-NEXT: Symbol: __imp_data2
178+
179+
54180
#--- test.def
55181
LIBRARY test.dll
56182
EXPORTS
57183
funcexp
184+
#mangledfunc
185+
?test_cpp_func@@YAHPEAX@Z
186+
expname=impname
58187
dataexp DATA
188+
189+
#--- exportas.def
190+
LIBRARY test.dll
191+
EXPORTS
192+
#func1 EXPORTAS func1
193+
func2 EXPORTAS func2
194+
func3 EXPORTAS #func3
195+
#func4 EXPORTAS #func4
196+
data1 DATA EXPORTAS #data1
197+
#data2 DATA EXPORTAS data2

0 commit comments

Comments
 (0)