1 /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
3 * Copyright (c) 2015 Apple Inc. All rights reserved.
5 * @APPLE_LICENSE_HEADER_START@
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
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.
22 * @APPLE_LICENSE_HEADER_END@
26 #include <sys/param.h>
31 #include "Architectures.hpp"
32 #include "bitcode.hpp"
33 #include "MachOFileAbstraction.hpp"
34 #include "MachOTrie.hpp"
35 #include "textstub_dylib_file.hpp"
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.
48 int compareMemory(const char* lhs
, const char* rhs
, size_t size
) const {
51 return ::memcmp(lhs
, rhs
, size
);
55 Token() : _p(nullptr), _size(0) {}
57 Token(const char* p
) : _p(p
), _size(0) {
62 Token(const char* p
, size_t s
) : _p(p
), _size(s
) {}
64 const char* data() const { return _p
; }
66 size_t size() const { return _size
; }
68 std::string
str() const { return std::move(std::string(_p
, _size
)); }
70 bool empty() const { return _size
== 0; }
72 bool operator==(Token other
) const {
73 if (_size
!= other
._size
)
75 return compareMemory(_p
, other
._p
, _size
) == 0;
78 bool operator!=(Token other
) const {
79 return !(*this == other
);
84 /// Simple text-based dynamic library file tokenizer.
92 void fetchNextToken();
93 void scanToNextToken();
94 void skip(unsigned distance
) {
96 assert(_current
<= _end
&& "Skipped past the end");
99 const char* skipLineBreak(const char* pos
) const;
100 bool isDelimiter(const char* pos
) const;
103 Tokenizer(const char* data
, uint64_t size
) : _start(data
), _current(data
), _end(data
+ size
) {}
110 Token
peek() { return _currentToken
; }
112 Token token
= peek();
118 const char* Tokenizer::skipLineBreak(const char* pos
) const
124 if ( *pos
== 0x0D ) {
126 if ( pos
+ 1 != _end
&& *(pos
+ 1) == 0x0A)
138 void Tokenizer::scanToNextToken() {
140 while ( isDelimiter(_current
) )
143 const char* i
= skipLineBreak(_current
);
152 bool Tokenizer::isDelimiter(const char* pos
) const {
155 if ( *pos
== ' ' || *pos
== '\t' || *pos
== '\r' || *pos
== '\n' || *pos
== ',' || *pos
== ':' || *pos
== '\'' || *pos
== '\"' )
160 void Tokenizer::fetchNextToken() {
163 if (_current
== _end
) {
164 _currentToken
= Token();
168 auto start
= _current
;
169 while ( !isDelimiter(_current
) ) {
173 _currentToken
= Token(start
, _current
- start
);
177 /// Representation of a parsed text-based dynamic library file.
179 struct DynamicLibrary
{
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
;
194 DynamicLibrary() : _currentVersion(0x10000), _compatibilityVersion(0x10000), _swiftVersion(0),
195 _objcConstraint(ld::File::objcConstraintNone
) {}
198 static uint32_t parseVersionNumber32(Token token
) {
199 if ( token
.size() >= 128 )
200 throwf("malformed version number");
208 // Make a null-terminated string.
209 ::memcpy(buffer
, token
.data(), token
.size());
210 buffer
[token
.size()] = '\0';
212 x
= strtoul(buffer
, &end
, 10);
214 y
= strtoul(&end
[1], &end
, 10);
216 z
= strtoul(&end
[1], &end
, 10);
219 if ( (x
> 0xffff) || (y
> 0xff) || (z
> 0xff) )
220 throwf("malformed 32-bit x.y.z version number: %s", buffer
);
222 return (x
<< 16) | ( y
<< 8 ) | z
;
226 /// A simple text-based dynamic library file parser.
229 Tokenizer _tokenizer
;
231 Token
peek() { return _tokenizer
.peek(); }
232 Token
next() { return _tokenizer
.next(); }
234 void expectToken(Token str
) {
235 Token token
= next();
237 throwf("unexpected token: %s", token
.str().c_str());
240 bool hasOptionalToken(Token str
) {
242 if ( token
== str
) {
250 void parseFlowSequence(std::function
<void (Token
)> func
) {
265 void parseAllowedClients(DynamicLibrary
& lib
) {
266 if ( !hasOptionalToken("allowed-clients") )
268 parseFlowSequence([&](Token name
) {
269 lib
._allowedClients
.emplace_back(name
);
273 void parseReexportedDylibs(DynamicLibrary
& lib
) {
274 if ( !hasOptionalToken("re-exports") )
276 parseFlowSequence([&](Token name
) {
277 lib
._reexportedLibraries
.emplace_back(name
);
281 void parseSymbols(DynamicLibrary
& lib
) {
282 if ( hasOptionalToken("symbols") ) {
283 parseFlowSequence([&](Token name
) {
284 lib
._symbols
.emplace_back(name
);
288 if ( hasOptionalToken("objc-classes") ) {
289 parseFlowSequence([&](Token name
) {
290 lib
._classes
.emplace_back(name
);
294 if ( hasOptionalToken("objc-ivars") ) {
295 parseFlowSequence([&](Token name
) {
296 lib
._ivars
.emplace_back(name
);
300 if ( hasOptionalToken("weak-def-symbols") ) {
301 parseFlowSequence([&](Token name
) {
302 lib
._weakDefSymbols
.emplace_back(name
);
306 if ( hasOptionalToken("thread-local-symbols") ) {
307 parseFlowSequence([&](Token name
) {
308 lib
._tlvSymbols
.emplace_back(name
);
313 bool parseArchFlowSequence(Token archName
) {
314 expectToken("archs");
316 bool foundArch
= false;
317 parseFlowSequence([&](Token name
) {
318 if ( name
== archName
)
325 void parsePlatform(DynamicLibrary
& lib
) {
326 expectToken("platform");
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
;
336 else if (token
== "tvos")
337 lib
._platform
= Options::kPlatform_tvOS
;
340 lib
._platform
= Options::kPlatformUnknown
;
343 void parseInstallName(DynamicLibrary
& lib
) {
344 expectToken("install-name");
346 lib
._installName
= next();
347 if ( lib
._installName
.empty() )
348 throwf("no install name specified");
351 void parseCurrentVersion(DynamicLibrary
& lib
) {
352 if ( !hasOptionalToken("current-version") )
354 lib
._currentVersion
= parseVersionNumber32(next());
357 void parseCompatibilityVersion(DynamicLibrary
& lib
) {
358 if ( !hasOptionalToken("compatibility-version") )
360 lib
._compatibilityVersion
= parseVersionNumber32(next());
363 void parseSwiftVersion(DynamicLibrary
& lib
) {
364 if ( !hasOptionalToken("swift-version") )
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;
374 throwf("unsupported Swift ABI version: %s", token
.str().c_str());
377 void parseObjCConstraint(DynamicLibrary
& lib
) {
378 if ( !hasOptionalToken("objc-constraint") )
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
;
392 throwf("unexpected token: %s", token
.str().c_str());
394 void parseExportsBlock(DynamicLibrary
& lib
, Token archName
) {
395 if ( !hasOptionalToken("exports") )
398 if ( !hasOptionalToken("-") )
402 if ( !parseArchFlowSequence(archName
) ) {
406 if ( token
== "archs" || token
== "..." || token
.empty() )
410 if (token
== "..." || token
.empty() )
416 parseAllowedClients(lib
);
417 parseReexportedDylibs(lib
);
419 if ( !hasOptionalToken("-") )
424 void parseDocument(DynamicLibrary
& lib
, Token archName
) {
425 if ( !parseArchFlowSequence(archName
) )
426 throwf("invalid arch");
429 parseInstallName(lib
);
430 parseCurrentVersion(lib
);
431 parseCompatibilityVersion(lib
);
432 parseSwiftVersion(lib
);
433 parseObjCConstraint(lib
);
434 parseExportsBlock(lib
, archName
);
438 TBDFile(const char* data
, uint64_t size
) : _tokenizer(data
, size
) {}
440 DynamicLibrary
parseFileForArch(Token archName
) {
444 parseDocument(lib
, archName
);
446 return std::move(lib
);
449 bool validForArch(Token archName
) {
452 if ( token
!= "---" )
454 return parseArchFlowSequence(archName
);
462 printf("token: %s\n", token
.str().c_str());
463 } while ( !token
.empty() );
467 } // end anonymous namespace
473 template <typename A
> class File
;
477 // An ExportAtom has no content. It exists so that the linker can track which imported
478 // symbols came from which dynamic libraries.
480 template <typename A
>
481 class ExportAtom
: public ld::Atom
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
) { }
500 typedef typename
A::P P
;
501 typedef typename
A::P::uint_t pint_t
;
503 virtual ~ExportAtom() {}
505 const File
<A
>& _file
;
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.
515 template <typename A
>
516 class File
: public ld::dylib::File
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
);
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
; }
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(); }
551 virtual void assertNoReExportCycles(ReExportChain
*) const;
554 typedef typename
A::P P
;
555 typedef typename
A::P::E E
;
556 typedef typename
A::P::uint_t pint_t
;
558 friend class ExportAtom
<A
>;
561 std::size_t operator()(const char* __s
) const {
562 unsigned long __h
= 0;
564 __h
= 5 * __h
+ *__s
;
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
;
572 struct Dependent
{ const char* path
; File
<A
>* dylib
; };
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;
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
);
582 const Options::Platform _platform
;
584 const uint32_t _linkMinOSVersion
;
585 const bool _allowSimToMacOSXLinking
;
586 const bool _addVersionLoadCommand
;
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
;
598 bool _hasWeakExports
;
599 bool _hasPublicInstallName
;
600 mutable bool _providedAtom
;
602 bool _installPathOverride
;
603 bool _indirectDylibsProcessed
;
604 std::unique_ptr
<ld::Bitcode
> _bitcode
;
605 static bool _s_logHashtable
;
608 template <typename A
>
609 bool File
<A
>::_s_logHashtable
= false;
612 template <typename A
>
613 File
<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
,
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))
631 // write out path for -t option
633 printf("%s\n", path
);
635 TBDFile
stub((const char*)fileContent
, fileLength
);
636 auto lib
= stub
.parseFileForArch(archName
);
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
);
647 for (auto &client
: lib
._allowedClients
)
648 _allowableClients
.push_back(strdup(client
.str().c_str()));
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;
654 if ( (lib
._platform
!= platform
) && (platform
!= Options::kPlatformUnknown
) ) {
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
);
662 throwf("building for %s, but linking against dylib built for %s (%s).",
663 Options::platformName(platform
), Options::platformName(lib
._platform
), path
);
668 _dependentDylibs
.reserve(lib
._reexportedLibraries
.size());
669 for ( auto& reexport
: lib
._reexportedLibraries
) {
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
);
678 buildExportHashTable(lib
);
680 munmap((caddr_t
)fileContent
, fileLength
);
683 template <typename A
>
684 void 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());
688 for (auto &sym
: lib
._symbols
)
689 addSymbol(sym
.str().c_str(), /*weak=*/false, /*tlv=*/false);
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);
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);
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);
708 for (auto &sym
: lib
._ivars
)
709 addSymbol(("_OBJC_IVAR_$" + sym
.str()).c_str(), /*weak=*/false, /*tlv=*/false);
711 for (auto &sym
: lib
._weakDefSymbols
)
712 addSymbol(sym
.str().c_str(), /*weak=*/true, /*tlv=*/false);
714 for (auto &sym
: lib
._tlvSymbols
)
715 addSymbol(sym
.str().c_str(), /*weak=*/false, /*tlv=*/true);
719 template <typename A
>
720 void File
<A
>::addSymbol(const char* name
, bool weakDef
, bool tlv
)
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 ) {
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 ) {
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
));
741 else if ( strncmp(symAction
, "add$", 4) == 0 ) {
742 this->addSymbol(symName
, weakDef
, false);
745 else if ( strncmp(symAction
, "install_name$", 13) == 0 ) {
746 _dylibInstallPath
= strdup(symName
);
747 _installPathOverride
= true;
750 else if ( strncmp(symAction
, "compatibility_version$", 22) == 0 ) {
751 _dylibCompatibilityVersion
= parseVersionNumber32(symName
);
755 warning("bad symbol action: %s in dylib %s", name
, this->path());
761 warning("bad symbol condition: %s in dylib %s", name
, this->path());
765 // add symbol as possible export if we are not supposed to ignore it
766 if ( _ignoreExports
.count(name
) == 0 ) {
768 bucket
.atom
= nullptr;
769 bucket
.weakDef
= weakDef
;
771 if ( _s_logHashtable
)
772 fprintf(stderr
, " adding %s to hash table for %s\n", name
, this->path());
773 _atoms
[strdup(name
)] = bucket
;
778 template <typename A
>
779 bool File
<A
>::forEachAtom(ld::File::AtomHandler
& handler
) const
781 handler
.doFile(*this);
786 template <typename A
>
787 std::pair
<bool, bool> File
<A
>::hasWeakDefinitionImpl(const char* name
) const
789 const auto pos
= _atoms
.find(name
);
790 if ( pos
!= _atoms
.end() )
791 return std::make_pair(true, pos
->second
.weakDef
);
793 // look in children that I re-export
794 for (const auto &dep
: _dependentDylibs
) {
795 auto ret
= dep
.dylib
->hasWeakDefinitionImpl(name
);
799 return std::make_pair(false, false);
803 template <typename A
>
804 bool File
<A
>::hasWeakDefinition(const char* name
) const
806 // if supposed to ignore this export, then pretend I don't have it
807 if ( _ignoreExports
.count(name
) != 0 )
810 return hasWeakDefinitionImpl(name
).second
;
814 // <rdar://problem/5529626> If only weak_import symbols are used, linker should use LD_LOAD_WEAK_DYLIB
815 template <typename A
>
816 bool File
<A
>::allSymbolsAreWeakImported() const
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;
826 foundNonWeakImport
= true;
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
;
836 template <typename A
>
837 bool File
<A
>::containsOrReExports(const char* name
, bool& weakDef
, bool& tlv
, uint64_t& addr
) const
839 if ( _ignoreExports
.count(name
) != 0 )
843 const auto pos
= _atoms
.find(name
);
844 if ( pos
!= _atoms
.end() ) {
845 weakDef
= pos
->second
.weakDef
;
846 tlv
= pos
->second
.tlv
;
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
) )
863 template <typename A
>
864 bool File
<A
>::justInTimeforEachAtom(const char* name
, ld::File::AtomHandler
& handler
) const
866 // if supposed to ignore this export, then pretend I don't have it
867 if ( _ignoreExports
.count(name
) != 0 )
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
);
889 template <typename A
>
890 bool File
<A
>::isPublicLocation(const char* path
)
892 // -no_implicit_dylibs disables this optimization
893 if ( ! _implicitlyLinkPublicDylibs
)
896 // /usr/lib is a public location
897 if ( (strncmp(path
, "/usr/lib/", 9) == 0) && (strchr(&path
[9], '/') == nullptr) )
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 )
918 template <typename A
>
919 void File
<A
>::processIndirectLibraries(ld::dylib::File::DylibHandler
* handler
, bool addImplicitDylibs
)
922 if ( _indirectDylibsProcessed
)
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());
932 else if ( _noRexports
) {
933 // MH_NO_REEXPORTED_DYLIBS bit set, then nothing to do
936 // two-level, might have re-exports
937 for (auto& lib
: _dependentDylibs
) {
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) ) {
946 fprintf(stderr
, "processIndirectLibraries() implicitly linking %s\n", lib
.dylib
->installPath());
947 lib
.dylib
->setImplicitlyLinked();
949 else if ( lib
.dylib
->explicitlyLinked() || lib
.dylib
->implicitlyLinked() ) {
951 fprintf(stderr
, "processIndirectLibraries() parent is not directly linked, but child is, so no need to re-export child\n");
954 fprintf(stderr
, "processIndirectLibraries() parent is not directly linked, so parent=%s will re-export child=%s\n", this->installPath(), lib
.path
);
957 // add all child's symbols to me
959 fprintf(stderr
, "processIndirectLibraries() child is not public, so parent=%s will re-export child=%s\n", this->installPath(), lib
.path
);
964 // check for re-export cycles
966 chain
.prev
= nullptr;
968 this->assertNoReExportCycles(&chain
);
970 _indirectDylibsProcessed
= true;
973 template <typename A
>
974 void File
<A
>::assertNoReExportCycles(ReExportChain
* prev
) const
976 // recursively check my re-exported dylibs
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());
987 if ( dep
.dylib
!= nullptr )
988 dep
.dylib
->assertNoReExportCycles(&chain
);
993 template <typename A
>
997 typedef typename
A::P P
;
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(),
1007 opts
.architecture(),
1008 opts
.architectureName(),
1009 opts
.minOSversion(),
1010 opts
.allowSimulatorToLinkWithMacOSX(),
1011 opts
.addVersionLoadCommand(),
1012 opts
.targetIOSSimulator(),
1019 template <typename A
>
1020 bool Parser
<A
>::validFile(const uint8_t* fileContent
, uint64_t fileLength
, const std::string
&path
, const char* archName
)
1022 if ( path
.find(".tbd", path
.size()-4) == std::string::npos
)
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());
1033 // main function used by linker to instantiate ld::Files
1035 ld::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
)
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
);
1046 #if SUPPORT_ARCH_i386
1048 if ( Parser
<x86
>::validFile(fileContent
, fileLength
, path
, opts
.architectureName()) )
1049 return Parser
<x86
>::parse(fileContent
, fileLength
, path
, modTime
, ordinal
, opts
, indirectDylib
);
1052 #if SUPPORT_ARCH_arm_any
1054 if ( Parser
<arm
>::validFile(fileContent
, fileLength
, path
, opts
.architectureName()) )
1055 return Parser
<arm
>::parse(fileContent
, fileLength
, path
, modTime
, ordinal
, opts
, indirectDylib
);
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
);
1069 } // namespace dylib
1070 } // namespace textstub