diff --git a/llvm/include/llvm/TextAPI/DylibReader.h b/llvm/include/llvm/TextAPI/DylibReader.h index d99f22c59cf8c..b556fbf6832a9 100644 --- a/llvm/include/llvm/TextAPI/DylibReader.h +++ b/llvm/include/llvm/TextAPI/DylibReader.h @@ -38,6 +38,11 @@ struct ParseOption { /// \return List of record slices. Expected readFile(MemoryBufferRef Buffer, const ParseOption &Opt); +/// Get TAPI file representation of binary dylib. +/// +/// \param Buffer Data that points to dylib. +Expected> get(MemoryBufferRef Buffer); + } // namespace llvm::MachO::DylibReader #endif // LLVM_TEXTAPI_DYLIBREADER_H diff --git a/llvm/include/llvm/TextAPI/Record.h b/llvm/include/llvm/TextAPI/Record.h index 13d0bf6e65738..4bb1be9d0ad4e 100644 --- a/llvm/include/llvm/TextAPI/Record.h +++ b/llvm/include/llvm/TextAPI/Record.h @@ -14,6 +14,7 @@ #ifndef LLVM_TEXTAPI_RECORD_H #define LLVM_TEXTAPI_RECORD_H +#include "llvm/ADT/MapVector.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/Casting.h" #include "llvm/TextAPI/Symbol.h" @@ -50,7 +51,7 @@ class Record { public: Record() = default; Record(StringRef Name, RecordLinkage Linkage, SymbolFlags Flags) - : Name(Name), Linkage(Linkage), Flags(Flags) {} + : Name(Name), Linkage(Linkage), Flags(mergeFlags(Flags, Linkage)) {} bool isWeakDefined() const { return (Flags & SymbolFlags::WeakDefined) == SymbolFlags::WeakDefined; @@ -79,6 +80,10 @@ class Record { bool isRexported() const { return Linkage == RecordLinkage::Rexported; } StringRef getName() const { return Name; } + SymbolFlags getFlags() const { return Flags; } + +private: + SymbolFlags mergeFlags(SymbolFlags Flags, RecordLinkage Linkage); protected: StringRef Name; @@ -137,6 +142,7 @@ class ObjCContainerRecord : public Record { ObjCIVarRecord *addObjCIVar(StringRef IVar, RecordLinkage Linkage); ObjCIVarRecord *findObjCIVar(StringRef IVar) const; + std::vector getObjCIVars() const; private: RecordMap IVars; @@ -163,6 +169,7 @@ class ObjCInterfaceRecord : public ObjCContainerRecord { bool hasExceptionAttribute() const { return HasEHType; } bool addObjCCategory(ObjCCategoryRecord *Record); + std::vector getObjCCategories() const; private: bool HasEHType; diff --git a/llvm/include/llvm/TextAPI/RecordVisitor.h b/llvm/include/llvm/TextAPI/RecordVisitor.h new file mode 100644 index 0000000000000..34e43f5b0027f --- /dev/null +++ b/llvm/include/llvm/TextAPI/RecordVisitor.h @@ -0,0 +1,54 @@ +//===- llvm/TextAPI/RecordSlice.h - TAPI RecordSlice ------------*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// +/// +/// Defines the TAPI Record Visitor. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TEXTAPI_RECORDVISITOR_H +#define LLVM_TEXTAPI_RECORDVISITOR_H + +#include "llvm/TextAPI/Record.h" +#include "llvm/TextAPI/SymbolSet.h" + +namespace llvm { +namespace MachO { + +/// Base class for any usage of traversing over collected Records. +class RecordVisitor { +public: + virtual ~RecordVisitor(); + + virtual void visitGlobal(const GlobalRecord &) = 0; + virtual void visitObjCInterface(const ObjCInterfaceRecord &); + virtual void visitObjCCategory(const ObjCCategoryRecord &); +}; + +/// Specialized RecordVisitor for collecting exported symbols +/// and undefined symbols if RecordSlice being visited represents a +/// flat-namespaced library. +class SymbolConverter : public RecordVisitor { +public: + SymbolConverter(SymbolSet *Symbols, const Target &T, + const bool RecordUndefs = false) + : Symbols(Symbols), Targ(T), RecordUndefs(RecordUndefs) {} + void visitGlobal(const GlobalRecord &) override; + void visitObjCInterface(const ObjCInterfaceRecord &) override; + void visitObjCCategory(const ObjCCategoryRecord &) override; + +private: + void addIVars(const ArrayRef, StringRef ContainerName); + SymbolSet *Symbols; + const Target Targ; + const bool RecordUndefs; +}; + +} // end namespace MachO. +} // end namespace llvm. + +#endif // LLVM_TEXTAPI_RECORDVISITOR_H diff --git a/llvm/include/llvm/TextAPI/RecordsSlice.h b/llvm/include/llvm/TextAPI/RecordsSlice.h index 461a6d2dcc576..0f9e3fad1a304 100644 --- a/llvm/include/llvm/TextAPI/RecordsSlice.h +++ b/llvm/include/llvm/TextAPI/RecordsSlice.h @@ -14,11 +14,11 @@ #ifndef LLVM_TEXTAPI_RECORDSLICE_H #define LLVM_TEXTAPI_RECORDSLICE_H -#include "llvm/ADT/MapVector.h" #include "llvm/Support/Allocator.h" #include "llvm/TextAPI/InterfaceFile.h" #include "llvm/TextAPI/PackedVersion.h" #include "llvm/TextAPI/Record.h" +#include "llvm/TextAPI/RecordVisitor.h" namespace llvm { namespace MachO { @@ -133,6 +133,9 @@ class RecordsSlice { Categories.empty(); } + // Visit all records known to RecordsSlice. + void visit(RecordVisitor &V) const; + struct BinaryAttrs { std::vector AllowableClients; std::vector RexportedLibraries; @@ -174,6 +177,12 @@ class RecordsSlice { R->Linkage = std::max(R->Linkage, L); } + /// Update set flags of requested record. + /// + /// \param R The global record to update. + /// \param F Flags to update to. + void updateFlags(GlobalRecord *R, SymbolFlags F) { R->Flags = F; } + RecordMap Globals; RecordMap Classes; RecordMap> Categories; @@ -182,6 +191,7 @@ class RecordsSlice { }; using Records = llvm::SmallVector, 4>; +std::unique_ptr convertToInterfaceFile(const Records &Slices); } // namespace MachO } // namespace llvm diff --git a/llvm/lib/TextAPI/BinaryReader/DylibReader.cpp b/llvm/lib/TextAPI/BinaryReader/DylibReader.cpp index b01130cee6062..40b57b5e40ea3 100644 --- a/llvm/lib/TextAPI/BinaryReader/DylibReader.cpp +++ b/llvm/lib/TextAPI/BinaryReader/DylibReader.cpp @@ -417,3 +417,13 @@ Expected DylibReader::readFile(MemoryBufferRef Buffer, return make_error(TextAPIErrorCode::EmptyResults); return Results; } + +Expected> +DylibReader::get(MemoryBufferRef Buffer) { + ParseOption Options; + auto SlicesOrErr = readFile(Buffer, Options); + if (!SlicesOrErr) + return SlicesOrErr.takeError(); + + return convertToInterfaceFile(*SlicesOrErr); +} diff --git a/llvm/lib/TextAPI/CMakeLists.txt b/llvm/lib/TextAPI/CMakeLists.txt index 75fc92f8e06a3..2017a1ad6398b 100644 --- a/llvm/lib/TextAPI/CMakeLists.txt +++ b/llvm/lib/TextAPI/CMakeLists.txt @@ -6,6 +6,7 @@ add_llvm_component_library(LLVMTextAPI PackedVersion.cpp Platform.cpp RecordsSlice.cpp + RecordVisitor.cpp Symbol.cpp SymbolSet.cpp Target.cpp diff --git a/llvm/lib/TextAPI/RecordVisitor.cpp b/llvm/lib/TextAPI/RecordVisitor.cpp new file mode 100644 index 0000000000000..cee04e6447555 --- /dev/null +++ b/llvm/lib/TextAPI/RecordVisitor.cpp @@ -0,0 +1,65 @@ +//===- RecordVisitor.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 +// +//===----------------------------------------------------------------------===// +/// +/// Implements the TAPI Record Visitor. +/// +//===----------------------------------------------------------------------===// + +#include "llvm/TextAPI/RecordVisitor.h" + +using namespace llvm; +using namespace llvm::MachO; + +RecordVisitor::~RecordVisitor() {} +void RecordVisitor::visitObjCInterface(const ObjCInterfaceRecord &) {} +void RecordVisitor::visitObjCCategory(const ObjCCategoryRecord &) {} + +static bool shouldSkipRecord(const Record &R, const bool RecordUndefs) { + if (R.isExported()) + return false; + + // Skip non exported symbols unless for flat namespace libraries. + return !(RecordUndefs && R.isUndefined()); +} + +void SymbolConverter::visitGlobal(const GlobalRecord &GR) { + auto [SymName, SymKind] = parseSymbol(GR.getName(), GR.getFlags()); + if (shouldSkipRecord(GR, RecordUndefs)) + return; + Symbols->addGlobal(SymKind, SymName, GR.getFlags(), Targ); +} + +void SymbolConverter::addIVars(const ArrayRef IVars, + StringRef ContainerName) { + for (auto *IV : IVars) { + if (shouldSkipRecord(*IV, RecordUndefs)) + continue; + std::string Name = + ObjCIVarRecord::createScopedName(ContainerName, IV->getName()); + Symbols->addGlobal(SymbolKind::ObjectiveCInstanceVariable, Name, + IV->getFlags(), Targ); + } +} + +void SymbolConverter::visitObjCInterface(const ObjCInterfaceRecord &ObjCR) { + if (!shouldSkipRecord(ObjCR, RecordUndefs)) { + Symbols->addGlobal(SymbolKind::ObjectiveCClass, ObjCR.getName(), + ObjCR.getFlags(), Targ); + if (ObjCR.hasExceptionAttribute()) + Symbols->addGlobal(SymbolKind::ObjectiveCClassEHType, ObjCR.getName(), + ObjCR.getFlags(), Targ); + } + + addIVars(ObjCR.getObjCIVars(), ObjCR.getName()); + for (const auto *Cat : ObjCR.getObjCCategories()) + addIVars(Cat->getObjCIVars(), ObjCR.getName()); +} + +void SymbolConverter::visitObjCCategory(const ObjCCategoryRecord &Cat) { + addIVars(Cat.getObjCIVars(), Cat.getName()); +} diff --git a/llvm/lib/TextAPI/RecordsSlice.cpp b/llvm/lib/TextAPI/RecordsSlice.cpp index a220b255aea38..7ceffc7c92844 100644 --- a/llvm/lib/TextAPI/RecordsSlice.cpp +++ b/llvm/lib/TextAPI/RecordsSlice.cpp @@ -11,6 +11,7 @@ //===----------------------------------------------------------------------===// #include "llvm/TextAPI/RecordsSlice.h" +#include "llvm/ADT/SetVector.h" #include "llvm/TextAPI/Record.h" #include "llvm/TextAPI/Symbol.h" #include @@ -142,8 +143,10 @@ GlobalRecord *RecordsSlice::addGlobal(StringRef Name, RecordLinkage Linkage, if (Result.second) Result.first->second = std::make_unique(Name, Linkage, Flags, GV); - else + else { updateLinkage(Result.first->second.get(), Linkage); + updateFlags(Result.first->second.get(), Flags); + } return Result.first->second.get(); } @@ -164,6 +167,19 @@ ObjCInterfaceRecord *RecordsSlice::addObjCInterface(StringRef Name, return Result.first->second.get(); } +SymbolFlags Record::mergeFlags(SymbolFlags Flags, RecordLinkage Linkage) { + // Add Linkage properties into Flags. + switch (Linkage) { + case RecordLinkage::Rexported: + Flags |= SymbolFlags::Rexported; + return Flags; + case RecordLinkage::Undefined: + Flags |= SymbolFlags::Undefined; + return Flags; + default: + return Flags; + } +} bool ObjCInterfaceRecord::addObjCCategory(ObjCCategoryRecord *Record) { auto Result = Categories.insert({Name, Record}); @@ -188,11 +204,26 @@ ObjCCategoryRecord *RecordsSlice::addObjCCategory(StringRef ClassToExtend, return Result.first->second.get(); } +std::vector ObjCContainerRecord::getObjCIVars() const { + std::vector Records; + llvm::for_each(IVars, + [&](auto &Record) { Records.push_back(Record.second.get()); }); + return Records; +} + +std::vector +ObjCInterfaceRecord::getObjCCategories() const { + std::vector Records; + llvm::for_each(Categories, + [&](auto &Record) { Records.push_back(Record.second); }); + return Records; +} + ObjCIVarRecord *ObjCContainerRecord::addObjCIVar(StringRef IVar, RecordLinkage Linkage) { auto Result = IVars.insert({IVar, nullptr}); if (Result.second) - Result.first->second = std::make_unique(Name, Linkage); + Result.first->second = std::make_unique(IVar, Linkage); return Result.first->second.get(); } @@ -222,3 +253,88 @@ RecordsSlice::BinaryAttrs &RecordsSlice::getBinaryAttrs() { BA = std::make_unique(); return *BA; } + +void RecordsSlice::visit(RecordVisitor &V) const { + for (auto &G : Globals) + V.visitGlobal(*G.second); + for (auto &C : Classes) + V.visitObjCInterface(*C.second); + for (auto &Cat : Categories) + V.visitObjCCategory(*Cat.second); +} + +static std::unique_ptr +createInterfaceFile(const Records &Slices, StringRef InstallName) { + // Pickup symbols first. + auto Symbols = std::make_unique(); + for (auto &S : Slices) { + if (S->empty()) + continue; + auto &BA = S->getBinaryAttrs(); + if (BA.InstallName != InstallName) + continue; + + SymbolConverter Converter(Symbols.get(), S->getTarget(), + !BA.TwoLevelNamespace); + S->visit(Converter); + } + + auto File = std::make_unique(std::move(Symbols)); + File->setInstallName(InstallName); + // Assign other attributes. + for (auto &S : Slices) { + if (S->empty()) + continue; + auto &BA = S->getBinaryAttrs(); + if (BA.InstallName != InstallName) + continue; + const Target &Targ = S->getTarget(); + File->addTarget(Targ); + if (File->getFileType() == FileType::Invalid) + File->setFileType(BA.File); + if (BA.AppExtensionSafe && !File->isApplicationExtensionSafe()) + File->setApplicationExtensionSafe(); + if (BA.TwoLevelNamespace && !File->isTwoLevelNamespace()) + File->setTwoLevelNamespace(); + if (BA.OSLibNotForSharedCache && !File->isOSLibNotForSharedCache()) + File->setOSLibNotForSharedCache(); + if (File->getCurrentVersion().empty()) + File->setCurrentVersion(BA.CurrentVersion); + if (File->getCompatibilityVersion().empty()) + File->setCompatibilityVersion(BA.CompatVersion); + if (File->getSwiftABIVersion() == 0) + File->setSwiftABIVersion(BA.SwiftABI); + if (File->getPath().empty()) + File->setPath(BA.Path); + if (!BA.ParentUmbrella.empty()) + File->addParentUmbrella(Targ, BA.ParentUmbrella); + for (const auto &Client : BA.AllowableClients) + File->addAllowableClient(Client, Targ); + for (const auto &Lib : BA.RexportedLibraries) + File->addReexportedLibrary(Lib, Targ); + } + + return File; +} + +std::unique_ptr +llvm::MachO::convertToInterfaceFile(const Records &Slices) { + std::unique_ptr File; + if (Slices.empty()) + return File; + + SetVector InstallNames; + for (auto &S : Slices) { + auto Name = S->getBinaryAttrs().InstallName; + if (Name.empty()) + continue; + InstallNames.insert(Name); + } + + File = createInterfaceFile(Slices, *InstallNames.begin()); + for (auto it = std::next(InstallNames.begin()); it != InstallNames.end(); + ++it) + File->addDocument(createInterfaceFile(Slices, *it)); + + return File; +}