1 | //===- llvm/TextAPI/InterfaceFile.h - TAPI Interface File -------*- 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 | // A generic and abstract interface representation for linkable objects. This |
10 | // could be an MachO executable, bundle, dylib, or text-based stub file. |
11 | // |
12 | //===----------------------------------------------------------------------===// |
13 | |
14 | #ifndef LLVM_TEXTAPI_INTERFACEFILE_H |
15 | #define LLVM_TEXTAPI_INTERFACEFILE_H |
16 | |
17 | #include "llvm/ADT/Hashing.h" |
18 | #include "llvm/ADT/StringRef.h" |
19 | #include "llvm/ADT/iterator.h" |
20 | #include "llvm/Support/Allocator.h" |
21 | #include "llvm/TextAPI/ArchitectureSet.h" |
22 | #include "llvm/TextAPI/FileTypes.h" |
23 | #include "llvm/TextAPI/PackedVersion.h" |
24 | #include "llvm/TextAPI/Platform.h" |
25 | #include "llvm/TextAPI/RecordsSlice.h" |
26 | #include "llvm/TextAPI/Symbol.h" |
27 | #include "llvm/TextAPI/SymbolSet.h" |
28 | #include "llvm/TextAPI/Target.h" |
29 | |
30 | namespace llvm { |
31 | namespace MachO { |
32 | |
33 | /// Defines a list of Objective-C constraints. |
34 | enum class ObjCConstraintType : unsigned { |
35 | /// No constraint. |
36 | None = 0, |
37 | |
38 | /// Retain/Release. |
39 | Retain_Release = 1, |
40 | |
41 | /// Retain/Release for Simulator. |
42 | Retain_Release_For_Simulator = 2, |
43 | |
44 | /// Retain/Release or Garbage Collection. |
45 | Retain_Release_Or_GC = 3, |
46 | |
47 | /// Garbage Collection. |
48 | GC = 4, |
49 | }; |
50 | |
51 | /// Reference to an interface file. |
52 | class InterfaceFileRef { |
53 | public: |
54 | InterfaceFileRef() = default; |
55 | |
56 | InterfaceFileRef(StringRef InstallName) : InstallName(InstallName) {} |
57 | |
58 | InterfaceFileRef(StringRef InstallName, const TargetList Targets) |
59 | : InstallName(InstallName), Targets(std::move(Targets)) {} |
60 | |
61 | StringRef getInstallName() const { return InstallName; }; |
62 | |
63 | void addTarget(const Target &Target); |
64 | template <typename RangeT> void addTargets(RangeT &&Targets) { |
65 | for (const auto &Target : Targets) |
66 | addTarget(Target: Target(Target)); |
67 | } |
68 | |
69 | bool hasTarget(Target &Targ) const { |
70 | return llvm::is_contained(Range: Targets, Element: Targ); |
71 | } |
72 | |
73 | using const_target_iterator = TargetList::const_iterator; |
74 | using const_target_range = llvm::iterator_range<const_target_iterator>; |
75 | const_target_range targets() const { return {Targets}; } |
76 | |
77 | ArchitectureSet getArchitectures() const { |
78 | return mapToArchitectureSet(Targets); |
79 | } |
80 | |
81 | PlatformSet getPlatforms() const { return mapToPlatformSet(Targets); } |
82 | |
83 | bool operator==(const InterfaceFileRef &O) const { |
84 | return std::tie(args: InstallName, args: Targets) == std::tie(args: O.InstallName, args: O.Targets); |
85 | } |
86 | |
87 | bool operator!=(const InterfaceFileRef &O) const { |
88 | return std::tie(args: InstallName, args: Targets) != std::tie(args: O.InstallName, args: O.Targets); |
89 | } |
90 | |
91 | bool operator<(const InterfaceFileRef &O) const { |
92 | return std::tie(args: InstallName, args: Targets) < std::tie(args: O.InstallName, args: O.Targets); |
93 | } |
94 | |
95 | private: |
96 | std::string InstallName; |
97 | TargetList Targets; |
98 | }; |
99 | |
100 | } // end namespace MachO. |
101 | |
102 | namespace MachO { |
103 | |
104 | /// Defines the interface file. |
105 | class InterfaceFile { |
106 | public: |
107 | InterfaceFile(std::unique_ptr<SymbolSet> &&InputSymbols) |
108 | : SymbolsSet(std::move(InputSymbols)) {} |
109 | |
110 | InterfaceFile() : SymbolsSet(std::make_unique<SymbolSet>()){}; |
111 | /// Set the path from which this file was generated (if applicable). |
112 | /// |
113 | /// \param Path_ The path to the source file. |
114 | void setPath(StringRef Path_) { Path = std::string(Path_); } |
115 | |
116 | /// Get the path from which this file was generated (if applicable). |
117 | /// |
118 | /// \return The path to the source file or empty. |
119 | StringRef getPath() const { return Path; } |
120 | |
121 | /// Set the file type. |
122 | /// |
123 | /// This is used by the YAML writer to identify the specification it should |
124 | /// use for writing the file. |
125 | /// |
126 | /// \param Kind The file type. |
127 | void setFileType(FileType Kind) { FileKind = Kind; } |
128 | |
129 | /// Get the file type. |
130 | /// |
131 | /// \return The file type. |
132 | FileType getFileType() const { return FileKind; } |
133 | |
134 | /// Get the architectures. |
135 | /// |
136 | /// \return The applicable architectures. |
137 | ArchitectureSet getArchitectures() const { |
138 | return mapToArchitectureSet(Targets); |
139 | } |
140 | |
141 | /// Get the platforms. |
142 | /// |
143 | /// \return The applicable platforms. |
144 | PlatformSet getPlatforms() const { return mapToPlatformSet(Targets); } |
145 | |
146 | /// Set and add target. |
147 | /// |
148 | /// \param Target the target to add into. |
149 | void addTarget(const Target &Target); |
150 | |
151 | /// Determine if target triple slice exists in file. |
152 | /// |
153 | /// \param Targ the value to find. |
154 | bool hasTarget(const Target &Targ) const { |
155 | return llvm::is_contained(Range: Targets, Element: Targ); |
156 | } |
157 | |
158 | /// Set and add targets. |
159 | /// |
160 | /// Add the subset of llvm::triples that is supported by Tapi |
161 | /// |
162 | /// \param Targets the collection of targets. |
163 | template <typename RangeT> void addTargets(RangeT &&Targets) { |
164 | for (const auto &Target_ : Targets) |
165 | addTarget(Target: Target(Target_)); |
166 | } |
167 | |
168 | using const_target_iterator = TargetList::const_iterator; |
169 | using const_target_range = llvm::iterator_range<const_target_iterator>; |
170 | const_target_range targets() const { return {Targets}; } |
171 | |
172 | using const_filtered_target_iterator = |
173 | llvm::filter_iterator<const_target_iterator, |
174 | std::function<bool(const Target &)>>; |
175 | using const_filtered_target_range = |
176 | llvm::iterator_range<const_filtered_target_iterator>; |
177 | const_filtered_target_range targets(ArchitectureSet Archs) const; |
178 | |
179 | /// Set the install name of the library. |
180 | void setInstallName(StringRef InstallName_) { |
181 | InstallName = std::string(InstallName_); |
182 | } |
183 | |
184 | /// Get the install name of the library. |
185 | StringRef getInstallName() const { return InstallName; } |
186 | |
187 | /// Set the current version of the library. |
188 | void setCurrentVersion(PackedVersion Version) { CurrentVersion = Version; } |
189 | |
190 | /// Get the current version of the library. |
191 | PackedVersion getCurrentVersion() const { return CurrentVersion; } |
192 | |
193 | /// Set the compatibility version of the library. |
194 | void setCompatibilityVersion(PackedVersion Version) { |
195 | CompatibilityVersion = Version; |
196 | } |
197 | |
198 | /// Get the compatibility version of the library. |
199 | PackedVersion getCompatibilityVersion() const { return CompatibilityVersion; } |
200 | |
201 | /// Set the Swift ABI version of the library. |
202 | void setSwiftABIVersion(uint8_t Version) { SwiftABIVersion = Version; } |
203 | |
204 | /// Get the Swift ABI version of the library. |
205 | uint8_t getSwiftABIVersion() const { return SwiftABIVersion; } |
206 | |
207 | /// Specify if the library uses two-level namespace (or flat namespace). |
208 | void setTwoLevelNamespace(bool V = true) { IsTwoLevelNamespace = V; } |
209 | |
210 | /// Check if the library uses two-level namespace. |
211 | bool isTwoLevelNamespace() const { return IsTwoLevelNamespace; } |
212 | |
213 | /// Specify if the library is an OS library but not shared cache eligible. |
214 | void setOSLibNotForSharedCache(bool V = true) { |
215 | IsOSLibNotForSharedCache = V; |
216 | } |
217 | |
218 | /// Check if the library is an OS library that is not shared cache eligible. |
219 | bool isOSLibNotForSharedCache() const { return IsOSLibNotForSharedCache; } |
220 | |
221 | /// Specify if the library is application extension safe (or not). |
222 | void setApplicationExtensionSafe(bool V = true) { IsAppExtensionSafe = V; } |
223 | |
224 | /// Check if the library is application extension safe. |
225 | bool isApplicationExtensionSafe() const { return IsAppExtensionSafe; } |
226 | |
227 | /// Check if the library has simulator support. |
228 | bool hasSimulatorSupport() const { return HasSimSupport; } |
229 | |
230 | /// Specify if the library has simulator support. |
231 | void setSimulatorSupport(bool V = true) { HasSimSupport = V; } |
232 | |
233 | /// Set the Objective-C constraint. |
234 | void setObjCConstraint(ObjCConstraintType Constraint) { |
235 | ObjcConstraint = Constraint; |
236 | } |
237 | |
238 | /// Get the Objective-C constraint. |
239 | ObjCConstraintType getObjCConstraint() const { return ObjcConstraint; } |
240 | |
241 | /// Set the parent umbrella frameworks. |
242 | /// \param Target_ The target applicable to Parent |
243 | /// \param Parent The name of Parent |
244 | void addParentUmbrella(const Target &Target_, StringRef Parent); |
245 | |
246 | /// Get the list of Parent Umbrella frameworks. |
247 | /// |
248 | /// \return Returns a list of target information and install name of parent |
249 | /// umbrellas. |
250 | const std::vector<std::pair<Target, std::string>> &umbrellas() const { |
251 | return ParentUmbrellas; |
252 | } |
253 | |
254 | /// Add an allowable client. |
255 | /// |
256 | /// Mach-O Dynamic libraries have the concept of allowable clients that are |
257 | /// checked during static link time. The name of the application or library |
258 | /// that is being generated needs to match one of the allowable clients or the |
259 | /// linker refuses to link this library. |
260 | /// |
261 | /// \param InstallName The name of the client that is allowed to link this |
262 | /// library. |
263 | /// \param Target The target triple for which this applies. |
264 | void addAllowableClient(StringRef InstallName, const Target &Target); |
265 | |
266 | /// Get the list of allowable clients. |
267 | /// |
268 | /// \return Returns a list of allowable clients. |
269 | const std::vector<InterfaceFileRef> &allowableClients() const { |
270 | return AllowableClients; |
271 | } |
272 | |
273 | /// Add a re-exported library. |
274 | /// |
275 | /// \param InstallName The name of the library to re-export. |
276 | /// \param Target The target triple for which this applies. |
277 | void addReexportedLibrary(StringRef InstallName, const Target &Target); |
278 | |
279 | /// Get the list of re-exported libraries. |
280 | /// |
281 | /// \return Returns a list of re-exported libraries. |
282 | const std::vector<InterfaceFileRef> &reexportedLibraries() const { |
283 | return ReexportedLibraries; |
284 | } |
285 | |
286 | /// Add a library for inlining to top level library. |
287 | /// |
288 | ///\param Document The library to inline with top level library. |
289 | void addDocument(std::shared_ptr<InterfaceFile> &&Document); |
290 | |
291 | /// Returns the pointer to parent document if exists or nullptr otherwise. |
292 | InterfaceFile *getParent() const { return Parent; } |
293 | |
294 | /// Get the list of inlined libraries. |
295 | /// |
296 | /// \return Returns a list of the inlined frameworks. |
297 | const std::vector<std::shared_ptr<InterfaceFile>> &documents() const { |
298 | return Documents; |
299 | } |
300 | |
301 | /// Set the runpath search paths. |
302 | /// \param InputTarget The target applicable to runpath search path. |
303 | /// \param RPath The name of runpath. |
304 | void addRPath(const Target &InputTarget, StringRef RPath); |
305 | |
306 | /// Get the list of runpath search paths. |
307 | /// |
308 | /// \return Returns a list of the rpaths per target. |
309 | const std::vector<std::pair<Target, std::string>> &rpaths() const { |
310 | return RPaths; |
311 | } |
312 | |
313 | /// Get symbol if exists in file. |
314 | /// |
315 | /// \param Kind The kind of global symbol to record. |
316 | /// \param Name The name of the symbol. |
317 | /// \param ObjCIF The ObjCInterface symbol type, if applicable. |
318 | std::optional<const Symbol *> |
319 | getSymbol(EncodeKind Kind, StringRef Name, |
320 | ObjCIFSymbolKind ObjCIF = ObjCIFSymbolKind::None) const { |
321 | if (auto *Sym = SymbolsSet->findSymbol(Kind, Name, ObjCIF)) |
322 | return Sym; |
323 | return std::nullopt; |
324 | } |
325 | |
326 | /// Add a symbol to the symbols list or extend an existing one. |
327 | template <typename RangeT, typename ElT = std::remove_reference_t< |
328 | decltype(*std::begin(std::declval<RangeT>()))>> |
329 | void addSymbol(EncodeKind Kind, StringRef Name, RangeT &&Targets, |
330 | SymbolFlags Flags = SymbolFlags::None) { |
331 | SymbolsSet->addGlobal(Kind, Name, Flags, Targets); |
332 | } |
333 | |
334 | /// Add Symbol with multiple targets. |
335 | /// |
336 | /// \param Kind The kind of global symbol to record. |
337 | /// \param Name The name of the symbol. |
338 | /// \param Targets The list of targets the symbol is defined in. |
339 | /// \param Flags The properties the symbol holds. |
340 | void addSymbol(EncodeKind Kind, StringRef Name, TargetList &&Targets, |
341 | SymbolFlags Flags = SymbolFlags::None) { |
342 | SymbolsSet->addGlobal(Kind, Name, Flags, Targets); |
343 | } |
344 | |
345 | /// Add Symbol with single target. |
346 | /// |
347 | /// \param Kind The kind of global symbol to record. |
348 | /// \param Name The name of the symbol. |
349 | /// \param Target The target the symbol is defined in. |
350 | /// \param Flags The properties the symbol holds. |
351 | void addSymbol(EncodeKind Kind, StringRef Name, Target &Target, |
352 | SymbolFlags Flags = SymbolFlags::None) { |
353 | SymbolsSet->addGlobal(Kind, Name, Flags, Targ: Target); |
354 | } |
355 | |
356 | /// Get size of symbol set. |
357 | /// \return The number of symbols the file holds. |
358 | size_t symbolsCount() const { return SymbolsSet->size(); } |
359 | |
360 | using const_symbol_range = SymbolSet::const_symbol_range; |
361 | using const_filtered_symbol_range = SymbolSet::const_filtered_symbol_range; |
362 | |
363 | const_symbol_range symbols() const { return SymbolsSet->symbols(); }; |
364 | const_filtered_symbol_range exports() const { return SymbolsSet->exports(); }; |
365 | const_filtered_symbol_range reexports() const { |
366 | return SymbolsSet->reexports(); |
367 | }; |
368 | const_filtered_symbol_range undefineds() const { |
369 | return SymbolsSet->undefineds(); |
370 | }; |
371 | |
372 | /// Extract architecture slice from Interface. |
373 | /// |
374 | /// \param Arch architecture to extract from. |
375 | /// \return New InterfaceFile with extracted architecture slice. |
376 | llvm::Expected<std::unique_ptr<InterfaceFile>> |
377 | (Architecture Arch) const; |
378 | |
379 | /// Remove architecture slice from Interface. |
380 | /// |
381 | /// \param Arch architecture to remove. |
382 | /// \return New Interface File with removed architecture slice. |
383 | llvm::Expected<std::unique_ptr<InterfaceFile>> |
384 | remove(Architecture Arch) const; |
385 | |
386 | /// Merge Interfaces for the same library. The following library attributes |
387 | /// must match. |
388 | /// * Install name, Current & Compatibility version, |
389 | /// * Two-level namespace enablement, and App extension enablement. |
390 | /// |
391 | /// \param O The Interface to merge. |
392 | /// \return New Interface File that was merged. |
393 | llvm::Expected<std::unique_ptr<InterfaceFile>> |
394 | merge(const InterfaceFile *O) const; |
395 | |
396 | /// Inline reexported library into Interface. |
397 | /// |
398 | /// \param Library Interface of reexported library. |
399 | /// \param Overwrite Whether to overwrite preexisting inlined library. |
400 | void inlineLibrary(std::shared_ptr<InterfaceFile> Library, |
401 | bool Overwrite = false); |
402 | |
403 | /// Set InterfaceFile properties from pre-gathered binary attributes, |
404 | /// if they are not set already. |
405 | /// |
406 | /// \param BA Attributes typically represented in load commands. |
407 | /// \param Targ MachO Target slice to add attributes to. |
408 | void setFromBinaryAttrs(const RecordsSlice::BinaryAttrs &BA, |
409 | const Target &Targ); |
410 | |
411 | /// The equality is determined by attributes that impact linking |
412 | /// compatibilities. Path, & FileKind are irrelevant since these by |
413 | /// itself should not impact linking. |
414 | /// This is an expensive operation. |
415 | bool operator==(const InterfaceFile &O) const; |
416 | |
417 | bool operator!=(const InterfaceFile &O) const { return !(*this == O); } |
418 | |
419 | private: |
420 | llvm::BumpPtrAllocator Allocator; |
421 | StringRef copyString(StringRef String) { |
422 | if (String.empty()) |
423 | return {}; |
424 | |
425 | void *Ptr = Allocator.Allocate(Size: String.size(), Alignment: 1); |
426 | memcpy(dest: Ptr, src: String.data(), n: String.size()); |
427 | return StringRef(reinterpret_cast<const char *>(Ptr), String.size()); |
428 | } |
429 | |
430 | TargetList Targets; |
431 | std::string Path; |
432 | FileType FileKind{FileType::Invalid}; |
433 | std::string InstallName; |
434 | PackedVersion CurrentVersion; |
435 | PackedVersion CompatibilityVersion; |
436 | uint8_t SwiftABIVersion{0}; |
437 | bool IsTwoLevelNamespace{false}; |
438 | bool IsOSLibNotForSharedCache{false}; |
439 | bool IsAppExtensionSafe{false}; |
440 | bool HasSimSupport{false}; |
441 | ObjCConstraintType ObjcConstraint = ObjCConstraintType::None; |
442 | std::vector<std::pair<Target, std::string>> ParentUmbrellas; |
443 | std::vector<InterfaceFileRef> AllowableClients; |
444 | std::vector<InterfaceFileRef> ReexportedLibraries; |
445 | std::vector<std::shared_ptr<InterfaceFile>> Documents; |
446 | std::vector<std::pair<Target, std::string>> RPaths; |
447 | std::unique_ptr<SymbolSet> SymbolsSet; |
448 | InterfaceFile *Parent = nullptr; |
449 | }; |
450 | |
451 | // Keep containers that hold InterfaceFileRefs in sorted order and uniqued. |
452 | template <typename C> |
453 | typename C::iterator addEntry(C &Container, StringRef InstallName) { |
454 | auto I = partition_point(Container, [=](const InterfaceFileRef &O) { |
455 | return O.getInstallName() < InstallName; |
456 | }); |
457 | if (I != Container.end() && I->getInstallName() == InstallName) |
458 | return I; |
459 | |
460 | return Container.emplace(I, InstallName); |
461 | } |
462 | |
463 | } // end namespace MachO. |
464 | } // end namespace llvm. |
465 | |
466 | #endif // LLVM_TEXTAPI_INTERFACEFILE_H |
467 | |