]> git.saurik.com Git - apple/ld64.git/blob - src/ld/parsers/textstub_dylib_file.cpp
ld64-253.9.tar.gz
[apple/ld64.git] / src / ld / parsers / textstub_dylib_file.cpp
1 /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
2 *
3 * Copyright (c) 2015 Apple Inc. All rights reserved.
4 *
5 * @APPLE_LICENSE_HEADER_START@
6 *
7 * This file contains Original Code and/or Modifications of Original Code
8 * as defined in and that are subject to the Apple Public Source License
9 * Version 2.0 (the 'License'). You may not use this file except in
10 * compliance with the License. Please obtain a copy of the License at
11 * http://www.opensource.apple.com/apsl/ and read it before using this
12 * file.
13 *
14 * The Original Code and all software distributed under the License are
15 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
16 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
17 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
19 * Please see the License for the specific language governing rights and
20 * limitations under the License.
21 *
22 * @APPLE_LICENSE_HEADER_END@
23 */
24
25
26 #include <sys/param.h>
27 #include <sys/mman.h>
28
29 #include <vector>
30
31 #include "Architectures.hpp"
32 #include "bitcode.hpp"
33 #include "MachOFileAbstraction.hpp"
34 #include "MachOTrie.hpp"
35 #include "textstub_dylib_file.hpp"
36
37 namespace {
38
39 ///
40 /// A token is a light-weight reference to the content of an nmap'ed file. It
41 /// doesn't own the data and it doesn't make a copy of it. The referenced data
42 /// is only valid as long as the file is mapped in.
43 ///
44 class Token {
45 const char* _p;
46 size_t _size;
47
48 int compareMemory(const char* lhs, const char* rhs, size_t size) const {
49 if (size == 0)
50 return 0;
51 return ::memcmp(lhs, rhs, size);
52 }
53
54 public:
55 Token() : _p(nullptr), _size(0) {}
56
57 Token(const char* p) : _p(p), _size(0) {
58 if (p)
59 _size = ::strlen(p);
60 }
61
62 Token(const char* p, size_t s) : _p(p), _size(s) {}
63
64 const char* data() const { return _p; }
65
66 size_t size() const { return _size; }
67
68 std::string str() const { return std::move(std::string(_p, _size)); }
69
70 bool empty() const { return _size == 0; }
71
72 bool operator==(Token other) const {
73 if (_size != other._size)
74 return false;
75 return compareMemory(_p, other._p, _size) == 0;
76 }
77
78 bool operator!=(Token other) const {
79 return !(*this == other);
80 }
81 };
82
83 ///
84 /// Simple text-based dynamic library file tokenizer.
85 ///
86 class Tokenizer {
87 const char* _start;
88 const char* _current;
89 const char* _end;
90 Token _currentToken;
91
92 void fetchNextToken();
93 void scanToNextToken();
94 void skip(unsigned distance) {
95 _current += distance;
96 assert(_current <= _end && "Skipped past the end");
97 }
98
99 const char* skipLineBreak(const char* pos) const;
100 bool isDelimiter(const char* pos) const;
101
102 public:
103 Tokenizer(const char* data, uint64_t size) : _start(data), _current(data), _end(data + size) {}
104
105 void reset() {
106 _current = _start;
107 fetchNextToken();
108 }
109
110 Token peek() { return _currentToken; }
111 Token next() {
112 Token token = peek();
113 fetchNextToken();
114 return token;
115 }
116 };
117
118 const char* Tokenizer::skipLineBreak(const char* pos) const
119 {
120 if ( pos == _end )
121 return pos;
122
123 // Carriage return.
124 if ( *pos == 0x0D ) {
125 // line feed.
126 if ( pos + 1 != _end && *(pos + 1) == 0x0A)
127 return pos + 2;
128 return pos + 1;
129 }
130
131 // line feed.
132 if ( *pos == 0x0A )
133 return pos + 1;
134
135 return pos;
136 }
137
138 void Tokenizer::scanToNextToken() {
139 while (true) {
140 while ( isDelimiter(_current) )
141 skip(1);
142
143 const char* i = skipLineBreak(_current);
144 if ( i == _current )
145 break;
146
147 _current = i;
148 }
149 }
150
151
152 bool Tokenizer::isDelimiter(const char* pos) const {
153 if ( pos == _end )
154 return false;
155 if ( *pos == ' ' || *pos == '\t' || *pos == '\r' || *pos == '\n' || *pos == ',' || *pos == ':' || *pos == '\'' || *pos == '\"' )
156 return true;
157 return false;
158 }
159
160 void Tokenizer::fetchNextToken() {
161 scanToNextToken();
162
163 if (_current == _end) {
164 _currentToken = Token();
165 return;
166 }
167
168 auto start = _current;
169 while ( !isDelimiter(_current) ) {
170 ++_current;
171 }
172
173 _currentToken = Token(start, _current - start);
174 }
175
176 ///
177 /// Representation of a parsed text-based dynamic library file.
178 ///
179 struct DynamicLibrary {
180 Token _installName;
181 uint32_t _currentVersion;
182 uint32_t _compatibilityVersion;
183 uint8_t _swiftVersion;
184 ld::File::ObjcConstraint _objcConstraint;
185 Options::Platform _platform;
186 std::vector<Token> _allowedClients;
187 std::vector<Token> _reexportedLibraries;
188 std::vector<Token> _symbols;
189 std::vector<Token> _classes;
190 std::vector<Token> _ivars;
191 std::vector<Token> _weakDefSymbols;
192 std::vector<Token> _tlvSymbols;
193
194 DynamicLibrary() : _currentVersion(0x10000), _compatibilityVersion(0x10000), _swiftVersion(0),
195 _objcConstraint(ld::File::objcConstraintNone) {}
196 };
197
198 static uint32_t parseVersionNumber32(Token token) {
199 if ( token.size() >= 128 )
200 throwf("malformed version number");
201
202 char buffer[128];
203 uint32_t x = 0;
204 uint32_t y = 0;
205 uint32_t z = 0;
206 char* end;
207
208 // Make a null-terminated string.
209 ::memcpy(buffer, token.data(), token.size());
210 buffer[token.size()] = '\0';
211
212 x = strtoul(buffer, &end, 10);
213 if ( *end == '.' ) {
214 y = strtoul(&end[1], &end, 10);
215 if ( *end == '.' ) {
216 z = strtoul(&end[1], &end, 10);
217 }
218 }
219 if ( (x > 0xffff) || (y > 0xff) || (z > 0xff) )
220 throwf("malformed 32-bit x.y.z version number: %s", buffer);
221
222 return (x << 16) | ( y << 8 ) | z;
223 }
224
225 ///
226 /// A simple text-based dynamic library file parser.
227 ///
228 class TBDFile {
229 Tokenizer _tokenizer;
230
231 Token peek() { return _tokenizer.peek(); }
232 Token next() { return _tokenizer.next(); }
233
234 void expectToken(Token str) {
235 Token token = next();
236 if (token != str)
237 throwf("unexpected token: %s", token.str().c_str());
238 }
239
240 bool hasOptionalToken(Token str) {
241 auto token = peek();
242 if ( token == str ) {
243 next();
244 return true;
245 }
246 return false;
247 }
248
249
250 void parseFlowSequence(std::function<void (Token)> func) {
251 expectToken("[");
252
253 while ( true ) {
254 auto token = peek();
255 if ( token == "]" )
256 break;
257
258 token = next();
259 func(token);
260 }
261
262 expectToken("]");
263 }
264
265 void parseAllowedClients(DynamicLibrary& lib) {
266 if ( !hasOptionalToken("allowed-clients") )
267 return;
268 parseFlowSequence([&](Token name) {
269 lib._allowedClients.emplace_back(name);
270 });
271 }
272
273 void parseReexportedDylibs(DynamicLibrary& lib) {
274 if ( !hasOptionalToken("re-exports") )
275 return;
276 parseFlowSequence([&](Token name) {
277 lib._reexportedLibraries.emplace_back(name);
278 });
279 }
280
281 void parseSymbols(DynamicLibrary& lib) {
282 if ( hasOptionalToken("symbols") ) {
283 parseFlowSequence([&](Token name) {
284 lib._symbols.emplace_back(name);
285 });
286 }
287
288 if ( hasOptionalToken("objc-classes") ) {
289 parseFlowSequence([&](Token name) {
290 lib._classes.emplace_back(name);
291 });
292 }
293
294 if ( hasOptionalToken("objc-ivars") ) {
295 parseFlowSequence([&](Token name) {
296 lib._ivars.emplace_back(name);
297 });
298 }
299
300 if ( hasOptionalToken("weak-def-symbols") ) {
301 parseFlowSequence([&](Token name) {
302 lib._weakDefSymbols.emplace_back(name);
303 });
304 }
305
306 if ( hasOptionalToken("thread-local-symbols") ) {
307 parseFlowSequence([&](Token name) {
308 lib._tlvSymbols.emplace_back(name);
309 });
310 }
311 }
312
313 bool parseArchFlowSequence(Token archName) {
314 expectToken("archs");
315
316 // <rdar://problem/22268737> x86_64h fails to link against text based stubs
317 if ( archName == "x86_64h" )
318 archName = "x86_64";
319
320 bool foundArch = false;
321 parseFlowSequence([&](Token name) {
322 if ( name == archName )
323 foundArch = true;
324 });
325
326 return foundArch;
327 }
328
329 void parsePlatform(DynamicLibrary& lib) {
330 expectToken("platform");
331
332 auto token = next();
333 if (token == "macosx")
334 lib._platform = Options::kPlatformOSX;
335 else if (token == "ios")
336 lib._platform = Options::kPlatformiOS;
337 else if (token == "watchos")
338 lib._platform = Options::kPlatformWatchOS;
339 #if SUPPORT_APPLE_TV
340 else if (token == "tvos")
341 lib._platform = Options::kPlatform_tvOS;
342 #endif
343 else
344 lib._platform = Options::kPlatformUnknown;
345 }
346
347 void parseInstallName(DynamicLibrary& lib) {
348 expectToken("install-name");
349
350 lib._installName = next();
351 if ( lib._installName.empty() )
352 throwf("no install name specified");
353 }
354
355 void parseCurrentVersion(DynamicLibrary& lib) {
356 if ( !hasOptionalToken("current-version") )
357 return;
358 lib._currentVersion = parseVersionNumber32(next());
359 }
360
361 void parseCompatibilityVersion(DynamicLibrary& lib) {
362 if ( !hasOptionalToken("compatibility-version") )
363 return;
364 lib._compatibilityVersion = parseVersionNumber32(next());
365 }
366
367 void parseSwiftVersion(DynamicLibrary& lib) {
368 if ( !hasOptionalToken("swift-version") )
369 return;
370 auto token = next();
371 if ( token == "1.0" )
372 lib._swiftVersion = 1;
373 else if ( token == "1.1" )
374 lib._swiftVersion = 2;
375 else if ( token == "2.0" )
376 lib._swiftVersion = 3;
377 else
378 throwf("unsupported Swift ABI version: %s", token.str().c_str());
379 }
380
381 void parseObjCConstraint(DynamicLibrary& lib) {
382 if ( !hasOptionalToken("objc-constraint") )
383 return;
384 auto token = next();
385 if ( token == "none" )
386 lib._objcConstraint = ld::File::objcConstraintNone;
387 else if ( token == "retain_release" )
388 lib._objcConstraint = ld::File::objcConstraintRetainRelease;
389 else if ( token == "retain_release_for_simulator" )
390 lib._objcConstraint = ld::File::objcConstraintRetainReleaseForSimulator;
391 else if ( token == "retain_release_or_gc" )
392 lib._objcConstraint = ld::File::objcConstraintRetainReleaseOrGC;
393 else if ( token == "gc" )
394 lib._objcConstraint = ld::File::objcConstraintGC;
395 else
396 throwf("unexpected token: %s", token.str().c_str());
397 }
398 void parseExportsBlock(DynamicLibrary& lib, Token archName) {
399 if ( !hasOptionalToken("exports") )
400 return;
401
402 if ( !hasOptionalToken("-") )
403 return;
404
405 while ( true ) {
406 if ( !parseArchFlowSequence(archName) ) {
407 Token token;
408 while ( true ) {
409 token = peek();
410 if ( token == "archs" || token == "..." || token.empty() )
411 break;
412 next();
413 }
414 if (token == "..." || token.empty() )
415 break;
416
417 continue;
418 }
419
420 parseAllowedClients(lib);
421 parseReexportedDylibs(lib);
422 parseSymbols(lib);
423 if ( !hasOptionalToken("-") )
424 break;
425 }
426 }
427
428 void parseDocument(DynamicLibrary& lib, Token archName) {
429 if ( !parseArchFlowSequence(archName) )
430 throwf("invalid arch");
431
432 parsePlatform(lib);
433 parseInstallName(lib);
434 parseCurrentVersion(lib);
435 parseCompatibilityVersion(lib);
436 parseSwiftVersion(lib);
437 parseObjCConstraint(lib);
438 parseExportsBlock(lib, archName);
439 }
440
441 public:
442 TBDFile(const char* data, uint64_t size) : _tokenizer(data, size) {}
443
444 DynamicLibrary parseFileForArch(Token archName) {
445 _tokenizer.reset();
446 DynamicLibrary lib;
447 expectToken("---");
448 parseDocument(lib, archName);
449 expectToken("...");
450 return std::move(lib);
451 }
452
453 bool validForArch(Token archName) {
454 _tokenizer.reset();
455 auto token = next();
456 if ( token != "---" )
457 return false;
458 return parseArchFlowSequence(archName);
459 }
460
461 void dumpTokens() {
462 _tokenizer.reset();
463 Token token;
464 do {
465 token = next();
466 printf("token: %s\n", token.str().c_str());
467 } while ( !token.empty() );
468 }
469 };
470
471 } // end anonymous namespace
472
473 namespace textstub {
474 namespace dylib {
475
476 // forward reference
477 template <typename A> class File;
478
479
480 //
481 // An ExportAtom has no content. It exists so that the linker can track which imported
482 // symbols came from which dynamic libraries.
483 //
484 template <typename A>
485 class ExportAtom : public ld::Atom
486 {
487 public:
488 ExportAtom(const File<A>& f, const char* nm, bool weakDef, bool tlv)
489 : ld::Atom(f._importProxySection, ld::Atom::definitionProxy,
490 (weakDef? ld::Atom::combineByName : ld::Atom::combineNever),
491 ld::Atom::scopeLinkageUnit,
492 (tlv ? ld::Atom::typeTLV : ld::Atom::typeUnclassified),
493 symbolTableNotIn, false, false, false, ld::Atom::Alignment(0)),
494 _file(f), _name(nm) {}
495 // overrides of ld::Atom
496 virtual const ld::File* file() const { return &_file; }
497 virtual const char* name() const { return _name; }
498 virtual uint64_t size() const { return 0; }
499 virtual uint64_t objectAddress() const { return 0; }
500 virtual void copyRawContent(uint8_t buffer[]) const { }
501 virtual void setScope(Scope) { }
502
503 protected:
504 typedef typename A::P P;
505 typedef typename A::P::uint_t pint_t;
506
507 virtual ~ExportAtom() {}
508
509 const File<A>& _file;
510 const char* _name;
511 };
512
513
514 //
515 // The reader for a dylib extracts all exported symbols names from the memory-mapped
516 // dylib, builds a hash table, then unmaps the file. This is an important memory
517 // savings for large dylibs.
518 //
519 template <typename A>
520 class File : public ld::dylib::File
521 {
522 public:
523 static bool validFile(const uint8_t* fileContent, bool executableOrDylib);
524 File(const uint8_t* fileContent, uint64_t fileLength, const char* path,
525 time_t mTime, ld::File::Ordinal ordinal, bool linkingFlatNamespace,
526 bool hoistImplicitPublicDylibs, Options::Platform platform,
527 cpu_type_t cpuType, const char* archName, uint32_t linkMinOSVersion,
528 bool allowSimToMacOSX, bool addVers, bool buildingForSimulator,
529 bool logAllFiles, const char* installPath, bool indirectDylib);
530 virtual ~File() {}
531
532 // overrides of ld::File
533 virtual bool forEachAtom(ld::File::AtomHandler&) const;
534 virtual bool justInTimeforEachAtom(const char* name, ld::File::AtomHandler&) const;
535 virtual ld::File::ObjcConstraint objCConstraint() const { return _objcConstraint; }
536 virtual uint8_t swiftVersion() const { return _swiftVersion; }
537
538 // overrides of ld::dylib::File
539 virtual void processIndirectLibraries(ld::dylib::File::DylibHandler*, bool);
540 virtual bool providedExportAtom() const { return _providedAtom; }
541 virtual const char* parentUmbrella() const { return nullptr; }
542 virtual const std::vector<const char*>* allowableClients() const { return _allowableClients.size() != 0 ? &_allowableClients : nullptr; }
543 virtual bool hasWeakExternals() const { return _hasWeakExports; }
544 virtual bool deadStrippable() const { return false; }
545 virtual bool hasPublicInstallName() const{ return _hasPublicInstallName; }
546 virtual bool hasWeakDefinition(const char* name) const;
547 virtual bool allSymbolsAreWeakImported() const;
548 virtual bool installPathVersionSpecific() const { return _installPathOverride; }
549 // All text-based stubs are per definition AppExtensionSafe.
550 virtual bool appExtensionSafe() const { return true; };
551 virtual ld::Bitcode* getBitcode() const { return _bitcode.get(); }
552
553
554 protected:
555 virtual void assertNoReExportCycles(ReExportChain*) const;
556
557 private:
558 typedef typename A::P P;
559 typedef typename A::P::E E;
560 typedef typename A::P::uint_t pint_t;
561
562 friend class ExportAtom<A>;
563
564 struct CStringHash {
565 std::size_t operator()(const char* __s) const {
566 unsigned long __h = 0;
567 for ( ; *__s; ++__s)
568 __h = 5 * __h + *__s;
569 return size_t(__h);
570 };
571 };
572 struct AtomAndWeak { ld::Atom* atom; bool weakDef; bool tlv; };
573 typedef std::unordered_map<const char*, AtomAndWeak, ld::CStringHash, ld::CStringEquals> NameToAtomMap;
574 typedef std::unordered_set<const char*, CStringHash, ld::CStringEquals> NameSet;
575
576 struct Dependent { const char* path; File<A>* dylib; };
577
578 virtual std::pair<bool, bool> hasWeakDefinitionImpl(const char* name) const;
579 virtual bool containsOrReExports(const char* name, bool& weakDef, bool& tlv, uint64_t& address) const;
580
581 void buildExportHashTable(const DynamicLibrary &lib);
582 bool isPublicLocation(const char* pth);
583 bool wrongOS() { return _wrongOS; }
584 void addSymbol(const char* name, bool weak, bool tlv);
585
586 const Options::Platform _platform;
587 cpu_type_t _cpuType;
588 const uint32_t _linkMinOSVersion;
589 const bool _allowSimToMacOSXLinking;
590 const bool _addVersionLoadCommand;
591 bool _linkingFlat;
592 bool _implicitlyLinkPublicDylibs;
593 ld::File::ObjcConstraint _objcConstraint;
594 uint8_t _swiftVersion;
595 ld::Section _importProxySection;
596 ld::Section _flatDummySection;
597 std::vector<Dependent> _dependentDylibs;
598 std::vector<const char*> _allowableClients;
599 mutable NameToAtomMap _atoms;
600 NameSet _ignoreExports;
601 bool _noRexports;
602 bool _hasWeakExports;
603 bool _hasPublicInstallName;
604 mutable bool _providedAtom;
605 bool _wrongOS;
606 bool _installPathOverride;
607 bool _indirectDylibsProcessed;
608 std::unique_ptr<ld::Bitcode> _bitcode;
609 static bool _s_logHashtable;
610 };
611
612 template <typename A>
613 bool File<A>::_s_logHashtable = false;
614
615
616 template <typename A>
617 File<A>::File(const uint8_t* fileContent, uint64_t fileLength, const char* path, time_t mTime,
618 ld::File::Ordinal ord, bool linkingFlatNamespace, bool hoistImplicitPublicDylibs,
619 Options::Platform platform, cpu_type_t cpuType, const char* archName,
620 uint32_t linkMinOSVersion, bool allowSimToMacOSX, bool addVers,
621 bool buildingForSimulator, bool logAllFiles, const char* targetInstallPath,
622 bool indirectDylib)
623 : ld::dylib::File(strdup(path), mTime, ord), _platform(platform), _cpuType(cpuType),
624 _linkMinOSVersion(linkMinOSVersion), _allowSimToMacOSXLinking(allowSimToMacOSX),
625 _addVersionLoadCommand(addVers), _linkingFlat(linkingFlatNamespace),
626 _implicitlyLinkPublicDylibs(hoistImplicitPublicDylibs),
627 _objcConstraint(ld::File::objcConstraintNone), _swiftVersion(0),
628 _importProxySection("__TEXT", "__import", ld::Section::typeImportProxies, true),
629 _flatDummySection("__LINKEDIT", "__flat_dummy", ld::Section::typeLinkEdit, true),
630 _noRexports(false), _hasWeakExports(false),
631 _hasPublicInstallName(false), _providedAtom(false), _wrongOS(false),
632 _installPathOverride(false), _indirectDylibsProcessed(false),
633 _bitcode(new ld::Bitcode(nullptr, 0))
634 {
635 // write out path for -t option
636 if ( logAllFiles )
637 printf("%s\n", path);
638
639 TBDFile stub((const char*)fileContent, fileLength);
640 auto lib = stub.parseFileForArch(archName);
641
642 _noRexports = lib._reexportedLibraries.empty();
643 _hasWeakExports = !lib._weakDefSymbols.empty();
644 _dylibInstallPath = strdup(lib._installName.str().c_str());
645 _dylibCurrentVersion = lib._currentVersion;
646 _dylibCompatibilityVersion = lib._compatibilityVersion;
647 _swiftVersion = lib._swiftVersion;
648 _objcConstraint = lib._objcConstraint;
649 _hasPublicInstallName = isPublicLocation(_dylibInstallPath);
650
651 for (auto &client : lib._allowedClients)
652 _allowableClients.push_back(strdup(client.str().c_str()));
653
654 // <rdar://problem/20659505> [TAPI] Don't hoist "public" (in /usr/lib/) dylibs that should not be directly linked
655 if ( !_allowableClients.empty() )
656 _hasPublicInstallName = false;
657
658 if ( (lib._platform != platform) && (platform != Options::kPlatformUnknown) ) {
659 _wrongOS = true;
660 if ( _addVersionLoadCommand && !indirectDylib ) {
661 if ( buildingForSimulator ) {
662 if ( !_allowSimToMacOSXLinking )
663 throwf("building for %s simulator, but linking against dylib built for %s (%s).",
664 Options::platformName(platform), Options::platformName(lib._platform), path);
665 } else {
666 throwf("building for %s, but linking against dylib built for %s (%s).",
667 Options::platformName(platform), Options::platformName(lib._platform), path);
668 }
669 }
670 }
671
672 _dependentDylibs.reserve(lib._reexportedLibraries.size());
673 for ( auto& reexport : lib._reexportedLibraries ) {
674 Dependent entry;
675 entry.path = strdup(reexport.str().c_str());
676 entry.dylib = nullptr;
677 if ( (targetInstallPath == nullptr) || (strcmp(targetInstallPath, entry.path) != 0) )
678 _dependentDylibs.push_back(entry);
679 }
680
681 // build hash table
682 buildExportHashTable(lib);
683
684 munmap((caddr_t)fileContent, fileLength);
685 }
686
687 template <typename A>
688 void File<A>::buildExportHashTable(const DynamicLibrary& lib) {
689 if ( _s_logHashtable )
690 fprintf(stderr, "ld: building hashtable from text-stub info in %s\n", this->path());
691
692 for (auto &sym : lib._symbols)
693 addSymbol(sym.str().c_str(), /*weak=*/false, /*tlv=*/false);
694
695 #if SUPPORT_ARCH_i386
696 if (_platform == Options::kPlatformOSX && _cpuType == CPU_TYPE_I386) {
697 for (auto &sym : lib._classes)
698 addSymbol((".objc_class_name" + sym.str()).c_str(), /*weak=*/false, /*tlv=*/false);
699 } else {
700 for (auto &sym : lib._classes) {
701 addSymbol(("_OBJC_CLASS_$" + sym.str()).c_str(), /*weak=*/false, /*tlv=*/false);
702 addSymbol(("_OBJC_METACLASS_$" + sym.str()).c_str(), /*weak=*/false, /*tlv=*/false);
703 }
704 }
705 #else
706 for (auto &sym : lib._classes) {
707 addSymbol(("_OBJC_CLASS_$" + sym.str()).c_str(), /*weak=*/false, /*tlv=*/false);
708 addSymbol(("_OBJC_METACLASS_$" + sym.str()).c_str(), /*weak=*/false, /*tlv=*/false);
709 }
710 #endif
711
712 for (auto &sym : lib._ivars)
713 addSymbol(("_OBJC_IVAR_$" + sym.str()).c_str(), /*weak=*/false, /*tlv=*/false);
714
715 for (auto &sym : lib._weakDefSymbols)
716 addSymbol(sym.str().c_str(), /*weak=*/true, /*tlv=*/false);
717
718 for (auto &sym : lib._tlvSymbols)
719 addSymbol(sym.str().c_str(), /*weak=*/false, /*tlv=*/true);
720 }
721
722
723 template <typename A>
724 void File<A>::addSymbol(const char* name, bool weakDef, bool tlv)
725 {
726 // symbols that start with $ld$ are meta-data to the static linker
727 // <rdar://problem/5182537> need way for ld and dyld to see different exported symbols in a dylib
728 if ( strncmp(name, "$ld$", 4) == 0 ) {
729 // $ld$ <action> $ <condition> $ <symbol-name>
730 const char* symAction = &name[4];
731 const char* symCond = strchr(symAction, '$');
732 if ( symCond != nullptr ) {
733 char curOSVers[16];
734 sprintf(curOSVers, "$os%d.%d$", (_linkMinOSVersion >> 16), ((_linkMinOSVersion >> 8) & 0xFF));
735 if ( strncmp(symCond, curOSVers, strlen(curOSVers)) == 0 ) {
736 const char* symName = strchr(&symCond[1], '$');
737 if ( symName != nullptr ) {
738 ++symName;
739 if ( strncmp(symAction, "hide$", 5) == 0 ) {
740 if ( _s_logHashtable )
741 fprintf(stderr, " adding %s to ignore set for %s\n", symName, this->path());
742 _ignoreExports.insert(strdup(symName));
743 return;
744 }
745 else if ( strncmp(symAction, "add$", 4) == 0 ) {
746 this->addSymbol(symName, weakDef, false);
747 return;
748 }
749 else if ( strncmp(symAction, "install_name$", 13) == 0 ) {
750 _dylibInstallPath = strdup(symName);
751 _installPathOverride = true;
752 return;
753 }
754 else if ( strncmp(symAction, "compatibility_version$", 22) == 0 ) {
755 _dylibCompatibilityVersion = parseVersionNumber32(symName);
756 return;
757 }
758 else {
759 warning("bad symbol action: %s in dylib %s", name, this->path());
760 }
761 }
762 }
763 }
764 else {
765 warning("bad symbol condition: %s in dylib %s", name, this->path());
766 }
767 }
768
769 // add symbol as possible export if we are not supposed to ignore it
770 if ( _ignoreExports.count(name) == 0 ) {
771 AtomAndWeak bucket;
772 bucket.atom = nullptr;
773 bucket.weakDef = weakDef;
774 bucket.tlv = tlv;
775 if ( _s_logHashtable )
776 fprintf(stderr, " adding %s to hash table for %s\n", name, this->path());
777 _atoms[strdup(name)] = bucket;
778 }
779 }
780
781
782 template <typename A>
783 bool File<A>::forEachAtom(ld::File::AtomHandler& handler) const
784 {
785 handler.doFile(*this);
786 return false;
787 }
788
789
790 template <typename A>
791 std::pair<bool, bool> File<A>::hasWeakDefinitionImpl(const char* name) const
792 {
793 const auto pos = _atoms.find(name);
794 if ( pos != _atoms.end() )
795 return std::make_pair(true, pos->second.weakDef);
796
797 // look in children that I re-export
798 for (const auto &dep : _dependentDylibs) {
799 auto ret = dep.dylib->hasWeakDefinitionImpl(name);
800 if ( ret.first )
801 return ret;
802 }
803 return std::make_pair(false, false);
804 }
805
806
807 template <typename A>
808 bool File<A>::hasWeakDefinition(const char* name) const
809 {
810 // if supposed to ignore this export, then pretend I don't have it
811 if ( _ignoreExports.count(name) != 0 )
812 return false;
813
814 return hasWeakDefinitionImpl(name).second;
815 }
816
817
818 // <rdar://problem/5529626> If only weak_import symbols are used, linker should use LD_LOAD_WEAK_DYLIB
819 template <typename A>
820 bool File<A>::allSymbolsAreWeakImported() const
821 {
822 bool foundNonWeakImport = false;
823 bool foundWeakImport = false;
824 for (const auto &it : _atoms) {
825 const ld::Atom* atom = it.second.atom;
826 if ( atom != nullptr ) {
827 if ( atom->weakImported() )
828 foundWeakImport = true;
829 else
830 foundNonWeakImport = true;
831 }
832 }
833
834 // don't automatically weak link dylib with no imports
835 // so at least one weak import symbol and no non-weak-imported symbols must be found
836 return foundWeakImport && !foundNonWeakImport;
837 }
838
839
840 template <typename A>
841 bool File<A>::containsOrReExports(const char* name, bool& weakDef, bool& tlv, uint64_t& addr) const
842 {
843 if ( _ignoreExports.count(name) != 0 )
844 return false;
845
846 // check myself
847 const auto pos = _atoms.find(name);
848 if ( pos != _atoms.end() ) {
849 weakDef = pos->second.weakDef;
850 tlv = pos->second.tlv;
851 addr = 0;
852 return true;
853 }
854
855 // check dylibs I re-export
856 for (const auto& lib : _dependentDylibs) {
857 if ( !lib.dylib->implicitlyLinked() ) {
858 if ( lib.dylib->containsOrReExports(name, weakDef, tlv, addr) )
859 return true;
860 }
861 }
862
863 return false;
864 }
865
866
867 template <typename A>
868 bool File<A>::justInTimeforEachAtom(const char* name, ld::File::AtomHandler& handler) const
869 {
870 // if supposed to ignore this export, then pretend I don't have it
871 if ( _ignoreExports.count(name) != 0 )
872 return false;
873
874
875 AtomAndWeak bucket;
876 uint64_t addr;
877 if ( this->containsOrReExports(name, bucket.weakDef, bucket.tlv, addr) ) {
878 bucket.atom = new ExportAtom<A>(*this, name, bucket.weakDef, bucket.tlv);
879 _atoms[name] = bucket;
880 _providedAtom = true;
881 if ( _s_logHashtable )
882 fprintf(stderr, "getJustInTimeAtomsFor: %s found in %s\n", name, this->path());
883 // call handler with new export atom
884 handler.doAtom(*bucket.atom);
885 return true;
886 }
887
888 return false;
889 }
890
891
892
893 template <typename A>
894 bool File<A>::isPublicLocation(const char* path)
895 {
896 // -no_implicit_dylibs disables this optimization
897 if ( ! _implicitlyLinkPublicDylibs )
898 return false;
899
900 // /usr/lib is a public location
901 if ( (strncmp(path, "/usr/lib/", 9) == 0) && (strchr(&path[9], '/') == nullptr) )
902 return true;
903
904 // /System/Library/Frameworks/ is a public location
905 if ( strncmp(path, "/System/Library/Frameworks/", 27) == 0 ) {
906 const char* frameworkDot = strchr(&path[27], '.');
907 // but only top level framework
908 // /System/Library/Frameworks/Foo.framework/Versions/A/Foo ==> true
909 // /System/Library/Frameworks/Foo.framework/Resources/libBar.dylib ==> false
910 // /System/Library/Frameworks/Foo.framework/Frameworks/Bar.framework/Bar ==> false
911 // /System/Library/Frameworks/Foo.framework/Frameworks/Xfoo.framework/XFoo ==> false
912 if ( frameworkDot != nullptr ) {
913 int frameworkNameLen = frameworkDot - &path[27];
914 if ( strncmp(&path[strlen(path)-frameworkNameLen-1], &path[26], frameworkNameLen+1) == 0 )
915 return true;
916 }
917 }
918
919 return false;
920 }
921
922 template <typename A>
923 void File<A>::processIndirectLibraries(ld::dylib::File::DylibHandler* handler, bool addImplicitDylibs)
924 {
925 // only do this once
926 if ( _indirectDylibsProcessed )
927 return;
928
929 const static bool log = false;
930 if ( log ) fprintf(stderr, "processIndirectLibraries(%s)\n", this->installPath());
931 if ( _linkingFlat ) {
932 for (auto& lib : _dependentDylibs) {
933 lib.dylib = (File<A>*)handler->findDylib(lib.path, this->path());
934 }
935 }
936 else if ( _noRexports ) {
937 // MH_NO_REEXPORTED_DYLIBS bit set, then nothing to do
938 }
939 else {
940 // two-level, might have re-exports
941 for (auto& lib : _dependentDylibs) {
942 if ( log )
943 fprintf(stderr, "processIndirectLibraries() parent=%s, child=%s\n", this->installPath(), lib.path);
944 // a LC_REEXPORT_DYLIB, LC_SUB_UMBRELLA or LC_SUB_LIBRARY says we re-export this child
945 lib.dylib = (File<A>*)handler->findDylib(lib.path, this->path());
946 if ( lib.dylib->hasPublicInstallName() && !lib.dylib->wrongOS() ) {
947 // promote this child to be automatically added as a direct dependent if this already is
948 if ( (this->explicitlyLinked() || this->implicitlyLinked()) && (strcmp(lib.path, lib.dylib->installPath()) == 0) ) {
949 if ( log )
950 fprintf(stderr, "processIndirectLibraries() implicitly linking %s\n", lib.dylib->installPath());
951 lib.dylib->setImplicitlyLinked();
952 }
953 else if ( lib.dylib->explicitlyLinked() || lib.dylib->implicitlyLinked() ) {
954 if ( log )
955 fprintf(stderr, "processIndirectLibraries() parent is not directly linked, but child is, so no need to re-export child\n");
956 } else {
957 if ( log )
958 fprintf(stderr, "processIndirectLibraries() parent is not directly linked, so parent=%s will re-export child=%s\n", this->installPath(), lib.path);
959 }
960 } else {
961 // add all child's symbols to me
962 if ( log )
963 fprintf(stderr, "processIndirectLibraries() child is not public, so parent=%s will re-export child=%s\n", this->installPath(), lib.path);
964 }
965 }
966 }
967
968 // check for re-export cycles
969 ReExportChain chain;
970 chain.prev = nullptr;
971 chain.file = this;
972 this->assertNoReExportCycles(&chain);
973
974 _indirectDylibsProcessed = true;
975 }
976
977 template <typename A>
978 void File<A>::assertNoReExportCycles(ReExportChain* prev) const
979 {
980 // recursively check my re-exported dylibs
981 ReExportChain chain;
982 chain.prev = prev;
983 chain.file = this;
984 for (const auto& dep : _dependentDylibs) {
985 ld::File* child = dep.dylib;
986 // check child is not already in chain
987 for (ReExportChain* p = prev; p != nullptr; p = p->prev) {
988 if ( p->file == child )
989 throwf("cycle in dylib re-exports with %s and %s", child->path(), this->path());
990 }
991 if ( dep.dylib != nullptr )
992 dep.dylib->assertNoReExportCycles(&chain);
993 }
994 }
995
996
997 template <typename A>
998 class Parser
999 {
1000 public:
1001 typedef typename A::P P;
1002
1003 static bool validFile(const uint8_t* fileContent, uint64_t fileLength, const std::string &path, const char* archName);
1004 static ld::dylib::File* parse(const uint8_t* fileContent, uint64_t fileLength, const char* path,
1005 time_t mTime, ld::File::Ordinal ordinal, const Options& opts,
1006 bool indirectDylib) {
1007 return new File<A>(fileContent, fileLength, path, mTime, ordinal,
1008 opts.flatNamespace(),
1009 opts.implicitlyLinkIndirectPublicDylibs(),
1010 opts.platform(),
1011 opts.architecture(),
1012 opts.architectureName(),
1013 opts.minOSversion(),
1014 opts.allowSimulatorToLinkWithMacOSX(),
1015 opts.addVersionLoadCommand(),
1016 opts.targetIOSSimulator(),
1017 opts.logAllFiles(),
1018 opts.installPath(),
1019 indirectDylib);
1020 }
1021 };
1022
1023 template <typename A>
1024 bool Parser<A>::validFile(const uint8_t* fileContent, uint64_t fileLength, const std::string &path, const char* archName)
1025 {
1026 if ( path.find(".tbd", path.size()-4) == std::string::npos )
1027 return false;
1028
1029 TBDFile stub((const char*)fileContent, fileLength);
1030 if ( !stub.validForArch(archName) )
1031 throwf("missing required architecture %s in file %s", archName, path.c_str());
1032
1033 return true;
1034 }
1035
1036 //
1037 // main function used by linker to instantiate ld::Files
1038 //
1039 ld::dylib::File* parse(const uint8_t* fileContent, uint64_t fileLength, const char* path,
1040 time_t modTime, const Options& opts, ld::File::Ordinal ordinal,
1041 bool bundleLoader, bool indirectDylib)
1042 {
1043 switch ( opts.architecture() ) {
1044 #if SUPPORT_ARCH_x86_64
1045 case CPU_TYPE_X86_64:
1046 if ( Parser<x86_64>::validFile(fileContent, fileLength, path, opts.architectureName()) )
1047 return Parser<x86_64>::parse(fileContent, fileLength, path, modTime, ordinal, opts, indirectDylib);
1048 break;
1049 #endif
1050 #if SUPPORT_ARCH_i386
1051 case CPU_TYPE_I386:
1052 if ( Parser<x86>::validFile(fileContent, fileLength, path, opts.architectureName()) )
1053 return Parser<x86>::parse(fileContent, fileLength, path, modTime, ordinal, opts, indirectDylib);
1054 break;
1055 #endif
1056 #if SUPPORT_ARCH_arm_any
1057 case CPU_TYPE_ARM:
1058 if ( Parser<arm>::validFile(fileContent, fileLength, path, opts.architectureName()) )
1059 return Parser<arm>::parse(fileContent, fileLength, path, modTime, ordinal, opts, indirectDylib);
1060 break;
1061 #endif
1062 #if SUPPORT_ARCH_arm64
1063 case CPU_TYPE_ARM64:
1064 if ( Parser<arm64>::validFile(fileContent, fileLength, path, opts.architectureName()) )
1065 return Parser<arm64>::parse(fileContent, fileLength, path, modTime, ordinal, opts, indirectDylib);
1066 break;
1067 #endif
1068 }
1069 return nullptr;
1070 }
1071
1072
1073 } // namespace dylib
1074 } // namespace textstub
1075
1076