brintos

brintos / llvm-project-archived public Read only

0
0
Text · 17.3 KiB · f55bc9c Raw
543 lines · cpp
1//===- DylibReader.cpp -------------- TAPI MachO Dylib Reader --*- C++ -*-===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8///9/// Implements the TAPI Reader for Mach-O dynamic libraries.10///11//===----------------------------------------------------------------------===//12 13#include "llvm/TextAPI/DylibReader.h"14#include "llvm/ADT/STLExtras.h"15#include "llvm/DebugInfo/DWARF/DWARFCompileUnit.h"16#include "llvm/DebugInfo/DWARF/DWARFContext.h"17#include "llvm/Object/Binary.h"18#include "llvm/Object/MachOUniversal.h"19#include "llvm/Support/Endian.h"20#include "llvm/TargetParser/Triple.h"21#include "llvm/TextAPI/InterfaceFile.h"22#include "llvm/TextAPI/RecordsSlice.h"23#include "llvm/TextAPI/TextAPIError.h"24#include <iomanip>25#include <sstream>26#include <string>27#include <tuple>28 29using namespace llvm;30using namespace llvm::object;31using namespace llvm::MachO;32using namespace llvm::MachO::DylibReader;33 34using TripleVec = std::vector<Triple>;35static TripleVec::iterator emplace(TripleVec &Container, Triple &&T) {36  auto I = partition_point(Container, [=](const Triple &CT) {37    return std::forward_as_tuple(CT.getArch(), CT.getOS(),38                                 CT.getEnvironment()) <39           std::forward_as_tuple(T.getArch(), T.getOS(), T.getEnvironment());40  });41 42  if (I != Container.end() && *I == T)43    return I;44  return Container.emplace(I, T);45}46 47static TripleVec constructTriples(MachOObjectFile *Obj,48                                  const Architecture ArchT) {49  auto getOSVersionStr = [](uint32_t V) {50    PackedVersion OSVersion(V);51    std::string Vers;52    raw_string_ostream VStream(Vers);53    VStream << OSVersion;54    return VStream.str();55  };56  auto getOSVersion = [&](const MachOObjectFile::LoadCommandInfo &cmd) {57    auto Vers = Obj->getVersionMinLoadCommand(cmd);58    return getOSVersionStr(Vers.version);59  };60 61  TripleVec Triples;62  bool IsIntel = ArchitectureSet(ArchT).hasX86();63  auto Arch = getArchitectureName(ArchT);64 65  for (const auto &cmd : Obj->load_commands()) {66    std::string OSVersion;67    switch (cmd.C.cmd) {68    case MachO::LC_VERSION_MIN_MACOSX:69      OSVersion = getOSVersion(cmd);70      emplace(Triples, {Arch, "apple", "macos" + OSVersion});71      break;72    case MachO::LC_VERSION_MIN_IPHONEOS:73      OSVersion = getOSVersion(cmd);74      if (IsIntel)75        emplace(Triples, {Arch, "apple", "ios" + OSVersion, "simulator"});76      else77        emplace(Triples, {Arch, "apple", "ios" + OSVersion});78      break;79    case MachO::LC_VERSION_MIN_TVOS:80      OSVersion = getOSVersion(cmd);81      if (IsIntel)82        emplace(Triples, {Arch, "apple", "tvos" + OSVersion, "simulator"});83      else84        emplace(Triples, {Arch, "apple", "tvos" + OSVersion});85      break;86    case MachO::LC_VERSION_MIN_WATCHOS:87      OSVersion = getOSVersion(cmd);88      if (IsIntel)89        emplace(Triples, {Arch, "apple", "watchos" + OSVersion, "simulator"});90      else91        emplace(Triples, {Arch, "apple", "watchos" + OSVersion});92      break;93    case MachO::LC_BUILD_VERSION: {94      OSVersion = getOSVersionStr(Obj->getBuildVersionLoadCommand(cmd).minos);95      switch (Obj->getBuildVersionLoadCommand(cmd).platform) {96      case MachO::PLATFORM_MACOS:97        emplace(Triples, {Arch, "apple", "macos" + OSVersion});98        break;99      case MachO::PLATFORM_IOS:100        emplace(Triples, {Arch, "apple", "ios" + OSVersion});101        break;102      case MachO::PLATFORM_TVOS:103        emplace(Triples, {Arch, "apple", "tvos" + OSVersion});104        break;105      case MachO::PLATFORM_WATCHOS:106        emplace(Triples, {Arch, "apple", "watchos" + OSVersion});107        break;108      case MachO::PLATFORM_BRIDGEOS:109        emplace(Triples, {Arch, "apple", "bridgeos" + OSVersion});110        break;111      case MachO::PLATFORM_MACCATALYST:112        emplace(Triples, {Arch, "apple", "ios" + OSVersion, "macabi"});113        break;114      case MachO::PLATFORM_IOSSIMULATOR:115        emplace(Triples, {Arch, "apple", "ios" + OSVersion, "simulator"});116        break;117      case MachO::PLATFORM_TVOSSIMULATOR:118        emplace(Triples, {Arch, "apple", "tvos" + OSVersion, "simulator"});119        break;120      case MachO::PLATFORM_WATCHOSSIMULATOR:121        emplace(Triples, {Arch, "apple", "watchos" + OSVersion, "simulator"});122        break;123      case MachO::PLATFORM_DRIVERKIT:124        emplace(Triples, {Arch, "apple", "driverkit" + OSVersion});125        break;126      default:127        break; // Skip any others.128      }129      break;130    }131    default:132      break;133    }134  }135 136  // Record unknown platform for older binaries that don't enforce platform137  // load commands.138  if (Triples.empty())139    emplace(Triples, {Arch, "apple", "unknown"});140 141  return Triples;142}143 144static Error readMachOHeader(MachOObjectFile *Obj, RecordsSlice &Slice) {145  auto H = Obj->getHeader();146  auto &BA = Slice.getBinaryAttrs();147 148  switch (H.filetype) {149  default:150    llvm_unreachable("unsupported binary type");151  case MachO::MH_DYLIB:152    BA.File = FileType::MachO_DynamicLibrary;153    break;154  case MachO::MH_DYLIB_STUB:155    BA.File = FileType::MachO_DynamicLibrary_Stub;156    break;157  case MachO::MH_BUNDLE:158    BA.File = FileType::MachO_Bundle;159    break;160  }161 162  if (H.flags & MachO::MH_TWOLEVEL)163    BA.TwoLevelNamespace = true;164  if (H.flags & MachO::MH_APP_EXTENSION_SAFE)165    BA.AppExtensionSafe = true;166 167  for (const auto &LCI : Obj->load_commands()) {168    switch (LCI.C.cmd) {169    case MachO::LC_ID_DYLIB: {170      auto DLLC = Obj->getDylibIDLoadCommand(LCI);171      BA.InstallName = Slice.copyString(LCI.Ptr + DLLC.dylib.name);172      BA.CurrentVersion = DLLC.dylib.current_version;173      BA.CompatVersion = DLLC.dylib.compatibility_version;174      break;175    }176    case MachO::LC_REEXPORT_DYLIB: {177      auto DLLC = Obj->getDylibIDLoadCommand(LCI);178      BA.RexportedLibraries.emplace_back(179          Slice.copyString(LCI.Ptr + DLLC.dylib.name));180      break;181    }182    case MachO::LC_SUB_FRAMEWORK: {183      auto SFC = Obj->getSubFrameworkCommand(LCI);184      BA.ParentUmbrella = Slice.copyString(LCI.Ptr + SFC.umbrella);185      break;186    }187    case MachO::LC_SUB_CLIENT: {188      auto SCLC = Obj->getSubClientCommand(LCI);189      BA.AllowableClients.emplace_back(Slice.copyString(LCI.Ptr + SCLC.client));190      break;191    }192    case MachO::LC_UUID: {193      auto UUIDLC = Obj->getUuidCommand(LCI);194      std::stringstream Stream;195      for (unsigned I = 0; I < 16; ++I) {196        if (I == 4 || I == 6 || I == 8 || I == 10)197          Stream << '-';198        Stream << std::setfill('0') << std::setw(2) << std::uppercase199               << std::hex << static_cast<int>(UUIDLC.uuid[I]);200      }201      BA.UUID = Slice.copyString(Stream.str());202      break;203    }204    case MachO::LC_RPATH: {205      auto RPLC = Obj->getRpathCommand(LCI);206      BA.RPaths.emplace_back(Slice.copyString(LCI.Ptr + RPLC.path));207      break;208    }209    case MachO::LC_SEGMENT_SPLIT_INFO: {210      auto SSILC = Obj->getLinkeditDataLoadCommand(LCI);211      if (SSILC.datasize == 0)212        BA.OSLibNotForSharedCache = true;213      break;214    }215    default:216      break;217    }218  }219 220  for (auto &Sect : Obj->sections()) {221    auto SectName = Sect.getName();222    if (!SectName)223      return SectName.takeError();224    if (*SectName != "__objc_imageinfo" && *SectName != "__image_info")225      continue;226 227    auto Content = Sect.getContents();228    if (!Content)229      return Content.takeError();230 231    if ((Content->size() >= 8) && (Content->front() == 0)) {232      uint32_t Flags;233      if (Obj->isLittleEndian()) {234        auto *p =235            reinterpret_cast<const support::ulittle32_t *>(Content->data() + 4);236        Flags = *p;237      } else {238        auto *p =239            reinterpret_cast<const support::ubig32_t *>(Content->data() + 4);240        Flags = *p;241      }242      BA.SwiftABI = (Flags >> 8) & 0xFF;243    }244  }245  return Error::success();246}247 248static Error readSymbols(MachOObjectFile *Obj, RecordsSlice &Slice,249                         const ParseOption &Opt) {250 251  auto parseExport = [](const auto ExportFlags,252                        auto Addr) -> std::tuple<SymbolFlags, RecordLinkage> {253    SymbolFlags Flags = SymbolFlags::None;254    switch (ExportFlags & MachO::EXPORT_SYMBOL_FLAGS_KIND_MASK) {255    case MachO::EXPORT_SYMBOL_FLAGS_KIND_REGULAR:256      if (ExportFlags & MachO::EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION)257        Flags |= SymbolFlags::WeakDefined;258      break;259    case MachO::EXPORT_SYMBOL_FLAGS_KIND_THREAD_LOCAL:260      Flags |= SymbolFlags::ThreadLocalValue;261      break;262    }263 264    RecordLinkage Linkage = (ExportFlags & MachO::EXPORT_SYMBOL_FLAGS_REEXPORT)265                                ? RecordLinkage::Rexported266                                : RecordLinkage::Exported;267    return {Flags, Linkage};268  };269 270  Error Err = Error::success();271 272  StringMap<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.276  for (auto &Sym : Obj->exports(Err)) {277    auto [Flags, Linkage] = parseExport(Sym.flags(), Sym.address());278    Slice.addRecord(Sym.name(), Flags, GlobalRecord::Kind::Unknown, Linkage);279    Exports[Sym.name()] = {Flags, Linkage};280  }281 282  for (const auto &Sym : Obj->symbols()) {283    auto FlagsOrErr = Sym.getFlags();284    if (!FlagsOrErr)285      return FlagsOrErr.takeError();286    auto Flags = *FlagsOrErr;287 288    auto NameOrErr = Sym.getName();289    if (!NameOrErr)290      return NameOrErr.takeError();291    auto Name = *NameOrErr;292 293    RecordLinkage Linkage = RecordLinkage::Unknown;294    SymbolFlags RecordFlags = SymbolFlags::None;295 296    if (Flags & SymbolRef::SF_Undefined) {297      if (Opt.Undefineds)298        Linkage = RecordLinkage::Undefined;299      else300        continue;301      if (Flags & SymbolRef::SF_Weak)302        RecordFlags |= SymbolFlags::WeakReferenced;303    } else if (Flags & SymbolRef::SF_Exported) {304      auto 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.308      if (Exp != Exports.end())309        std::tie(RecordFlags, Linkage) = Exp->second;310      else311        Linkage = RecordLinkage::Exported;312    } else if (Flags & SymbolRef::SF_Hidden) {313      Linkage = RecordLinkage::Internal;314    } else315      continue;316 317    auto TypeOrErr = Sym.getType();318    if (!TypeOrErr)319      return TypeOrErr.takeError();320    auto Type = *TypeOrErr;321 322    GlobalRecord::Kind GV = (Type & SymbolRef::ST_Function)323                                ? GlobalRecord::Kind::Function324                                : GlobalRecord::Kind::Variable;325 326    if (GV == GlobalRecord::Kind::Function)327      RecordFlags |= SymbolFlags::Text;328    else329      RecordFlags |= SymbolFlags::Data;330 331    Slice.addRecord(Name, RecordFlags, GV, Linkage);332  }333  return Err;334}335 336static Error load(MachOObjectFile *Obj, RecordsSlice &Slice,337                  const ParseOption &Opt, const Architecture Arch) {338  if (Arch == AK_unknown)339    return make_error<TextAPIError>(TextAPIErrorCode::UnsupportedTarget);340 341  if (Opt.MachOHeader)342    if (auto Err = readMachOHeader(Obj, Slice))343      return Err;344 345  if (Opt.SymbolTable)346    if (auto Err = readSymbols(Obj, Slice, Opt))347      return Err;348 349  return Error::success();350}351 352Expected<Records> DylibReader::readFile(MemoryBufferRef Buffer,353                                        const ParseOption &Opt) {354  Records Results;355 356  auto BinOrErr = createBinary(Buffer);357  if (!BinOrErr)358    return BinOrErr.takeError();359 360  Binary &Bin = *BinOrErr.get();361  if (auto *Obj = dyn_cast<MachOObjectFile>(&Bin)) {362    const auto Arch = getArchitectureFromCpuType(Obj->getHeader().cputype,363                                                 Obj->getHeader().cpusubtype);364    if (!Opt.Archs.has(Arch))365      return make_error<TextAPIError>(TextAPIErrorCode::NoSuchArchitecture);366 367    auto Triples = constructTriples(Obj, Arch);368    for (const auto &T : Triples) {369      if (mapToPlatformType(T) == PLATFORM_UNKNOWN)370        return make_error<TextAPIError>(TextAPIErrorCode::UnsupportedTarget);371      Results.emplace_back(std::make_shared<RecordsSlice>(RecordsSlice({T})));372      if (auto Err = load(Obj, *Results.back(), Opt, Arch))373        return std::move(Err);374      Results.back()->getBinaryAttrs().Path = Buffer.getBufferIdentifier();375    }376    return Results;377  }378 379  // Only expect MachO universal binaries at this point.380  assert(isa<MachOUniversalBinary>(&Bin) &&381         "Expected a MachO universal binary.");382  auto *UB = cast<MachOUniversalBinary>(&Bin);383 384  for (auto OI = UB->begin_objects(), OE = UB->end_objects(); OI != OE; ++OI) {385    // Skip architecture if not requested.386    auto Arch =387        getArchitectureFromCpuType(OI->getCPUType(), OI->getCPUSubType());388    if (!Opt.Archs.has(Arch))389      continue;390 391    // Skip unknown architectures.392    if (Arch == AK_unknown)393      continue;394 395    // This can fail if the object is an archive.396    auto ObjOrErr = OI->getAsObjectFile();397 398    // Skip the archive and consume the error.399    if (!ObjOrErr) {400      consumeError(ObjOrErr.takeError());401      continue;402    }403 404    auto &Obj = *ObjOrErr.get();405    switch (Obj.getHeader().filetype) {406    default:407      break;408    case MachO::MH_BUNDLE:409    case MachO::MH_DYLIB:410    case MachO::MH_DYLIB_STUB:411      for (const auto &T : constructTriples(&Obj, Arch)) {412        Results.emplace_back(std::make_shared<RecordsSlice>(RecordsSlice({T})));413        if (auto Err = load(&Obj, *Results.back(), Opt, Arch))414          return std::move(Err);415        Results.back()->getBinaryAttrs().Path = Buffer.getBufferIdentifier();416      }417      break;418    }419  }420 421  if (Results.empty())422    return make_error<TextAPIError>(TextAPIErrorCode::EmptyResults);423  return Results;424}425 426Expected<std::unique_ptr<InterfaceFile>>427DylibReader::get(MemoryBufferRef Buffer) {428  ParseOption Options;429  auto SlicesOrErr = readFile(Buffer, Options);430  if (!SlicesOrErr)431    return SlicesOrErr.takeError();432 433  return convertToInterfaceFile(*SlicesOrErr);434}435 436static void DWARFErrorHandler(Error Err) { /**/ }437 438static SymbolToSourceLocMap439accumulateLocs(MachOObjectFile &Obj,440               const std::unique_ptr<DWARFContext> &DiCtx) {441  SymbolToSourceLocMap LocMap;442  for (const auto &Symbol : Obj.symbols()) {443    Expected<uint32_t> FlagsOrErr = Symbol.getFlags();444    if (!FlagsOrErr) {445      consumeError(FlagsOrErr.takeError());446      continue;447    }448 449    if (!(*FlagsOrErr & SymbolRef::SF_Exported))450      continue;451 452    Expected<uint64_t> AddressOrErr = Symbol.getAddress();453    if (!AddressOrErr) {454      consumeError(AddressOrErr.takeError());455      continue;456    }457    const uint64_t Address = *AddressOrErr;458 459    auto TypeOrErr = Symbol.getType();460    if (!TypeOrErr) {461      consumeError(TypeOrErr.takeError());462      continue;463    }464    const bool IsCode = (*TypeOrErr & SymbolRef::ST_Function);465 466    auto *DWARFCU = IsCode ? DiCtx->getCompileUnitForCodeAddress(Address)467                           : DiCtx->getCompileUnitForDataAddress(Address);468    if (!DWARFCU)469      continue;470 471    const DWARFDie &DIE = IsCode ? DWARFCU->getSubroutineForAddress(Address)472                                 : DWARFCU->getVariableForAddress(Address);473    const std::string File = DIE.getDeclFile(474        llvm::DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath);475    const uint64_t Line = DIE.getDeclLine();476 477    auto NameOrErr = Symbol.getName();478    if (!NameOrErr) {479      consumeError(NameOrErr.takeError());480      continue;481    }482    auto Name = *NameOrErr;483    auto Sym = parseSymbol(Name);484 485    if (!File.empty() && Line != 0)486      LocMap.insert({Sym.Name, RecordLoc(File, Line)});487  }488 489  return LocMap;490}491 492SymbolToSourceLocMap493DylibReader::accumulateSourceLocFromDSYM(const StringRef DSYM,494                                         const Target &T) {495  // Find sidecar file.496  auto DSYMsOrErr = MachOObjectFile::findDsymObjectMembers(DSYM);497  if (!DSYMsOrErr) {498    consumeError(DSYMsOrErr.takeError());499    return SymbolToSourceLocMap();500  }501  if (DSYMsOrErr->empty())502    return SymbolToSourceLocMap();503 504  const StringRef Path = DSYMsOrErr->front();505  auto BufOrErr = MemoryBuffer::getFile(Path);506  if (auto Err = BufOrErr.getError())507    return SymbolToSourceLocMap();508 509  auto BinOrErr = createBinary(*BufOrErr.get());510  if (!BinOrErr) {511    consumeError(BinOrErr.takeError());512    return SymbolToSourceLocMap();513  }514  // Handle single arch.515  if (auto *Single = dyn_cast<MachOObjectFile>(BinOrErr->get())) {516    auto DiCtx = DWARFContext::create(517        *Single, DWARFContext::ProcessDebugRelocations::Process, nullptr, "",518        DWARFErrorHandler, DWARFErrorHandler);519 520    return accumulateLocs(*Single, DiCtx);521  }522  // Handle universal companion file.523  if (auto *Fat = dyn_cast<MachOUniversalBinary>(BinOrErr->get())) {524    auto ObjForArch = Fat->getObjectForArch(getArchitectureName(T.Arch));525    if (!ObjForArch) {526      consumeError(ObjForArch.takeError());527      return SymbolToSourceLocMap();528    }529    auto MachOOrErr = ObjForArch->getAsObjectFile();530    if (!MachOOrErr) {531      consumeError(MachOOrErr.takeError());532      return SymbolToSourceLocMap();533    }534    auto &Obj = **MachOOrErr;535    auto DiCtx = DWARFContext::create(536        Obj, DWARFContext::ProcessDebugRelocations::Process, nullptr, "",537        DWARFErrorHandler, DWARFErrorHandler);538 539    return accumulateLocs(Obj, DiCtx);540  }541  return SymbolToSourceLocMap();542}543