Skip to content

Commit 8f23464

Browse files
authored
[llvm-lib][llvm-dlltool][Object] Add support for EXPORTAS name types. (#78772)
EXPORTAS is a new name type in import libraries. It's used by default on ARM64EC, but it's allowed on other platforms as well.
1 parent 8509f75 commit 8f23464

File tree

6 files changed

+162
-23
lines changed

6 files changed

+162
-23
lines changed

llvm/include/llvm/BinaryFormat/COFF.h

+4-1
Original file line numberDiff line numberDiff line change
@@ -716,7 +716,10 @@ enum ImportNameType : unsigned {
716716
IMPORT_NAME_NOPREFIX = 2,
717717
/// The import name is the public symbol name, but skipping the leading ?,
718718
/// @, or optionally _, and truncating at the first @.
719-
IMPORT_NAME_UNDECORATE = 3
719+
IMPORT_NAME_UNDECORATE = 3,
720+
/// The import name is specified as a separate string in the import library
721+
/// object file.
722+
IMPORT_NAME_EXPORTAS = 4
720723
};
721724

722725
enum class GuardFlags : uint32_t {

llvm/include/llvm/Object/COFFImportFile.h

+4
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,10 @@ struct COFFShortExport {
9292
/// file, this is "baz" in "EXPORTS\nfoo = bar == baz".
9393
std::string AliasTarget;
9494

95+
/// Specifies EXPORTAS name. In a .def file, this is "bar" in
96+
/// "EXPORTS\nfoo EXPORTAS bar".
97+
std::string ExportAs;
98+
9599
uint16_t Ordinal = 0;
96100
bool Noname = false;
97101
bool Data = false;

llvm/lib/Object/COFFImportFile.cpp

+45-21
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,12 @@ StringRef COFFImportFile::getExportName() const {
7171
name = ltrim1(name, "?@_");
7272
name = name.substr(0, name.find('@'));
7373
break;
74+
case IMPORT_NAME_EXPORTAS: {
75+
// Skip DLL name
76+
name = Data.getBuffer().substr(sizeof(*hdr) + name.size() + 1);
77+
name = name.split('\0').second.split('\0').first;
78+
break;
79+
}
7480
default:
7581
break;
7682
}
@@ -209,6 +215,7 @@ class ObjectFactory {
209215
// Library Format.
210216
NewArchiveMember createShortImport(StringRef Sym, uint16_t Ordinal,
211217
ImportType Type, ImportNameType NameType,
218+
StringRef ExportName,
212219
MachineTypes Machine);
213220

214221
// Create a weak external file which is described in PE/COFF Aux Format 3.
@@ -500,12 +507,13 @@ NewArchiveMember ObjectFactory::createNullThunk(std::vector<uint8_t> &Buffer) {
500507
return {MemoryBufferRef{F, ImportName}};
501508
}
502509

503-
NewArchiveMember ObjectFactory::createShortImport(StringRef Sym,
504-
uint16_t Ordinal,
505-
ImportType ImportType,
506-
ImportNameType NameType,
507-
MachineTypes Machine) {
510+
NewArchiveMember
511+
ObjectFactory::createShortImport(StringRef Sym, uint16_t Ordinal,
512+
ImportType ImportType, ImportNameType NameType,
513+
StringRef ExportName, MachineTypes Machine) {
508514
size_t ImpSize = ImportName.size() + Sym.size() + 2; // +2 for NULs
515+
if (!ExportName.empty())
516+
ImpSize += ExportName.size() + 1;
509517
size_t Size = sizeof(coff_import_header) + ImpSize;
510518
char *Buf = Alloc.Allocate<char>(Size);
511519
memset(Buf, 0, Size);
@@ -525,6 +533,10 @@ NewArchiveMember ObjectFactory::createShortImport(StringRef Sym,
525533
memcpy(P, Sym.data(), Sym.size());
526534
P += Sym.size() + 1;
527535
memcpy(P, ImportName.data(), ImportName.size());
536+
if (!ExportName.empty()) {
537+
P += ImportName.size() + 1;
538+
memcpy(P, ExportName.data(), ExportName.size());
539+
}
528540

529541
return {MemoryBufferRef(StringRef(Buf, Size), ImportName)};
530542
}
@@ -641,27 +653,39 @@ Error writeImportLibrary(StringRef ImportName, StringRef Path,
641653
ImportType = IMPORT_CONST;
642654

643655
StringRef SymbolName = E.SymbolName.empty() ? E.Name : E.SymbolName;
644-
ImportNameType NameType = E.Noname
645-
? IMPORT_ORDINAL
646-
: getNameType(SymbolName, E.Name,
647-
Machine, MinGW);
648-
Expected<std::string> Name = E.ExtName.empty()
649-
? std::string(SymbolName)
650-
: replace(SymbolName, E.Name, E.ExtName);
651-
652-
if (!Name)
653-
return Name.takeError();
654-
655-
if (!E.AliasTarget.empty() && *Name != E.AliasTarget) {
656+
std::string Name;
657+
658+
if (E.ExtName.empty()) {
659+
Name = std::string(SymbolName);
660+
} else {
661+
Expected<std::string> ReplacedName =
662+
replace(SymbolName, E.Name, E.ExtName);
663+
if (!ReplacedName)
664+
return ReplacedName.takeError();
665+
Name.swap(*ReplacedName);
666+
}
667+
668+
if (!E.AliasTarget.empty() && Name != E.AliasTarget) {
656669
Members.push_back(
657-
OF.createWeakExternal(E.AliasTarget, *Name, false, Machine));
670+
OF.createWeakExternal(E.AliasTarget, Name, false, Machine));
658671
Members.push_back(
659-
OF.createWeakExternal(E.AliasTarget, *Name, true, Machine));
672+
OF.createWeakExternal(E.AliasTarget, Name, true, Machine));
660673
continue;
661674
}
662675

663-
Members.push_back(
664-
OF.createShortImport(*Name, E.Ordinal, ImportType, NameType, Machine));
676+
ImportNameType NameType;
677+
std::string ExportName;
678+
if (E.Noname) {
679+
NameType = IMPORT_ORDINAL;
680+
} else if (!E.ExportAs.empty()) {
681+
NameType = IMPORT_NAME_EXPORTAS;
682+
ExportName = E.ExportAs;
683+
} else {
684+
NameType = getNameType(SymbolName, E.Name, Machine, MinGW);
685+
}
686+
687+
Members.push_back(OF.createShortImport(Name, E.Ordinal, ImportType,
688+
NameType, ExportName, Machine));
665689
}
666690

667691
return writeArchive(Path, Members, SymtabWritingMode::NormalSymtab,

llvm/lib/Object/COFFModuleDefinition.cpp

+12-1
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ enum Kind {
3939
KwConstant,
4040
KwData,
4141
KwExports,
42+
KwExportAs,
4243
KwHeapsize,
4344
KwLibrary,
4445
KwName,
@@ -116,6 +117,7 @@ class Lexer {
116117
.Case("CONSTANT", KwConstant)
117118
.Case("DATA", KwData)
118119
.Case("EXPORTS", KwExports)
120+
.Case("EXPORTAS", KwExportAs)
119121
.Case("HEAPSIZE", KwHeapsize)
120122
.Case("LIBRARY", KwLibrary)
121123
.Case("NAME", KwName)
@@ -284,7 +286,16 @@ class Parser {
284286
E.AliasTarget = std::string("_").append(E.AliasTarget);
285287
continue;
286288
}
287-
unget();
289+
// EXPORTAS must be at the end of export definition
290+
if (Tok.K == KwExportAs) {
291+
read();
292+
if (Tok.K == Eof)
293+
return createError(
294+
"unexpected end of file, EXPORTAS identifier expected");
295+
E.ExportAs = std::string(Tok.Value);
296+
} else {
297+
unget();
298+
}
288299
Info.Exports.push_back(E);
289300
return Error::success();
290301
}
+94
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
Test EXPORTAS in importlibs.
2+
3+
RUN: split-file %s %t.dir && cd %t.dir
4+
RUN: llvm-lib -machine:amd64 -def:test.def -out:test.lib
5+
6+
RUN: llvm-nm --print-armap test.lib | FileCheck --check-prefix=ARMAP %s
7+
8+
ARMAP: Archive map
9+
ARMAP-NEXT: __IMPORT_DESCRIPTOR_test in test.dll
10+
ARMAP-NEXT: __NULL_IMPORT_DESCRIPTOR in test.dll
11+
ARMAP-NEXT: __imp_func in test.dll
12+
ARMAP-NEXT: __imp_func2 in test.dll
13+
ARMAP-NEXT: __imp_func3 in test.dll
14+
ARMAP-NEXT: __imp_mydata in test.dll
15+
ARMAP-NEXT: func in test.dll
16+
ARMAP-NEXT: func2 in test.dll
17+
ARMAP-NEXT: func3 in test.dll
18+
ARMAP-NEXT: test_NULL_THUNK_DATA in test.dll
19+
20+
RUN: llvm-readobj test.lib | FileCheck --check-prefix=READOBJ %s
21+
22+
READOBJ: File: test.lib(test.dll)
23+
READOBJ-NEXT: Format: COFF-x86-64
24+
READOBJ-NEXT: Arch: x86_64
25+
READOBJ-NEXT: AddressSize: 64bit
26+
READOBJ-EMPTY:
27+
READOBJ-NEXT: File: test.lib(test.dll)
28+
READOBJ-NEXT: Format: COFF-x86-64
29+
READOBJ-NEXT: Arch: x86_64
30+
READOBJ-NEXT: AddressSize: 64bit
31+
READOBJ-EMPTY:
32+
READOBJ-NEXT: File: test.lib(test.dll)
33+
READOBJ-NEXT: Format: COFF-x86-64
34+
READOBJ-NEXT: Arch: x86_64
35+
READOBJ-NEXT: AddressSize: 64bit
36+
READOBJ-EMPTY:
37+
READOBJ-NEXT: File: test.dll
38+
READOBJ-NEXT: Format: COFF-import-file-x86-64
39+
READOBJ-NEXT: Type: code
40+
READOBJ-NEXT: Name type: export as
41+
READOBJ-NEXT: Export name: expfunc
42+
READOBJ-NEXT: Symbol: __imp_func
43+
READOBJ-NEXT: Symbol: func
44+
READOBJ-EMPTY:
45+
READOBJ-NEXT: File: test.dll
46+
READOBJ-NEXT: Format: COFF-import-file-x86-64
47+
READOBJ-NEXT: Type: data
48+
READOBJ-NEXT: Name type: export as
49+
READOBJ-NEXT: Export name: expdata
50+
READOBJ-NEXT: Symbol: __imp_mydata
51+
READOBJ-EMPTY:
52+
READOBJ-NEXT: File: test.dll
53+
READOBJ-NEXT: Format: COFF-import-file-x86-64
54+
READOBJ-NEXT: Type: code
55+
READOBJ-NEXT: Name type: export as
56+
READOBJ-NEXT: Export name: expfunc2
57+
READOBJ-NEXT: Symbol: __imp_func2
58+
READOBJ-NEXT: Symbol: func2
59+
READOBJ-EMPTY:
60+
READOBJ-NEXT: File: test.dll
61+
READOBJ-NEXT: Format: COFF-import-file-x86-64
62+
READOBJ-NEXT: Type: code
63+
READOBJ-NEXT: Name type: export as
64+
READOBJ-NEXT: Export name: expfunc3
65+
READOBJ-NEXT: Symbol: __imp_func3
66+
READOBJ-NEXT: Symbol: func3
67+
68+
69+
EXPORTAS must be at the end of entry declaration.
70+
RUN: not llvm-lib -machine:amd64 -def:test2.def -out:test2.lib 2>&1 \
71+
RUN: | FileCheck --check-prefix=ERROR %s
72+
RUN: not llvm-lib -machine:amd64 -def:test3.def -out:test3.lib 2>&1 \
73+
RUN: | FileCheck --check-prefix=ERROR %s
74+
ERROR: Invalid data was encountered while parsing the file
75+
76+
77+
#--- test.def
78+
LIBRARY test.dll
79+
EXPORTS
80+
func EXPORTAS expfunc
81+
mydata DATA EXPORTAS expdata
82+
func2 = myfunc2 EXPORTAS expfunc2
83+
func3 = otherdll.otherfunc3 EXPORTAS expfunc3
84+
85+
#--- test2.def
86+
LIBRARY test.dll
87+
EXPORTS
88+
func EXPORTAS expfunc
89+
mydata EXPORTAS expdata DATA
90+
91+
#--- test3.def
92+
LIBRARY test.dll
93+
EXPORTS
94+
mydata EXPORTAS

llvm/tools/llvm-readobj/COFFImportDumper.cpp

+3
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,9 @@ void dumpCOFFImportFile(const COFFImportFile *File, ScopedPrinter &Writer) {
4545
case COFF::IMPORT_NAME_UNDECORATE:
4646
Writer.printString("Name type", "undecorate");
4747
break;
48+
case COFF::IMPORT_NAME_EXPORTAS:
49+
Writer.printString("Name type", "export as");
50+
break;
4851
}
4952

5053
if (H->getNameType() != COFF::IMPORT_ORDINAL)

0 commit comments

Comments
 (0)