Skip to content

Commit

Permalink
[llvm-lib][llvm-dlltool][Object] Add support for EXPORTAS name types. (
Browse files Browse the repository at this point in the history
…#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.
  • Loading branch information
cjacek authored Feb 10, 2024
1 parent 8509f75 commit 8f23464
Show file tree
Hide file tree
Showing 6 changed files with 162 additions and 23 deletions.
5 changes: 4 additions & 1 deletion llvm/include/llvm/BinaryFormat/COFF.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
4 changes: 4 additions & 0 deletions llvm/include/llvm/Object/COFFImportFile.h
Original file line number Diff line number Diff line change
Expand Up @@ -92,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;
Expand Down
66 changes: 45 additions & 21 deletions llvm/lib/Object/COFFImportFile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,12 @@ StringRef COFFImportFile::getExportName() const {
name = ltrim1(name, "?@_");
name = name.substr(0, name.find('@'));
break;
case IMPORT_NAME_EXPORTAS: {
// Skip DLL name
name = Data.getBuffer().substr(sizeof(*hdr) + name.size() + 1);
name = name.split('\0').second.split('\0').first;
break;
}
default:
break;
}
Expand Down Expand Up @@ -209,6 +215,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.
Expand Down Expand Up @@ -500,12 +507,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);
Expand All @@ -525,6 +533,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)};
}
Expand Down Expand Up @@ -641,27 +653,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,
Expand Down
13 changes: 12 additions & 1 deletion llvm/lib/Object/COFFModuleDefinition.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ enum Kind {
KwConstant,
KwData,
KwExports,
KwExportAs,
KwHeapsize,
KwLibrary,
KwName,
Expand Down Expand Up @@ -116,6 +117,7 @@ class Lexer {
.Case("CONSTANT", KwConstant)
.Case("DATA", KwData)
.Case("EXPORTS", KwExports)
.Case("EXPORTAS", KwExportAs)
.Case("HEAPSIZE", KwHeapsize)
.Case("LIBRARY", KwLibrary)
.Case("NAME", KwName)
Expand Down Expand Up @@ -284,7 +286,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();
}
Expand Down
94 changes: 94 additions & 0 deletions llvm/test/tools/llvm-lib/exportas.test
Original file line number Diff line number Diff line change
@@ -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
3 changes: 3 additions & 0 deletions llvm/tools/llvm-readobj/COFFImportDumper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@ void dumpCOFFImportFile(const COFFImportFile *File, ScopedPrinter &Writer) {
case COFF::IMPORT_NAME_UNDECORATE:
Writer.printString("Name type", "undecorate");
break;
case COFF::IMPORT_NAME_EXPORTAS:
Writer.printString("Name type", "export as");
break;
}

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

0 comments on commit 8f23464

Please sign in to comment.