Skip to content
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

Merged
merged 1 commit into from
Feb 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading