xref: /freebsd/contrib/llvm-project/llvm/lib/TextAPI/BinaryReader/DylibReader.cpp (revision 48edad2edf6eb7a539e40dad8e1f87e3fa4973fd)
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  
28  using namespace llvm;
29  using namespace llvm::object;
30  using namespace llvm::MachO;
31  using namespace llvm::MachO::DylibReader;
32  
33  using TripleVec = std::vector<Triple>;
34  static 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  
46  static 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  
143  static 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  
247  static 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  
332  static 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  
348  Expected<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  
421  Expected<std::unique_ptr<InterfaceFile>>
422  DylibReader::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