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 // <rdar://problem/22268737> x86_64h fails to link against text based stubs
317 if ( archName
== "x86_64h" )
320 bool foundArch
= false;
321 parseFlowSequence([&](Token name
) {
322 if ( name
== archName
)
329 void parsePlatform(DynamicLibrary
& lib
) {
330 expectToken("platform");
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
;
340 else if (token
== "tvos")
341 lib
._platform
= Options::kPlatform_tvOS
;
344 lib
._platform
= Options::kPlatformUnknown
;
347 void parseInstallName(DynamicLibrary
& lib
) {
348 expectToken("install-name");
350 lib
._installName
= next();
351 if ( lib
._installName
.empty() )
352 throwf("no install name specified");
355 void parseCurrentVersion(DynamicLibrary
& lib
) {
356 if ( !hasOptionalToken("current-version") )
358 lib
._currentVersion
= parseVersionNumber32(next());
361 void parseCompatibilityVersion(DynamicLibrary
& lib
) {
362 if ( !hasOptionalToken("compatibility-version") )
364 lib
._compatibilityVersion
= parseVersionNumber32(next());
367 void parseSwiftVersion(DynamicLibrary
& lib
) {
368 if ( !hasOptionalToken("swift-version") )
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;
378 throwf("unsupported Swift ABI version: %s", token
.str().c_str());
381 void parseObjCConstraint(DynamicLibrary
& lib
) {
382 if ( !hasOptionalToken("objc-constraint") )
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
;
396 throwf("unexpected token: %s", token
.str().c_str());
398 void parseExportsBlock(DynamicLibrary
& lib
, Token archName
) {
399 if ( !hasOptionalToken("exports") )
402 if ( !hasOptionalToken("-") )
406 if ( !parseArchFlowSequence(archName
) ) {
410 if ( token
== "archs" || token
== "..." || token
.empty() )
414 if (token
== "..." || token
.empty() )
420 parseAllowedClients(lib
);
421 parseReexportedDylibs(lib
);
423 if ( !hasOptionalToken("-") )
428 void parseDocument(DynamicLibrary
& lib
, Token archName
) {
429 if ( !parseArchFlowSequence(archName
) )
430 throwf("invalid arch");
433 parseInstallName(lib
);
434 parseCurrentVersion(lib
);
435 parseCompatibilityVersion(lib
);
436 parseSwiftVersion(lib
);
437 parseObjCConstraint(lib
);
438 parseExportsBlock(lib
, archName
);
442 TBDFile(const char* data
, uint64_t size
) : _tokenizer(data
, size
) {}
444 DynamicLibrary
parseFileForArch(Token archName
) {
448 parseDocument(lib
, archName
);
450 return std::move(lib
);
453 bool validForArch(Token archName
) {
456 if ( token
!= "---" )
458 return parseArchFlowSequence(archName
);
466 printf("token: %s\n", token
.str().c_str());
467 } while ( !token
.empty() );
471 } // end anonymous namespace
477 template <typename A
> class File
;
481 // An ExportAtom has no content. It exists so that the linker can track which imported
482 // symbols came from which dynamic libraries.
484 template <typename A
>
485 class ExportAtom
: public ld::Atom
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
) { }
504 typedef typename
A::P P
;
505 typedef typename
A::P::uint_t pint_t
;
507 virtual ~ExportAtom() {}
509 const File
<A
>& _file
;
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.
519 template <typename A
>
520 class File
: public ld::dylib::File
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
);
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
; }
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(); }
555 virtual void assertNoReExportCycles(ReExportChain
*) const;
558 typedef typename
A::P P
;
559 typedef typename
A::P::E E
;
560 typedef typename
A::P::uint_t pint_t
;
562 friend class ExportAtom
<A
>;
565 std::size_t operator()(const char* __s
) const {
566 unsigned long __h
= 0;
568 __h
= 5 * __h
+ *__s
;
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
;
576 struct Dependent
{ const char* path
; File
<A
>* dylib
; };
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;
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
);
586 const Options::Platform _platform
;
588 const uint32_t _linkMinOSVersion
;
589 const bool _allowSimToMacOSXLinking
;
590 const bool _addVersionLoadCommand
;
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
;
602 bool _hasWeakExports
;
603 bool _hasPublicInstallName
;
604 mutable bool _providedAtom
;
606 bool _installPathOverride
;
607 bool _indirectDylibsProcessed
;
608 std::unique_ptr
<ld::Bitcode
> _bitcode
;
609 static bool _s_logHashtable
;
612 template <typename A
>
613 bool File
<A
>::_s_logHashtable
= false;
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
,
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))
635 // write out path for -t option
637 printf("%s\n", path
);
639 TBDFile
stub((const char*)fileContent
, fileLength
);
640 auto lib
= stub
.parseFileForArch(archName
);
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
);
651 for (auto &client
: lib
._allowedClients
)
652 _allowableClients
.push_back(strdup(client
.str().c_str()));
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;
658 if ( (lib
._platform
!= platform
) && (platform
!= Options::kPlatformUnknown
) ) {
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
);
666 throwf("building for %s, but linking against dylib built for %s (%s).",
667 Options::platformName(platform
), Options::platformName(lib
._platform
), path
);
672 _dependentDylibs
.reserve(lib
._reexportedLibraries
.size());
673 for ( auto& reexport
: lib
._reexportedLibraries
) {
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
);
682 buildExportHashTable(lib
);
684 munmap((caddr_t
)fileContent
, fileLength
);
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());
692 for (auto &sym
: lib
._symbols
)
693 addSymbol(sym
.str().c_str(), /*weak=*/false, /*tlv=*/false);
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);
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);
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);
712 for (auto &sym
: lib
._ivars
)
713 addSymbol(("_OBJC_IVAR_$" + sym
.str()).c_str(), /*weak=*/false, /*tlv=*/false);
715 for (auto &sym
: lib
._weakDefSymbols
)
716 addSymbol(sym
.str().c_str(), /*weak=*/true, /*tlv=*/false);
718 for (auto &sym
: lib
._tlvSymbols
)
719 addSymbol(sym
.str().c_str(), /*weak=*/false, /*tlv=*/true);
723 template <typename A
>
724 void File
<A
>::addSymbol(const char* name
, bool weakDef
, bool tlv
)
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 ) {
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 ) {
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
));
745 else if ( strncmp(symAction
, "add$", 4) == 0 ) {
746 this->addSymbol(symName
, weakDef
, false);
749 else if ( strncmp(symAction
, "install_name$", 13) == 0 ) {
750 _dylibInstallPath
= strdup(symName
);
751 _installPathOverride
= true;
754 else if ( strncmp(symAction
, "compatibility_version$", 22) == 0 ) {
755 _dylibCompatibilityVersion
= parseVersionNumber32(symName
);
759 warning("bad symbol action: %s in dylib %s", name
, this->path());
765 warning("bad symbol condition: %s in dylib %s", name
, this->path());
769 // add symbol as possible export if we are not supposed to ignore it
770 if ( _ignoreExports
.count(name
) == 0 ) {
772 bucket
.atom
= nullptr;
773 bucket
.weakDef
= weakDef
;
775 if ( _s_logHashtable
)
776 fprintf(stderr
, " adding %s to hash table for %s\n", name
, this->path());
777 _atoms
[strdup(name
)] = bucket
;
782 template <typename A
>
783 bool File
<A
>::forEachAtom(ld::File::AtomHandler
& handler
) const
785 handler
.doFile(*this);
790 template <typename A
>
791 std::pair
<bool, bool> File
<A
>::hasWeakDefinitionImpl(const char* name
) const
793 const auto pos
= _atoms
.find(name
);
794 if ( pos
!= _atoms
.end() )
795 return std::make_pair(true, pos
->second
.weakDef
);
797 // look in children that I re-export
798 for (const auto &dep
: _dependentDylibs
) {
799 auto ret
= dep
.dylib
->hasWeakDefinitionImpl(name
);
803 return std::make_pair(false, false);
807 template <typename A
>
808 bool File
<A
>::hasWeakDefinition(const char* name
) const
810 // if supposed to ignore this export, then pretend I don't have it
811 if ( _ignoreExports
.count(name
) != 0 )
814 return hasWeakDefinitionImpl(name
).second
;
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
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;
830 foundNonWeakImport
= true;
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
;
840 template <typename A
>
841 bool File
<A
>::containsOrReExports(const char* name
, bool& weakDef
, bool& tlv
, uint64_t& addr
) const
843 if ( _ignoreExports
.count(name
) != 0 )
847 const auto pos
= _atoms
.find(name
);
848 if ( pos
!= _atoms
.end() ) {
849 weakDef
= pos
->second
.weakDef
;
850 tlv
= pos
->second
.tlv
;
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
) )
867 template <typename A
>
868 bool File
<A
>::justInTimeforEachAtom(const char* name
, ld::File::AtomHandler
& handler
) const
870 // if supposed to ignore this export, then pretend I don't have it
871 if ( _ignoreExports
.count(name
) != 0 )
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
);
893 template <typename A
>
894 bool File
<A
>::isPublicLocation(const char* path
)
896 // -no_implicit_dylibs disables this optimization
897 if ( ! _implicitlyLinkPublicDylibs
)
900 // /usr/lib is a public location
901 if ( (strncmp(path
, "/usr/lib/", 9) == 0) && (strchr(&path
[9], '/') == nullptr) )
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 )
922 template <typename A
>
923 void File
<A
>::processIndirectLibraries(ld::dylib::File::DylibHandler
* handler
, bool addImplicitDylibs
)
926 if ( _indirectDylibsProcessed
)
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());
936 else if ( _noRexports
) {
937 // MH_NO_REEXPORTED_DYLIBS bit set, then nothing to do
940 // two-level, might have re-exports
941 for (auto& lib
: _dependentDylibs
) {
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) ) {
950 fprintf(stderr
, "processIndirectLibraries() implicitly linking %s\n", lib
.dylib
->installPath());
951 lib
.dylib
->setImplicitlyLinked();
953 else if ( lib
.dylib
->explicitlyLinked() || lib
.dylib
->implicitlyLinked() ) {
955 fprintf(stderr
, "processIndirectLibraries() parent is not directly linked, but child is, so no need to re-export child\n");
958 fprintf(stderr
, "processIndirectLibraries() parent is not directly linked, so parent=%s will re-export child=%s\n", this->installPath(), lib
.path
);
961 // add all child's symbols to me
963 fprintf(stderr
, "processIndirectLibraries() child is not public, so parent=%s will re-export child=%s\n", this->installPath(), lib
.path
);
968 // check for re-export cycles
970 chain
.prev
= nullptr;
972 this->assertNoReExportCycles(&chain
);
974 _indirectDylibsProcessed
= true;
977 template <typename A
>
978 void File
<A
>::assertNoReExportCycles(ReExportChain
* prev
) const
980 // recursively check my re-exported dylibs
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());
991 if ( dep
.dylib
!= nullptr )
992 dep
.dylib
->assertNoReExportCycles(&chain
);
997 template <typename A
>
1001 typedef typename
A::P P
;
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(),
1011 opts
.architecture(),
1012 opts
.architectureName(),
1013 opts
.minOSversion(),
1014 opts
.allowSimulatorToLinkWithMacOSX(),
1015 opts
.addVersionLoadCommand(),
1016 opts
.targetIOSSimulator(),
1023 template <typename A
>
1024 bool Parser
<A
>::validFile(const uint8_t* fileContent
, uint64_t fileLength
, const std::string
&path
, const char* archName
)
1026 if ( path
.find(".tbd", path
.size()-4) == std::string::npos
)
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());
1037 // main function used by linker to instantiate ld::Files
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
)
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
);
1050 #if SUPPORT_ARCH_i386
1052 if ( Parser
<x86
>::validFile(fileContent
, fileLength
, path
, opts
.architectureName()) )
1053 return Parser
<x86
>::parse(fileContent
, fileLength
, path
, modTime
, ordinal
, opts
, indirectDylib
);
1056 #if SUPPORT_ARCH_arm_any
1058 if ( Parser
<arm
>::validFile(fileContent
, fileLength
, path
, opts
.architectureName()) )
1059 return Parser
<arm
>::parse(fileContent
, fileLength
, path
, modTime
, ordinal
, opts
, indirectDylib
);
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
);
1073 } // namespace dylib
1074 } // namespace textstub