diff options
Diffstat (limited to 'contrib/llvm-project/llvm/lib/TextAPI')
4 files changed, 652 insertions, 2 deletions
diff --git a/contrib/llvm-project/llvm/lib/TextAPI/BinaryReader/DylibReader.cpp b/contrib/llvm-project/llvm/lib/TextAPI/BinaryReader/DylibReader.cpp new file mode 100644 index 000000000000..40b57b5e40ea --- /dev/null +++ b/contrib/llvm-project/llvm/lib/TextAPI/BinaryReader/DylibReader.cpp @@ -0,0 +1,429 @@ +//===- DylibReader.cpp -------------- TAPI MachO Dylib Reader --*- 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 +// +//===----------------------------------------------------------------------===// +/// +/// Implements the TAPI Reader for Mach-O dynamic libraries. +/// +//===----------------------------------------------------------------------===// + +#include "llvm/TextAPI/DylibReader.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/StringMap.h" +#include "llvm/Object/Binary.h" +#include "llvm/Object/MachOUniversal.h" +#include "llvm/Support/Endian.h" +#include "llvm/TargetParser/Triple.h" +#include "llvm/TextAPI/RecordsSlice.h" +#include "llvm/TextAPI/TextAPIError.h" +#include <iomanip> +#include <set> +#include <sstream> +#include <string> +#include <tuple> + +using namespace llvm; +using namespace llvm::object; +using namespace llvm::MachO; +using namespace llvm::MachO::DylibReader; + +using TripleVec = std::vector<Triple>; +static typename TripleVec::iterator emplace(TripleVec &Container, Triple &&T) { + auto I = partition_point(Container, [=](const Triple &CT) { + return std::forward_as_tuple(CT.getArch(), CT.getOS(), + CT.getEnvironment()) < + std::forward_as_tuple(T.getArch(), T.getOS(), T.getEnvironment()); + }); + + if (I != Container.end() && *I == T) + return I; + return Container.emplace(I, T); +} + +static TripleVec constructTriples(MachOObjectFile *Obj, + const Architecture ArchT) { + auto getOSVersionStr = [](uint32_t V) { + PackedVersion OSVersion(V); + std::string Vers; + raw_string_ostream VStream(Vers); + VStream << OSVersion; + return VStream.str(); + }; + auto getOSVersion = [&](const MachOObjectFile::LoadCommandInfo &cmd) { + auto Vers = Obj->getVersionMinLoadCommand(cmd); + return getOSVersionStr(Vers.version); + }; + + TripleVec Triples; + bool IsIntel = ArchitectureSet(ArchT).hasX86(); + auto Arch = getArchitectureName(ArchT); + + for (const auto &cmd : Obj->load_commands()) { + std::string OSVersion; + switch (cmd.C.cmd) { + case MachO::LC_VERSION_MIN_MACOSX: + OSVersion = getOSVersion(cmd); + emplace(Triples, {Arch, "apple", "macos" + OSVersion}); + break; + case MachO::LC_VERSION_MIN_IPHONEOS: + OSVersion = getOSVersion(cmd); + if (IsIntel) + emplace(Triples, {Arch, "apple", "ios" + OSVersion, "simulator"}); + else + emplace(Triples, {Arch, "apple", "ios" + OSVersion}); + break; + case MachO::LC_VERSION_MIN_TVOS: + OSVersion = getOSVersion(cmd); + if (IsIntel) + emplace(Triples, {Arch, "apple", "tvos" + OSVersion, "simulator"}); + else + emplace(Triples, {Arch, "apple", "tvos" + OSVersion}); + break; + case MachO::LC_VERSION_MIN_WATCHOS: + OSVersion = getOSVersion(cmd); + if (IsIntel) + emplace(Triples, {Arch, "apple", "watchos" + OSVersion, "simulator"}); + else + emplace(Triples, {Arch, "apple", "watchos" + OSVersion}); + break; + case MachO::LC_BUILD_VERSION: { + OSVersion = getOSVersionStr(Obj->getBuildVersionLoadCommand(cmd).minos); + switch (Obj->getBuildVersionLoadCommand(cmd).platform) { + case MachO::PLATFORM_MACOS: + emplace(Triples, {Arch, "apple", "macos" + OSVersion}); + break; + case MachO::PLATFORM_IOS: + emplace(Triples, {Arch, "apple", "ios" + OSVersion}); + break; + case MachO::PLATFORM_TVOS: + emplace(Triples, {Arch, "apple", "tvos" + OSVersion}); + break; + case MachO::PLATFORM_WATCHOS: + emplace(Triples, {Arch, "apple", "watchos" + OSVersion}); + break; + case MachO::PLATFORM_BRIDGEOS: + emplace(Triples, {Arch, "apple", "bridgeos" + OSVersion}); + break; + case MachO::PLATFORM_MACCATALYST: + emplace(Triples, {Arch, "apple", "ios" + OSVersion, "macabi"}); + break; + case MachO::PLATFORM_IOSSIMULATOR: + emplace(Triples, {Arch, "apple", "ios" + OSVersion, "simulator"}); + break; + case MachO::PLATFORM_TVOSSIMULATOR: + emplace(Triples, {Arch, "apple", "tvos" + OSVersion, "simulator"}); + break; + case MachO::PLATFORM_WATCHOSSIMULATOR: + emplace(Triples, {Arch, "apple", "watchos" + OSVersion, "simulator"}); + break; + case MachO::PLATFORM_DRIVERKIT: + emplace(Triples, {Arch, "apple", "driverkit" + OSVersion}); + break; + default: + break; // Skip any others. + } + break; + } + default: + break; + } + } + + // Record unknown platform for older binaries that don't enforce platform + // load commands. + if (Triples.empty()) + emplace(Triples, {Arch, "apple", "unknown"}); + + return Triples; +} + +static Error readMachOHeader(MachOObjectFile *Obj, RecordsSlice &Slice) { + auto H = Obj->getHeader(); + auto &BA = Slice.getBinaryAttrs(); + + switch (H.filetype) { + default: + llvm_unreachable("unsupported binary type"); + case MachO::MH_DYLIB: + BA.File = FileType::MachO_DynamicLibrary; + break; + case MachO::MH_DYLIB_STUB: + BA.File = FileType::MachO_DynamicLibrary_Stub; + break; + case MachO::MH_BUNDLE: + BA.File = FileType::MachO_Bundle; + break; + } + + if (H.flags & MachO::MH_TWOLEVEL) + BA.TwoLevelNamespace = true; + if (H.flags & MachO::MH_APP_EXTENSION_SAFE) + BA.AppExtensionSafe = true; + + for (const auto &LCI : Obj->load_commands()) { + switch (LCI.C.cmd) { + case MachO::LC_ID_DYLIB: { + auto DLLC = Obj->getDylibIDLoadCommand(LCI); + BA.InstallName = Slice.copyString(LCI.Ptr + DLLC.dylib.name); + BA.CurrentVersion = DLLC.dylib.current_version; + BA.CompatVersion = DLLC.dylib.compatibility_version; + break; + } + case MachO::LC_REEXPORT_DYLIB: { + auto DLLC = Obj->getDylibIDLoadCommand(LCI); + BA.RexportedLibraries.emplace_back( + Slice.copyString(LCI.Ptr + DLLC.dylib.name)); + break; + } + case MachO::LC_SUB_FRAMEWORK: { + auto SFC = Obj->getSubFrameworkCommand(LCI); + BA.ParentUmbrella = Slice.copyString(LCI.Ptr + SFC.umbrella); + break; + } + case MachO::LC_SUB_CLIENT: { + auto SCLC = Obj->getSubClientCommand(LCI); + BA.AllowableClients.emplace_back(Slice.copyString(LCI.Ptr + SCLC.client)); + break; + } + case MachO::LC_UUID: { + auto UUIDLC = Obj->getUuidCommand(LCI); + std::stringstream Stream; + for (unsigned I = 0; I < 16; ++I) { + if (I == 4 || I == 6 || I == 8 || I == 10) + Stream << '-'; + Stream << std::setfill('0') << std::setw(2) << std::uppercase + << std::hex << static_cast<int>(UUIDLC.uuid[I]); + } + BA.UUID = Slice.copyString(Stream.str()); + break; + } + case MachO::LC_RPATH: { + auto RPLC = Obj->getRpathCommand(LCI); + BA.RPaths.emplace_back(Slice.copyString(LCI.Ptr + RPLC.path)); + break; + } + case MachO::LC_SEGMENT_SPLIT_INFO: { + auto SSILC = Obj->getLinkeditDataLoadCommand(LCI); + if (SSILC.datasize == 0) + BA.OSLibNotForSharedCache = true; + break; + } + default: + break; + } + } + + for (auto &Sect : Obj->sections()) { + auto SectName = Sect.getName(); + if (!SectName) + return SectName.takeError(); + if (*SectName != "__objc_imageinfo" && *SectName != "__image_info") + continue; + + auto Content = Sect.getContents(); + if (!Content) + return Content.takeError(); + + if ((Content->size() >= 8) && (Content->front() == 0)) { + uint32_t Flags; + if (Obj->isLittleEndian()) { + auto *p = + reinterpret_cast<const support::ulittle32_t *>(Content->data() + 4); + Flags = *p; + } else { + auto *p = + reinterpret_cast<const support::ubig32_t *>(Content->data() + 4); + Flags = *p; + } + BA.SwiftABI = (Flags >> 8) & 0xFF; + } + } + return Error::success(); +} + +static Error readSymbols(MachOObjectFile *Obj, RecordsSlice &Slice, + const ParseOption &Opt) { + + auto parseExport = [](const auto ExportFlags, + auto Addr) -> std::tuple<SymbolFlags, RecordLinkage> { + SymbolFlags Flags = SymbolFlags::None; + switch (ExportFlags & MachO::EXPORT_SYMBOL_FLAGS_KIND_MASK) { + case MachO::EXPORT_SYMBOL_FLAGS_KIND_REGULAR: + if (ExportFlags & MachO::EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION) + Flags |= SymbolFlags::WeakDefined; + break; + case MachO::EXPORT_SYMBOL_FLAGS_KIND_THREAD_LOCAL: + Flags |= SymbolFlags::ThreadLocalValue; + break; + } + + RecordLinkage Linkage = (ExportFlags & MachO::EXPORT_SYMBOL_FLAGS_REEXPORT) + ? RecordLinkage::Rexported + : RecordLinkage::Exported; + return {Flags, Linkage}; + }; + + Error Err = Error::success(); + + StringMap<std::pair<SymbolFlags, RecordLinkage>> Exports; + // Collect symbols from export trie first. Sometimes, there are more exports + // in the trie than in n-list due to stripping. This is common for swift + // mangled symbols. + for (auto &Sym : Obj->exports(Err)) { + auto [Flags, Linkage] = parseExport(Sym.flags(), Sym.address()); + Slice.addRecord(Sym.name(), Flags, GlobalRecord::Kind::Unknown, Linkage); + Exports[Sym.name()] = {Flags, Linkage}; + } + + for (const auto &Sym : Obj->symbols()) { + auto FlagsOrErr = Sym.getFlags(); + if (!FlagsOrErr) + return FlagsOrErr.takeError(); + auto Flags = *FlagsOrErr; + + auto NameOrErr = Sym.getName(); + if (!NameOrErr) + return NameOrErr.takeError(); + auto Name = *NameOrErr; + + RecordLinkage Linkage = RecordLinkage::Unknown; + SymbolFlags RecordFlags = SymbolFlags::None; + + if (Opt.Undefineds && (Flags & SymbolRef::SF_Undefined)) { + Linkage = RecordLinkage::Undefined; + if (Flags & SymbolRef::SF_Weak) + RecordFlags |= SymbolFlags::WeakReferenced; + } else if (Flags & SymbolRef::SF_Exported) { + auto Exp = Exports.find(Name); + // This should never be possible when binaries are produced with Apple + // linkers. However it is possible to craft dylibs where the export trie + // is either malformed or has conflicting symbols compared to n_list. + if (Exp != Exports.end()) + std::tie(RecordFlags, Linkage) = Exp->second; + else + Linkage = RecordLinkage::Exported; + } else if (Flags & SymbolRef::SF_Hidden) { + Linkage = RecordLinkage::Internal; + } else + continue; + + auto TypeOrErr = Sym.getType(); + if (!TypeOrErr) + return TypeOrErr.takeError(); + auto Type = *TypeOrErr; + + GlobalRecord::Kind GV = (Type & SymbolRef::ST_Function) + ? GlobalRecord::Kind::Function + : GlobalRecord::Kind::Variable; + + if (GV == GlobalRecord::Kind::Function) + RecordFlags |= SymbolFlags::Text; + else + RecordFlags |= SymbolFlags::Data; + + Slice.addRecord(Name, RecordFlags, GV, Linkage); + } + return Err; +} + +static Error load(MachOObjectFile *Obj, RecordsSlice &Slice, + const ParseOption &Opt, const Architecture Arch) { + if (Arch == AK_unknown) + return make_error<TextAPIError>(TextAPIErrorCode::UnsupportedTarget); + + if (Opt.MachOHeader) + if (auto Err = readMachOHeader(Obj, Slice)) + return Err; + + if (Opt.SymbolTable) + if (auto Err = readSymbols(Obj, Slice, Opt)) + return Err; + + return Error::success(); +} + +Expected<Records> DylibReader::readFile(MemoryBufferRef Buffer, + const ParseOption &Opt) { + Records Results; + + auto BinOrErr = createBinary(Buffer); + if (!BinOrErr) + return BinOrErr.takeError(); + + Binary &Bin = *BinOrErr.get(); + if (auto *Obj = dyn_cast<MachOObjectFile>(&Bin)) { + const auto Arch = getArchitectureFromCpuType(Obj->getHeader().cputype, + Obj->getHeader().cpusubtype); + if (!Opt.Archs.has(Arch)) + return make_error<TextAPIError>(TextAPIErrorCode::NoSuchArchitecture); + + auto Triples = constructTriples(Obj, Arch); + for (const auto &T : Triples) { + if (mapToPlatformType(T) == PLATFORM_UNKNOWN) + return make_error<TextAPIError>(TextAPIErrorCode::UnsupportedTarget); + Results.emplace_back(std::make_shared<RecordsSlice>(RecordsSlice({T}))); + if (auto Err = load(Obj, *Results.back(), Opt, Arch)) + return std::move(Err); + Results.back()->getBinaryAttrs().Path = Buffer.getBufferIdentifier(); + } + return Results; + } + + // Only expect MachO universal binaries at this point. + assert(isa<MachOUniversalBinary>(&Bin) && + "Expected a MachO universal binary."); + auto *UB = cast<MachOUniversalBinary>(&Bin); + + for (auto OI = UB->begin_objects(), OE = UB->end_objects(); OI != OE; ++OI) { + // Skip architecture if not requested. + auto Arch = + getArchitectureFromCpuType(OI->getCPUType(), OI->getCPUSubType()); + if (!Opt.Archs.has(Arch)) + continue; + + // Skip unknown architectures. + if (Arch == AK_unknown) + continue; + + // This can fail if the object is an archive. + auto ObjOrErr = OI->getAsObjectFile(); + + // Skip the archive and consume the error. + if (!ObjOrErr) { + consumeError(ObjOrErr.takeError()); + continue; + } + + auto &Obj = *ObjOrErr.get(); + switch (Obj.getHeader().filetype) { + default: + break; + case MachO::MH_BUNDLE: + case MachO::MH_DYLIB: + case MachO::MH_DYLIB_STUB: + for (const auto &T : constructTriples(&Obj, Arch)) { + Results.emplace_back(std::make_shared<RecordsSlice>(RecordsSlice({T}))); + if (auto Err = load(&Obj, *Results.back(), Opt, Arch)) + return std::move(Err); + } + break; + } + } + + if (Results.empty()) + return make_error<TextAPIError>(TextAPIErrorCode::EmptyResults); + return Results; +} + +Expected<std::unique_ptr<InterfaceFile>> +DylibReader::get(MemoryBufferRef Buffer) { + ParseOption Options; + auto SlicesOrErr = readFile(Buffer, Options); + if (!SlicesOrErr) + return SlicesOrErr.takeError(); + + return convertToInterfaceFile(*SlicesOrErr); +} diff --git a/contrib/llvm-project/llvm/lib/TextAPI/RecordVisitor.cpp b/contrib/llvm-project/llvm/lib/TextAPI/RecordVisitor.cpp new file mode 100644 index 000000000000..cee04e644755 --- /dev/null +++ b/contrib/llvm-project/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<ObjCIVarRecord *> 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/contrib/llvm-project/llvm/lib/TextAPI/RecordsSlice.cpp b/contrib/llvm-project/llvm/lib/TextAPI/RecordsSlice.cpp index a220b255aea3..7ceffc7c9284 100644 --- a/contrib/llvm-project/llvm/lib/TextAPI/RecordsSlice.cpp +++ b/contrib/llvm-project/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 <utility> @@ -142,8 +143,10 @@ GlobalRecord *RecordsSlice::addGlobal(StringRef Name, RecordLinkage Linkage, if (Result.second) Result.first->second = std::make_unique<GlobalRecord>(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<ObjCIVarRecord *> ObjCContainerRecord::getObjCIVars() const { + std::vector<ObjCIVarRecord *> Records; + llvm::for_each(IVars, + [&](auto &Record) { Records.push_back(Record.second.get()); }); + return Records; +} + +std::vector<ObjCCategoryRecord *> +ObjCInterfaceRecord::getObjCCategories() const { + std::vector<ObjCCategoryRecord *> 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<ObjCIVarRecord>(Name, Linkage); + Result.first->second = std::make_unique<ObjCIVarRecord>(IVar, Linkage); return Result.first->second.get(); } @@ -222,3 +253,88 @@ RecordsSlice::BinaryAttrs &RecordsSlice::getBinaryAttrs() { BA = std::make_unique<BinaryAttrs>(); 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<InterfaceFile> +createInterfaceFile(const Records &Slices, StringRef InstallName) { + // Pickup symbols first. + auto Symbols = std::make_unique<SymbolSet>(); + 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<InterfaceFile>(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<InterfaceFile> +llvm::MachO::convertToInterfaceFile(const Records &Slices) { + std::unique_ptr<InterfaceFile> File; + if (Slices.empty()) + return File; + + SetVector<StringRef> 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; +} diff --git a/contrib/llvm-project/llvm/lib/TextAPI/Utils.cpp b/contrib/llvm-project/llvm/lib/TextAPI/Utils.cpp new file mode 100644 index 000000000000..6d85083e0b54 --- /dev/null +++ b/contrib/llvm-project/llvm/lib/TextAPI/Utils.cpp @@ -0,0 +1,40 @@ +//===- Utils.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 utility functions for TextAPI Darwin operations. +// +//===----------------------------------------------------------------------===// + +#include "llvm/TextAPI/Utils.h" + +using namespace llvm; +using namespace llvm::MachO; + +void llvm::MachO::replace_extension(SmallVectorImpl<char> &Path, + const Twine &Extension) { + StringRef P(Path.begin(), Path.size()); + auto ParentPath = sys::path::parent_path(P); + auto Filename = sys::path::filename(P); + + if (!ParentPath.ends_with(Filename.str() + ".framework")) { + sys::path::replace_extension(Path, Extension); + return; + } + // Framework dylibs do not have a file extension, in those cases the new + // extension is appended. e.g. given Path: "Foo.framework/Foo" and Extension: + // "tbd", the result is "Foo.framework/Foo.tbd". + SmallString<8> Storage; + StringRef Ext = Extension.toStringRef(Storage); + + // Append '.' if needed. + if (!Ext.empty() && Ext[0] != '.') + Path.push_back('.'); + + // Append extension. + Path.append(Ext.begin(), Ext.end()); +} |