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>
28 #include <tapi/tapi.h>
33 #include "Architectures.hpp"
34 #include "Bitcode.hpp"
35 #include "MachOFileAbstraction.hpp"
36 #include "MachOTrie.hpp"
37 #include "generic_dylib_file.hpp"
38 #include "textstub_dylib_file.hpp"
45 // The reader for a dylib extracts all exported symbols names from the memory-mapped
46 // dylib, builds a hash table, then unmaps the file. This is an important memory
47 // savings for large dylibs.
50 class File final
: public generic::dylib::File
<A
>
52 using Base
= generic::dylib::File
<A
>;
55 File(const char* path
, const uint8_t* fileContent
, uint64_t fileLength
,
56 time_t mTime
, ld::File::Ordinal ordinal
, bool linkingFlatNamespace
,
57 bool linkingMainExecutable
, bool hoistImplicitPublicDylibs
,
58 Options::Platform platform
, uint32_t linkMinOSVersion
, bool allowWeakImports
,
59 cpu_type_t cpuType
, cpu_subtype_t cpuSubType
, bool enforceDylibSubtypesMatch
,
60 bool allowSimToMacOSX
, bool addVers
, bool buildingForSimulator
,
61 bool logAllFiles
, const char* installPath
, bool indirectDylib
);
62 virtual ~File() noexcept
{}
65 void buildExportHashTable(const tapi::LinkerInterfaceFile
* file
);
68 static ld::File::ObjcConstraint
mapObjCConstraint(tapi::ObjCConstraint constraint
) {
70 case tapi::ObjCConstraint::None
:
71 return ld::File::objcConstraintNone
;
72 case tapi::ObjCConstraint::Retain_Release
:
73 return ld::File::objcConstraintRetainRelease
;
74 case tapi::ObjCConstraint::Retain_Release_For_Simulator
:
75 return ld::File::objcConstraintRetainReleaseForSimulator
;
76 case tapi::ObjCConstraint::Retain_Release_Or_GC
:
77 return ld::File::objcConstraintRetainReleaseOrGC
;
78 case tapi::ObjCConstraint::GC
:
79 return ld::File::objcConstraintGC
;
82 return ld::File::objcConstraintNone
;
85 static Options::Platform
mapPlatform(tapi::Platform platform
) {
87 case tapi::Platform::Unknown
:
88 return Options::kPlatformUnknown
;
89 case tapi::Platform::OSX
:
90 return Options::kPlatformOSX
;
91 case tapi::Platform::iOS
:
92 return Options::kPlatformiOS
;
93 case tapi::Platform::watchOS
:
94 return Options::kPlatformWatchOS
;
95 case tapi::Platform::tvOS
:
96 return Options::kPlatform_tvOS
;
99 return Options::kPlatformUnknown
;
102 template <typename A
>
103 File
<A
>::File(const char* path
, const uint8_t* fileContent
, uint64_t fileLength
,
104 time_t mTime
, ld::File::Ordinal ord
, bool linkingFlatNamespace
,
105 bool linkingMainExecutable
, bool hoistImplicitPublicDylibs
, Options::Platform platform
,
106 uint32_t linkMinOSVersion
, bool allowWeakImports
, cpu_type_t cpuType
, cpu_subtype_t cpuSubType
,
107 bool enforceDylibSubtypesMatch
, bool allowSimToMacOSX
, bool addVers
,
108 bool buildingForSimulator
, bool logAllFiles
, const char* targetInstallPath
,
110 : Base(strdup(path
), mTime
, ord
, platform
, linkMinOSVersion
, allowWeakImports
, linkingFlatNamespace
,
111 hoistImplicitPublicDylibs
, allowSimToMacOSX
, addVers
)
113 auto matchingType
= enforceDylibSubtypesMatch
?
114 tapi::CpuSubTypeMatching::Exact
: tapi::CpuSubTypeMatching::ABI_Compatible
;
116 std::string errorMessage
;
117 auto file
= std::unique_ptr
<tapi::LinkerInterfaceFile
>(
118 tapi::LinkerInterfaceFile::create(path
, fileContent
, fileLength
, cpuType
,
119 cpuSubType
, matchingType
,
120 tapi::PackedVersion32(linkMinOSVersion
), errorMessage
));
123 throw strdup(errorMessage
.c_str());
125 // unmap file - it is no longer needed.
126 munmap((caddr_t
)fileContent
, fileLength
);
128 // write out path for -t option
130 printf("%s\n", path
);
132 this->_bitcode
= std::unique_ptr
<ld::Bitcode
>(new ld::Bitcode(nullptr, 0));
133 this->_noRexports
= !file
->hasReexportedLibraries();
134 this->_hasWeakExports
= file
->hasWeakDefinedExports();
135 this->_dylibInstallPath
= strdup(file
->getInstallName().c_str());
136 this->_installPathOverride
= file
->isInstallNameVersionSpecific();
137 this->_dylibCurrentVersion
= file
->getCurrentVersion();
138 this->_dylibCompatibilityVersion
= file
->getCompatibilityVersion();
139 this->_swiftVersion
= file
->getSwiftVersion();
140 this->_objcConstraint
= mapObjCConstraint(file
->getObjCConstraint());
141 this->_parentUmbrella
= file
->getParentFrameworkName().empty() ? nullptr : strdup(file
->getParentFrameworkName().c_str());
142 this->_appExtensionSafe
= file
->isApplicationExtensionSafe();
144 // if framework, capture framework name
145 const char* lastSlash
= strrchr(this->_dylibInstallPath
, '/');
146 if ( lastSlash
!= NULL
) {
147 const char* leafName
= lastSlash
+1;
148 char frname
[strlen(leafName
)+32];
149 strcpy(frname
, leafName
);
150 strcat(frname
, ".framework/");
152 if ( strstr(this->_dylibInstallPath
, frname
) != NULL
)
153 this->_frameworkName
= leafName
;
156 for (auto &client
: file
->allowableClients())
157 this->_allowableClients
.push_back(strdup(client
.c_str()));
159 // <rdar://problem/20659505> [TAPI] Don't hoist "public" (in /usr/lib/) dylibs that should not be directly linked
160 this->_hasPublicInstallName
= file
->hasAllowableClients() ? false : this->isPublicLocation(file
->getInstallName().c_str());
162 for (const auto &client
: file
->allowableClients())
163 this->_allowableClients
.emplace_back(strdup(client
.c_str()));
165 auto dylibPlatform
= mapPlatform(file
->getPlatform());
166 if ( (dylibPlatform
!= platform
) && (platform
!= Options::kPlatformUnknown
) ) {
167 this->_wrongOS
= true;
168 if ( this->_addVersionLoadCommand
&& !indirectDylib
) {
169 if ( buildingForSimulator
) {
170 if ( !this->_allowSimToMacOSXLinking
)
171 throwf("building for %s simulator, but linking against dylib built for %s (%s).",
172 Options::platformName(platform
), Options::platformName(dylibPlatform
), path
);
174 throwf("building for %s, but linking against dylib built for %s (%s).",
175 Options::platformName(platform
), Options::platformName(dylibPlatform
), path
);
180 for (const auto& reexport
: file
->reexportedLibraries()) {
181 const char *path
= strdup(reexport
.c_str());
182 if ( (targetInstallPath
== nullptr) || (strcmp(targetInstallPath
, path
) != 0) )
183 this->_dependentDylibs
.emplace_back(path
, true);
186 for (const auto& symbol
: file
->ignoreExports())
187 this->_ignoreExports
.insert(strdup(symbol
.c_str()));
189 // if linking flat and this is a flat dylib, create one atom that references all imported symbols.
190 if ( linkingFlatNamespace
&& linkingMainExecutable
&& (file
->hasTwoLevelNamespace() == false) ) {
191 std::vector
<const char*> importNames
;
192 importNames
.reserve(file
->undefineds().size());
193 // We do not need to strdup the name, because that will be done by the
194 // ImportAtom constructor.
195 for (const auto &sym
: file
->undefineds())
196 importNames
.emplace_back(sym
.getName().c_str());
197 this->_importAtom
= new generic::dylib::ImportAtom
<A
>(*this, importNames
);
201 buildExportHashTable(file
.get());
204 template <typename A
>
205 void File
<A
>::buildExportHashTable(const tapi::LinkerInterfaceFile
* file
) {
206 if (this->_s_logHashtable
)
207 fprintf(stderr
, "ld: building hashtable from text-stub info in %s\n", this->path());
209 for (const auto &sym
: file
->exports()) {
210 const char* name
= sym
.getName().c_str();
211 bool weakDef
= sym
.isWeakDefined();
212 bool tlv
= sym
.isThreadLocalValue();
214 typename
Base::AtomAndWeak bucket
= { nullptr, weakDef
, tlv
, 0 };
215 if ( this->_s_logHashtable
)
216 fprintf(stderr
, " adding %s to hash table for %s\n", name
, this->path());
217 this->_atoms
[strdup(name
)] = bucket
;
221 template <typename A
>
225 using P
= typename
A::P
;
227 static ld::dylib::File
* parse(const char* path
, const uint8_t* fileContent
,
228 uint64_t fileLength
, time_t mTime
,
229 ld::File::Ordinal ordinal
, const Options
& opts
,
232 return new File
<A
>(path
, fileContent
, fileLength
,mTime
, ordinal
,
233 opts
.flatNamespace(),
234 opts
.linkingMainExecutable(),
235 opts
.implicitlyLinkIndirectPublicDylibs(),
238 opts
.allowWeakImports(),
240 opts
.subArchitecture(),
241 opts
.enforceDylibSubtypesMatch(),
242 opts
.allowSimulatorToLinkWithMacOSX(),
243 opts
.addVersionLoadCommand(),
244 opts
.targetIOSSimulator(),
252 // main function used by linker to instantiate ld::Files
254 ld::dylib::File
* parse(const uint8_t* fileContent
, uint64_t fileLength
, const char* path
,
255 time_t modTime
, const Options
& opts
, ld::File::Ordinal ordinal
,
256 bool bundleLoader
, bool indirectDylib
)
259 switch ( opts
.architecture() ) {
260 #if SUPPORT_ARCH_x86_64
261 case CPU_TYPE_X86_64
:
262 if (tapi::LinkerInterfaceFile::isSupported(path
, fileContent
, fileLength
))
263 return Parser
<x86_64
>::parse(path
, fileContent
, fileLength
, modTime
, ordinal
, opts
, indirectDylib
);
265 #if SUPPORT_ARCH_i386
267 if (tapi::LinkerInterfaceFile::isSupported(path
, fileContent
, fileLength
))
268 return Parser
<x86
>::parse(path
, fileContent
, fileLength
, modTime
, ordinal
, opts
, indirectDylib
);
270 #if SUPPORT_ARCH_arm_any
272 if (tapi::LinkerInterfaceFile::isSupported(path
, fileContent
, fileLength
))
273 return Parser
<arm
>::parse(path
, fileContent
, fileLength
, modTime
, ordinal
, opts
, indirectDylib
);
275 #if SUPPORT_ARCH_arm64
277 if (tapi::LinkerInterfaceFile::isSupported(path
, fileContent
, fileLength
))
278 return Parser
<arm64
>::parse(path
, fileContent
, fileLength
, modTime
, ordinal
, opts
, indirectDylib
);
286 } // namespace textstub