diff options
Diffstat (limited to 'contrib/llvm-project/llvm/lib/TextAPI/BinaryReader/DylibReader.cpp')
-rw-r--r-- | contrib/llvm-project/llvm/lib/TextAPI/BinaryReader/DylibReader.cpp | 429 |
1 files changed, 429 insertions, 0 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); +} |