From 0311c75231aa0845e99038c7a9bab77c3582cca0 Mon Sep 17 00:00:00 2001 From: Jacek Caban Date: Wed, 17 Jan 2024 16:12:29 +0100 Subject: [PATCH] [llvm-lib][llvm-dlltool][Object] Add support for EXPORTAS name types. --- llvm/include/llvm/BinaryFormat/COFF.h | 5 +- llvm/include/llvm/Object/COFFImportFile.h | 4 + llvm/lib/Object/COFFImportFile.cpp | 66 +++++++++----- llvm/lib/Object/COFFModuleDefinition.cpp | 13 ++- llvm/test/tools/llvm-lib/exportas.test | 94 ++++++++++++++++++++ llvm/tools/llvm-readobj/COFFImportDumper.cpp | 3 + 6 files changed, 162 insertions(+), 23 deletions(-) create mode 100644 llvm/test/tools/llvm-lib/exportas.test 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 45a4a795fd1903..7c5846e9c044e3 100644 --- a/llvm/include/llvm/Object/COFFImportFile.h +++ b/llvm/include/llvm/Object/COFFImportFile.h @@ -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; diff --git a/llvm/lib/Object/COFFImportFile.cpp b/llvm/lib/Object/COFFImportFile.cpp index d7d26f4f4180c3..51e6274dcf235c 100644 --- a/llvm/lib/Object/COFFImportFile.cpp +++ b/llvm/lib/Object/COFFImportFile.cpp @@ -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; } @@ -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. @@ -500,12 +507,13 @@ NewArchiveMember ObjectFactory::createNullThunk(std::vector &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(Size); memset(Buf, 0, Size); @@ -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)}; } @@ -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 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 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 35e6ab837b2abd..549348ac9226d0 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, @@ -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) @@ -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(); } 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/tools/llvm-readobj/COFFImportDumper.cpp b/llvm/tools/llvm-readobj/COFFImportDumper.cpp index 656ca32f03a77d..0ab2a17655653e 100644 --- a/llvm/tools/llvm-readobj/COFFImportDumper.cpp +++ b/llvm/tools/llvm-readobj/COFFImportDumper.cpp @@ -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)