//===--- InterfaceStubFunctionsConsumer.cpp -------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "clang/AST/Mangle.h" #include "clang/AST/RecursiveASTVisitor.h" #include "clang/Basic/TargetInfo.h" #include "clang/Frontend/CompilerInstance.h" #include "clang/Frontend/FrontendActions.h" #include "clang/Sema/TemplateInstCallback.h" #include "llvm/BinaryFormat/ELF.h" using namespace clang; namespace { class InterfaceStubFunctionsConsumer : public ASTConsumer { CompilerInstance &Instance; StringRef InFile; StringRef Format; std::set ParsedTemplates; enum RootDeclOrigin { TopLevel = 0, FromTU = 1, IsLate = 2 }; struct MangledSymbol { std::string ParentName; uint8_t Type; uint8_t Binding; std::vector Names; MangledSymbol() = delete; MangledSymbol(const std::string &ParentName, uint8_t Type, uint8_t Binding, std::vector Names) : ParentName(ParentName), Type(Type), Binding(Binding), Names(Names) {} }; using MangledSymbols = std::map; bool WriteNamedDecl(const NamedDecl *ND, MangledSymbols &Symbols, int RDO) { // Here we filter out anything that's not set to DefaultVisibility. // DefaultVisibility is set on a decl when -fvisibility is not specified on // the command line (or specified as default) and the decl does not have // __attribute__((visibility("hidden"))) set or when the command line // argument is set to hidden but the decl explicitly has // __attribute__((visibility ("default"))) set. We do this so that the user // can have fine grain control of what they want to expose in the stub. auto isVisible = [](const NamedDecl *ND) -> bool { return ND->getVisibility() == DefaultVisibility; }; auto ignoreDecl = [this, isVisible](const NamedDecl *ND) -> bool { if (!isVisible(ND)) return true; if (const VarDecl *VD = dyn_cast(ND)) { if (const auto *Parent = VD->getParentFunctionOrMethod()) if (isa(Parent) || isa(Parent)) return true; if ((VD->getStorageClass() == StorageClass::SC_Extern) || (VD->getStorageClass() == StorageClass::SC_Static && VD->getParentFunctionOrMethod() == nullptr)) return true; } if (const FunctionDecl *FD = dyn_cast(ND)) { if (FD->isInlined() && !isa(FD) && !Instance.getLangOpts().GNUInline) return true; if (const CXXMethodDecl *MD = dyn_cast(FD)) { if (const auto *RC = dyn_cast(MD->getParent())) if (isa(RC->getParent()) || !isVisible(RC)) return true; if (MD->isDependentContext() || !MD->hasBody()) return true; } if (FD->getStorageClass() == StorageClass::SC_Static) return true; } return false; }; auto getParentFunctionDecl = [](const NamedDecl *ND) -> const NamedDecl * { if (const VarDecl *VD = dyn_cast(ND)) if (const auto *FD = dyn_cast_or_null(VD->getParentFunctionOrMethod())) return FD; return nullptr; }; auto getMangledNames = [](const NamedDecl *ND) -> std::vector { if (!ND) return {""}; ASTNameGenerator NameGen(ND->getASTContext()); std::vector MangledNames = NameGen.getAllManglings(ND); if (isa(ND) || isa(ND)) return MangledNames; #ifdef EXPENSIVE_CHECKS assert(MangledNames.size() <= 1 && "Expected only one name mangling."); #endif return {NameGen.getName(ND)}; }; if (!(RDO & FromTU)) return true; if (Symbols.find(ND) != Symbols.end()) return true; // - Currently have not figured out how to produce the names for FieldDecls. // - Do not want to produce symbols for function paremeters. if (isa(ND) || isa(ND)) return true; const NamedDecl *ParentDecl = getParentFunctionDecl(ND); if ((ParentDecl && ignoreDecl(ParentDecl)) || ignoreDecl(ND)) return true; if (RDO & IsLate) { Instance.getDiagnostics().Report(diag::err_asm_invalid_type_in_input) << "Generating Interface Stubs is not supported with " "delayed template parsing."; } else { if (const auto *FD = dyn_cast(ND)) if (FD->isDependentContext()) return true; const bool IsWeak = (ND->hasAttr() || ND->hasAttr() || ND->isWeakImported()); Symbols.insert(std::make_pair( ND, MangledSymbol(getMangledNames(ParentDecl).front(), // Type: isa(ND) ? llvm::ELF::STT_OBJECT : llvm::ELF::STT_FUNC, // Binding: IsWeak ? llvm::ELF::STB_WEAK : llvm::ELF::STB_GLOBAL, getMangledNames(ND)))); } return true; } void HandleDecls(const llvm::iterator_range &Decls, MangledSymbols &Symbols, int RDO) { for (const auto *D : Decls) HandleNamedDecl(dyn_cast(D), Symbols, RDO); } void HandleTemplateSpecializations(const FunctionTemplateDecl &FTD, MangledSymbols &Symbols, int RDO) { for (const auto *D : FTD.specializations()) HandleNamedDecl(dyn_cast(D), Symbols, RDO); } void HandleTemplateSpecializations(const ClassTemplateDecl &CTD, MangledSymbols &Symbols, int RDO) { for (const auto *D : CTD.specializations()) HandleNamedDecl(dyn_cast(D), Symbols, RDO); } bool HandleNamedDecl(const NamedDecl *ND, MangledSymbols &Symbols, int RDO) { if (!ND) return false; switch (ND->getKind()) { default: break; case Decl::Kind::Namespace: HandleDecls(cast(ND)->decls(), Symbols, RDO); return true; case Decl::Kind::CXXRecord: HandleDecls(cast(ND)->decls(), Symbols, RDO); return true; case Decl::Kind::ClassTemplateSpecialization: HandleDecls(cast(ND)->decls(), Symbols, RDO); return true; case Decl::Kind::ClassTemplate: HandleTemplateSpecializations(*cast(ND), Symbols, RDO); return true; case Decl::Kind::FunctionTemplate: HandleTemplateSpecializations(*cast(ND), Symbols, RDO); return true; case Decl::Kind::Record: case Decl::Kind::Typedef: case Decl::Kind::Enum: case Decl::Kind::EnumConstant: case Decl::Kind::TemplateTypeParm: case Decl::Kind::NonTypeTemplateParm: case Decl::Kind::CXXConversion: case Decl::Kind::UnresolvedUsingValue: case Decl::Kind::Using: case Decl::Kind::UsingShadow: case Decl::Kind::TypeAliasTemplate: case Decl::Kind::TypeAlias: case Decl::Kind::VarTemplate: case Decl::Kind::VarTemplateSpecialization: case Decl::Kind::UsingDirective: case Decl::Kind::TemplateTemplateParm: case Decl::Kind::ClassTemplatePartialSpecialization: case Decl::Kind::IndirectField: case Decl::Kind::ConstructorUsingShadow: case Decl::Kind::CXXDeductionGuide: case Decl::Kind::NamespaceAlias: case Decl::Kind::UnresolvedUsingTypename: return true; case Decl::Kind::Var: { // Bail on any VarDecl that either has no named symbol. if (!ND->getIdentifier()) return true; const auto *VD = cast(ND); // Bail on any VarDecl that is a dependent or templated type. if (VD->isTemplated() || VD->getType()->isDependentType()) return true; if (WriteNamedDecl(ND, Symbols, RDO)) return true; break; } case Decl::Kind::ParmVar: case Decl::Kind::CXXMethod: case Decl::Kind::CXXConstructor: case Decl::Kind::CXXDestructor: case Decl::Kind::Function: case Decl::Kind::Field: if (WriteNamedDecl(ND, Symbols, RDO)) return true; } // While interface stubs are in the development stage, it's probably best to // catch anything that's not a VarDecl or Template/FunctionDecl. Instance.getDiagnostics().Report(diag::err_asm_invalid_type_in_input) << "Expected a function or function template decl."; return false; } public: InterfaceStubFunctionsConsumer(CompilerInstance &Instance, StringRef InFile, StringRef Format) : Instance(Instance), InFile(InFile), Format(Format) {} void HandleTranslationUnit(ASTContext &context) override { struct Visitor : public RecursiveASTVisitor { bool VisitNamedDecl(NamedDecl *ND) { if (const auto *FD = dyn_cast(ND)) if (FD->isLateTemplateParsed()) { LateParsedDecls.insert(FD); return true; } if (const auto *VD = dyn_cast(ND)) { ValueDecls.insert(VD); return true; } NamedDecls.insert(ND); return true; } std::set LateParsedDecls; std::set NamedDecls; std::set ValueDecls; } v; v.TraverseDecl(context.getTranslationUnitDecl()); MangledSymbols Symbols; auto OS = Instance.createDefaultOutputFile(/*Binary=*/false, InFile, "ifs"); if (!OS) return; if (Instance.getLangOpts().DelayedTemplateParsing) { clang::Sema &S = Instance.getSema(); for (const auto *FD : v.LateParsedDecls) { clang::LateParsedTemplate &LPT = *S.LateParsedTemplateMap.find(cast(FD))->second; S.LateTemplateParser(S.OpaqueParser, LPT); HandleNamedDecl(FD, Symbols, (FromTU | IsLate)); } } for (const NamedDecl *ND : v.ValueDecls) HandleNamedDecl(ND, Symbols, FromTU); for (const NamedDecl *ND : v.NamedDecls) HandleNamedDecl(ND, Symbols, FromTU); auto writeIfsV1 = [this](const llvm::Triple &T, const MangledSymbols &Symbols, const ASTContext &context, StringRef Format, raw_ostream &OS) -> void { OS << "--- !" << Format << "\n"; OS << "IfsVersion: 2.0\n"; OS << "Triple: " << T.str() << "\n"; OS << "ObjectFileFormat: " << "ELF" << "\n"; // TODO: For now, just ELF. OS << "Symbols:\n"; for (const auto &E : Symbols) { const MangledSymbol &Symbol = E.second; for (auto Name : Symbol.Names) { OS << " - { Name: \"" << (Symbol.ParentName.empty() || Instance.getLangOpts().CPlusPlus ? "" : (Symbol.ParentName + ".")) << Name << "\", Type: "; switch (Symbol.Type) { default: llvm_unreachable( "clang -emit-interface-stubs: Unexpected symbol type."); case llvm::ELF::STT_NOTYPE: OS << "NoType"; break; case llvm::ELF::STT_OBJECT: { auto VD = cast(E.first)->getType(); OS << "Object, Size: " << context.getTypeSizeInChars(VD).getQuantity(); break; } case llvm::ELF::STT_FUNC: OS << "Func"; break; } if (Symbol.Binding == llvm::ELF::STB_WEAK) OS << ", Weak: true"; OS << " }\n"; } } OS << "...\n"; OS.flush(); }; assert(Format == "experimental-ifs-v2" && "Unexpected IFS Format."); writeIfsV1(Instance.getTarget().getTriple(), Symbols, context, Format, *OS); } }; } // namespace std::unique_ptr GenerateInterfaceStubsAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) { return std::make_unique( CI, InFile, "experimental-ifs-v2"); }