]> git.saurik.com Git - apple/ld64.git/blame - src/ld/parsers/textstub_dylib_file.cpp
ld64-253.3.tar.gz
[apple/ld64.git] / src / ld / parsers / textstub_dylib_file.cpp
CommitLineData
eaf282aa
A
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
37namespace {
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///
44class 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
54public:
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///
86class 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
102public:
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
118const 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
138void 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
152bool 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
160void 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///
179struct 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
198static 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///
228class 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 bool foundArch = false;
317 parseFlowSequence([&](Token name) {
318 if ( name == archName )
319 foundArch = true;
320 });
321
322 return foundArch;
323 }
324
325 void parsePlatform(DynamicLibrary& lib) {
326 expectToken("platform");
327
328 auto token = next();
329 if (token == "macosx")
330 lib._platform = Options::kPlatformOSX;
331 else if (token == "ios")
332 lib._platform = Options::kPlatformiOS;
333 else if (token == "watchos")
334 lib._platform = Options::kPlatformWatchOS;
335#if SUPPORT_APPLE_TV
336 else if (token == "tvos")
337 lib._platform = Options::kPlatform_tvOS;
338#endif
339 else
340 lib._platform = Options::kPlatformUnknown;
341 }
342
343 void parseInstallName(DynamicLibrary& lib) {
344 expectToken("install-name");
345
346 lib._installName = next();
347 if ( lib._installName.empty() )
348 throwf("no install name specified");
349 }
350
351 void parseCurrentVersion(DynamicLibrary& lib) {
352 if ( !hasOptionalToken("current-version") )
353 return;
354 lib._currentVersion = parseVersionNumber32(next());
355 }
356
357 void parseCompatibilityVersion(DynamicLibrary& lib) {
358 if ( !hasOptionalToken("compatibility-version") )
359 return;
360 lib._compatibilityVersion = parseVersionNumber32(next());
361 }
362
363 void parseSwiftVersion(DynamicLibrary& lib) {
364 if ( !hasOptionalToken("swift-version") )
365 return;
366 auto token = next();
367 if ( token == "1.0" )
368 lib._swiftVersion = 1;
369 else if ( token == "1.1" )
370 lib._swiftVersion = 2;
371 else if ( token == "2.0" )
372 lib._swiftVersion = 3;
373 else
374 throwf("unsupported Swift ABI version: %s", token.str().c_str());
375 }
376
377 void parseObjCConstraint(DynamicLibrary& lib) {
378 if ( !hasOptionalToken("objc-constraint") )
379 return;
380 auto token = next();
381 if ( token == "none" )
382 lib._objcConstraint = ld::File::objcConstraintNone;
383 else if ( token == "retain_release" )
384 lib._objcConstraint = ld::File::objcConstraintRetainRelease;
385 else if ( token == "retain_release_for_simulator" )
386 lib._objcConstraint = ld::File::objcConstraintRetainReleaseForSimulator;
387 else if ( token == "retain_release_or_gc" )
388 lib._objcConstraint = ld::File::objcConstraintRetainReleaseOrGC;
389 else if ( token == "gc" )
390 lib._objcConstraint = ld::File::objcConstraintGC;
391 else
392 throwf("unexpected token: %s", token.str().c_str());
393 }
394 void parseExportsBlock(DynamicLibrary& lib, Token archName) {
395 if ( !hasOptionalToken("exports") )
396 return;
397
398 if ( !hasOptionalToken("-") )
399 return;
400
401 while ( true ) {
402 if ( !parseArchFlowSequence(archName) ) {
403 Token token;
404 while ( true ) {
405 token = peek();
406 if ( token == "archs" || token == "..." || token.empty() )
407 break;
408 next();
409 }
410 if (token == "..." || token.empty() )
411 break;
412
413 continue;
414 }
415
416 parseAllowedClients(lib);
417 parseReexportedDylibs(lib);
418 parseSymbols(lib);
419 if ( !hasOptionalToken("-") )
420 break;
421 }
422 }
423
424 void parseDocument(DynamicLibrary& lib, Token archName) {
425 if ( !parseArchFlowSequence(archName) )
426 throwf("invalid arch");
427
428 parsePlatform(lib);
429 parseInstallName(lib);
430 parseCurrentVersion(lib);
431 parseCompatibilityVersion(lib);
432 parseSwiftVersion(lib);
433 parseObjCConstraint(lib);
434 parseExportsBlock(lib, archName);
435 }
436
437public:
438 TBDFile(const char* data, uint64_t size) : _tokenizer(data, size) {}
439
440 DynamicLibrary parseFileForArch(Token archName) {
441 _tokenizer.reset();
442 DynamicLibrary lib;
443 expectToken("---");
444 parseDocument(lib, archName);
445 expectToken("...");
446 return std::move(lib);
447 }
448
449 bool validForArch(Token archName) {
450 _tokenizer.reset();
451 auto token = next();
452 if ( token != "---" )
453 return false;
454 return parseArchFlowSequence(archName);
455 }
456
457 void dumpTokens() {
458 _tokenizer.reset();
459 Token token;
460 do {
461 token = next();
462 printf("token: %s\n", token.str().c_str());
463 } while ( !token.empty() );
464 }
465};
466
467} // end anonymous namespace
468
469namespace textstub {
470namespace dylib {
471
472// forward reference
473template <typename A> class File;
474
475
476//
477// An ExportAtom has no content. It exists so that the linker can track which imported
478// symbols came from which dynamic libraries.
479//
480template <typename A>
481class ExportAtom : public ld::Atom
482{
483public:
484 ExportAtom(const File<A>& f, const char* nm, bool weakDef, bool tlv)
485 : ld::Atom(f._importProxySection, ld::Atom::definitionProxy,
486 (weakDef? ld::Atom::combineByName : ld::Atom::combineNever),
487 ld::Atom::scopeLinkageUnit,
488 (tlv ? ld::Atom::typeTLV : ld::Atom::typeUnclassified),
489 symbolTableNotIn, false, false, false, ld::Atom::Alignment(0)),
490 _file(f), _name(nm) {}
491 // overrides of ld::Atom
492 virtual const ld::File* file() const { return &_file; }
493 virtual const char* name() const { return _name; }
494 virtual uint64_t size() const { return 0; }
495 virtual uint64_t objectAddress() const { return 0; }
496 virtual void copyRawContent(uint8_t buffer[]) const { }
497 virtual void setScope(Scope) { }
498
499protected:
500 typedef typename A::P P;
501 typedef typename A::P::uint_t pint_t;
502
503 virtual ~ExportAtom() {}
504
505 const File<A>& _file;
506 const char* _name;
507};
508
509
510//
511// The reader for a dylib extracts all exported symbols names from the memory-mapped
512// dylib, builds a hash table, then unmaps the file. This is an important memory
513// savings for large dylibs.
514//
515template <typename A>
516class File : public ld::dylib::File
517{
518public:
519 static bool validFile(const uint8_t* fileContent, bool executableOrDylib);
520 File(const uint8_t* fileContent, uint64_t fileLength, const char* path,
521 time_t mTime, ld::File::Ordinal ordinal, bool linkingFlatNamespace,
522 bool hoistImplicitPublicDylibs, Options::Platform platform,
523 cpu_type_t cpuType, const char* archName, uint32_t linkMinOSVersion,
524 bool allowSimToMacOSX, bool addVers, bool buildingForSimulator,
525 bool logAllFiles, const char* installPath, bool indirectDylib);
526 virtual ~File() {}
527
528 // overrides of ld::File
529 virtual bool forEachAtom(ld::File::AtomHandler&) const;
530 virtual bool justInTimeforEachAtom(const char* name, ld::File::AtomHandler&) const;
531 virtual ld::File::ObjcConstraint objCConstraint() const { return _objcConstraint; }
532 virtual uint8_t swiftVersion() const { return _swiftVersion; }
533
534 // overrides of ld::dylib::File
535 virtual void processIndirectLibraries(ld::dylib::File::DylibHandler*, bool);
536 virtual bool providedExportAtom() const { return _providedAtom; }
537 virtual const char* parentUmbrella() const { return nullptr; }
538 virtual const std::vector<const char*>* allowableClients() const { return _allowableClients.size() != 0 ? &_allowableClients : nullptr; }
539 virtual bool hasWeakExternals() const { return _hasWeakExports; }
540 virtual bool deadStrippable() const { return false; }
541 virtual bool hasPublicInstallName() const{ return _hasPublicInstallName; }
542 virtual bool hasWeakDefinition(const char* name) const;
543 virtual bool allSymbolsAreWeakImported() const;
544 virtual bool installPathVersionSpecific() const { return _installPathOverride; }
545 // All text-based stubs are per definition AppExtensionSafe.
546 virtual bool appExtensionSafe() const { return true; };
547 virtual ld::Bitcode* getBitcode() const { return _bitcode.get(); }
548
549
550protected:
551 virtual void assertNoReExportCycles(ReExportChain*) const;
552
553private:
554 typedef typename A::P P;
555 typedef typename A::P::E E;
556 typedef typename A::P::uint_t pint_t;
557
558 friend class ExportAtom<A>;
559
560 struct CStringHash {
561 std::size_t operator()(const char* __s) const {
562 unsigned long __h = 0;
563 for ( ; *__s; ++__s)
564 __h = 5 * __h + *__s;
565 return size_t(__h);
566 };
567 };
568 struct AtomAndWeak { ld::Atom* atom; bool weakDef; bool tlv; };
569 typedef std::unordered_map<const char*, AtomAndWeak, ld::CStringHash, ld::CStringEquals> NameToAtomMap;
570 typedef std::unordered_set<const char*, CStringHash, ld::CStringEquals> NameSet;
571
572 struct Dependent { const char* path; File<A>* dylib; };
573
574 virtual std::pair<bool, bool> hasWeakDefinitionImpl(const char* name) const;
575 virtual bool containsOrReExports(const char* name, bool& weakDef, bool& tlv, uint64_t& address) const;
576
577 void buildExportHashTable(const DynamicLibrary &lib);
578 bool isPublicLocation(const char* pth);
579 bool wrongOS() { return _wrongOS; }
580 void addSymbol(const char* name, bool weak, bool tlv);
581
582 const Options::Platform _platform;
583 cpu_type_t _cpuType;
584 const uint32_t _linkMinOSVersion;
585 const bool _allowSimToMacOSXLinking;
586 const bool _addVersionLoadCommand;
587 bool _linkingFlat;
588 bool _implicitlyLinkPublicDylibs;
589 ld::File::ObjcConstraint _objcConstraint;
590 uint8_t _swiftVersion;
591 ld::Section _importProxySection;
592 ld::Section _flatDummySection;
593 std::vector<Dependent> _dependentDylibs;
594 std::vector<const char*> _allowableClients;
595 mutable NameToAtomMap _atoms;
596 NameSet _ignoreExports;
597 bool _noRexports;
598 bool _hasWeakExports;
599 bool _hasPublicInstallName;
600 mutable bool _providedAtom;
601 bool _wrongOS;
602 bool _installPathOverride;
603 bool _indirectDylibsProcessed;
604 std::unique_ptr<ld::Bitcode> _bitcode;
605 static bool _s_logHashtable;
606};
607
608template <typename A>
609bool File<A>::_s_logHashtable = false;
610
611
612template <typename A>
613File<A>::File(const uint8_t* fileContent, uint64_t fileLength, const char* path, time_t mTime,
614 ld::File::Ordinal ord, bool linkingFlatNamespace, bool hoistImplicitPublicDylibs,
615 Options::Platform platform, cpu_type_t cpuType, const char* archName,
616 uint32_t linkMinOSVersion, bool allowSimToMacOSX, bool addVers,
617 bool buildingForSimulator, bool logAllFiles, const char* targetInstallPath,
618 bool indirectDylib)
619 : ld::dylib::File(strdup(path), mTime, ord), _platform(platform), _cpuType(cpuType),
620 _linkMinOSVersion(linkMinOSVersion), _allowSimToMacOSXLinking(allowSimToMacOSX),
621 _addVersionLoadCommand(addVers), _linkingFlat(linkingFlatNamespace),
622 _implicitlyLinkPublicDylibs(hoistImplicitPublicDylibs),
623 _objcConstraint(ld::File::objcConstraintNone), _swiftVersion(0),
624 _importProxySection("__TEXT", "__import", ld::Section::typeImportProxies, true),
625 _flatDummySection("__LINKEDIT", "__flat_dummy", ld::Section::typeLinkEdit, true),
626 _noRexports(false), _hasWeakExports(false),
627 _hasPublicInstallName(false), _providedAtom(false), _wrongOS(false),
628 _installPathOverride(false), _indirectDylibsProcessed(false),
629 _bitcode(new ld::Bitcode(nullptr, 0))
630{
631 // write out path for -t option
632 if ( logAllFiles )
633 printf("%s\n", path);
634
635 TBDFile stub((const char*)fileContent, fileLength);
636 auto lib = stub.parseFileForArch(archName);
637
638 _noRexports = lib._reexportedLibraries.empty();
639 _hasWeakExports = !lib._weakDefSymbols.empty();
640 _dylibInstallPath = strdup(lib._installName.str().c_str());
641 _dylibCurrentVersion = lib._currentVersion;
642 _dylibCompatibilityVersion = lib._compatibilityVersion;
643 _swiftVersion = lib._swiftVersion;
644 _objcConstraint = lib._objcConstraint;
645 _hasPublicInstallName = isPublicLocation(_dylibInstallPath);
646
647 for (auto &client : lib._allowedClients)
648 _allowableClients.push_back(strdup(client.str().c_str()));
649
650 // <rdar://problem/20659505> [TAPI] Don't hoist "public" (in /usr/lib/) dylibs that should not be directly linked
651 if ( !_allowableClients.empty() )
652 _hasPublicInstallName = false;
653
654 if ( (lib._platform != platform) && (platform != Options::kPlatformUnknown) ) {
655 _wrongOS = true;
656 if ( _addVersionLoadCommand && !indirectDylib ) {
657 if ( buildingForSimulator ) {
658 if ( !_allowSimToMacOSXLinking )
659 throwf("building for %s simulator, but linking against dylib built for %s (%s).",
660 Options::platformName(platform), Options::platformName(lib._platform), path);
661 } else {
662 throwf("building for %s, but linking against dylib built for %s (%s).",
663 Options::platformName(platform), Options::platformName(lib._platform), path);
664 }
665 }
666 }
667
668 _dependentDylibs.reserve(lib._reexportedLibraries.size());
669 for ( auto& reexport : lib._reexportedLibraries ) {
670 Dependent entry;
671 entry.path = strdup(reexport.str().c_str());
672 entry.dylib = nullptr;
673 if ( (targetInstallPath == nullptr) || (strcmp(targetInstallPath, entry.path) != 0) )
674 _dependentDylibs.push_back(entry);
675 }
676
677 // build hash table
678 buildExportHashTable(lib);
679
680 munmap((caddr_t)fileContent, fileLength);
681}
682
683template <typename A>
684void File<A>::buildExportHashTable(const DynamicLibrary& lib) {
685 if ( _s_logHashtable )
686 fprintf(stderr, "ld: building hashtable from text-stub info in %s\n", this->path());
687
688 for (auto &sym : lib._symbols)
689 addSymbol(sym.str().c_str(), /*weak=*/false, /*tlv=*/false);
690
691#if SUPPORT_ARCH_i386
692 if (_platform == Options::kPlatformOSX && _cpuType == CPU_TYPE_I386) {
693 for (auto &sym : lib._classes)
694 addSymbol((".objc_class_name" + sym.str()).c_str(), /*weak=*/false, /*tlv=*/false);
695 } else {
696 for (auto &sym : lib._classes) {
697 addSymbol(("_OBJC_CLASS_$" + sym.str()).c_str(), /*weak=*/false, /*tlv=*/false);
698 addSymbol(("_OBJC_METACLASS_$" + sym.str()).c_str(), /*weak=*/false, /*tlv=*/false);
699 }
700 }
701#else
702 for (auto &sym : lib._classes) {
703 addSymbol(("_OBJC_CLASS_$" + sym.str()).c_str(), /*weak=*/false, /*tlv=*/false);
704 addSymbol(("_OBJC_METACLASS_$" + sym.str()).c_str(), /*weak=*/false, /*tlv=*/false);
705 }
706#endif
707
708 for (auto &sym : lib._ivars)
709 addSymbol(("_OBJC_IVAR_$" + sym.str()).c_str(), /*weak=*/false, /*tlv=*/false);
710
711 for (auto &sym : lib._weakDefSymbols)
712 addSymbol(sym.str().c_str(), /*weak=*/true, /*tlv=*/false);
713
714 for (auto &sym : lib._tlvSymbols)
715 addSymbol(sym.str().c_str(), /*weak=*/false, /*tlv=*/true);
716}
717
718
719template <typename A>
720void File<A>::addSymbol(const char* name, bool weakDef, bool tlv)
721{
722 // symbols that start with $ld$ are meta-data to the static linker
723 // <rdar://problem/5182537> need way for ld and dyld to see different exported symbols in a dylib
724 if ( strncmp(name, "$ld$", 4) == 0 ) {
725 // $ld$ <action> $ <condition> $ <symbol-name>
726 const char* symAction = &name[4];
727 const char* symCond = strchr(symAction, '$');
728 if ( symCond != nullptr ) {
729 char curOSVers[16];
730 sprintf(curOSVers, "$os%d.%d$", (_linkMinOSVersion >> 16), ((_linkMinOSVersion >> 8) & 0xFF));
731 if ( strncmp(symCond, curOSVers, strlen(curOSVers)) == 0 ) {
732 const char* symName = strchr(&symCond[1], '$');
733 if ( symName != nullptr ) {
734 ++symName;
735 if ( strncmp(symAction, "hide$", 5) == 0 ) {
736 if ( _s_logHashtable )
737 fprintf(stderr, " adding %s to ignore set for %s\n", symName, this->path());
738 _ignoreExports.insert(strdup(symName));
739 return;
740 }
741 else if ( strncmp(symAction, "add$", 4) == 0 ) {
742 this->addSymbol(symName, weakDef, false);
743 return;
744 }
745 else if ( strncmp(symAction, "install_name$", 13) == 0 ) {
746 _dylibInstallPath = strdup(symName);
747 _installPathOverride = true;
748 return;
749 }
750 else if ( strncmp(symAction, "compatibility_version$", 22) == 0 ) {
751 _dylibCompatibilityVersion = parseVersionNumber32(symName);
752 return;
753 }
754 else {
755 warning("bad symbol action: %s in dylib %s", name, this->path());
756 }
757 }
758 }
759 }
760 else {
761 warning("bad symbol condition: %s in dylib %s", name, this->path());
762 }
763 }
764
765 // add symbol as possible export if we are not supposed to ignore it
766 if ( _ignoreExports.count(name) == 0 ) {
767 AtomAndWeak bucket;
768 bucket.atom = nullptr;
769 bucket.weakDef = weakDef;
770 bucket.tlv = tlv;
771 if ( _s_logHashtable )
772 fprintf(stderr, " adding %s to hash table for %s\n", name, this->path());
773 _atoms[strdup(name)] = bucket;
774 }
775}
776
777
778template <typename A>
779bool File<A>::forEachAtom(ld::File::AtomHandler& handler) const
780{
781 handler.doFile(*this);
782 return false;
783}
784
785
786template <typename A>
787std::pair<bool, bool> File<A>::hasWeakDefinitionImpl(const char* name) const
788{
789 const auto pos = _atoms.find(name);
790 if ( pos != _atoms.end() )
791 return std::make_pair(true, pos->second.weakDef);
792
793 // look in children that I re-export
794 for (const auto &dep : _dependentDylibs) {
795 auto ret = dep.dylib->hasWeakDefinitionImpl(name);
796 if ( ret.first )
797 return ret;
798 }
799 return std::make_pair(false, false);
800}
801
802
803template <typename A>
804bool File<A>::hasWeakDefinition(const char* name) const
805{
806 // if supposed to ignore this export, then pretend I don't have it
807 if ( _ignoreExports.count(name) != 0 )
808 return false;
809
810 return hasWeakDefinitionImpl(name).second;
811}
812
813
814// <rdar://problem/5529626> If only weak_import symbols are used, linker should use LD_LOAD_WEAK_DYLIB
815template <typename A>
816bool File<A>::allSymbolsAreWeakImported() const
817{
818 bool foundNonWeakImport = false;
819 bool foundWeakImport = false;
820 for (const auto &it : _atoms) {
821 const ld::Atom* atom = it.second.atom;
822 if ( atom != nullptr ) {
823 if ( atom->weakImported() )
824 foundWeakImport = true;
825 else
826 foundNonWeakImport = true;
827 }
828 }
829
830 // don't automatically weak link dylib with no imports
831 // so at least one weak import symbol and no non-weak-imported symbols must be found
832 return foundWeakImport && !foundNonWeakImport;
833}
834
835
836template <typename A>
837bool File<A>::containsOrReExports(const char* name, bool& weakDef, bool& tlv, uint64_t& addr) const
838{
839 if ( _ignoreExports.count(name) != 0 )
840 return false;
841
842 // check myself
843 const auto pos = _atoms.find(name);
844 if ( pos != _atoms.end() ) {
845 weakDef = pos->second.weakDef;
846 tlv = pos->second.tlv;
847 addr = 0;
848 return true;
849 }
850
851 // check dylibs I re-export
852 for (const auto& lib : _dependentDylibs) {
853 if ( !lib.dylib->implicitlyLinked() ) {
854 if ( lib.dylib->containsOrReExports(name, weakDef, tlv, addr) )
855 return true;
856 }
857 }
858
859 return false;
860}
861
862
863template <typename A>
864bool File<A>::justInTimeforEachAtom(const char* name, ld::File::AtomHandler& handler) const
865{
866 // if supposed to ignore this export, then pretend I don't have it
867 if ( _ignoreExports.count(name) != 0 )
868 return false;
869
870
871 AtomAndWeak bucket;
872 uint64_t addr;
873 if ( this->containsOrReExports(name, bucket.weakDef, bucket.tlv, addr) ) {
874 bucket.atom = new ExportAtom<A>(*this, name, bucket.weakDef, bucket.tlv);
875 _atoms[name] = bucket;
876 _providedAtom = true;
877 if ( _s_logHashtable )
878 fprintf(stderr, "getJustInTimeAtomsFor: %s found in %s\n", name, this->path());
879 // call handler with new export atom
880 handler.doAtom(*bucket.atom);
881 return true;
882 }
883
884 return false;
885}
886
887
888
889template <typename A>
890bool File<A>::isPublicLocation(const char* path)
891{
892 // -no_implicit_dylibs disables this optimization
893 if ( ! _implicitlyLinkPublicDylibs )
894 return false;
895
896 // /usr/lib is a public location
897 if ( (strncmp(path, "/usr/lib/", 9) == 0) && (strchr(&path[9], '/') == nullptr) )
898 return true;
899
900 // /System/Library/Frameworks/ is a public location
901 if ( strncmp(path, "/System/Library/Frameworks/", 27) == 0 ) {
902 const char* frameworkDot = strchr(&path[27], '.');
903 // but only top level framework
904 // /System/Library/Frameworks/Foo.framework/Versions/A/Foo ==> true
905 // /System/Library/Frameworks/Foo.framework/Resources/libBar.dylib ==> false
906 // /System/Library/Frameworks/Foo.framework/Frameworks/Bar.framework/Bar ==> false
907 // /System/Library/Frameworks/Foo.framework/Frameworks/Xfoo.framework/XFoo ==> false
908 if ( frameworkDot != nullptr ) {
909 int frameworkNameLen = frameworkDot - &path[27];
910 if ( strncmp(&path[strlen(path)-frameworkNameLen-1], &path[26], frameworkNameLen+1) == 0 )
911 return true;
912 }
913 }
914
915 return false;
916}
917
918template <typename A>
919void File<A>::processIndirectLibraries(ld::dylib::File::DylibHandler* handler, bool addImplicitDylibs)
920{
921 // only do this once
922 if ( _indirectDylibsProcessed )
923 return;
924
925 const static bool log = false;
926 if ( log ) fprintf(stderr, "processIndirectLibraries(%s)\n", this->installPath());
927 if ( _linkingFlat ) {
928 for (auto& lib : _dependentDylibs) {
929 lib.dylib = (File<A>*)handler->findDylib(lib.path, this->path());
930 }
931 }
932 else if ( _noRexports ) {
933 // MH_NO_REEXPORTED_DYLIBS bit set, then nothing to do
934 }
935 else {
936 // two-level, might have re-exports
937 for (auto& lib : _dependentDylibs) {
938 if ( log )
939 fprintf(stderr, "processIndirectLibraries() parent=%s, child=%s\n", this->installPath(), lib.path);
940 // a LC_REEXPORT_DYLIB, LC_SUB_UMBRELLA or LC_SUB_LIBRARY says we re-export this child
941 lib.dylib = (File<A>*)handler->findDylib(lib.path, this->path());
942 if ( lib.dylib->hasPublicInstallName() && !lib.dylib->wrongOS() ) {
943 // promote this child to be automatically added as a direct dependent if this already is
944 if ( (this->explicitlyLinked() || this->implicitlyLinked()) && (strcmp(lib.path, lib.dylib->installPath()) == 0) ) {
945 if ( log )
946 fprintf(stderr, "processIndirectLibraries() implicitly linking %s\n", lib.dylib->installPath());
947 lib.dylib->setImplicitlyLinked();
948 }
949 else if ( lib.dylib->explicitlyLinked() || lib.dylib->implicitlyLinked() ) {
950 if ( log )
951 fprintf(stderr, "processIndirectLibraries() parent is not directly linked, but child is, so no need to re-export child\n");
952 } else {
953 if ( log )
954 fprintf(stderr, "processIndirectLibraries() parent is not directly linked, so parent=%s will re-export child=%s\n", this->installPath(), lib.path);
955 }
956 } else {
957 // add all child's symbols to me
958 if ( log )
959 fprintf(stderr, "processIndirectLibraries() child is not public, so parent=%s will re-export child=%s\n", this->installPath(), lib.path);
960 }
961 }
962 }
963
964 // check for re-export cycles
965 ReExportChain chain;
966 chain.prev = nullptr;
967 chain.file = this;
968 this->assertNoReExportCycles(&chain);
969
970 _indirectDylibsProcessed = true;
971}
972
973template <typename A>
974void File<A>::assertNoReExportCycles(ReExportChain* prev) const
975{
976 // recursively check my re-exported dylibs
977 ReExportChain chain;
978 chain.prev = prev;
979 chain.file = this;
980 for (const auto& dep : _dependentDylibs) {
981 ld::File* child = dep.dylib;
982 // check child is not already in chain
983 for (ReExportChain* p = prev; p != nullptr; p = p->prev) {
984 if ( p->file == child )
985 throwf("cycle in dylib re-exports with %s and %s", child->path(), this->path());
986 }
987 if ( dep.dylib != nullptr )
988 dep.dylib->assertNoReExportCycles(&chain);
989 }
990}
991
992
993template <typename A>
994class Parser
995{
996public:
997 typedef typename A::P P;
998
999 static bool validFile(const uint8_t* fileContent, uint64_t fileLength, const std::string &path, const char* archName);
1000 static ld::dylib::File* parse(const uint8_t* fileContent, uint64_t fileLength, const char* path,
1001 time_t mTime, ld::File::Ordinal ordinal, const Options& opts,
1002 bool indirectDylib) {
1003 return new File<A>(fileContent, fileLength, path, mTime, ordinal,
1004 opts.flatNamespace(),
1005 opts.implicitlyLinkIndirectPublicDylibs(),
1006 opts.platform(),
1007 opts.architecture(),
1008 opts.architectureName(),
1009 opts.minOSversion(),
1010 opts.allowSimulatorToLinkWithMacOSX(),
1011 opts.addVersionLoadCommand(),
1012 opts.targetIOSSimulator(),
1013 opts.logAllFiles(),
1014 opts.installPath(),
1015 indirectDylib);
1016 }
1017};
1018
1019template <typename A>
1020bool Parser<A>::validFile(const uint8_t* fileContent, uint64_t fileLength, const std::string &path, const char* archName)
1021{
1022 if ( path.find(".tbd", path.size()-4) == std::string::npos )
1023 return false;
1024
1025 TBDFile stub((const char*)fileContent, fileLength);
1026 if ( !stub.validForArch(archName) )
1027 throwf("missing required architecture %s in file %s", archName, path.c_str());
1028
1029 return true;
1030}
1031
1032//
1033// main function used by linker to instantiate ld::Files
1034//
1035ld::dylib::File* parse(const uint8_t* fileContent, uint64_t fileLength, const char* path,
1036 time_t modTime, const Options& opts, ld::File::Ordinal ordinal,
1037 bool bundleLoader, bool indirectDylib)
1038{
1039 switch ( opts.architecture() ) {
1040#if SUPPORT_ARCH_x86_64
1041 case CPU_TYPE_X86_64:
1042 if ( Parser<x86_64>::validFile(fileContent, fileLength, path, opts.architectureName()) )
1043 return Parser<x86_64>::parse(fileContent, fileLength, path, modTime, ordinal, opts, indirectDylib);
1044 break;
1045#endif
1046#if SUPPORT_ARCH_i386
1047 case CPU_TYPE_I386:
1048 if ( Parser<x86>::validFile(fileContent, fileLength, path, opts.architectureName()) )
1049 return Parser<x86>::parse(fileContent, fileLength, path, modTime, ordinal, opts, indirectDylib);
1050 break;
1051#endif
1052#if SUPPORT_ARCH_arm_any
1053 case CPU_TYPE_ARM:
1054 if ( Parser<arm>::validFile(fileContent, fileLength, path, opts.architectureName()) )
1055 return Parser<arm>::parse(fileContent, fileLength, path, modTime, ordinal, opts, indirectDylib);
1056 break;
1057#endif
1058#if SUPPORT_ARCH_arm64
1059 case CPU_TYPE_ARM64:
1060 if ( Parser<arm64>::validFile(fileContent, fileLength, path, opts.architectureName()) )
1061 return Parser<arm64>::parse(fileContent, fileLength, path, modTime, ordinal, opts, indirectDylib);
1062 break;
1063#endif
1064 }
1065 return nullptr;
1066}
1067
1068
1069} // namespace dylib
1070} // namespace textstub
1071
1072