diff --git a/clang-tools-extra/clangd/HeuristicResolver.cpp b/clang-tools-extra/clangd/HeuristicResolver.cpp index 3c147b6b582bf0..26d54200eeffd2 100644 --- a/clang-tools-extra/clangd/HeuristicResolver.cpp +++ b/clang-tools-extra/clangd/HeuristicResolver.cpp @@ -16,6 +16,80 @@ namespace clang { namespace clangd { +namespace { + +// Helper class for implementing HeuristicResolver. +// Unlike HeuristicResolver which is a long-lived class, +// a new instance of this class is created for every external +// call into a HeuristicResolver operation. That allows this +// class to store state that's local to such a top-level call, +// particularly "recursion protection sets" that keep track of +// nodes that have already been seen to avoid infinite recursion. +class HeuristicResolverImpl { +public: + HeuristicResolverImpl(ASTContext &Ctx) : Ctx(Ctx) {} + + // These functions match the public interface of HeuristicResolver + // (but aren't const since they may modify the recursion protection sets). + std::vector + resolveMemberExpr(const CXXDependentScopeMemberExpr *ME); + std::vector + resolveDeclRefExpr(const DependentScopeDeclRefExpr *RE); + std::vector resolveTypeOfCallExpr(const CallExpr *CE); + std::vector resolveCalleeOfCallExpr(const CallExpr *CE); + std::vector + resolveUsingValueDecl(const UnresolvedUsingValueDecl *UUVD); + std::vector + resolveDependentNameType(const DependentNameType *DNT); + std::vector resolveTemplateSpecializationType( + const DependentTemplateSpecializationType *DTST); + const Type *resolveNestedNameSpecifierToType(const NestedNameSpecifier *NNS); + const Type *getPointeeType(const Type *T); + +private: + ASTContext &Ctx; + + // Recursion protection sets + llvm::SmallSet SeenDependentNameTypes; + + // Given a tag-decl type and a member name, heuristically resolve the + // name to one or more declarations. + // The current heuristic is simply to look up the name in the primary + // template. This is a heuristic because the template could potentially + // have specializations that declare different members. + // Multiple declarations could be returned if the name is overloaded + // (e.g. an overloaded method in the primary template). + // This heuristic will give the desired answer in many cases, e.g. + // for a call to vector::size(). + std::vector + resolveDependentMember(const Type *T, DeclarationName Name, + llvm::function_ref Filter); + + // Try to heuristically resolve the type of a possibly-dependent expression + // `E`. + const Type *resolveExprToType(const Expr *E); + std::vector resolveExprToDecls(const Expr *E); + + // Helper function for HeuristicResolver::resolveDependentMember() + // which takes a possibly-dependent type `T` and heuristically + // resolves it to a CXXRecordDecl in which we can try name lookup. + CXXRecordDecl *resolveTypeToRecordDecl(const Type *T); + + // This is a reimplementation of CXXRecordDecl::lookupDependentName() + // so that the implementation can call into other HeuristicResolver helpers. + // FIXME: Once HeuristicResolver is upstreamed to the clang libraries + // (https://github.com/clangd/clangd/discussions/1662), + // CXXRecordDecl::lookupDepenedentName() can be removed, and its call sites + // can be modified to benefit from the more comprehensive heuristics offered + // by HeuristicResolver instead. + std::vector + lookupDependentName(CXXRecordDecl *RD, DeclarationName Name, + llvm::function_ref Filter); + bool findOrdinaryMemberInDependentClasses(const CXXBaseSpecifier *Specifier, + CXXBasePath &Path, + DeclarationName Name); +}; + // Convenience lambdas for use as the 'Filter' parameter of // HeuristicResolver::resolveDependentMember(). const auto NoFilter = [](const NamedDecl *D) { return true; }; @@ -31,8 +105,6 @@ const auto TemplateFilter = [](const NamedDecl *D) { return isa(D); }; -namespace { - const Type *resolveDeclsToType(const std::vector &Decls, ASTContext &Ctx) { if (Decls.size() != 1) // Names an overload set -- just bail. @@ -46,12 +118,10 @@ const Type *resolveDeclsToType(const std::vector &Decls, return nullptr; } -} // namespace - // Helper function for HeuristicResolver::resolveDependentMember() // which takes a possibly-dependent type `T` and heuristically // resolves it to a CXXRecordDecl in which we can try name lookup. -CXXRecordDecl *HeuristicResolver::resolveTypeToRecordDecl(const Type *T) const { +CXXRecordDecl *HeuristicResolverImpl::resolveTypeToRecordDecl(const Type *T) { assert(T); // Unwrap type sugar such as type aliases. @@ -84,7 +154,7 @@ CXXRecordDecl *HeuristicResolver::resolveTypeToRecordDecl(const Type *T) const { return TD->getTemplatedDecl(); } -const Type *HeuristicResolver::getPointeeType(const Type *T) const { +const Type *HeuristicResolverImpl::getPointeeType(const Type *T) { if (!T) return nullptr; @@ -117,8 +187,8 @@ const Type *HeuristicResolver::getPointeeType(const Type *T) const { return FirstArg.getAsType().getTypePtrOrNull(); } -std::vector HeuristicResolver::resolveMemberExpr( - const CXXDependentScopeMemberExpr *ME) const { +std::vector HeuristicResolverImpl::resolveMemberExpr( + const CXXDependentScopeMemberExpr *ME) { // If the expression has a qualifier, try resolving the member inside the // qualifier's type. // Note that we cannot use a NonStaticFilter in either case, for a couple @@ -164,14 +234,14 @@ std::vector HeuristicResolver::resolveMemberExpr( return resolveDependentMember(BaseType, ME->getMember(), NoFilter); } -std::vector HeuristicResolver::resolveDeclRefExpr( - const DependentScopeDeclRefExpr *RE) const { +std::vector +HeuristicResolverImpl::resolveDeclRefExpr(const DependentScopeDeclRefExpr *RE) { return resolveDependentMember(RE->getQualifier()->getAsType(), RE->getDeclName(), StaticFilter); } std::vector -HeuristicResolver::resolveTypeOfCallExpr(const CallExpr *CE) const { +HeuristicResolverImpl::resolveTypeOfCallExpr(const CallExpr *CE) { const auto *CalleeType = resolveExprToType(CE->getCallee()); if (!CalleeType) return {}; @@ -187,7 +257,7 @@ HeuristicResolver::resolveTypeOfCallExpr(const CallExpr *CE) const { } std::vector -HeuristicResolver::resolveCalleeOfCallExpr(const CallExpr *CE) const { +HeuristicResolverImpl::resolveCalleeOfCallExpr(const CallExpr *CE) { if (const auto *ND = dyn_cast_or_null(CE->getCalleeDecl())) { return {ND}; } @@ -195,29 +265,31 @@ HeuristicResolver::resolveCalleeOfCallExpr(const CallExpr *CE) const { return resolveExprToDecls(CE->getCallee()); } -std::vector HeuristicResolver::resolveUsingValueDecl( - const UnresolvedUsingValueDecl *UUVD) const { +std::vector HeuristicResolverImpl::resolveUsingValueDecl( + const UnresolvedUsingValueDecl *UUVD) { return resolveDependentMember(UUVD->getQualifier()->getAsType(), UUVD->getNameInfo().getName(), ValueFilter); } -std::vector HeuristicResolver::resolveDependentNameType( - const DependentNameType *DNT) const { +std::vector +HeuristicResolverImpl::resolveDependentNameType(const DependentNameType *DNT) { + if (auto [_, inserted] = SeenDependentNameTypes.insert(DNT); !inserted) + return {}; return resolveDependentMember( resolveNestedNameSpecifierToType(DNT->getQualifier()), DNT->getIdentifier(), TypeFilter); } std::vector -HeuristicResolver::resolveTemplateSpecializationType( - const DependentTemplateSpecializationType *DTST) const { +HeuristicResolverImpl::resolveTemplateSpecializationType( + const DependentTemplateSpecializationType *DTST) { return resolveDependentMember( resolveNestedNameSpecifierToType(DTST->getQualifier()), DTST->getIdentifier(), TemplateFilter); } std::vector -HeuristicResolver::resolveExprToDecls(const Expr *E) const { +HeuristicResolverImpl::resolveExprToDecls(const Expr *E) { if (const auto *ME = dyn_cast(E)) { return resolveMemberExpr(ME); } @@ -236,7 +308,7 @@ HeuristicResolver::resolveExprToDecls(const Expr *E) const { return {}; } -const Type *HeuristicResolver::resolveExprToType(const Expr *E) const { +const Type *HeuristicResolverImpl::resolveExprToType(const Expr *E) { std::vector Decls = resolveExprToDecls(E); if (!Decls.empty()) return resolveDeclsToType(Decls, Ctx); @@ -244,8 +316,8 @@ const Type *HeuristicResolver::resolveExprToType(const Expr *E) const { return E->getType().getTypePtr(); } -const Type *HeuristicResolver::resolveNestedNameSpecifierToType( - const NestedNameSpecifier *NNS) const { +const Type *HeuristicResolverImpl::resolveNestedNameSpecifierToType( + const NestedNameSpecifier *NNS) { if (!NNS) return nullptr; @@ -270,8 +342,6 @@ const Type *HeuristicResolver::resolveNestedNameSpecifierToType( return nullptr; } -namespace { - bool isOrdinaryMember(const NamedDecl *ND) { return ND->isInIdentifierNamespace(Decl::IDNS_Ordinary | Decl::IDNS_Tag | Decl::IDNS_Member); @@ -287,11 +357,9 @@ bool findOrdinaryMember(const CXXRecordDecl *RD, CXXBasePath &Path, return false; } -} // namespace - -bool HeuristicResolver::findOrdinaryMemberInDependentClasses( +bool HeuristicResolverImpl::findOrdinaryMemberInDependentClasses( const CXXBaseSpecifier *Specifier, CXXBasePath &Path, - DeclarationName Name) const { + DeclarationName Name) { CXXRecordDecl *RD = resolveTypeToRecordDecl(Specifier->getType().getTypePtr()); if (!RD) @@ -299,9 +367,9 @@ bool HeuristicResolver::findOrdinaryMemberInDependentClasses( return findOrdinaryMember(RD, Path, Name); } -std::vector HeuristicResolver::lookupDependentName( +std::vector HeuristicResolverImpl::lookupDependentName( CXXRecordDecl *RD, DeclarationName Name, - llvm::function_ref Filter) const { + llvm::function_ref Filter) { std::vector Results; // Lookup in the class. @@ -332,9 +400,9 @@ std::vector HeuristicResolver::lookupDependentName( return Results; } -std::vector HeuristicResolver::resolveDependentMember( +std::vector HeuristicResolverImpl::resolveDependentMember( const Type *T, DeclarationName Name, - llvm::function_ref Filter) const { + llvm::function_ref Filter) { if (!T) return {}; if (auto *ET = T->getAs()) { @@ -349,6 +417,44 @@ std::vector HeuristicResolver::resolveDependentMember( } return {}; } +} // namespace + +std::vector HeuristicResolver::resolveMemberExpr( + const CXXDependentScopeMemberExpr *ME) const { + return HeuristicResolverImpl(Ctx).resolveMemberExpr(ME); +} +std::vector HeuristicResolver::resolveDeclRefExpr( + const DependentScopeDeclRefExpr *RE) const { + return HeuristicResolverImpl(Ctx).resolveDeclRefExpr(RE); +} +std::vector +HeuristicResolver::resolveTypeOfCallExpr(const CallExpr *CE) const { + return HeuristicResolverImpl(Ctx).resolveTypeOfCallExpr(CE); +} +std::vector +HeuristicResolver::resolveCalleeOfCallExpr(const CallExpr *CE) const { + return HeuristicResolverImpl(Ctx).resolveCalleeOfCallExpr(CE); +} +std::vector HeuristicResolver::resolveUsingValueDecl( + const UnresolvedUsingValueDecl *UUVD) const { + return HeuristicResolverImpl(Ctx).resolveUsingValueDecl(UUVD); +} +std::vector HeuristicResolver::resolveDependentNameType( + const DependentNameType *DNT) const { + return HeuristicResolverImpl(Ctx).resolveDependentNameType(DNT); +} +std::vector +HeuristicResolver::resolveTemplateSpecializationType( + const DependentTemplateSpecializationType *DTST) const { + return HeuristicResolverImpl(Ctx).resolveTemplateSpecializationType(DTST); +} +const Type *HeuristicResolver::resolveNestedNameSpecifierToType( + const NestedNameSpecifier *NNS) const { + return HeuristicResolverImpl(Ctx).resolveNestedNameSpecifierToType(NNS); +} +const Type *HeuristicResolver::getPointeeType(const Type *T) const { + return HeuristicResolverImpl(Ctx).getPointeeType(T); +} } // namespace clangd } // namespace clang diff --git a/clang-tools-extra/clangd/HeuristicResolver.h b/clang-tools-extra/clangd/HeuristicResolver.h index dc04123d37593c..dcc063bbc4adc0 100644 --- a/clang-tools-extra/clangd/HeuristicResolver.h +++ b/clang-tools-extra/clangd/HeuristicResolver.h @@ -77,43 +77,6 @@ class HeuristicResolver { private: ASTContext &Ctx; - - // Given a tag-decl type and a member name, heuristically resolve the - // name to one or more declarations. - // The current heuristic is simply to look up the name in the primary - // template. This is a heuristic because the template could potentially - // have specializations that declare different members. - // Multiple declarations could be returned if the name is overloaded - // (e.g. an overloaded method in the primary template). - // This heuristic will give the desired answer in many cases, e.g. - // for a call to vector::size(). - std::vector resolveDependentMember( - const Type *T, DeclarationName Name, - llvm::function_ref Filter) const; - - // Try to heuristically resolve the type of a possibly-dependent expression - // `E`. - const Type *resolveExprToType(const Expr *E) const; - std::vector resolveExprToDecls(const Expr *E) const; - - // Helper function for HeuristicResolver::resolveDependentMember() - // which takes a possibly-dependent type `T` and heuristically - // resolves it to a CXXRecordDecl in which we can try name lookup. - CXXRecordDecl *resolveTypeToRecordDecl(const Type *T) const; - - // This is a reimplementation of CXXRecordDecl::lookupDependentName() - // so that the implementation can call into other HeuristicResolver helpers. - // FIXME: Once HeuristicResolver is upstreamed to the clang libraries - // (https://github.com/clangd/clangd/discussions/1662), - // CXXRecordDecl::lookupDepenedentName() can be removed, and its call sites - // can be modified to benefit from the more comprehensive heuristics offered - // by HeuristicResolver instead. - std::vector lookupDependentName( - CXXRecordDecl *RD, DeclarationName Name, - llvm::function_ref Filter) const; - bool findOrdinaryMemberInDependentClasses(const CXXBaseSpecifier *Specifier, - CXXBasePath &Path, - DeclarationName Name) const; }; } // namespace clangd diff --git a/clang-tools-extra/clangd/unittests/FindTargetTests.cpp b/clang-tools-extra/clangd/unittests/FindTargetTests.cpp index 29cff68cf03b2e..0af6036734ba53 100644 --- a/clang-tools-extra/clangd/unittests/FindTargetTests.cpp +++ b/clang-tools-extra/clangd/unittests/FindTargetTests.cpp @@ -1009,6 +1009,33 @@ TEST_F(TargetDeclTest, DependentTypes) { )cpp"; EXPECT_DECLS("DependentTemplateSpecializationTypeLoc", "template struct B"); + + // Dependent name with recursive definition. We don't expect a + // result, but we shouldn't get into a stack overflow either. + Code = R"cpp( + template + struct waldo { + typedef typename waldo::type::[[next]] type; + }; + )cpp"; + EXPECT_DECLS("DependentNameTypeLoc"); + + // Similar to above but using mutually recursive templates. + Code = R"cpp( + template + struct odd; + + template + struct even { + using type = typename odd::type::next; + }; + + template + struct odd { + using type = typename even::type::[[next]]; + }; + )cpp"; + EXPECT_DECLS("DependentNameTypeLoc"); } TEST_F(TargetDeclTest, TypedefCascade) { diff --git a/libcxx/test/tools/clang_tidy_checks/CMakeLists.txt b/libcxx/test/tools/clang_tidy_checks/CMakeLists.txt index 978e7095216522..a52140e2b9938a 100644 --- a/libcxx/test/tools/clang_tidy_checks/CMakeLists.txt +++ b/libcxx/test/tools/clang_tidy_checks/CMakeLists.txt @@ -5,7 +5,7 @@ set(LLVM_DIR_SAVE ${LLVM_DIR}) set(Clang_DIR_SAVE ${Clang_DIR}) -find_package(Clang 18) +find_package(Clang 18.1) if (NOT Clang_FOUND) find_package(Clang 17) endif() diff --git a/lld/ELF/Arch/LoongArch.cpp b/lld/ELF/Arch/LoongArch.cpp index 8a6f6db68f2909..464f5dfb320ccc 100644 --- a/lld/ELF/Arch/LoongArch.cpp +++ b/lld/ELF/Arch/LoongArch.cpp @@ -159,8 +159,9 @@ static bool isJirl(uint32_t insn) { static void handleUleb128(uint8_t *loc, uint64_t val) { const uint32_t maxcount = 1 + 64 / 7; uint32_t count; - uint64_t orig = decodeULEB128(loc, &count); - if (count > maxcount) + const char *error = nullptr; + uint64_t orig = decodeULEB128(loc, &count, nullptr, &error); + if (count > maxcount || (count == maxcount && error)) errorOrWarn(getErrorLocation(loc) + "extra space for uleb128"); uint64_t mask = count < maxcount ? (1ULL << 7 * count) - 1 : -1ULL; encodeULEB128((orig + val) & mask, loc, count); 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/lld/test/ELF/loongarch-reloc-leb128.s b/lld/test/ELF/loongarch-reloc-leb128.s index 9e6f221e62b639..2dd327d1564ebd 100644 --- a/lld/test/ELF/loongarch-reloc-leb128.s +++ b/lld/test/ELF/loongarch-reloc-leb128.s @@ -99,4 +99,4 @@ w2: .reloc ., R_LARCH_ADD_ULEB128, w2 .reloc ., R_LARCH_SUB_ULEB128, w1 .fill 10, 1, 0x80 -.byte 0 +.byte 1 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/COFF.h b/llvm/include/llvm/Object/COFF.h index a548b2c15c5fdc..2a5c3d8913b15c 100644 --- a/llvm/include/llvm/Object/COFF.h +++ b/llvm/include/llvm/Object/COFF.h @@ -1362,6 +1362,47 @@ class SectionStrippedError SectionStrippedError() { setErrorCode(object_error::section_stripped); } }; +inline std::optional +getArm64ECMangledFunctionName(StringRef Name) { + bool IsCppFn = Name[0] == '?'; + if (IsCppFn && Name.find("$$h") != std::string::npos) + return std::nullopt; + if (!IsCppFn && Name[0] == '#') + return std::nullopt; + + StringRef Prefix = "$$h"; + size_t InsertIdx = 0; + if (IsCppFn) { + InsertIdx = Name.find("@@"); + size_t ThreeAtSignsIdx = Name.find("@@@"); + if (InsertIdx != std::string::npos && InsertIdx != ThreeAtSignsIdx) { + InsertIdx += 2; + } else { + InsertIdx = Name.find("@"); + if (InsertIdx != std::string::npos) + InsertIdx++; + } + } else { + Prefix = "#"; + } + + return std::optional( + (Name.substr(0, InsertIdx) + Prefix + Name.substr(InsertIdx)).str()); +} + +inline std::optional +getArm64ECDemangledFunctionName(StringRef Name) { + if (Name[0] == '#') + return std::string(Name.substr(1)); + if (Name[0] != '?') + return std::nullopt; + + std::pair Pair = Name.split("$$h"); + if (Pair.second.empty()) + return std::nullopt; + return (Pair.first + Pair.second).str(); +} + } // end namespace object } // end namespace llvm diff --git a/llvm/include/llvm/Object/COFFImportFile.h b/llvm/include/llvm/Object/COFFImportFile.h index edc836ff0348cb..8358197309f000 100644 --- a/llvm/include/llvm/Object/COFFImportFile.h +++ b/llvm/include/llvm/Object/COFFImportFile.h @@ -26,7 +26,16 @@ namespace llvm { namespace object { +constexpr std::string_view ImportDescriptorPrefix = "__IMPORT_DESCRIPTOR_"; +constexpr std::string_view NullImportDescriptorSymbolName = + "__NULL_IMPORT_DESCRIPTOR"; +constexpr std::string_view NullThunkDataPrefix = "\x7f"; +constexpr std::string_view NullThunkDataSuffix = "_NULL_THUNK_DATA"; + class COFFImportFile : public SymbolicFile { +private: + enum SymbolIndex { ImpSymbol, ThunkSymbol, ECAuxSymbol, ECThunkSymbol }; + public: COFFImportFile(MemoryBufferRef Source) : SymbolicFile(ID_COFFImportFile, Source) {} @@ -36,9 +45,23 @@ class COFFImportFile : public SymbolicFile { void moveSymbolNext(DataRefImpl &Symb) const override { ++Symb.p; } Error printSymbolName(raw_ostream &OS, DataRefImpl Symb) const override { - if (Symb.p == 0) + switch (Symb.p) { + case ImpSymbol: OS << "__imp_"; - OS << StringRef(Data.getBufferStart() + sizeof(coff_import_header)); + break; + case ECAuxSymbol: + OS << "__imp_aux_"; + break; + } + const char *Name = Data.getBufferStart() + sizeof(coff_import_header); + if (Symb.p != ECThunkSymbol && COFF::isArm64EC(getMachine())) { + if (std::optional DemangledName = + getArm64ECDemangledFunctionName(Name)) { + OS << StringRef(*DemangledName); + return Error::success(); + } + } + OS << StringRef(Name); return Error::success(); } @@ -52,7 +75,12 @@ class COFFImportFile : public SymbolicFile { basic_symbol_iterator symbol_end() const override { DataRefImpl Symb; - Symb.p = isData() ? 1 : 2; + if (isData()) + Symb.p = ImpSymbol + 1; + else if (COFF::isArm64EC(getMachine())) + Symb.p = ECThunkSymbol + 1; + else + Symb.p = ThunkSymbol + 1; return BasicSymbolRef(Symb, this); } @@ -66,6 +94,7 @@ class COFFImportFile : public SymbolicFile { uint16_t getMachine() const { return getCOFFImportHeader()->Machine; } StringRef getFileFormatName() const; + StringRef getExportName() const; private: bool isData() const { diff --git a/llvm/lib/Object/ArchiveWriter.cpp b/llvm/lib/Object/ArchiveWriter.cpp index 155926a8c5949d..1a7ed2db543964 100644 --- a/llvm/lib/Object/ArchiveWriter.cpp +++ b/llvm/lib/Object/ArchiveWriter.cpp @@ -677,6 +677,13 @@ static bool isECObject(object::SymbolicFile &Obj) { return false; } +bool isImportDescriptor(StringRef Name) { + return Name.starts_with(ImportDescriptorPrefix) || + Name == StringRef{NullImportDescriptorSymbolName} || + (Name.starts_with(NullThunkDataPrefix) && + Name.ends_with(NullThunkDataSuffix)); +} + static Expected> getSymbols(SymbolicFile *Obj, uint16_t Index, raw_ostream &SymNames, @@ -704,6 +711,10 @@ static Expected> getSymbols(SymbolicFile *Obj, if (Map == &SymMap->Map) { Ret.push_back(SymNames.tell()); SymNames << Name << '\0'; + // If EC is enabled, then the import descriptors are NOT put into EC + // objects so we need to copy them to the EC map manually. + if (SymMap->UseECMap && isImportDescriptor(Name)) + SymMap->ECMap[Name] = Index; } } else { Ret.push_back(SymNames.tell()); diff --git a/llvm/lib/Object/COFFImportFile.cpp b/llvm/lib/Object/COFFImportFile.cpp index 60556c149bf735..d3b5cf2d9f7b52 100644 --- a/llvm/lib/Object/COFFImportFile.cpp +++ b/llvm/lib/Object/COFFImportFile.cpp @@ -52,6 +52,38 @@ StringRef COFFImportFile::getFileFormatName() const { } } +StringRef COFFImportFile::getExportName() const { + const coff_import_header *hdr = getCOFFImportHeader(); + StringRef name = Data.getBuffer().substr(sizeof(*hdr)).split('\0').first; + + auto ltrim1 = [](StringRef s, StringRef chars) { + return !s.empty() && chars.contains(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: { + // Skip DLL name + name = Data.getBuffer().substr(sizeof(*hdr) + name.size() + 1); + name = name.split('\0').second.split('\0').first; + break; + } + default: + break; + } + + return name; +} + static uint16_t getImgRelRelocation(MachineTypes Machine) { switch (Machine) { default: @@ -76,7 +108,7 @@ template static void append(std::vector &B, const T &Data) { } static void writeStringTable(std::vector &B, - ArrayRef Strings) { + ArrayRef Strings) { // The COFF string table consists of a 4-byte value which is the size of the // table, including the length field itself. This value is followed by the // string content itself, which is an array of null-terminated C-style @@ -139,9 +171,6 @@ static Expected replace(StringRef S, StringRef From, return (Twine(S.substr(0, Pos)) + To + S.substr(Pos + From.size())).str(); } -static const std::string NullImportDescriptorSymbolName = - "__NULL_IMPORT_DESCRIPTOR"; - namespace { // This class constructs various small object files necessary to support linking // symbols imported from a DLL. The contents are pretty strictly defined and @@ -160,8 +189,9 @@ class ObjectFactory { public: ObjectFactory(StringRef S, MachineTypes M) : NativeMachine(M), ImportName(S), Library(llvm::sys::path::stem(S)), - ImportDescriptorSymbolName(("__IMPORT_DESCRIPTOR_" + Library).str()), - NullThunkSymbolName(("\x7f" + Library + "_NULL_THUNK_DATA").str()) {} + ImportDescriptorSymbolName((ImportDescriptorPrefix + Library).str()), + NullThunkSymbolName( + (NullThunkDataPrefix + Library + NullThunkDataSuffix).str()) {} // Creates an Import Descriptor. This is a small object file which contains a // reference to the terminators and contains the library name (entry) for the @@ -183,6 +213,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 +505,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); @@ -499,6 +531,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 +651,51 @@ 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 { + NameType = getNameType(SymbolName, E.Name, Machine, MinGW); + } + + // On ARM64EC, use EXPORTAS to import demangled name for mangled symbols. + if (ImportType == IMPORT_CODE && isArm64EC(Machine)) { + if (std::optional MangledName = + getArm64ECMangledFunctionName(Name)) { + if (ExportName.empty()) { + NameType = IMPORT_NAME_EXPORTAS; + ExportName.swap(Name); + } + Name = std::move(*MangledName); + } else if (ExportName.empty()) { + NameType = IMPORT_NAME_EXPORTAS; + ExportName = std::move(*getArm64ECDemangledFunctionName(Name)); + } + } + + Members.push_back(OF.createShortImport(Name, E.Ordinal, ImportType, + NameType, ExportName, Machine)); } return writeArchive(Path, Members, SymtabWritingMode::NormalSymtab, diff --git a/llvm/lib/Target/AArch64/AArch64Arm64ECCallLowering.cpp b/llvm/lib/Target/AArch64/AArch64Arm64ECCallLowering.cpp index 03d641d04413ef..55c5bbc66a3f4f 100644 --- a/llvm/lib/Target/AArch64/AArch64Arm64ECCallLowering.cpp +++ b/llvm/lib/Target/AArch64/AArch64Arm64ECCallLowering.cpp @@ -24,11 +24,13 @@ #include "llvm/IR/IRBuilder.h" #include "llvm/IR/Instruction.h" #include "llvm/InitializePasses.h" +#include "llvm/Object/COFF.h" #include "llvm/Pass.h" #include "llvm/Support/CommandLine.h" #include "llvm/TargetParser/Triple.h" using namespace llvm; +using namespace llvm::object; using OperandBundleDef = OperandBundleDefT; diff --git a/llvm/lib/Target/AArch64/AArch64MCInstLower.cpp b/llvm/lib/Target/AArch64/AArch64MCInstLower.cpp index 1e12cf545fa777..37d621cd2f6580 100644 --- a/llvm/lib/Target/AArch64/AArch64MCInstLower.cpp +++ b/llvm/lib/Target/AArch64/AArch64MCInstLower.cpp @@ -23,11 +23,13 @@ #include "llvm/MC/MCExpr.h" #include "llvm/MC/MCInst.h" #include "llvm/MC/MCStreamer.h" +#include "llvm/Object/COFF.h" #include "llvm/Support/CodeGen.h" #include "llvm/Support/CommandLine.h" #include "llvm/Target/TargetLoweringObjectFile.h" #include "llvm/Target/TargetMachine.h" using namespace llvm; +using namespace llvm::object; extern cl::opt EnableAArch64ELFLocalDynamicTLSGeneration; diff --git a/llvm/lib/Target/AArch64/Utils/AArch64BaseInfo.h b/llvm/lib/Target/AArch64/Utils/AArch64BaseInfo.h index 10e69655f77e10..8b32d593d2a812 100644 --- a/llvm/lib/Target/AArch64/Utils/AArch64BaseInfo.h +++ b/llvm/lib/Target/AArch64/Utils/AArch64BaseInfo.h @@ -248,34 +248,6 @@ static inline bool atomicBarrierDroppedOnZero(unsigned Opcode) { return false; } -static inline std::optional -getArm64ECMangledFunctionName(std::string Name) { - bool IsCppFn = Name[0] == '?'; - if (IsCppFn && Name.find("$$h") != std::string::npos) - return std::nullopt; - if (!IsCppFn && Name[0] == '#') - return std::nullopt; - - StringRef Prefix = "$$h"; - size_t InsertIdx = 0; - if (IsCppFn) { - InsertIdx = Name.find("@@"); - size_t ThreeAtSignsIdx = Name.find("@@@"); - if (InsertIdx != std::string::npos && InsertIdx != ThreeAtSignsIdx) { - InsertIdx += 2; - } else { - InsertIdx = Name.find("@"); - if (InsertIdx != std::string::npos) - InsertIdx++; - } - } else { - Prefix = "#"; - } - - Name.insert(Name.begin() + InsertIdx, Prefix.begin(), Prefix.end()); - return std::optional(Name); -} - namespace AArch64CC { // The CondCodes constants map directly to the 4-bit encoding of the condition 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..ebc1b166ee4ea6 100644 --- a/llvm/test/tools/llvm-lib/arm64ec-implib.test +++ b/llvm/test/tools/llvm-lib/arm64ec-implib.test @@ -11,9 +11,26 @@ ARMAP-NEXT: __NULL_IMPORT_DESCRIPTOR in test.dll ARMAP-NEXT: test_NULL_THUNK_DATA in test.dll ARMAP-EMPTY: ARMAP-NEXT: Archive EC map +ARMAP-NEXT: #expname in test.dll +ARMAP-NEXT: #funcexp in test.dll +ARMAP-NEXT: #mangledfunc in test.dll +ARMAP-NEXT: ?test_cpp_func@@$$hYAHPEAX@Z in test.dll +ARMAP-NEXT: ?test_cpp_func@@YAHPEAX@Z in test.dll +ARMAP-NEXT: __IMPORT_DESCRIPTOR_test in test.dll +ARMAP-NEXT: __NULL_IMPORT_DESCRIPTOR in test.dll +ARMAP-NEXT: __imp_?test_cpp_func@@YAHPEAX@Z in test.dll +ARMAP-NEXT: __imp_aux_?test_cpp_func@@YAHPEAX@Z in test.dll +ARMAP-NEXT: __imp_aux_expname in test.dll +ARMAP-NEXT: __imp_aux_funcexp in test.dll +ARMAP-NEXT: __imp_aux_mangledfunc in test.dll ARMAP-NEXT: __imp_dataexp in test.dll +ARMAP-NEXT: __imp_expname in test.dll ARMAP-NEXT: __imp_funcexp in test.dll +ARMAP-NEXT: __imp_mangledfunc in test.dll +ARMAP-NEXT: expname in test.dll ARMAP-NEXT: funcexp in test.dll +ARMAP-NEXT: mangledfunc in test.dll +ARMAP-NEXT: test_NULL_THUNK_DATA in test.dll RUN: llvm-readobj test.lib | FileCheck -check-prefix=READOBJ %s @@ -35,14 +52,48 @@ READOBJ-EMPTY: READOBJ-NEXT: File: test.dll READOBJ-NEXT: Format: COFF-import-file-ARM64EC READOBJ-NEXT: Type: code -READOBJ-NEXT: Name type: name +READOBJ-NEXT: Name type: export as +READOBJ-NEXT: Export name: funcexp READOBJ-NEXT: Symbol: __imp_funcexp READOBJ-NEXT: Symbol: funcexp +READOBJ-NEXT: Symbol: __imp_aux_funcexp +READOBJ-NEXT: Symbol: #funcexp +READOBJ-EMPTY: +READOBJ-NEXT: File: test.dll +READOBJ-NEXT: Format: COFF-import-file-ARM64EC +READOBJ-NEXT: Type: code +READOBJ-NEXT: Name type: export as +READOBJ-NEXT: Export name: mangledfunc +READOBJ-NEXT: Symbol: __imp_mangledfunc +READOBJ-NEXT: Symbol: mangledfunc +READOBJ-NEXT: Symbol: __imp_aux_mangledfunc +READOBJ-NEXT: Symbol: #mangledfunc +READOBJ-EMPTY: +READOBJ-NEXT: File: test.dll +READOBJ-NEXT: Format: COFF-import-file-ARM64EC +READOBJ-NEXT: Type: code +READOBJ-NEXT: Name type: export as +READOBJ-NEXT: Export name: ?test_cpp_func@@YAHPEAX@Z +READOBJ-NEXT: Symbol: __imp_?test_cpp_func@@YAHPEAX@Z +READOBJ-NEXT: Symbol: ?test_cpp_func@@YAHPEAX@Z +READOBJ-NEXT: Symbol: __imp_aux_?test_cpp_func@@YAHPEAX@Z +READOBJ-NEXT: Symbol: ?test_cpp_func@@$$hYAHPEAX@Z +READOBJ-EMPTY: +READOBJ-NEXT: File: test.dll +READOBJ-NEXT: Format: COFF-import-file-ARM64EC +READOBJ-NEXT: Type: code +READOBJ-NEXT: Name type: export as +READOBJ-NEXT: Export name: expname +READOBJ-NEXT: Symbol: __imp_expname +READOBJ-NEXT: Symbol: expname +READOBJ-NEXT: Symbol: __imp_aux_expname +READOBJ-NEXT: Symbol: #expname READOBJ-EMPTY: 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: @@ -53,4 +104,7 @@ RUN: llvm-nm --print-armap test2.lib | FileCheck -check-prefix=ARMAP %s LIBRARY test.dll EXPORTS funcexp + #mangledfunc + ?test_cpp_func@@YAHPEAX@Z + expname=impname dataexp DATA 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-readobj/COFF/file-headers.test @@ -323,6 +323,7 @@ symbols: # IMPORTLIB:Format: COFF-import-file-i386 # IMPORTLIB-NEXT:Type: code # IMPORTLIB-NEXT:Name type: noprefix +# IMPORTLIB-NEXT:Export name: func # IMPORTLIB-NEXT:Symbol: __imp__func # IMPORTLIB-NEXT:Symbol: _func # IMPORTLIB-NOT:{{.}} diff --git a/llvm/tools/llvm-readobj/COFFImportDumper.cpp b/llvm/tools/llvm-readobj/COFFImportDumper.cpp index 8aedc310ae3a9f..0ab2a17655653e 100644 --- a/llvm/tools/llvm-readobj/COFFImportDumper.cpp +++ b/llvm/tools/llvm-readobj/COFFImportDumper.cpp @@ -45,8 +45,14 @@ 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) + Writer.printString("Export name", File->getExportName()); + for (const object::BasicSymbolRef &Sym : File->symbols()) { raw_ostream &OS = Writer.startLine(); OS << "Symbol: ";