xref: /llvm-project/llvm/lib/TextAPI/BinaryReader/DylibReader.cpp (revision 3a080a0195ed21b8e12f825cfa00c8fa79f851a6)
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/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>
28 
29 using namespace llvm;
30 using namespace llvm::object;
31 using namespace llvm::MachO;
32 using namespace llvm::MachO::DylibReader;
33 
34 using TripleVec = std::vector<Triple>;
35 static typename 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 
47 static 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       else
77         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       else
84         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       else
91         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 platform
137   // load commands.
138   if (Triples.empty())
139     emplace(Triples, {Arch, "apple", "unknown"});
140 
141   return Triples;
142 }
143 
144 static 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::uppercase
199                << 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 
248 static 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::Rexported
266                                 : 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 exports
274   // in the trie than in n-list due to stripping. This is common for swift
275   // 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 (Opt.Undefineds && (Flags & SymbolRef::SF_Undefined)) {
297       Linkage = RecordLinkage::Undefined;
298       if (Flags & SymbolRef::SF_Weak)
299         RecordFlags |= SymbolFlags::WeakReferenced;
300     } else if (Flags & SymbolRef::SF_Exported) {
301       auto Exp = Exports.find(Name);
302       // This should never be possible when binaries are produced with Apple
303       // linkers. However it is possible to craft dylibs where the export trie
304       // is either malformed or has conflicting symbols compared to n_list.
305       if (Exp != Exports.end())
306         std::tie(RecordFlags, Linkage) = Exp->second;
307       else
308         Linkage = RecordLinkage::Exported;
309     } else if (Flags & SymbolRef::SF_Hidden) {
310       Linkage = RecordLinkage::Internal;
311     } else
312       continue;
313 
314     auto TypeOrErr = Sym.getType();
315     if (!TypeOrErr)
316       return TypeOrErr.takeError();
317     auto Type = *TypeOrErr;
318 
319     GlobalRecord::Kind GV = (Type & SymbolRef::ST_Function)
320                                 ? GlobalRecord::Kind::Function
321                                 : GlobalRecord::Kind::Variable;
322 
323     if (GV == GlobalRecord::Kind::Function)
324       RecordFlags |= SymbolFlags::Text;
325     else
326       RecordFlags |= SymbolFlags::Data;
327 
328     Slice.addRecord(Name, RecordFlags, GV, Linkage);
329   }
330   return Err;
331 }
332 
333 static Error load(MachOObjectFile *Obj, RecordsSlice &Slice,
334                   const ParseOption &Opt, const Architecture Arch) {
335   if (Arch == AK_unknown)
336     return make_error<TextAPIError>(TextAPIErrorCode::UnsupportedTarget);
337 
338   if (Opt.MachOHeader)
339     if (auto Err = readMachOHeader(Obj, Slice))
340       return Err;
341 
342   if (Opt.SymbolTable)
343     if (auto Err = readSymbols(Obj, Slice, Opt))
344       return Err;
345 
346   return Error::success();
347 }
348 
349 Expected<Records> DylibReader::readFile(MemoryBufferRef Buffer,
350                                         const ParseOption &Opt) {
351   Records Results;
352 
353   auto BinOrErr = createBinary(Buffer);
354   if (!BinOrErr)
355     return BinOrErr.takeError();
356 
357   Binary &Bin = *BinOrErr.get();
358   if (auto *Obj = dyn_cast<MachOObjectFile>(&Bin)) {
359     const auto Arch = getArchitectureFromCpuType(Obj->getHeader().cputype,
360                                                  Obj->getHeader().cpusubtype);
361     if (!Opt.Archs.has(Arch))
362       return make_error<TextAPIError>(TextAPIErrorCode::NoSuchArchitecture);
363 
364     auto Triples = constructTriples(Obj, Arch);
365     for (const auto &T : Triples) {
366       if (mapToPlatformType(T) == PLATFORM_UNKNOWN)
367         return make_error<TextAPIError>(TextAPIErrorCode::UnsupportedTarget);
368       Results.emplace_back(std::make_shared<RecordsSlice>(RecordsSlice({T})));
369       if (auto Err = load(Obj, *Results.back(), Opt, Arch))
370         return std::move(Err);
371       Results.back()->getBinaryAttrs().Path = Buffer.getBufferIdentifier();
372     }
373     return Results;
374   }
375 
376   // Only expect MachO universal binaries at this point.
377   assert(isa<MachOUniversalBinary>(&Bin) &&
378          "Expected a MachO universal binary.");
379   auto *UB = cast<MachOUniversalBinary>(&Bin);
380 
381   for (auto OI = UB->begin_objects(), OE = UB->end_objects(); OI != OE; ++OI) {
382     // Skip architecture if not requested.
383     auto Arch =
384         getArchitectureFromCpuType(OI->getCPUType(), OI->getCPUSubType());
385     if (!Opt.Archs.has(Arch))
386       continue;
387 
388     // Skip unknown architectures.
389     if (Arch == AK_unknown)
390       continue;
391 
392     // This can fail if the object is an archive.
393     auto ObjOrErr = OI->getAsObjectFile();
394 
395     // Skip the archive and consume the error.
396     if (!ObjOrErr) {
397       consumeError(ObjOrErr.takeError());
398       continue;
399     }
400 
401     auto &Obj = *ObjOrErr.get();
402     switch (Obj.getHeader().filetype) {
403     default:
404       break;
405     case MachO::MH_BUNDLE:
406     case MachO::MH_DYLIB:
407     case MachO::MH_DYLIB_STUB:
408       for (const auto &T : constructTriples(&Obj, Arch)) {
409         Results.emplace_back(std::make_shared<RecordsSlice>(RecordsSlice({T})));
410         if (auto Err = load(&Obj, *Results.back(), Opt, Arch))
411           return std::move(Err);
412         Results.back()->getBinaryAttrs().Path = Buffer.getBufferIdentifier();
413       }
414       break;
415     }
416   }
417 
418   if (Results.empty())
419     return make_error<TextAPIError>(TextAPIErrorCode::EmptyResults);
420   return Results;
421 }
422 
423 Expected<std::unique_ptr<InterfaceFile>>
424 DylibReader::get(MemoryBufferRef Buffer) {
425   ParseOption Options;
426   auto SlicesOrErr = readFile(Buffer, Options);
427   if (!SlicesOrErr)
428     return SlicesOrErr.takeError();
429 
430   return convertToInterfaceFile(*SlicesOrErr);
431 }
432