Path: blob/main/contrib/llvm-project/llvm/lib/TextAPI/BinaryReader/DylibReader.cpp
35291 views
//===- DylibReader.cpp -------------- TAPI MachO Dylib Reader --*- C++ -*-===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7///8/// Implements the TAPI Reader for Mach-O dynamic libraries.9///10//===----------------------------------------------------------------------===//1112#include "llvm/TextAPI/DylibReader.h"13#include "llvm/ADT/STLExtras.h"14#include "llvm/DebugInfo/DWARF/DWARFCompileUnit.h"15#include "llvm/DebugInfo/DWARF/DWARFContext.h"16#include "llvm/Object/Binary.h"17#include "llvm/Object/MachOUniversal.h"18#include "llvm/Support/Endian.h"19#include "llvm/TargetParser/Triple.h"20#include "llvm/TextAPI/InterfaceFile.h"21#include "llvm/TextAPI/RecordsSlice.h"22#include "llvm/TextAPI/TextAPIError.h"23#include <iomanip>24#include <set>25#include <sstream>26#include <string>27#include <tuple>2829using namespace llvm;30using namespace llvm::object;31using namespace llvm::MachO;32using namespace llvm::MachO::DylibReader;3334using TripleVec = std::vector<Triple>;35static typename TripleVec::iterator emplace(TripleVec &Container, Triple &&T) {36auto I = partition_point(Container, [=](const Triple &CT) {37return std::forward_as_tuple(CT.getArch(), CT.getOS(),38CT.getEnvironment()) <39std::forward_as_tuple(T.getArch(), T.getOS(), T.getEnvironment());40});4142if (I != Container.end() && *I == T)43return I;44return Container.emplace(I, T);45}4647static TripleVec constructTriples(MachOObjectFile *Obj,48const Architecture ArchT) {49auto getOSVersionStr = [](uint32_t V) {50PackedVersion OSVersion(V);51std::string Vers;52raw_string_ostream VStream(Vers);53VStream << OSVersion;54return VStream.str();55};56auto getOSVersion = [&](const MachOObjectFile::LoadCommandInfo &cmd) {57auto Vers = Obj->getVersionMinLoadCommand(cmd);58return getOSVersionStr(Vers.version);59};6061TripleVec Triples;62bool IsIntel = ArchitectureSet(ArchT).hasX86();63auto Arch = getArchitectureName(ArchT);6465for (const auto &cmd : Obj->load_commands()) {66std::string OSVersion;67switch (cmd.C.cmd) {68case MachO::LC_VERSION_MIN_MACOSX:69OSVersion = getOSVersion(cmd);70emplace(Triples, {Arch, "apple", "macos" + OSVersion});71break;72case MachO::LC_VERSION_MIN_IPHONEOS:73OSVersion = getOSVersion(cmd);74if (IsIntel)75emplace(Triples, {Arch, "apple", "ios" + OSVersion, "simulator"});76else77emplace(Triples, {Arch, "apple", "ios" + OSVersion});78break;79case MachO::LC_VERSION_MIN_TVOS:80OSVersion = getOSVersion(cmd);81if (IsIntel)82emplace(Triples, {Arch, "apple", "tvos" + OSVersion, "simulator"});83else84emplace(Triples, {Arch, "apple", "tvos" + OSVersion});85break;86case MachO::LC_VERSION_MIN_WATCHOS:87OSVersion = getOSVersion(cmd);88if (IsIntel)89emplace(Triples, {Arch, "apple", "watchos" + OSVersion, "simulator"});90else91emplace(Triples, {Arch, "apple", "watchos" + OSVersion});92break;93case MachO::LC_BUILD_VERSION: {94OSVersion = getOSVersionStr(Obj->getBuildVersionLoadCommand(cmd).minos);95switch (Obj->getBuildVersionLoadCommand(cmd).platform) {96case MachO::PLATFORM_MACOS:97emplace(Triples, {Arch, "apple", "macos" + OSVersion});98break;99case MachO::PLATFORM_IOS:100emplace(Triples, {Arch, "apple", "ios" + OSVersion});101break;102case MachO::PLATFORM_TVOS:103emplace(Triples, {Arch, "apple", "tvos" + OSVersion});104break;105case MachO::PLATFORM_WATCHOS:106emplace(Triples, {Arch, "apple", "watchos" + OSVersion});107break;108case MachO::PLATFORM_BRIDGEOS:109emplace(Triples, {Arch, "apple", "bridgeos" + OSVersion});110break;111case MachO::PLATFORM_MACCATALYST:112emplace(Triples, {Arch, "apple", "ios" + OSVersion, "macabi"});113break;114case MachO::PLATFORM_IOSSIMULATOR:115emplace(Triples, {Arch, "apple", "ios" + OSVersion, "simulator"});116break;117case MachO::PLATFORM_TVOSSIMULATOR:118emplace(Triples, {Arch, "apple", "tvos" + OSVersion, "simulator"});119break;120case MachO::PLATFORM_WATCHOSSIMULATOR:121emplace(Triples, {Arch, "apple", "watchos" + OSVersion, "simulator"});122break;123case MachO::PLATFORM_DRIVERKIT:124emplace(Triples, {Arch, "apple", "driverkit" + OSVersion});125break;126default:127break; // Skip any others.128}129break;130}131default:132break;133}134}135136// Record unknown platform for older binaries that don't enforce platform137// load commands.138if (Triples.empty())139emplace(Triples, {Arch, "apple", "unknown"});140141return Triples;142}143144static Error readMachOHeader(MachOObjectFile *Obj, RecordsSlice &Slice) {145auto H = Obj->getHeader();146auto &BA = Slice.getBinaryAttrs();147148switch (H.filetype) {149default:150llvm_unreachable("unsupported binary type");151case MachO::MH_DYLIB:152BA.File = FileType::MachO_DynamicLibrary;153break;154case MachO::MH_DYLIB_STUB:155BA.File = FileType::MachO_DynamicLibrary_Stub;156break;157case MachO::MH_BUNDLE:158BA.File = FileType::MachO_Bundle;159break;160}161162if (H.flags & MachO::MH_TWOLEVEL)163BA.TwoLevelNamespace = true;164if (H.flags & MachO::MH_APP_EXTENSION_SAFE)165BA.AppExtensionSafe = true;166167for (const auto &LCI : Obj->load_commands()) {168switch (LCI.C.cmd) {169case MachO::LC_ID_DYLIB: {170auto DLLC = Obj->getDylibIDLoadCommand(LCI);171BA.InstallName = Slice.copyString(LCI.Ptr + DLLC.dylib.name);172BA.CurrentVersion = DLLC.dylib.current_version;173BA.CompatVersion = DLLC.dylib.compatibility_version;174break;175}176case MachO::LC_REEXPORT_DYLIB: {177auto DLLC = Obj->getDylibIDLoadCommand(LCI);178BA.RexportedLibraries.emplace_back(179Slice.copyString(LCI.Ptr + DLLC.dylib.name));180break;181}182case MachO::LC_SUB_FRAMEWORK: {183auto SFC = Obj->getSubFrameworkCommand(LCI);184BA.ParentUmbrella = Slice.copyString(LCI.Ptr + SFC.umbrella);185break;186}187case MachO::LC_SUB_CLIENT: {188auto SCLC = Obj->getSubClientCommand(LCI);189BA.AllowableClients.emplace_back(Slice.copyString(LCI.Ptr + SCLC.client));190break;191}192case MachO::LC_UUID: {193auto UUIDLC = Obj->getUuidCommand(LCI);194std::stringstream Stream;195for (unsigned I = 0; I < 16; ++I) {196if (I == 4 || I == 6 || I == 8 || I == 10)197Stream << '-';198Stream << std::setfill('0') << std::setw(2) << std::uppercase199<< std::hex << static_cast<int>(UUIDLC.uuid[I]);200}201BA.UUID = Slice.copyString(Stream.str());202break;203}204case MachO::LC_RPATH: {205auto RPLC = Obj->getRpathCommand(LCI);206BA.RPaths.emplace_back(Slice.copyString(LCI.Ptr + RPLC.path));207break;208}209case MachO::LC_SEGMENT_SPLIT_INFO: {210auto SSILC = Obj->getLinkeditDataLoadCommand(LCI);211if (SSILC.datasize == 0)212BA.OSLibNotForSharedCache = true;213break;214}215default:216break;217}218}219220for (auto &Sect : Obj->sections()) {221auto SectName = Sect.getName();222if (!SectName)223return SectName.takeError();224if (*SectName != "__objc_imageinfo" && *SectName != "__image_info")225continue;226227auto Content = Sect.getContents();228if (!Content)229return Content.takeError();230231if ((Content->size() >= 8) && (Content->front() == 0)) {232uint32_t Flags;233if (Obj->isLittleEndian()) {234auto *p =235reinterpret_cast<const support::ulittle32_t *>(Content->data() + 4);236Flags = *p;237} else {238auto *p =239reinterpret_cast<const support::ubig32_t *>(Content->data() + 4);240Flags = *p;241}242BA.SwiftABI = (Flags >> 8) & 0xFF;243}244}245return Error::success();246}247248static Error readSymbols(MachOObjectFile *Obj, RecordsSlice &Slice,249const ParseOption &Opt) {250251auto parseExport = [](const auto ExportFlags,252auto Addr) -> std::tuple<SymbolFlags, RecordLinkage> {253SymbolFlags Flags = SymbolFlags::None;254switch (ExportFlags & MachO::EXPORT_SYMBOL_FLAGS_KIND_MASK) {255case MachO::EXPORT_SYMBOL_FLAGS_KIND_REGULAR:256if (ExportFlags & MachO::EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION)257Flags |= SymbolFlags::WeakDefined;258break;259case MachO::EXPORT_SYMBOL_FLAGS_KIND_THREAD_LOCAL:260Flags |= SymbolFlags::ThreadLocalValue;261break;262}263264RecordLinkage Linkage = (ExportFlags & MachO::EXPORT_SYMBOL_FLAGS_REEXPORT)265? RecordLinkage::Rexported266: RecordLinkage::Exported;267return {Flags, Linkage};268};269270Error Err = Error::success();271272StringMap<std::pair<SymbolFlags, RecordLinkage>> Exports;273// Collect symbols from export trie first. Sometimes, there are more exports274// in the trie than in n-list due to stripping. This is common for swift275// mangled symbols.276for (auto &Sym : Obj->exports(Err)) {277auto [Flags, Linkage] = parseExport(Sym.flags(), Sym.address());278Slice.addRecord(Sym.name(), Flags, GlobalRecord::Kind::Unknown, Linkage);279Exports[Sym.name()] = {Flags, Linkage};280}281282for (const auto &Sym : Obj->symbols()) {283auto FlagsOrErr = Sym.getFlags();284if (!FlagsOrErr)285return FlagsOrErr.takeError();286auto Flags = *FlagsOrErr;287288auto NameOrErr = Sym.getName();289if (!NameOrErr)290return NameOrErr.takeError();291auto Name = *NameOrErr;292293RecordLinkage Linkage = RecordLinkage::Unknown;294SymbolFlags RecordFlags = SymbolFlags::None;295296if (Flags & SymbolRef::SF_Undefined) {297if (Opt.Undefineds)298Linkage = RecordLinkage::Undefined;299else300continue;301if (Flags & SymbolRef::SF_Weak)302RecordFlags |= SymbolFlags::WeakReferenced;303} else if (Flags & SymbolRef::SF_Exported) {304auto Exp = Exports.find(Name);305// This should never be possible when binaries are produced with Apple306// linkers. However it is possible to craft dylibs where the export trie307// is either malformed or has conflicting symbols compared to n_list.308if (Exp != Exports.end())309std::tie(RecordFlags, Linkage) = Exp->second;310else311Linkage = RecordLinkage::Exported;312} else if (Flags & SymbolRef::SF_Hidden) {313Linkage = RecordLinkage::Internal;314} else315continue;316317auto TypeOrErr = Sym.getType();318if (!TypeOrErr)319return TypeOrErr.takeError();320auto Type = *TypeOrErr;321322GlobalRecord::Kind GV = (Type & SymbolRef::ST_Function)323? GlobalRecord::Kind::Function324: GlobalRecord::Kind::Variable;325326if (GV == GlobalRecord::Kind::Function)327RecordFlags |= SymbolFlags::Text;328else329RecordFlags |= SymbolFlags::Data;330331Slice.addRecord(Name, RecordFlags, GV, Linkage);332}333return Err;334}335336static Error load(MachOObjectFile *Obj, RecordsSlice &Slice,337const ParseOption &Opt, const Architecture Arch) {338if (Arch == AK_unknown)339return make_error<TextAPIError>(TextAPIErrorCode::UnsupportedTarget);340341if (Opt.MachOHeader)342if (auto Err = readMachOHeader(Obj, Slice))343return Err;344345if (Opt.SymbolTable)346if (auto Err = readSymbols(Obj, Slice, Opt))347return Err;348349return Error::success();350}351352Expected<Records> DylibReader::readFile(MemoryBufferRef Buffer,353const ParseOption &Opt) {354Records Results;355356auto BinOrErr = createBinary(Buffer);357if (!BinOrErr)358return BinOrErr.takeError();359360Binary &Bin = *BinOrErr.get();361if (auto *Obj = dyn_cast<MachOObjectFile>(&Bin)) {362const auto Arch = getArchitectureFromCpuType(Obj->getHeader().cputype,363Obj->getHeader().cpusubtype);364if (!Opt.Archs.has(Arch))365return make_error<TextAPIError>(TextAPIErrorCode::NoSuchArchitecture);366367auto Triples = constructTriples(Obj, Arch);368for (const auto &T : Triples) {369if (mapToPlatformType(T) == PLATFORM_UNKNOWN)370return make_error<TextAPIError>(TextAPIErrorCode::UnsupportedTarget);371Results.emplace_back(std::make_shared<RecordsSlice>(RecordsSlice({T})));372if (auto Err = load(Obj, *Results.back(), Opt, Arch))373return std::move(Err);374Results.back()->getBinaryAttrs().Path = Buffer.getBufferIdentifier();375}376return Results;377}378379// Only expect MachO universal binaries at this point.380assert(isa<MachOUniversalBinary>(&Bin) &&381"Expected a MachO universal binary.");382auto *UB = cast<MachOUniversalBinary>(&Bin);383384for (auto OI = UB->begin_objects(), OE = UB->end_objects(); OI != OE; ++OI) {385// Skip architecture if not requested.386auto Arch =387getArchitectureFromCpuType(OI->getCPUType(), OI->getCPUSubType());388if (!Opt.Archs.has(Arch))389continue;390391// Skip unknown architectures.392if (Arch == AK_unknown)393continue;394395// This can fail if the object is an archive.396auto ObjOrErr = OI->getAsObjectFile();397398// Skip the archive and consume the error.399if (!ObjOrErr) {400consumeError(ObjOrErr.takeError());401continue;402}403404auto &Obj = *ObjOrErr.get();405switch (Obj.getHeader().filetype) {406default:407break;408case MachO::MH_BUNDLE:409case MachO::MH_DYLIB:410case MachO::MH_DYLIB_STUB:411for (const auto &T : constructTriples(&Obj, Arch)) {412Results.emplace_back(std::make_shared<RecordsSlice>(RecordsSlice({T})));413if (auto Err = load(&Obj, *Results.back(), Opt, Arch))414return std::move(Err);415Results.back()->getBinaryAttrs().Path = Buffer.getBufferIdentifier();416}417break;418}419}420421if (Results.empty())422return make_error<TextAPIError>(TextAPIErrorCode::EmptyResults);423return Results;424}425426Expected<std::unique_ptr<InterfaceFile>>427DylibReader::get(MemoryBufferRef Buffer) {428ParseOption Options;429auto SlicesOrErr = readFile(Buffer, Options);430if (!SlicesOrErr)431return SlicesOrErr.takeError();432433return convertToInterfaceFile(*SlicesOrErr);434}435436static void DWARFErrorHandler(Error Err) { /**/ }437438static SymbolToSourceLocMap439accumulateLocs(MachOObjectFile &Obj,440const std::unique_ptr<DWARFContext> &DiCtx) {441SymbolToSourceLocMap LocMap;442for (const auto &Symbol : Obj.symbols()) {443Expected<uint32_t> FlagsOrErr = Symbol.getFlags();444if (!FlagsOrErr) {445consumeError(FlagsOrErr.takeError());446continue;447}448449if (!(*FlagsOrErr & SymbolRef::SF_Exported))450continue;451452Expected<uint64_t> AddressOrErr = Symbol.getAddress();453if (!AddressOrErr) {454consumeError(AddressOrErr.takeError());455continue;456}457const uint64_t Address = *AddressOrErr;458459auto TypeOrErr = Symbol.getType();460if (!TypeOrErr) {461consumeError(TypeOrErr.takeError());462continue;463}464const bool IsCode = (*TypeOrErr & SymbolRef::ST_Function);465466auto *DWARFCU = IsCode ? DiCtx->getCompileUnitForCodeAddress(Address)467: DiCtx->getCompileUnitForDataAddress(Address);468if (!DWARFCU)469continue;470471const DWARFDie &DIE = IsCode ? DWARFCU->getSubroutineForAddress(Address)472: DWARFCU->getVariableForAddress(Address);473const std::string File = DIE.getDeclFile(474llvm::DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath);475const uint64_t Line = DIE.getDeclLine();476477auto NameOrErr = Symbol.getName();478if (!NameOrErr) {479consumeError(NameOrErr.takeError());480continue;481}482auto Name = *NameOrErr;483auto Sym = parseSymbol(Name);484485if (!File.empty() && Line != 0)486LocMap.insert({Sym.Name, RecordLoc(File, Line)});487}488489return LocMap;490}491492SymbolToSourceLocMap493DylibReader::accumulateSourceLocFromDSYM(const StringRef DSYM,494const Target &T) {495// Find sidecar file.496auto DSYMsOrErr = MachOObjectFile::findDsymObjectMembers(DSYM);497if (!DSYMsOrErr) {498consumeError(DSYMsOrErr.takeError());499return SymbolToSourceLocMap();500}501if (DSYMsOrErr->empty())502return SymbolToSourceLocMap();503504const StringRef Path = DSYMsOrErr->front();505auto BufOrErr = MemoryBuffer::getFile(Path);506if (auto Err = BufOrErr.getError())507return SymbolToSourceLocMap();508509auto BinOrErr = createBinary(*BufOrErr.get());510if (!BinOrErr) {511consumeError(BinOrErr.takeError());512return SymbolToSourceLocMap();513}514// Handle single arch.515if (auto *Single = dyn_cast<MachOObjectFile>(BinOrErr->get())) {516auto DiCtx = DWARFContext::create(517*Single, DWARFContext::ProcessDebugRelocations::Process, nullptr, "",518DWARFErrorHandler, DWARFErrorHandler);519520return accumulateLocs(*Single, DiCtx);521}522// Handle universal companion file.523if (auto *Fat = dyn_cast<MachOUniversalBinary>(BinOrErr->get())) {524auto ObjForArch = Fat->getObjectForArch(getArchitectureName(T.Arch));525if (!ObjForArch) {526consumeError(ObjForArch.takeError());527return SymbolToSourceLocMap();528}529auto MachOOrErr = ObjForArch->getAsObjectFile();530if (!MachOOrErr) {531consumeError(MachOOrErr.takeError());532return SymbolToSourceLocMap();533}534auto &Obj = **MachOOrErr;535auto DiCtx = DWARFContext::create(536Obj, DWARFContext::ProcessDebugRelocations::Process, nullptr, "",537DWARFErrorHandler, DWARFErrorHandler);538539return accumulateLocs(Obj, DiCtx);540}541return SymbolToSourceLocMap();542}543544545