-
Notifications
You must be signed in to change notification settings - Fork 12.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[llvm-lib][llvm-dlltool][Object] Add support for EXPORTAS name types. #78772
Conversation
@llvm/pr-subscribers-llvm-binary-utilities @llvm/pr-subscribers-lld Author: Jacek Caban (cjacek) ChangesEXPORTAS is a new name type in import libraries. As far as I can tell, they are not documented. It's used by default on ARM64EC (this is not part of this PR, see cjacek@70946eb for a prototype of it), but MSVC allows using it on other platforms. I found the syntax from compiler-generated __declspec(dllexport) directives, which look like cc @bylaws Patch is 21.07 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/78772.diff 15 Files Affected:
diff --git a/lld/test/COFF/def-export-cpp.s b/lld/test/COFF/def-export-cpp.s
index e00b35b1c5b39b..370b8ddba4104b 100644
--- a/lld/test/COFF/def-export-cpp.s
+++ b/lld/test/COFF/def-export-cpp.s
@@ -10,6 +10,7 @@
# IMPLIB: File: foo.dll
# IMPLIB: Name type: undecorate
+# IMPLIB-NEXT: Export name: GetPathOnDisk
# IMPLIB-NEXT: Symbol: __imp_?GetPathOnDisk@@YA_NPEA_W@Z
# IMPLIB-NEXT: Symbol: ?GetPathOnDisk@@YA_NPEA_W@Z
diff --git a/lld/test/COFF/def-export-stdcall.s b/lld/test/COFF/def-export-stdcall.s
index f015e205c74a33..7e4e04c77cbe7a 100644
--- a/lld/test/COFF/def-export-stdcall.s
+++ b/lld/test/COFF/def-export-stdcall.s
@@ -6,15 +6,19 @@
# RUN: llvm-readobj --coff-exports %t.dll | FileCheck -check-prefix UNDECORATED-EXPORTS %s
# UNDECORATED-IMPLIB: Name type: noprefix
+# UNDECORATED-IMPLIB-NEXT: Export name: _underscored
# UNDECORATED-IMPLIB-NEXT: __imp___underscored
# UNDECORATED-IMPLIB-NEXT: __underscored
# UNDECORATED-IMPLIB: Name type: undecorate
+# UNDECORATED-IMPLIB-NEXT: Export name: fastcall
# UNDECORATED-IMPLIB-NEXT: __imp_@fastcall@8
# UNDECORATED-IMPLIB-NEXT: fastcall@8
# UNDECORATED-IMPLIB: Name type: undecorate
+# UNDECORATED-IMPLIB-NEXT: Export name: stdcall
# UNDECORATED-IMPLIB-NEXT: __imp__stdcall@8
# UNDECORATED-IMPLIB-NEXT: _stdcall@8
# UNDECORATED-IMPLIB: Name type: undecorate
+# UNDECORATED-IMPLIB-NEXT: Export name: vectorcall
# UNDECORATED-IMPLIB-NEXT: __imp_vectorcall@@8
# UNDECORATED-IMPLIB-NEXT: vectorcall@@8
@@ -30,12 +34,15 @@
# RUN: llvm-readobj --coff-exports %t.dll | FileCheck -check-prefix DECORATED-EXPORTS %s
# DECORATED-IMPLIB: Name type: name
+# DECORATED-IMPLIB-NEXT: Export name: @fastcall@8
# DECORATED-IMPLIB-NEXT: __imp_@fastcall@8
# DECORATED-IMPLIB-NEXT: @fastcall@8
# DECORATED-IMPLIB: Name type: name
+# DECORATED-IMPLIB-NEXT: Export name: _stdcall@8
# DECORATED-IMPLIB-NEXT: __imp__stdcall@8
# DECORATED-IMPLIB-NEXT: _stdcall@8
# DECORATED-IMPLIB: Name type: name
+# DECORATED-IMPLIB-NEXT: Export name: vectorcall@@8
# DECORATED-IMPLIB-NEXT: __imp_vectorcall@@8
# DECORATED-IMPLIB-NEXT: vectorcall@@8
@@ -51,14 +58,17 @@
# RUN: llvm-readobj --coff-exports %t.dll | FileCheck -check-prefix DECORATED-MINGW-EXPORTS %s
# DECORATED-MINGW-IMPLIB: Name type: name
+# DECORATED-MINGW-IMPLIB-NEXT: Export name: @fastcall@8
# DECORATED-MINGW-IMPLIB-NEXT: __imp_@fastcall@8
# DECORATED-MINGW-IMPLIB-NEXT: fastcall@8
# DECORATED-MINGW-IMPLIB: Name type: noprefix
+# DECORATED-MINGW-IMPLIB-NEXT: Export name: stdcall@8
# DECORATED-MINGW-IMPLIB-NEXT: __imp__stdcall@8
# DECORATED-MINGW-IMPLIB-NEXT: _stdcall@8
# GNU tools don't support vectorcall, but this test is just to track that
# lld's behaviour remains consistent over time.
# DECORATED-MINGW-IMPLIB: Name type: name
+# DECORATED-MINGW-IMPLIB-NEXT: Export name: vectorcall@@8
# DECORATED-MINGW-IMPLIB-NEXT: __imp_vectorcall@@8
# DECORATED-MINGW-IMPLIB-NEXT: vectorcall@@8
@@ -75,14 +85,17 @@
# RUN: llvm-readobj --coff-exports %t.dll | FileCheck -check-prefix MINGW-KILL-AT-EXPORTS %s
# MINGW-KILL-AT-IMPLIB: Name type: noprefix
+# MINGW-KILL-AT-IMPLIB: Export name: fastcall
# MINGW-KILL-AT-IMPLIB: __imp__fastcall
# MINGW-KILL-AT-IMPLIB-NEXT: _fastcall
# MINGW-KILL-AT-IMPLIB: Name type: noprefix
+# MINGW-KILL-AT-IMPLIB-NEXT: Export name: stdcall
# MINGW-KILL-AT-IMPLIB-NEXT: __imp__stdcall
# MINGW-KILL-AT-IMPLIB-NEXT: _stdcall
# GNU tools don't support vectorcall, but this test is just to track that
# lld's behaviour remains consistent over time.
# MINGW-KILL-AT-IMPLIB: Name type: noprefix
+# MINGW-KILL-AT-IMPLIB-NEXT: Export name: vectorcall
# MINGW-KILL-AT-IMPLIB-NEXT: __imp__vectorcall
# MINGW-KILL-AT-IMPLIB-NEXT: _vectorcall
diff --git a/lld/test/COFF/dllexport.s b/lld/test/COFF/dllexport.s
index a238b70ce1b4f6..b04ebc3a33c3e2 100644
--- a/lld/test/COFF/dllexport.s
+++ b/lld/test/COFF/dllexport.s
@@ -6,15 +6,19 @@
# RUN: llvm-readobj --coff-exports %t.dll | FileCheck -check-prefix DECORATED-EXPORTS %s
# DECORATED-IMPLIB: Name type: name
+# DECORATED-IMPLIB-NEXT: Export name: @fastcall@8
# DECORATED-IMPLIB-NEXT: __imp_@fastcall@8
# DECORATED-IMPLIB-NEXT: @fastcall@8
# DECORATED-IMPLIB: Name type: name
+# DECORATED-IMPLIB-NEXT: Export name: _stdcall@8
# DECORATED-IMPLIB-NEXT: __imp__stdcall@8
# DECORATED-IMPLIB-NEXT: _stdcall@8
# DECORATED-IMPLIB: Name type: noprefix
+# DECORATED-IMPLIB-NEXT: Export name: _underscored
# DECORATED-IMPLIB-NEXT: __imp___underscored
# DECORATED-IMPLIB-NEXT: __underscored
# DECORATED-IMPLIB: Name type: name
+# DECORATED-IMPLIB-NEXT: Export name: vectorcall@@8
# DECORATED-IMPLIB-NEXT: __imp_vectorcall@@8
# DECORATED-IMPLIB-NEXT: vectorcall@@8
diff --git a/llvm/include/llvm/BinaryFormat/COFF.h b/llvm/include/llvm/BinaryFormat/COFF.h
index 522ee37da6e830..72461d0d9c316a 100644
--- a/llvm/include/llvm/BinaryFormat/COFF.h
+++ b/llvm/include/llvm/BinaryFormat/COFF.h
@@ -716,7 +716,10 @@ enum ImportNameType : unsigned {
IMPORT_NAME_NOPREFIX = 2,
/// The import name is the public symbol name, but skipping the leading ?,
/// @, or optionally _, and truncating at the first @.
- IMPORT_NAME_UNDECORATE = 3
+ IMPORT_NAME_UNDECORATE = 3,
+ /// The import name is specified as a separate string in the import library
+ /// object file.
+ IMPORT_NAME_EXPORTAS = 4
};
enum class GuardFlags : uint32_t {
diff --git a/llvm/include/llvm/Object/COFFImportFile.h b/llvm/include/llvm/Object/COFFImportFile.h
index edc836ff0348cb..7c5846e9c044e3 100644
--- a/llvm/include/llvm/Object/COFFImportFile.h
+++ b/llvm/include/llvm/Object/COFFImportFile.h
@@ -66,6 +66,7 @@ class COFFImportFile : public SymbolicFile {
uint16_t getMachine() const { return getCOFFImportHeader()->Machine; }
StringRef getFileFormatName() const;
+ StringRef getExportName() const;
private:
bool isData() const {
@@ -91,6 +92,10 @@ struct COFFShortExport {
/// file, this is "baz" in "EXPORTS\nfoo = bar == baz".
std::string AliasTarget;
+ /// Specifies EXPORTAS name. In a .def file, this is "bar" in
+ /// "EXPORTS\nfoo EXPORTAS bar".
+ std::string ExportAs;
+
uint16_t Ordinal = 0;
bool Noname = false;
bool Data = false;
diff --git a/llvm/lib/Object/COFFImportFile.cpp b/llvm/lib/Object/COFFImportFile.cpp
index 60556c149bf735..b666ca3fd40300 100644
--- a/llvm/lib/Object/COFFImportFile.cpp
+++ b/llvm/lib/Object/COFFImportFile.cpp
@@ -52,6 +52,37 @@ StringRef COFFImportFile::getFileFormatName() const {
}
}
+StringRef COFFImportFile::getExportName() const {
+ const coff_import_header *hdr = getCOFFImportHeader();
+ StringRef name = StringRef(Data.getBufferStart() + sizeof(*hdr));
+
+ auto ltrim1 = [](StringRef s, const char *chars) {
+ return !s.empty() && strchr(chars, s[0]) ? s.substr(1) : s;
+ };
+
+ switch (hdr->getNameType()) {
+ case IMPORT_ORDINAL:
+ name = "";
+ break;
+ case IMPORT_NAME_NOPREFIX:
+ name = ltrim1(name, "?@_");
+ break;
+ case IMPORT_NAME_UNDECORATE:
+ name = ltrim1(name, "?@_");
+ name = name.substr(0, name.find('@'));
+ break;
+ case IMPORT_NAME_EXPORTAS: {
+ StringRef dllName = StringRef(name.begin() + name.size() + 1);
+ name = StringRef(dllName.begin() + dllName.size() + 1);
+ break;
+ }
+ default:
+ break;
+ }
+
+ return name;
+}
+
static uint16_t getImgRelRelocation(MachineTypes Machine) {
switch (Machine) {
default:
@@ -183,6 +214,7 @@ class ObjectFactory {
// Library Format.
NewArchiveMember createShortImport(StringRef Sym, uint16_t Ordinal,
ImportType Type, ImportNameType NameType,
+ StringRef ExportName,
MachineTypes Machine);
// Create a weak external file which is described in PE/COFF Aux Format 3.
@@ -474,12 +506,13 @@ NewArchiveMember ObjectFactory::createNullThunk(std::vector<uint8_t> &Buffer) {
return {MemoryBufferRef{F, ImportName}};
}
-NewArchiveMember ObjectFactory::createShortImport(StringRef Sym,
- uint16_t Ordinal,
- ImportType ImportType,
- ImportNameType NameType,
- MachineTypes Machine) {
+NewArchiveMember
+ObjectFactory::createShortImport(StringRef Sym, uint16_t Ordinal,
+ ImportType ImportType, ImportNameType NameType,
+ StringRef ExportName, MachineTypes Machine) {
size_t ImpSize = ImportName.size() + Sym.size() + 2; // +2 for NULs
+ if (!ExportName.empty())
+ ImpSize += ExportName.size() + 1;
size_t Size = sizeof(coff_import_header) + ImpSize;
char *Buf = Alloc.Allocate<char>(Size);
memset(Buf, 0, Size);
@@ -499,6 +532,10 @@ NewArchiveMember ObjectFactory::createShortImport(StringRef Sym,
memcpy(P, Sym.data(), Sym.size());
P += Sym.size() + 1;
memcpy(P, ImportName.data(), ImportName.size());
+ if (!ExportName.empty()) {
+ P += ImportName.size() + 1;
+ memcpy(P, ExportName.data(), ExportName.size());
+ }
return {MemoryBufferRef(StringRef(Buf, Size), ImportName)};
}
@@ -615,27 +652,39 @@ Error writeImportLibrary(StringRef ImportName, StringRef Path,
ImportType = IMPORT_CONST;
StringRef SymbolName = E.SymbolName.empty() ? E.Name : E.SymbolName;
- ImportNameType NameType = E.Noname
- ? IMPORT_ORDINAL
- : getNameType(SymbolName, E.Name,
- Machine, MinGW);
- Expected<std::string> Name = E.ExtName.empty()
- ? std::string(SymbolName)
- : replace(SymbolName, E.Name, E.ExtName);
-
- if (!Name)
- return Name.takeError();
-
- if (!E.AliasTarget.empty() && *Name != E.AliasTarget) {
+ std::string Name;
+
+ if (E.ExtName.empty()) {
+ Name = std::string(SymbolName);
+ } else {
+ Expected<std::string> ReplacedName =
+ replace(SymbolName, E.Name, E.ExtName);
+ if (!ReplacedName)
+ return ReplacedName.takeError();
+ Name.swap(*ReplacedName);
+ }
+
+ if (!E.AliasTarget.empty() && Name != E.AliasTarget) {
Members.push_back(
- OF.createWeakExternal(E.AliasTarget, *Name, false, Machine));
+ OF.createWeakExternal(E.AliasTarget, Name, false, Machine));
Members.push_back(
- OF.createWeakExternal(E.AliasTarget, *Name, true, Machine));
+ OF.createWeakExternal(E.AliasTarget, Name, true, Machine));
continue;
}
- Members.push_back(
- OF.createShortImport(*Name, E.Ordinal, ImportType, NameType, Machine));
+ ImportNameType NameType;
+ std::string ExportName;
+ if (E.Noname) {
+ NameType = IMPORT_ORDINAL;
+ } else if (!E.ExportAs.empty()) {
+ NameType = IMPORT_NAME_EXPORTAS;
+ ExportName = E.ExportAs;
+ } else {
+ NameType = getNameType(SymbolName, E.Name, Machine, MinGW);
+ }
+
+ Members.push_back(OF.createShortImport(Name, E.Ordinal, ImportType,
+ NameType, ExportName, Machine));
}
return writeArchive(Path, Members, SymtabWritingMode::NormalSymtab,
diff --git a/llvm/lib/Object/COFFModuleDefinition.cpp b/llvm/lib/Object/COFFModuleDefinition.cpp
index 648f01f823d007..f60dd49793685f 100644
--- a/llvm/lib/Object/COFFModuleDefinition.cpp
+++ b/llvm/lib/Object/COFFModuleDefinition.cpp
@@ -39,6 +39,7 @@ enum Kind {
KwConstant,
KwData,
KwExports,
+ KwExportAs,
KwHeapsize,
KwLibrary,
KwName,
@@ -118,6 +119,7 @@ class Lexer {
.Case("CONSTANT", KwConstant)
.Case("DATA", KwData)
.Case("EXPORTS", KwExports)
+ .Case("EXPORTAS", KwExportAs)
.Case("HEAPSIZE", KwHeapsize)
.Case("LIBRARY", KwLibrary)
.Case("NAME", KwName)
@@ -286,7 +288,16 @@ class Parser {
E.AliasTarget = std::string("_").append(E.AliasTarget);
continue;
}
- unget();
+ // EXPORTAS must be at the end of export definition
+ if (Tok.K == KwExportAs) {
+ read();
+ if (Tok.K == Eof)
+ return createError(
+ "unexpected end of file, EXPORTAS identifier expected");
+ E.ExportAs = std::string(Tok.Value);
+ } else {
+ unget();
+ }
Info.Exports.push_back(E);
return Error::success();
}
diff --git a/llvm/test/tools/llvm-dlltool/coff-decorated.def b/llvm/test/tools/llvm-dlltool/coff-decorated.def
index 856804686168b1..fc81f23d09d6c4 100644
--- a/llvm/test/tools/llvm-dlltool/coff-decorated.def
+++ b/llvm/test/tools/llvm-dlltool/coff-decorated.def
@@ -14,25 +14,32 @@ OtherStdcallExportName@4=CdeclInternalFunction
CdeclExportName=StdcallInternalFunction@4
; CHECK: Name type: noprefix
+; CHECK-NEXT: Export name: CdeclFunction
; CHECK-NEXT: Symbol: __imp__CdeclFunction
; CHECK-NEXT: Symbol: _CdeclFunction
; CHECK: Name type: undecorate
+; CHECK-NEXT: Export name: StdcallFunction
; CHECK-NEXT: Symbol: __imp__StdcallFunction@4
; CHECK-NEXT: Symbol: _StdcallFunction@4
; CHECK: Name type: undecorate
+; CHECK-NEXT: Export name: FastcallFunction
; CHECK-NEXT: Symbol: __imp_@FastcallFunction@4
; CHECK-NEXT: Symbol: @FastcallFunction@4
; CHECK: Name type: name
+; CHECK-NEXT: Export name: ??_7exception@@6B@
; CHECK-NEXT: Symbol: __imp_??_7exception@@6B@
; CHECK-NEXT: Symbol: ??_7exception@@6B@
; CHECK-NM: W _StdcallAlias@4
; CHECK-NM: U _StdcallFunction@4
; CHECK: Name type: undecorate
+; CHECK-NEXT: Export name: StdcallExportName
; CHECK-NEXT: Symbol: __imp__StdcallExportName@4{{$}}
; CHECK-NEXT: Symbol: _StdcallExportName@4{{$}}
; CHECK: Name type: undecorate
+; CHECK-NEXT: Export name: OtherStdcallExportName
; CHECK-NEXT: Symbol: __imp__OtherStdcallExportName@4{{$}}
; CHECK-NEXT: Symbol: _OtherStdcallExportName@4{{$}}
; CHECK: Name type: noprefix
+; CHECK-NEXT: Export name: CdeclExportName
; CHECK-NEXT: Symbol: __imp__CdeclExportName
; CHECK-NEXT: Symbol: _CdeclExportName
diff --git a/llvm/test/tools/llvm-dlltool/coff-exports.def b/llvm/test/tools/llvm-dlltool/coff-exports.def
index 57c55744602156..267424db1b8c1d 100644
--- a/llvm/test/tools/llvm-dlltool/coff-exports.def
+++ b/llvm/test/tools/llvm-dlltool/coff-exports.def
@@ -17,12 +17,15 @@ AnotherFunction
; CHECK-ARM64: Format: COFF-import-file-ARM64
; CHECK: Type: code
; CHECK: Name type: name
+; CHECK-NEXT: Export name: TestFunction1
; CHECK-NEXT: Symbol: __imp_TestFunction1
; CHECK-NEXT: Symbol: TestFunction1
; CHECK: Name type: name
+; CHECK-NEXT: Export name: TestFunction2
; CHECK-NEXT: Symbol: __imp_TestFunction2{{$}}
; CHECK-NEXT: Symbol: TestFunction2{{$}}
; CHECK: Name type: name
+; CHECK-NEXT: Export name: TestFunction3
; CHECK-NEXT: Symbol: __imp_TestFunction3{{$}}
; CHECK-NEXT: Symbol: TestFunction3{{$}}
diff --git a/llvm/test/tools/llvm-dlltool/coff-noname.def b/llvm/test/tools/llvm-dlltool/coff-noname.def
index 27e60efbd2d802..7cb05846ce28a2 100644
--- a/llvm/test/tools/llvm-dlltool/coff-noname.def
+++ b/llvm/test/tools/llvm-dlltool/coff-noname.def
@@ -12,5 +12,6 @@ ByNameFunction
; CHECK-NEXT: Symbol: __imp__ByOrdinalFunction
; CHECK-NEXT: Symbol: _ByOrdinalFunction
; CHECK: Name type: noprefix
+; CHECK-NEXT: Export name: ByNameFunction
; CHECK-NEXT: Symbol: __imp__ByNameFunction
; CHECK-NEXT: Symbol: _ByNameFunction
diff --git a/llvm/test/tools/llvm-dlltool/no-leading-underscore.def b/llvm/test/tools/llvm-dlltool/no-leading-underscore.def
index 6b78e15d2b5f69..9c5e77ca29a821 100644
--- a/llvm/test/tools/llvm-dlltool/no-leading-underscore.def
+++ b/llvm/test/tools/llvm-dlltool/no-leading-underscore.def
@@ -9,9 +9,11 @@ alias == func
DecoratedFunction@4
; CHECK: Name type: name
+; CHECK-NEXT: Export name: func
; CHECK-NEXT: Symbol: __imp_func
; CHECK-NEXT: Symbol: func
; CHECK: Name type: undecorate
+; CHECK-NEXT: Export name: DecoratedFunction
; CHECK-NEXT: Symbol: __imp_DecoratedFunction@4
; CHECK-NEXT: Symbol: DecoratedFunction@4
diff --git a/llvm/test/tools/llvm-lib/arm64ec-implib.test b/llvm/test/tools/llvm-lib/arm64ec-implib.test
index 2672f8d38b7f70..4250c775daa601 100644
--- a/llvm/test/tools/llvm-lib/arm64ec-implib.test
+++ b/llvm/test/tools/llvm-lib/arm64ec-implib.test
@@ -36,6 +36,7 @@ READOBJ-NEXT: File: test.dll
READOBJ-NEXT: Format: COFF-import-file-ARM64EC
READOBJ-NEXT: Type: code
READOBJ-NEXT: Name type: name
+READOBJ-NEXT: Export name: funcexp
READOBJ-NEXT: Symbol: __imp_funcexp
READOBJ-NEXT: Symbol: funcexp
READOBJ-EMPTY:
@@ -43,6 +44,7 @@ READOBJ-NEXT: File: test.dll
READOBJ-NEXT: Format: COFF-import-file-ARM64EC
READOBJ-NEXT: Type: data
READOBJ-NEXT: Name type: name
+READOBJ-NEXT: Export name: dataexp
READOBJ-NEXT: Symbol: __imp_dataexp
Creating a new lib containing the existing lib:
diff --git a/llvm/test/tools/llvm-lib/exportas.test b/llvm/test/tools/llvm-lib/exportas.test
new file mode 100644
index 00000000000000..f6e845ca174664
--- /dev/null
+++ b/llvm/test/tools/llvm-lib/exportas.test
@@ -0,0 +1,94 @@
+Test EXPORTAS in importlibs.
+
+RUN: split-file %s %t.dir && cd %t.dir
+RUN: llvm-lib -machine:amd64 -def:test.def -out:test.lib
+
+RUN: llvm-nm --print-armap test.lib | FileCheck --check-prefix=ARMAP %s
+
+ARMAP: Archive map
+ARMAP-NEXT: __IMPORT_DESCRIPTOR_test in test.dll
+ARMAP-NEXT: __NULL_IMPORT_DESCRIPTOR in test.dll
+ARMAP-NEXT: __imp_func in test.dll
+ARMAP-NEXT: __imp_func2 in test.dll
+ARMAP-NEXT: __imp_func3 in test.dll
+ARMAP-NEXT: __imp_mydata in test.dll
+ARMAP-NEXT: func in test.dll
+ARMAP-NEXT: func2 in test.dll
+ARMAP-NEXT: func3 in test.dll
+ARMAP-NEXT: test_NULL_THUNK_DATA in test.dll
+
+RUN: llvm-readobj test.lib | FileCheck --check-prefix=READOBJ %s
+
+READOBJ: File: test.lib(test.dll)
+READOBJ-NEXT: Format: COFF-x86-64
+READOBJ-NEXT: Arch: x86_64
+READOBJ-NEXT: AddressSize: 64bit
+READOBJ-EMPTY:
+READOBJ-NEXT: File: test.lib(test.dll)
+READOBJ-NEXT: Format: COFF-x86-64
+READOBJ-NEXT: Arch: x86_64
+READOBJ-NEXT: AddressSize: 64bit
+READOBJ-EMPTY:
+READOBJ-NEXT: File: test.lib(test.dll)
+READOBJ-NEXT: Format: COFF-x86-64
+READOBJ-NEXT: Arch: x86_64
+READOBJ-NEXT: AddressSize: 64bit
+READOBJ-EMPTY:
+READOBJ-NEXT: File: test.dll
+READOBJ-NEXT: Format: COFF-import-file-x86-64
+READOBJ-NEXT: Type: code
+READOBJ-NEXT: Name type: export as
+READOBJ-NEXT: Export name: expfunc
+READOBJ-NEXT: Symbol: __imp_func
+READOBJ-NEXT: Symbol: func
+READOBJ-EMPTY:
+READOBJ-NEXT: File: test.dll
+READOBJ-NEXT: Format: COFF-import-file-x86-64
+READOBJ-NEXT: Type: data
+READOBJ-NEXT: Name type: export as
+READOBJ-NEXT: Export name: expdata
+READOBJ-NEXT: Symbol: __imp_mydata
+READOBJ-EMPTY:
+READOBJ-NEXT: File: test.dll
+READOBJ-NEXT: Format: COFF-import-file-x86-64
+READOBJ-NEXT: Type: code
+READOBJ-NEXT: Name type: export as
+READOBJ-NEXT: Export name: expfunc2
+READOBJ-NEXT: Symbol: __imp_func2
+READOBJ-NEXT: Symbol: func2
+READOBJ-EMPTY:
+READOBJ-NEXT: File: test.dll
+READOBJ-NEXT: Format: COFF-import-file-x86-64
+READOBJ-NEXT: Type: code
+READOBJ-NEXT: Name type: export as
+READOBJ-NEXT: Export name: expfunc3
+READOBJ-NEXT: Symbol: __imp_func3
+READOBJ-NEXT: Symbol: func3
+
+
+EXPORTAS must be at the end of entry declaration.
+RUN: not llvm-lib -machine:amd64 -def:test2.def -out:test2.lib 2>&1 \
+RUN: | FileCheck --check-prefix=ERROR %s
+RUN: not llvm-lib -machine:amd64 -def:test3.def -out:test3.lib 2>&1 \
+RUN: | FileCheck --check-prefix=ERROR %s
+ERROR: Invalid data was encountered while parsing the file
+
+
+#--- test.def
+LIBRARY test.dll
+EXPORTS
+ func EXPORTAS expfunc
+ mydata DATA EXPORTAS expdata
+ func2 = myfunc2 EXPORTAS expfunc2
+ func3 = otherdll.otherfunc3 EXPORTAS expfunc3
+
+#--- test2.def
+LIBRARY test.dll
+EXPORTS
+ func EXPORTAS expfunc
+ mydata EXPORTAS expdata DATA
+
+#--- test3.def
+LIBRARY test.dll
+EXPORTS
+ mydata EXPORTAS
diff --git a/llvm/test/tools/llvm-readobj/COFF/file-headers.test b/llvm/test/tools/llvm-readobj/COFF/file-headers.test
index b83a6cf5b972b3..32f39e196b0001 100644
--- a/llvm/test/tools/llvm-readobj/COFF/file-headers.test
+++ b/llvm/test/tools/llvm-reado...
[truncated]
|
@llvm/pr-subscribers-lld-coff Author: Jacek Caban (cjacek) ChangesEXPORTAS is a new name type in import libraries. As far as I can tell, they are not documented. It's used by default on ARM64EC (this is not part of this PR, see cjacek@70946eb for a prototype of it), but MSVC allows using it on other platforms. I found the syntax from compiler-generated __declspec(dllexport) directives, which look like cc @bylaws Patch is 21.10 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/78772.diff 15 Files Affected:
diff --git a/lld/test/COFF/def-export-cpp.s b/lld/test/COFF/def-export-cpp.s
index e00b35b1c5b39bb..370b8ddba4104b4 100644
--- a/lld/test/COFF/def-export-cpp.s
+++ b/lld/test/COFF/def-export-cpp.s
@@ -10,6 +10,7 @@
# IMPLIB: File: foo.dll
# IMPLIB: Name type: undecorate
+# IMPLIB-NEXT: Export name: GetPathOnDisk
# IMPLIB-NEXT: Symbol: __imp_?GetPathOnDisk@@YA_NPEA_W@Z
# IMPLIB-NEXT: Symbol: ?GetPathOnDisk@@YA_NPEA_W@Z
diff --git a/lld/test/COFF/def-export-stdcall.s b/lld/test/COFF/def-export-stdcall.s
index f015e205c74a33d..7e4e04c77cbe7a5 100644
--- a/lld/test/COFF/def-export-stdcall.s
+++ b/lld/test/COFF/def-export-stdcall.s
@@ -6,15 +6,19 @@
# RUN: llvm-readobj --coff-exports %t.dll | FileCheck -check-prefix UNDECORATED-EXPORTS %s
# UNDECORATED-IMPLIB: Name type: noprefix
+# UNDECORATED-IMPLIB-NEXT: Export name: _underscored
# UNDECORATED-IMPLIB-NEXT: __imp___underscored
# UNDECORATED-IMPLIB-NEXT: __underscored
# UNDECORATED-IMPLIB: Name type: undecorate
+# UNDECORATED-IMPLIB-NEXT: Export name: fastcall
# UNDECORATED-IMPLIB-NEXT: __imp_@fastcall@8
# UNDECORATED-IMPLIB-NEXT: fastcall@8
# UNDECORATED-IMPLIB: Name type: undecorate
+# UNDECORATED-IMPLIB-NEXT: Export name: stdcall
# UNDECORATED-IMPLIB-NEXT: __imp__stdcall@8
# UNDECORATED-IMPLIB-NEXT: _stdcall@8
# UNDECORATED-IMPLIB: Name type: undecorate
+# UNDECORATED-IMPLIB-NEXT: Export name: vectorcall
# UNDECORATED-IMPLIB-NEXT: __imp_vectorcall@@8
# UNDECORATED-IMPLIB-NEXT: vectorcall@@8
@@ -30,12 +34,15 @@
# RUN: llvm-readobj --coff-exports %t.dll | FileCheck -check-prefix DECORATED-EXPORTS %s
# DECORATED-IMPLIB: Name type: name
+# DECORATED-IMPLIB-NEXT: Export name: @fastcall@8
# DECORATED-IMPLIB-NEXT: __imp_@fastcall@8
# DECORATED-IMPLIB-NEXT: @fastcall@8
# DECORATED-IMPLIB: Name type: name
+# DECORATED-IMPLIB-NEXT: Export name: _stdcall@8
# DECORATED-IMPLIB-NEXT: __imp__stdcall@8
# DECORATED-IMPLIB-NEXT: _stdcall@8
# DECORATED-IMPLIB: Name type: name
+# DECORATED-IMPLIB-NEXT: Export name: vectorcall@@8
# DECORATED-IMPLIB-NEXT: __imp_vectorcall@@8
# DECORATED-IMPLIB-NEXT: vectorcall@@8
@@ -51,14 +58,17 @@
# RUN: llvm-readobj --coff-exports %t.dll | FileCheck -check-prefix DECORATED-MINGW-EXPORTS %s
# DECORATED-MINGW-IMPLIB: Name type: name
+# DECORATED-MINGW-IMPLIB-NEXT: Export name: @fastcall@8
# DECORATED-MINGW-IMPLIB-NEXT: __imp_@fastcall@8
# DECORATED-MINGW-IMPLIB-NEXT: fastcall@8
# DECORATED-MINGW-IMPLIB: Name type: noprefix
+# DECORATED-MINGW-IMPLIB-NEXT: Export name: stdcall@8
# DECORATED-MINGW-IMPLIB-NEXT: __imp__stdcall@8
# DECORATED-MINGW-IMPLIB-NEXT: _stdcall@8
# GNU tools don't support vectorcall, but this test is just to track that
# lld's behaviour remains consistent over time.
# DECORATED-MINGW-IMPLIB: Name type: name
+# DECORATED-MINGW-IMPLIB-NEXT: Export name: vectorcall@@8
# DECORATED-MINGW-IMPLIB-NEXT: __imp_vectorcall@@8
# DECORATED-MINGW-IMPLIB-NEXT: vectorcall@@8
@@ -75,14 +85,17 @@
# RUN: llvm-readobj --coff-exports %t.dll | FileCheck -check-prefix MINGW-KILL-AT-EXPORTS %s
# MINGW-KILL-AT-IMPLIB: Name type: noprefix
+# MINGW-KILL-AT-IMPLIB: Export name: fastcall
# MINGW-KILL-AT-IMPLIB: __imp__fastcall
# MINGW-KILL-AT-IMPLIB-NEXT: _fastcall
# MINGW-KILL-AT-IMPLIB: Name type: noprefix
+# MINGW-KILL-AT-IMPLIB-NEXT: Export name: stdcall
# MINGW-KILL-AT-IMPLIB-NEXT: __imp__stdcall
# MINGW-KILL-AT-IMPLIB-NEXT: _stdcall
# GNU tools don't support vectorcall, but this test is just to track that
# lld's behaviour remains consistent over time.
# MINGW-KILL-AT-IMPLIB: Name type: noprefix
+# MINGW-KILL-AT-IMPLIB-NEXT: Export name: vectorcall
# MINGW-KILL-AT-IMPLIB-NEXT: __imp__vectorcall
# MINGW-KILL-AT-IMPLIB-NEXT: _vectorcall
diff --git a/lld/test/COFF/dllexport.s b/lld/test/COFF/dllexport.s
index a238b70ce1b4f69..b04ebc3a33c3e2d 100644
--- a/lld/test/COFF/dllexport.s
+++ b/lld/test/COFF/dllexport.s
@@ -6,15 +6,19 @@
# RUN: llvm-readobj --coff-exports %t.dll | FileCheck -check-prefix DECORATED-EXPORTS %s
# DECORATED-IMPLIB: Name type: name
+# DECORATED-IMPLIB-NEXT: Export name: @fastcall@8
# DECORATED-IMPLIB-NEXT: __imp_@fastcall@8
# DECORATED-IMPLIB-NEXT: @fastcall@8
# DECORATED-IMPLIB: Name type: name
+# DECORATED-IMPLIB-NEXT: Export name: _stdcall@8
# DECORATED-IMPLIB-NEXT: __imp__stdcall@8
# DECORATED-IMPLIB-NEXT: _stdcall@8
# DECORATED-IMPLIB: Name type: noprefix
+# DECORATED-IMPLIB-NEXT: Export name: _underscored
# DECORATED-IMPLIB-NEXT: __imp___underscored
# DECORATED-IMPLIB-NEXT: __underscored
# DECORATED-IMPLIB: Name type: name
+# DECORATED-IMPLIB-NEXT: Export name: vectorcall@@8
# DECORATED-IMPLIB-NEXT: __imp_vectorcall@@8
# DECORATED-IMPLIB-NEXT: vectorcall@@8
diff --git a/llvm/include/llvm/BinaryFormat/COFF.h b/llvm/include/llvm/BinaryFormat/COFF.h
index 522ee37da6e830c..72461d0d9c316a3 100644
--- a/llvm/include/llvm/BinaryFormat/COFF.h
+++ b/llvm/include/llvm/BinaryFormat/COFF.h
@@ -716,7 +716,10 @@ enum ImportNameType : unsigned {
IMPORT_NAME_NOPREFIX = 2,
/// The import name is the public symbol name, but skipping the leading ?,
/// @, or optionally _, and truncating at the first @.
- IMPORT_NAME_UNDECORATE = 3
+ IMPORT_NAME_UNDECORATE = 3,
+ /// The import name is specified as a separate string in the import library
+ /// object file.
+ IMPORT_NAME_EXPORTAS = 4
};
enum class GuardFlags : uint32_t {
diff --git a/llvm/include/llvm/Object/COFFImportFile.h b/llvm/include/llvm/Object/COFFImportFile.h
index edc836ff0348cb2..7c5846e9c044e39 100644
--- a/llvm/include/llvm/Object/COFFImportFile.h
+++ b/llvm/include/llvm/Object/COFFImportFile.h
@@ -66,6 +66,7 @@ class COFFImportFile : public SymbolicFile {
uint16_t getMachine() const { return getCOFFImportHeader()->Machine; }
StringRef getFileFormatName() const;
+ StringRef getExportName() const;
private:
bool isData() const {
@@ -91,6 +92,10 @@ struct COFFShortExport {
/// file, this is "baz" in "EXPORTS\nfoo = bar == baz".
std::string AliasTarget;
+ /// Specifies EXPORTAS name. In a .def file, this is "bar" in
+ /// "EXPORTS\nfoo EXPORTAS bar".
+ std::string ExportAs;
+
uint16_t Ordinal = 0;
bool Noname = false;
bool Data = false;
diff --git a/llvm/lib/Object/COFFImportFile.cpp b/llvm/lib/Object/COFFImportFile.cpp
index 60556c149bf735b..b666ca3fd40300d 100644
--- a/llvm/lib/Object/COFFImportFile.cpp
+++ b/llvm/lib/Object/COFFImportFile.cpp
@@ -52,6 +52,37 @@ StringRef COFFImportFile::getFileFormatName() const {
}
}
+StringRef COFFImportFile::getExportName() const {
+ const coff_import_header *hdr = getCOFFImportHeader();
+ StringRef name = StringRef(Data.getBufferStart() + sizeof(*hdr));
+
+ auto ltrim1 = [](StringRef s, const char *chars) {
+ return !s.empty() && strchr(chars, s[0]) ? s.substr(1) : s;
+ };
+
+ switch (hdr->getNameType()) {
+ case IMPORT_ORDINAL:
+ name = "";
+ break;
+ case IMPORT_NAME_NOPREFIX:
+ name = ltrim1(name, "?@_");
+ break;
+ case IMPORT_NAME_UNDECORATE:
+ name = ltrim1(name, "?@_");
+ name = name.substr(0, name.find('@'));
+ break;
+ case IMPORT_NAME_EXPORTAS: {
+ StringRef dllName = StringRef(name.begin() + name.size() + 1);
+ name = StringRef(dllName.begin() + dllName.size() + 1);
+ break;
+ }
+ default:
+ break;
+ }
+
+ return name;
+}
+
static uint16_t getImgRelRelocation(MachineTypes Machine) {
switch (Machine) {
default:
@@ -183,6 +214,7 @@ class ObjectFactory {
// Library Format.
NewArchiveMember createShortImport(StringRef Sym, uint16_t Ordinal,
ImportType Type, ImportNameType NameType,
+ StringRef ExportName,
MachineTypes Machine);
// Create a weak external file which is described in PE/COFF Aux Format 3.
@@ -474,12 +506,13 @@ NewArchiveMember ObjectFactory::createNullThunk(std::vector<uint8_t> &Buffer) {
return {MemoryBufferRef{F, ImportName}};
}
-NewArchiveMember ObjectFactory::createShortImport(StringRef Sym,
- uint16_t Ordinal,
- ImportType ImportType,
- ImportNameType NameType,
- MachineTypes Machine) {
+NewArchiveMember
+ObjectFactory::createShortImport(StringRef Sym, uint16_t Ordinal,
+ ImportType ImportType, ImportNameType NameType,
+ StringRef ExportName, MachineTypes Machine) {
size_t ImpSize = ImportName.size() + Sym.size() + 2; // +2 for NULs
+ if (!ExportName.empty())
+ ImpSize += ExportName.size() + 1;
size_t Size = sizeof(coff_import_header) + ImpSize;
char *Buf = Alloc.Allocate<char>(Size);
memset(Buf, 0, Size);
@@ -499,6 +532,10 @@ NewArchiveMember ObjectFactory::createShortImport(StringRef Sym,
memcpy(P, Sym.data(), Sym.size());
P += Sym.size() + 1;
memcpy(P, ImportName.data(), ImportName.size());
+ if (!ExportName.empty()) {
+ P += ImportName.size() + 1;
+ memcpy(P, ExportName.data(), ExportName.size());
+ }
return {MemoryBufferRef(StringRef(Buf, Size), ImportName)};
}
@@ -615,27 +652,39 @@ Error writeImportLibrary(StringRef ImportName, StringRef Path,
ImportType = IMPORT_CONST;
StringRef SymbolName = E.SymbolName.empty() ? E.Name : E.SymbolName;
- ImportNameType NameType = E.Noname
- ? IMPORT_ORDINAL
- : getNameType(SymbolName, E.Name,
- Machine, MinGW);
- Expected<std::string> Name = E.ExtName.empty()
- ? std::string(SymbolName)
- : replace(SymbolName, E.Name, E.ExtName);
-
- if (!Name)
- return Name.takeError();
-
- if (!E.AliasTarget.empty() && *Name != E.AliasTarget) {
+ std::string Name;
+
+ if (E.ExtName.empty()) {
+ Name = std::string(SymbolName);
+ } else {
+ Expected<std::string> ReplacedName =
+ replace(SymbolName, E.Name, E.ExtName);
+ if (!ReplacedName)
+ return ReplacedName.takeError();
+ Name.swap(*ReplacedName);
+ }
+
+ if (!E.AliasTarget.empty() && Name != E.AliasTarget) {
Members.push_back(
- OF.createWeakExternal(E.AliasTarget, *Name, false, Machine));
+ OF.createWeakExternal(E.AliasTarget, Name, false, Machine));
Members.push_back(
- OF.createWeakExternal(E.AliasTarget, *Name, true, Machine));
+ OF.createWeakExternal(E.AliasTarget, Name, true, Machine));
continue;
}
- Members.push_back(
- OF.createShortImport(*Name, E.Ordinal, ImportType, NameType, Machine));
+ ImportNameType NameType;
+ std::string ExportName;
+ if (E.Noname) {
+ NameType = IMPORT_ORDINAL;
+ } else if (!E.ExportAs.empty()) {
+ NameType = IMPORT_NAME_EXPORTAS;
+ ExportName = E.ExportAs;
+ } else {
+ NameType = getNameType(SymbolName, E.Name, Machine, MinGW);
+ }
+
+ Members.push_back(OF.createShortImport(Name, E.Ordinal, ImportType,
+ NameType, ExportName, Machine));
}
return writeArchive(Path, Members, SymtabWritingMode::NormalSymtab,
diff --git a/llvm/lib/Object/COFFModuleDefinition.cpp b/llvm/lib/Object/COFFModuleDefinition.cpp
index 648f01f823d007a..f60dd49793685fa 100644
--- a/llvm/lib/Object/COFFModuleDefinition.cpp
+++ b/llvm/lib/Object/COFFModuleDefinition.cpp
@@ -39,6 +39,7 @@ enum Kind {
KwConstant,
KwData,
KwExports,
+ KwExportAs,
KwHeapsize,
KwLibrary,
KwName,
@@ -118,6 +119,7 @@ class Lexer {
.Case("CONSTANT", KwConstant)
.Case("DATA", KwData)
.Case("EXPORTS", KwExports)
+ .Case("EXPORTAS", KwExportAs)
.Case("HEAPSIZE", KwHeapsize)
.Case("LIBRARY", KwLibrary)
.Case("NAME", KwName)
@@ -286,7 +288,16 @@ class Parser {
E.AliasTarget = std::string("_").append(E.AliasTarget);
continue;
}
- unget();
+ // EXPORTAS must be at the end of export definition
+ if (Tok.K == KwExportAs) {
+ read();
+ if (Tok.K == Eof)
+ return createError(
+ "unexpected end of file, EXPORTAS identifier expected");
+ E.ExportAs = std::string(Tok.Value);
+ } else {
+ unget();
+ }
Info.Exports.push_back(E);
return Error::success();
}
diff --git a/llvm/test/tools/llvm-dlltool/coff-decorated.def b/llvm/test/tools/llvm-dlltool/coff-decorated.def
index 856804686168b17..fc81f23d09d6c48 100644
--- a/llvm/test/tools/llvm-dlltool/coff-decorated.def
+++ b/llvm/test/tools/llvm-dlltool/coff-decorated.def
@@ -14,25 +14,32 @@ OtherStdcallExportName@4=CdeclInternalFunction
CdeclExportName=StdcallInternalFunction@4
; CHECK: Name type: noprefix
+; CHECK-NEXT: Export name: CdeclFunction
; CHECK-NEXT: Symbol: __imp__CdeclFunction
; CHECK-NEXT: Symbol: _CdeclFunction
; CHECK: Name type: undecorate
+; CHECK-NEXT: Export name: StdcallFunction
; CHECK-NEXT: Symbol: __imp__StdcallFunction@4
; CHECK-NEXT: Symbol: _StdcallFunction@4
; CHECK: Name type: undecorate
+; CHECK-NEXT: Export name: FastcallFunction
; CHECK-NEXT: Symbol: __imp_@FastcallFunction@4
; CHECK-NEXT: Symbol: @FastcallFunction@4
; CHECK: Name type: name
+; CHECK-NEXT: Export name: ??_7exception@@6B@
; CHECK-NEXT: Symbol: __imp_??_7exception@@6B@
; CHECK-NEXT: Symbol: ??_7exception@@6B@
; CHECK-NM: W _StdcallAlias@4
; CHECK-NM: U _StdcallFunction@4
; CHECK: Name type: undecorate
+; CHECK-NEXT: Export name: StdcallExportName
; CHECK-NEXT: Symbol: __imp__StdcallExportName@4{{$}}
; CHECK-NEXT: Symbol: _StdcallExportName@4{{$}}
; CHECK: Name type: undecorate
+; CHECK-NEXT: Export name: OtherStdcallExportName
; CHECK-NEXT: Symbol: __imp__OtherStdcallExportName@4{{$}}
; CHECK-NEXT: Symbol: _OtherStdcallExportName@4{{$}}
; CHECK: Name type: noprefix
+; CHECK-NEXT: Export name: CdeclExportName
; CHECK-NEXT: Symbol: __imp__CdeclExportName
; CHECK-NEXT: Symbol: _CdeclExportName
diff --git a/llvm/test/tools/llvm-dlltool/coff-exports.def b/llvm/test/tools/llvm-dlltool/coff-exports.def
index 57c557446021565..267424db1b8c1da 100644
--- a/llvm/test/tools/llvm-dlltool/coff-exports.def
+++ b/llvm/test/tools/llvm-dlltool/coff-exports.def
@@ -17,12 +17,15 @@ AnotherFunction
; CHECK-ARM64: Format: COFF-import-file-ARM64
; CHECK: Type: code
; CHECK: Name type: name
+; CHECK-NEXT: Export name: TestFunction1
; CHECK-NEXT: Symbol: __imp_TestFunction1
; CHECK-NEXT: Symbol: TestFunction1
; CHECK: Name type: name
+; CHECK-NEXT: Export name: TestFunction2
; CHECK-NEXT: Symbol: __imp_TestFunction2{{$}}
; CHECK-NEXT: Symbol: TestFunction2{{$}}
; CHECK: Name type: name
+; CHECK-NEXT: Export name: TestFunction3
; CHECK-NEXT: Symbol: __imp_TestFunction3{{$}}
; CHECK-NEXT: Symbol: TestFunction3{{$}}
diff --git a/llvm/test/tools/llvm-dlltool/coff-noname.def b/llvm/test/tools/llvm-dlltool/coff-noname.def
index 27e60efbd2d8027..7cb05846ce28a20 100644
--- a/llvm/test/tools/llvm-dlltool/coff-noname.def
+++ b/llvm/test/tools/llvm-dlltool/coff-noname.def
@@ -12,5 +12,6 @@ ByNameFunction
; CHECK-NEXT: Symbol: __imp__ByOrdinalFunction
; CHECK-NEXT: Symbol: _ByOrdinalFunction
; CHECK: Name type: noprefix
+; CHECK-NEXT: Export name: ByNameFunction
; CHECK-NEXT: Symbol: __imp__ByNameFunction
; CHECK-NEXT: Symbol: _ByNameFunction
diff --git a/llvm/test/tools/llvm-dlltool/no-leading-underscore.def b/llvm/test/tools/llvm-dlltool/no-leading-underscore.def
index 6b78e15d2b5f695..9c5e77ca29a8212 100644
--- a/llvm/test/tools/llvm-dlltool/no-leading-underscore.def
+++ b/llvm/test/tools/llvm-dlltool/no-leading-underscore.def
@@ -9,9 +9,11 @@ alias == func
DecoratedFunction@4
; CHECK: Name type: name
+; CHECK-NEXT: Export name: func
; CHECK-NEXT: Symbol: __imp_func
; CHECK-NEXT: Symbol: func
; CHECK: Name type: undecorate
+; CHECK-NEXT: Export name: DecoratedFunction
; CHECK-NEXT: Symbol: __imp_DecoratedFunction@4
; CHECK-NEXT: Symbol: DecoratedFunction@4
diff --git a/llvm/test/tools/llvm-lib/arm64ec-implib.test b/llvm/test/tools/llvm-lib/arm64ec-implib.test
index 2672f8d38b7f704..4250c775daa6012 100644
--- a/llvm/test/tools/llvm-lib/arm64ec-implib.test
+++ b/llvm/test/tools/llvm-lib/arm64ec-implib.test
@@ -36,6 +36,7 @@ READOBJ-NEXT: File: test.dll
READOBJ-NEXT: Format: COFF-import-file-ARM64EC
READOBJ-NEXT: Type: code
READOBJ-NEXT: Name type: name
+READOBJ-NEXT: Export name: funcexp
READOBJ-NEXT: Symbol: __imp_funcexp
READOBJ-NEXT: Symbol: funcexp
READOBJ-EMPTY:
@@ -43,6 +44,7 @@ READOBJ-NEXT: File: test.dll
READOBJ-NEXT: Format: COFF-import-file-ARM64EC
READOBJ-NEXT: Type: data
READOBJ-NEXT: Name type: name
+READOBJ-NEXT: Export name: dataexp
READOBJ-NEXT: Symbol: __imp_dataexp
Creating a new lib containing the existing lib:
diff --git a/llvm/test/tools/llvm-lib/exportas.test b/llvm/test/tools/llvm-lib/exportas.test
new file mode 100644
index 000000000000000..f6e845ca174664a
--- /dev/null
+++ b/llvm/test/tools/llvm-lib/exportas.test
@@ -0,0 +1,94 @@
+Test EXPORTAS in importlibs.
+
+RUN: split-file %s %t.dir && cd %t.dir
+RUN: llvm-lib -machine:amd64 -def:test.def -out:test.lib
+
+RUN: llvm-nm --print-armap test.lib | FileCheck --check-prefix=ARMAP %s
+
+ARMAP: Archive map
+ARMAP-NEXT: __IMPORT_DESCRIPTOR_test in test.dll
+ARMAP-NEXT: __NULL_IMPORT_DESCRIPTOR in test.dll
+ARMAP-NEXT: __imp_func in test.dll
+ARMAP-NEXT: __imp_func2 in test.dll
+ARMAP-NEXT: __imp_func3 in test.dll
+ARMAP-NEXT: __imp_mydata in test.dll
+ARMAP-NEXT: func in test.dll
+ARMAP-NEXT: func2 in test.dll
+ARMAP-NEXT: func3 in test.dll
+ARMAP-NEXT: test_NULL_THUNK_DATA in test.dll
+
+RUN: llvm-readobj test.lib | FileCheck --check-prefix=READOBJ %s
+
+READOBJ: File: test.lib(test.dll)
+READOBJ-NEXT: Format: COFF-x86-64
+READOBJ-NEXT: Arch: x86_64
+READOBJ-NEXT: AddressSize: 64bit
+READOBJ-EMPTY:
+READOBJ-NEXT: File: test.lib(test.dll)
+READOBJ-NEXT: Format: COFF-x86-64
+READOBJ-NEXT: Arch: x86_64
+READOBJ-NEXT: AddressSize: 64bit
+READOBJ-EMPTY:
+READOBJ-NEXT: File: test.lib(test.dll)
+READOBJ-NEXT: Format: COFF-x86-64
+READOBJ-NEXT: Arch: x86_64
+READOBJ-NEXT: AddressSize: 64bit
+READOBJ-EMPTY:
+READOBJ-NEXT: File: test.dll
+READOBJ-NEXT: Format: COFF-import-file-x86-64
+READOBJ-NEXT: Type: code
+READOBJ-NEXT: Name type: export as
+READOBJ-NEXT: Export name: expfunc
+READOBJ-NEXT: Symbol: __imp_func
+READOBJ-NEXT: Symbol: func
+READOBJ-EMPTY:
+READOBJ-NEXT: File: test.dll
+READOBJ-NEXT: Format: COFF-import-file-x86-64
+READOBJ-NEXT: Type: data
+READOBJ-NEXT: Name type: export as
+READOBJ-NEXT: Export name: expdata
+READOBJ-NEXT: Symbol: __imp_mydata
+READOBJ-EMPTY:
+READOBJ-NEXT: File: test.dll
+READOBJ-NEXT: Format: COFF-import-file-x86-64
+READOBJ-NEXT: Type: code
+READOBJ-NEXT: Name type: export as
+READOBJ-NEXT: Export name: expfunc2
+READOBJ-NEXT: Symbol: __imp_func2
+READOBJ-NEXT: Symbol: func2
+READOBJ-EMPTY:
+READOBJ-NEXT: File: test.dll
+READOBJ-NEXT: Format: COFF-import-file-x86-64
+READOBJ-NEXT: Type: code
+READOBJ-NEXT: Name type: export as
+READOBJ-NEXT: Export name: expfunc3
+READOBJ-NEXT: Symbol: __imp_func3
+READOBJ-NEXT: Symbol: func3
+
+
+EXPORTAS must be at the end of entry declaration.
+RUN: not llvm-lib -machine:amd64 -def:test2.def -out:test2.lib 2>&1 \
+RUN: | FileCheck --check-prefix=ERROR %s
+RUN: not llvm-lib -machine:amd64 -def:test3.def -out:test3.lib 2>&1 \
+RUN: | FileCheck --check-prefix=ERROR %s
+ERROR: Invalid data was encountered while parsing the file
+
+
+#--- test.def
+LIBRARY test.dll
+EXPORTS
+ func EXPORTAS expfunc
+ mydata DATA EXPORTAS expdata
+ func2 = myfunc2 EXPORTAS expfunc2
+ func3 = otherdll.otherfunc3 EXPORTAS expfunc3
+
+#--- test2.def
+LIBRARY test.dll
+EXPORTS
+ func EXPORTAS expfunc
+ mydata EXPORTAS expdata DATA
+
+#--- test3.def
+LIBRARY test.dll
+EXPORTS
+ mydata EXPORTAS
diff --git a/llvm/test/tools/llvm-readobj/COFF/file-headers.test b/llvm/test/tools/llvm-readobj/COFF/file-headers.test
index b83a6cf5b972b34..32f39e196b0001d 100644
--- a/llvm/test/tools/llvm-readobj/COFF/file-headers.test
+++ ...
[truncated]
|
Depends on #78769. |
What's the difference between this and MinGW's |
MinGW extension adds a COFF object to import library that creates a weak external symbol. EXPORTAS is extends short import file format to store export name separate from symbol name. |
Really? In LLVM, maybe. I could've sworn that GNU |
I meant llvm-dlltool. You're right, GNU dlltool indeed uses a regular COFF object to achieve essentially the same result. That makes EXPORTAS a potential replacement for using weak externals. I don't think we want to change that by default as it would break compatibility with linkers that don't support EXPORTAS. However, doing that for ARM64EC, which has additional auxiliary IAT symbols, seems right. |
New new version contains updated version of #78769 commit and similar string changes to |
Rebased after #78769 landed. |
Feels like this depends on #81059 |
My intention was for #81059 to depend on this one. Its first commit is the same as this PR, I meant it to be "stacked" PR, sorry if I made it wrong. |
Ah, I didn't see that in the other PR, GitHub doesn't make it as explicit as phabricator did. |
…llvm#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.
…llvm#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.
…llvm#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.
…llvm#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.
…llvm#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.
…llvm#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.
#78772 added similar support for .def file parser and import library writer. This PR adds missing bits in LLD to propagate EXPORTAS name and allow it in `/export` parser. This is syntax is used by MSVC for ARM64EC `__declspec(dllexport)` handling.
EXPORTAS is a new name type in import libraries. As far as I can tell, they are not documented. It's used by default on ARM64EC (this is not part of this PR, see cjacek@70946eb for a prototype of it), but MSVC allows using it on other platforms. I found the syntax from compiler-generated __declspec(dllexport) directives, which look like
-export:#func,EXPORTAS,func
, but MSVC also allows using it in .def files as well, which is what this PR implements. For the context, I also have a prototype of linker support: cjacek@1dbe8a6.cc @bylaws