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-exception
6//
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/ADT/StringMap.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/RecordsSlice.h"
21#include "llvm/TextAPI/TextAPIError.h"
22#include <iomanip>
23#include <set>
24#include <sstream>
25#include <string>
26#include <tuple>
27
28using namespace llvm;
29using namespace llvm::object;
30using namespace llvm::MachO;
31using namespace llvm::MachO::DylibReader;
32
33using TripleVec = std::vector<Triple>;
34static typename TripleVec::iterator emplace(TripleVec &Container, Triple &&T) {
35  auto I = partition_point(Container, [=](const Triple &CT) {
36    return std::forward_as_tuple(CT.getArch(), CT.getOS(),
37                                 CT.getEnvironment()) <
38           std::forward_as_tuple(T.getArch(), T.getOS(), T.getEnvironment());
39  });
40
41  if (I != Container.end() && *I == T)
42    return I;
43  return Container.emplace(I, T);
44}
45
46static TripleVec constructTriples(MachOObjectFile *Obj,
47                                  const Architecture ArchT) {
48  auto getOSVersionStr = [](uint32_t V) {
49    PackedVersion OSVersion(V);
50    std::string Vers;
51    raw_string_ostream VStream(Vers);
52    VStream << OSVersion;
53    return VStream.str();
54  };
55  auto getOSVersion = [&](const MachOObjectFile::LoadCommandInfo &cmd) {
56    auto Vers = Obj->getVersionMinLoadCommand(cmd);
57    return getOSVersionStr(Vers.version);
58  };
59
60  TripleVec Triples;
61  bool IsIntel = ArchitectureSet(ArchT).hasX86();
62  auto Arch = getArchitectureName(ArchT);
63
64  for (const auto &cmd : Obj->load_commands()) {
65    std::string OSVersion;
66    switch (cmd.C.cmd) {
67    case MachO::LC_VERSION_MIN_MACOSX:
68      OSVersion = getOSVersion(cmd);
69      emplace(Triples, {Arch, "apple", "macos" + OSVersion});
70      break;
71    case MachO::LC_VERSION_MIN_IPHONEOS:
72      OSVersion = getOSVersion(cmd);
73      if (IsIntel)
74        emplace(Triples, {Arch, "apple", "ios" + OSVersion, "simulator"});
75      else
76        emplace(Triples, {Arch, "apple", "ios" + OSVersion});
77      break;
78    case MachO::LC_VERSION_MIN_TVOS:
79      OSVersion = getOSVersion(cmd);
80      if (IsIntel)
81        emplace(Triples, {Arch, "apple", "tvos" + OSVersion, "simulator"});
82      else
83        emplace(Triples, {Arch, "apple", "tvos" + OSVersion});
84      break;
85    case MachO::LC_VERSION_MIN_WATCHOS:
86      OSVersion = getOSVersion(cmd);
87      if (IsIntel)
88        emplace(Triples, {Arch, "apple", "watchos" + OSVersion, "simulator"});
89      else
90        emplace(Triples, {Arch, "apple", "watchos" + OSVersion});
91      break;
92    case MachO::LC_BUILD_VERSION: {
93      OSVersion = getOSVersionStr(Obj->getBuildVersionLoadCommand(cmd).minos);
94      switch (Obj->getBuildVersionLoadCommand(cmd).platform) {
95      case MachO::PLATFORM_MACOS:
96        emplace(Triples, {Arch, "apple", "macos" + OSVersion});
97        break;
98      case MachO::PLATFORM_IOS:
99        emplace(Triples, {Arch, "apple", "ios" + OSVersion});
100        break;
101      case MachO::PLATFORM_TVOS:
102        emplace(Triples, {Arch, "apple", "tvos" + OSVersion});
103        break;
104      case MachO::PLATFORM_WATCHOS:
105        emplace(Triples, {Arch, "apple", "watchos" + OSVersion});
106        break;
107      case MachO::PLATFORM_BRIDGEOS:
108        emplace(Triples, {Arch, "apple", "bridgeos" + OSVersion});
109        break;
110      case MachO::PLATFORM_MACCATALYST:
111        emplace(Triples, {Arch, "apple", "ios" + OSVersion, "macabi"});
112        break;
113      case MachO::PLATFORM_IOSSIMULATOR:
114        emplace(Triples, {Arch, "apple", "ios" + OSVersion, "simulator"});
115        break;
116      case MachO::PLATFORM_TVOSSIMULATOR:
117        emplace(Triples, {Arch, "apple", "tvos" + OSVersion, "simulator"});
118        break;
119      case MachO::PLATFORM_WATCHOSSIMULATOR:
120        emplace(Triples, {Arch, "apple", "watchos" + OSVersion, "simulator"});
121        break;
122      case MachO::PLATFORM_DRIVERKIT:
123        emplace(Triples, {Arch, "apple", "driverkit" + OSVersion});
124        break;
125      default:
126        break; // Skip any others.
127      }
128      break;
129    }
130    default:
131      break;
132    }
133  }
134
135  // Record unknown platform for older binaries that don't enforce platform
136  // load commands.
137  if (Triples.empty())
138    emplace(Triples, {Arch, "apple", "unknown"});
139
140  return Triples;
141}
142
143static Error readMachOHeader(MachOObjectFile *Obj, RecordsSlice &Slice) {
144  auto H = Obj->getHeader();
145  auto &BA = Slice.getBinaryAttrs();
146
147  switch (H.filetype) {
148  default:
149    llvm_unreachable("unsupported binary type");
150  case MachO::MH_DYLIB:
151    BA.File = FileType::MachO_DynamicLibrary;
152    break;
153  case MachO::MH_DYLIB_STUB:
154    BA.File = FileType::MachO_DynamicLibrary_Stub;
155    break;
156  case MachO::MH_BUNDLE:
157    BA.File = FileType::MachO_Bundle;
158    break;
159  }
160
161  if (H.flags & MachO::MH_TWOLEVEL)
162    BA.TwoLevelNamespace = true;
163  if (H.flags & MachO::MH_APP_EXTENSION_SAFE)
164    BA.AppExtensionSafe = true;
165
166  for (const auto &LCI : Obj->load_commands()) {
167    switch (LCI.C.cmd) {
168    case MachO::LC_ID_DYLIB: {
169      auto DLLC = Obj->getDylibIDLoadCommand(LCI);
170      BA.InstallName = Slice.copyString(LCI.Ptr + DLLC.dylib.name);
171      BA.CurrentVersion = DLLC.dylib.current_version;
172      BA.CompatVersion = DLLC.dylib.compatibility_version;
173      break;
174    }
175    case MachO::LC_REEXPORT_DYLIB: {
176      auto DLLC = Obj->getDylibIDLoadCommand(LCI);
177      BA.RexportedLibraries.emplace_back(
178          Slice.copyString(LCI.Ptr + DLLC.dylib.name));
179      break;
180    }
181    case MachO::LC_SUB_FRAMEWORK: {
182      auto SFC = Obj->getSubFrameworkCommand(LCI);
183      BA.ParentUmbrella = Slice.copyString(LCI.Ptr + SFC.umbrella);
184      break;
185    }
186    case MachO::LC_SUB_CLIENT: {
187      auto SCLC = Obj->getSubClientCommand(LCI);
188      BA.AllowableClients.emplace_back(Slice.copyString(LCI.Ptr + SCLC.client));
189      break;
190    }
191    case MachO::LC_UUID: {
192      auto UUIDLC = Obj->getUuidCommand(LCI);
193      std::stringstream Stream;
194      for (unsigned I = 0; I < 16; ++I) {
195        if (I == 4 || I == 6 || I == 8 || I == 10)
196          Stream << '-';
197        Stream << std::setfill('0') << std::setw(2) << std::uppercase
198               << std::hex << static_cast<int>(UUIDLC.uuid[I]);
199      }
200      BA.UUID = Slice.copyString(Stream.str());
201      break;
202    }
203    case MachO::LC_RPATH: {
204      auto RPLC = Obj->getRpathCommand(LCI);
205      BA.RPaths.emplace_back(Slice.copyString(LCI.Ptr + RPLC.path));
206      break;
207    }
208    case MachO::LC_SEGMENT_SPLIT_INFO: {
209      auto SSILC = Obj->getLinkeditDataLoadCommand(LCI);
210      if (SSILC.datasize == 0)
211        BA.OSLibNotForSharedCache = true;
212      break;
213    }
214    default:
215      break;
216    }
217  }
218
219  for (auto &Sect : Obj->sections()) {
220    auto SectName = Sect.getName();
221    if (!SectName)
222      return SectName.takeError();
223    if (*SectName != "__objc_imageinfo" && *SectName != "__image_info")
224      continue;
225
226    auto Content = Sect.getContents();
227    if (!Content)
228      return Content.takeError();
229
230    if ((Content->size() >= 8) && (Content->front() == 0)) {
231      uint32_t Flags;
232      if (Obj->isLittleEndian()) {
233        auto *p =
234            reinterpret_cast<const support::ulittle32_t *>(Content->data() + 4);
235        Flags = *p;
236      } else {
237        auto *p =
238            reinterpret_cast<const support::ubig32_t *>(Content->data() + 4);
239        Flags = *p;
240      }
241      BA.SwiftABI = (Flags >> 8) & 0xFF;
242    }
243  }
244  return Error::success();
245}
246
247static Error readSymbols(MachOObjectFile *Obj, RecordsSlice &Slice,
248                         const ParseOption &Opt) {
249
250  auto parseExport = [](const auto ExportFlags,
251                        auto Addr) -> std::tuple<SymbolFlags, RecordLinkage> {
252    SymbolFlags Flags = SymbolFlags::None;
253    switch (ExportFlags & MachO::EXPORT_SYMBOL_FLAGS_KIND_MASK) {
254    case MachO::EXPORT_SYMBOL_FLAGS_KIND_REGULAR:
255      if (ExportFlags & MachO::EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION)
256        Flags |= SymbolFlags::WeakDefined;
257      break;
258    case MachO::EXPORT_SYMBOL_FLAGS_KIND_THREAD_LOCAL:
259      Flags |= SymbolFlags::ThreadLocalValue;
260      break;
261    }
262
263    RecordLinkage Linkage = (ExportFlags & MachO::EXPORT_SYMBOL_FLAGS_REEXPORT)
264                                ? RecordLinkage::Rexported
265                                : RecordLinkage::Exported;
266    return {Flags, Linkage};
267  };
268
269  Error Err = Error::success();
270
271  StringMap<std::pair<SymbolFlags, RecordLinkage>> Exports;
272  // Collect symbols from export trie first. Sometimes, there are more exports
273  // in the trie than in n-list due to stripping. This is common for swift
274  // mangled symbols.
275  for (auto &Sym : Obj->exports(Err)) {
276    auto [Flags, Linkage] = parseExport(Sym.flags(), Sym.address());
277    Slice.addRecord(Sym.name(), Flags, GlobalRecord::Kind::Unknown, Linkage);
278    Exports[Sym.name()] = {Flags, Linkage};
279  }
280
281  for (const auto &Sym : Obj->symbols()) {
282    auto FlagsOrErr = Sym.getFlags();
283    if (!FlagsOrErr)
284      return FlagsOrErr.takeError();
285    auto Flags = *FlagsOrErr;
286
287    auto NameOrErr = Sym.getName();
288    if (!NameOrErr)
289      return NameOrErr.takeError();
290    auto Name = *NameOrErr;
291
292    RecordLinkage Linkage = RecordLinkage::Unknown;
293    SymbolFlags RecordFlags = SymbolFlags::None;
294
295    if (Opt.Undefineds && (Flags & SymbolRef::SF_Undefined)) {
296      Linkage = RecordLinkage::Undefined;
297      if (Flags & SymbolRef::SF_Weak)
298        RecordFlags |= SymbolFlags::WeakReferenced;
299    } else if (Flags & SymbolRef::SF_Exported) {
300      auto Exp = Exports.find(Name);
301      // This should never be possible when binaries are produced with Apple
302      // linkers. However it is possible to craft dylibs where the export trie
303      // is either malformed or has conflicting symbols compared to n_list.
304      if (Exp != Exports.end())
305        std::tie(RecordFlags, Linkage) = Exp->second;
306      else
307        Linkage = RecordLinkage::Exported;
308    } else if (Flags & SymbolRef::SF_Hidden) {
309      Linkage = RecordLinkage::Internal;
310    } else
311      continue;
312
313    auto TypeOrErr = Sym.getType();
314    if (!TypeOrErr)
315      return TypeOrErr.takeError();
316    auto Type = *TypeOrErr;
317
318    GlobalRecord::Kind GV = (Type & SymbolRef::ST_Function)
319                                ? GlobalRecord::Kind::Function
320                                : GlobalRecord::Kind::Variable;
321
322    if (GV == GlobalRecord::Kind::Function)
323      RecordFlags |= SymbolFlags::Text;
324    else
325      RecordFlags |= SymbolFlags::Data;
326
327    Slice.addRecord(Name, RecordFlags, GV, Linkage);
328  }
329  return Err;
330}
331
332static Error load(MachOObjectFile *Obj, RecordsSlice &Slice,
333                  const ParseOption &Opt, const Architecture Arch) {
334  if (Arch == AK_unknown)
335    return make_error<TextAPIError>(TextAPIErrorCode::UnsupportedTarget);
336
337  if (Opt.MachOHeader)
338    if (auto Err = readMachOHeader(Obj, Slice))
339      return Err;
340
341  if (Opt.SymbolTable)
342    if (auto Err = readSymbols(Obj, Slice, Opt))
343      return Err;
344
345  return Error::success();
346}
347
348Expected<Records> DylibReader::readFile(MemoryBufferRef Buffer,
349                                        const ParseOption &Opt) {
350  Records Results;
351
352  auto BinOrErr = createBinary(Buffer);
353  if (!BinOrErr)
354    return BinOrErr.takeError();
355
356  Binary &Bin = *BinOrErr.get();
357  if (auto *Obj = dyn_cast<MachOObjectFile>(&Bin)) {
358    const auto Arch = getArchitectureFromCpuType(Obj->getHeader().cputype,
359                                                 Obj->getHeader().cpusubtype);
360    if (!Opt.Archs.has(Arch))
361      return make_error<TextAPIError>(TextAPIErrorCode::NoSuchArchitecture);
362
363    auto Triples = constructTriples(Obj, Arch);
364    for (const auto &T : Triples) {
365      if (mapToPlatformType(T) == PLATFORM_UNKNOWN)
366        return make_error<TextAPIError>(TextAPIErrorCode::UnsupportedTarget);
367      Results.emplace_back(std::make_shared<RecordsSlice>(RecordsSlice({T})));
368      if (auto Err = load(Obj, *Results.back(), Opt, Arch))
369        return std::move(Err);
370      Results.back()->getBinaryAttrs().Path = Buffer.getBufferIdentifier();
371    }
372    return Results;
373  }
374
375  // Only expect MachO universal binaries at this point.
376  assert(isa<MachOUniversalBinary>(&Bin) &&
377         "Expected a MachO universal binary.");
378  auto *UB = cast<MachOUniversalBinary>(&Bin);
379
380  for (auto OI = UB->begin_objects(), OE = UB->end_objects(); OI != OE; ++OI) {
381    // Skip architecture if not requested.
382    auto Arch =
383        getArchitectureFromCpuType(OI->getCPUType(), OI->getCPUSubType());
384    if (!Opt.Archs.has(Arch))
385      continue;
386
387    // Skip unknown architectures.
388    if (Arch == AK_unknown)
389      continue;
390
391    // This can fail if the object is an archive.
392    auto ObjOrErr = OI->getAsObjectFile();
393
394    // Skip the archive and consume the error.
395    if (!ObjOrErr) {
396      consumeError(ObjOrErr.takeError());
397      continue;
398    }
399
400    auto &Obj = *ObjOrErr.get();
401    switch (Obj.getHeader().filetype) {
402    default:
403      break;
404    case MachO::MH_BUNDLE:
405    case MachO::MH_DYLIB:
406    case MachO::MH_DYLIB_STUB:
407      for (const auto &T : constructTriples(&Obj, Arch)) {
408        Results.emplace_back(std::make_shared<RecordsSlice>(RecordsSlice({T})));
409        if (auto Err = load(&Obj, *Results.back(), Opt, Arch))
410          return std::move(Err);
411      }
412      break;
413    }
414  }
415
416  if (Results.empty())
417    return make_error<TextAPIError>(TextAPIErrorCode::EmptyResults);
418  return Results;
419}
420
421Expected<std::unique_ptr<InterfaceFile>>
422DylibReader::get(MemoryBufferRef Buffer) {
423  ParseOption Options;
424  auto SlicesOrErr = readFile(Buffer, Options);
425  if (!SlicesOrErr)
426    return SlicesOrErr.takeError();
427
428  return convertToInterfaceFile(*SlicesOrErr);
429}
430