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>;
emplace(TripleVec & Container,Triple && T)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
constructTriples(MachOObjectFile * Obj,const Architecture ArchT)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
readMachOHeader(MachOObjectFile * Obj,RecordsSlice & Slice)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
readSymbols(MachOObjectFile * Obj,RecordsSlice & Slice,const ParseOption & Opt)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
load(MachOObjectFile * Obj,RecordsSlice & Slice,const ParseOption & Opt,const Architecture Arch)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
readFile(MemoryBufferRef Buffer,const ParseOption & Opt)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>>
get(MemoryBufferRef Buffer)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