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>
31 #include "Architectures.hpp"
32 #include "Bitcode.hpp"
33 #include "MachOFileAbstraction.hpp"
34 #include "MachOTrie.hpp"
35 #include "generic_dylib_file.hpp"
36 #include "textstub_dylib_file.hpp"
43 // The reader for a dylib extracts all exported symbols names from the memory-mapped
44 // dylib, builds a hash table, then unmaps the file. This is an important memory
45 // savings for large dylibs.
48 class File final
: public generic::dylib::File
<A
>
50 using Base
= generic::dylib::File
<A
>;
53 File(const char* path
, const uint8_t* fileContent
, uint64_t fileLength
, const Options
*opts
,
54 time_t mTime
, ld::File::Ordinal ordinal
, bool linkingFlatNamespace
,
55 bool linkingMainExecutable
, bool hoistImplicitPublicDylibs
,
56 Options::Platform platform
, uint32_t linkMinOSVersion
, bool allowWeakImports
,
57 cpu_type_t cpuType
, cpu_subtype_t cpuSubType
, bool enforceDylibSubtypesMatch
,
58 bool allowSimToMacOSX
, bool addVers
, bool buildingForSimulator
,
59 bool logAllFiles
, const char* installPath
, bool indirectDylib
);
60 File(std::unique_ptr
<tapi::LinkerInterfaceFile
> &&file
, const char *path
, const Options
*opts
,
61 time_t mTime
, ld::File::Ordinal ordinal
, bool linkingFlatNamespace
,
62 bool linkingMainExecutable
, bool hoistImplicitPublicDylibs
,
63 Options::Platform platform
, uint32_t linkMinOSVersion
, bool allowWeakImports
,
64 cpu_type_t cpuType
, cpu_subtype_t cpuSubType
, bool enforceDylibSubtypesMatch
,
65 bool allowSimToMacOSX
, bool addVers
, bool buildingForSimulator
,
66 bool logAllFiles
, const char* installPath
, bool indirectDylib
);
67 virtual ~File() noexcept {}
69 // overrides of generic::dylib::File
70 virtual void processIndirectLibraries(ld::dylib::File::DylibHandler
*, bool addImplicitDylibs
) override final
;
73 void init(tapi::LinkerInterfaceFile
*file
, const Options
*opts
, bool buildingForSimulator
,
74 bool indirectDylib
, bool linkingFlatNamespace
, bool linkingMainExecutable
,
75 const char *path
, Options::Platform platform
, const char *targetInstallPath
);
76 void buildExportHashTable(const tapi::LinkerInterfaceFile
* file
);
79 std::unique_ptr
<tapi::LinkerInterfaceFile
> _interface
;
82 static ld::File::ObjcConstraint
mapObjCConstraint(tapi::ObjCConstraint constraint
) {
84 case tapi::ObjCConstraint::None
:
85 return ld::File::objcConstraintNone
;
86 case tapi::ObjCConstraint::Retain_Release
:
87 return ld::File::objcConstraintRetainRelease
;
88 case tapi::ObjCConstraint::Retain_Release_For_Simulator
:
89 return ld::File::objcConstraintRetainReleaseForSimulator
;
90 case tapi::ObjCConstraint::Retain_Release_Or_GC
:
91 return ld::File::objcConstraintRetainReleaseOrGC
;
92 case tapi::ObjCConstraint::GC
:
93 return ld::File::objcConstraintGC
;
96 return ld::File::objcConstraintNone
;
99 static Options::Platform
mapPlatform(tapi::Platform platform
) {
101 case tapi::Platform::Unknown
:
102 return Options::kPlatformUnknown
;
103 case tapi::Platform::OSX
:
104 return Options::kPlatformOSX
;
105 case tapi::Platform::iOS
:
106 return Options::kPlatformiOS
;
107 case tapi::Platform::watchOS
:
108 return Options::kPlatformWatchOS
;
109 case tapi::Platform::tvOS
:
110 return Options::kPlatform_tvOS
;
111 #if ((TAPI_API_VERSION_MAJOR == 1 && TAPI_API_VERSION_MINOR >= 2) || (TAPI_API_VERSION_MAJOR > 1))
112 case tapi::Platform::bridgeOS
:
113 return Options::kPlatform_bridgeOS
;
117 return Options::kPlatformUnknown
;
120 template <typename A
>
121 File
<A
>::File(const char* path
, const uint8_t* fileContent
, uint64_t fileLength
, const Options
*opts
,
122 time_t mTime
, ld::File::Ordinal ord
, bool linkingFlatNamespace
,
123 bool linkingMainExecutable
, bool hoistImplicitPublicDylibs
, Options::Platform platform
,
124 uint32_t linkMinOSVersion
, bool allowWeakImports
, cpu_type_t cpuType
, cpu_subtype_t cpuSubType
,
125 bool enforceDylibSubtypesMatch
, bool allowSimToMacOSX
, bool addVers
,
126 bool buildingForSimulator
, bool logAllFiles
, const char* targetInstallPath
,
128 : Base(strdup(path
), mTime
, ord
, platform
, linkMinOSVersion
, allowWeakImports
, linkingFlatNamespace
,
129 hoistImplicitPublicDylibs
, allowSimToMacOSX
, addVers
)
131 std::string errorMessage
;
133 // <rdar://problem/29038544> Support $ld$weak symbols in .tbd files
134 #if ((TAPI_API_VERSION_MAJOR == 1 && TAPI_API_VERSION_MINOR >= 3) || (TAPI_API_VERSION_MAJOR > 1))
135 // Check if the library supports the new create API.
136 if (tapi::APIVersion::isAtLeast(1, 3)) {
137 tapi::ParsingFlags flags
= tapi::ParsingFlags::None
;
138 if (enforceDylibSubtypesMatch
)
139 flags
|= tapi::ParsingFlags::ExactCpuSubType
;
141 if (!allowWeakImports
)
142 flags
|= tapi::ParsingFlags::DisallowWeakImports
;
144 _interface
.reset(tapi::LinkerInterfaceFile::create(
145 path
, cpuType
, cpuSubType
, flags
,
146 tapi::PackedVersion32(linkMinOSVersion
), errorMessage
));
149 #if ((TAPI_API_VERSION_MAJOR == 1 && TAPI_API_VERSION_MINOR >= 1) || (TAPI_API_VERSION_MAJOR > 1))
150 if (tapi::APIVersion::isAtLeast(1, 1)) {
151 tapi::ParsingFlags flags
= tapi::ParsingFlags::None
;
152 if (enforceDylibSubtypesMatch
)
153 flags
|= tapi::ParsingFlags::ExactCpuSubType
;
155 if (!allowWeakImports
)
156 flags
|= tapi::ParsingFlags::DisallowWeakImports
;
158 _interface
.reset(tapi::LinkerInterfaceFile::create(
159 path
, fileContent
, fileLength
, cpuType
, cpuSubType
, flags
,
160 tapi::PackedVersion32(linkMinOSVersion
), errorMessage
));
163 #if (TAPI_API_VERSION_MAJOR >= 1)
165 auto matchingType
= enforceDylibSubtypesMatch
?
166 tapi::CpuSubTypeMatching::Exact
: tapi::CpuSubTypeMatching::ABI_Compatible
;
168 _interface
.reset(tapi::LinkerInterfaceFile::create(
169 path
, fileContent
, fileLength
, cpuType
, cpuSubType
, matchingType
,
170 tapi::PackedVersion32(linkMinOSVersion
), errorMessage
));
175 throw strdup(errorMessage
.c_str());
177 // unmap file - it is no longer needed.
178 munmap((caddr_t
)fileContent
, fileLength
);
180 // write out path for -t option
182 printf("%s\n", path
);
184 init(_interface
.get(), opts
, buildingForSimulator
, indirectDylib
, linkingFlatNamespace
,
185 linkingMainExecutable
, path
, platform
, targetInstallPath
);
189 File
<A
>::File(std::unique_ptr
<tapi::LinkerInterfaceFile
> &&file
, const char* path
, const Options
*opts
,
190 time_t mTime
, ld::File::Ordinal ordinal
, bool linkingFlatNamespace
,
191 bool linkingMainExecutable
, bool hoistImplicitPublicDylibs
,
192 Options::Platform platform
, uint32_t linkMinOSVersion
, bool allowWeakImports
,
193 cpu_type_t cpuType
, cpu_subtype_t cpuSubType
, bool enforceDylibSubtypesMatch
,
194 bool allowSimToMacOSX
, bool addVers
, bool buildingForSimulator
,
195 bool logAllFiles
, const char* installPath
, bool indirectDylib
)
196 : Base(strdup(path
), mTime
, ordinal
, platform
, linkMinOSVersion
, allowWeakImports
, linkingFlatNamespace
,
197 hoistImplicitPublicDylibs
, allowSimToMacOSX
, addVers
), _interface(std::move(file
))
199 init(_interface
.get(), opts
, buildingForSimulator
, indirectDylib
, linkingFlatNamespace
,
200 linkingMainExecutable
, path
, platform
, installPath
);
204 void File
<A
>::init(tapi::LinkerInterfaceFile
*file
, const Options
*opts
, bool buildingForSimulator
,
205 bool indirectDylib
, bool linkingFlatNamespace
, bool linkingMainExecutable
,
206 const char *path
, Options::Platform platform
, const char *targetInstallPath
) {
208 this->_bitcode
= std::unique_ptr
<ld::Bitcode
>(new ld::Bitcode(nullptr, 0));
209 this->_noRexports
= !file
->hasReexportedLibraries();
210 this->_hasWeakExports
= file
->hasWeakDefinedExports();
211 this->_dylibInstallPath
= strdup(file
->getInstallName().c_str());
212 this->_installPathOverride
= file
->isInstallNameVersionSpecific();
213 this->_dylibCurrentVersion
= file
->getCurrentVersion();
214 this->_dylibCompatibilityVersion
= file
->getCompatibilityVersion();
215 this->_swiftVersion
= file
->getSwiftVersion();
216 this->_objcConstraint
= mapObjCConstraint(file
->getObjCConstraint());
217 this->_parentUmbrella
= file
->getParentFrameworkName().empty() ? nullptr : strdup(file
->getParentFrameworkName().c_str());
218 this->_appExtensionSafe
= file
->isApplicationExtensionSafe();
220 // if framework, capture framework name
221 const char* lastSlash
= strrchr(this->_dylibInstallPath
, '/');
222 if ( lastSlash
!= NULL
) {
223 const char* leafName
= lastSlash
+1;
224 char frname
[strlen(leafName
)+32];
225 strcpy(frname
, leafName
);
226 strcat(frname
, ".framework/");
228 if ( strstr(this->_dylibInstallPath
, frname
) != NULL
)
229 this->_frameworkName
= leafName
;
232 for (auto &client
: file
->allowableClients())
233 this->_allowableClients
.push_back(strdup(client
.c_str()));
235 // <rdar://problem/20659505> [TAPI] Don't hoist "public" (in /usr/lib/) dylibs that should not be directly linked
236 this->_hasPublicInstallName
= file
->hasAllowableClients() ? false : this->isPublicLocation(file
->getInstallName().c_str());
238 for (const auto &client
: file
->allowableClients())
239 this->_allowableClients
.emplace_back(strdup(client
.c_str()));
241 auto dylibPlatform
= mapPlatform(file
->getPlatform());
242 if ( (dylibPlatform
!= platform
) && (platform
!= Options::kPlatformUnknown
) ) {
243 this->_wrongOS
= true;
244 if ( this->_addVersionLoadCommand
&& !indirectDylib
) {
245 if ( buildingForSimulator
) {
246 if ( !this->_allowSimToMacOSXLinking
)
247 throwf("building for %s simulator, but linking against dylib built for %s (%s).",
248 Options::platformName(platform
), Options::platformName(dylibPlatform
), path
);
250 throwf("building for %s, but linking against dylib built for %s (%s).",
251 Options::platformName(platform
), Options::platformName(dylibPlatform
), path
);
256 for (const auto& reexport
: file
->reexportedLibraries()) {
257 const char *path
= strdup(reexport
.c_str());
258 if ( (targetInstallPath
== nullptr) || (strcmp(targetInstallPath
, path
) != 0) )
259 this->_dependentDylibs
.emplace_back(path
, true);
262 for (const auto& symbol
: file
->ignoreExports())
263 this->_ignoreExports
.insert(strdup(symbol
.c_str()));
265 // if linking flat and this is a flat dylib, create one atom that references all imported symbols.
266 if ( linkingFlatNamespace
&& linkingMainExecutable
&& (file
->hasTwoLevelNamespace() == false) ) {
267 std::vector
<const char*> importNames
;
268 importNames
.reserve(file
->undefineds().size());
269 // We do not need to strdup the name, because that will be done by the
270 // ImportAtom constructor.
271 for (const auto &sym
: file
->undefineds())
272 importNames
.emplace_back(sym
.getName().c_str());
273 this->_importAtom
= new generic::dylib::ImportAtom
<A
>(*this, importNames
);
277 buildExportHashTable(file
);
280 template <typename A
>
281 void File
<A
>::buildExportHashTable(const tapi::LinkerInterfaceFile
* file
) {
282 if (this->_s_logHashtable
)
283 fprintf(stderr
, "ld: building hashtable from text-stub info in %s\n", this->path());
285 for (const auto &sym
: file
->exports()) {
286 const char* name
= sym
.getName().c_str();
287 bool weakDef
= sym
.isWeakDefined();
288 bool tlv
= sym
.isThreadLocalValue();
290 typename
Base::AtomAndWeak bucket
= { nullptr, weakDef
, tlv
, 0 };
291 if ( this->_s_logHashtable
)
292 fprintf(stderr
, " adding %s to hash table for %s\n", name
, this->path());
293 this->_atoms
[strdup(name
)] = bucket
;
297 template <typename A
>
298 void File
<A
>::processIndirectLibraries(ld::dylib::File::DylibHandler
* handler
, bool addImplicitDylibs
) {
300 _opts
->addTAPIInterface(_interface
.get(), this->path());
301 Base::processIndirectLibraries(handler
, addImplicitDylibs
);
304 template <typename A
>
308 using P
= typename
A::P
;
310 static ld::dylib::File
* parse(const char* path
, const uint8_t* fileContent
,
311 uint64_t fileLength
, time_t mTime
,
312 ld::File::Ordinal ordinal
, const Options
& opts
,
315 return new File
<A
>(path
, fileContent
, fileLength
, &opts
, mTime
, ordinal
,
316 opts
.flatNamespace(),
317 opts
.linkingMainExecutable(),
318 opts
.implicitlyLinkIndirectPublicDylibs(),
321 opts
.allowWeakImports(),
323 opts
.subArchitecture(),
324 opts
.enforceDylibSubtypesMatch(),
325 opts
.allowSimulatorToLinkWithMacOSX(),
326 opts
.addVersionLoadCommand(),
327 opts
.targetIOSSimulator(),
333 static ld::dylib::File
* parse(const char* path
, std::unique_ptr
<tapi::LinkerInterfaceFile
> &&file
, time_t mTime
,
334 ld::File::Ordinal ordinal
, const Options
& opts
,
337 return new File
<A
>(std::move(file
), path
, &opts
, mTime
, ordinal
,
338 opts
.flatNamespace(),
339 opts
.linkingMainExecutable(),
340 opts
.implicitlyLinkIndirectPublicDylibs(),
343 opts
.allowWeakImports(),
345 opts
.subArchitecture(),
346 opts
.enforceDylibSubtypesMatch(),
347 opts
.allowSimulatorToLinkWithMacOSX(),
348 opts
.addVersionLoadCommand(),
349 opts
.targetIOSSimulator(),
358 // main function used by linker to instantiate ld::Files
360 ld::dylib::File
* parse(const uint8_t* fileContent
, uint64_t fileLength
, const char* path
,
361 time_t modTime
, const Options
& opts
, ld::File::Ordinal ordinal
,
362 bool bundleLoader
, bool indirectDylib
)
365 switch ( opts
.architecture() ) {
366 #if SUPPORT_ARCH_x86_64
367 case CPU_TYPE_X86_64
:
368 if (tapi::LinkerInterfaceFile::isSupported(path
, fileContent
, fileLength
))
369 return Parser
<x86_64
>::parse(path
, fileContent
, fileLength
, modTime
, ordinal
, opts
, indirectDylib
);
371 #if SUPPORT_ARCH_i386
373 if (tapi::LinkerInterfaceFile::isSupported(path
, fileContent
, fileLength
))
374 return Parser
<x86
>::parse(path
, fileContent
, fileLength
, modTime
, ordinal
, opts
, indirectDylib
);
376 #if SUPPORT_ARCH_arm_any
378 if (tapi::LinkerInterfaceFile::isSupported(path
, fileContent
, fileLength
))
379 return Parser
<arm
>::parse(path
, fileContent
, fileLength
, modTime
, ordinal
, opts
, indirectDylib
);
381 #if SUPPORT_ARCH_arm64
383 if (tapi::LinkerInterfaceFile::isSupported(path
, fileContent
, fileLength
))
384 return Parser
<arm64
>::parse(path
, fileContent
, fileLength
, modTime
, ordinal
, opts
, indirectDylib
);
390 ld::dylib::File
*parse(const char *path
, std::unique_ptr
<tapi::LinkerInterfaceFile
> &&file
, time_t modTime
,
391 ld::File::Ordinal ordinal
, const Options
& opts
, bool indirectDylib
) {
392 switch ( opts
.architecture() ) {
393 #if SUPPORT_ARCH_x86_64
394 case CPU_TYPE_X86_64
:
395 return Parser
<x86_64
>::parse(path
, std::move(file
), modTime
, ordinal
, opts
, indirectDylib
);
397 #if SUPPORT_ARCH_i386
399 return Parser
<x86
>::parse(path
, std::move(file
), modTime
, ordinal
, opts
, indirectDylib
);
401 #if SUPPORT_ARCH_arm_any
403 return Parser
<arm
>::parse(path
, std::move(file
), modTime
, ordinal
, opts
, indirectDylib
);
405 #if SUPPORT_ARCH_arm64
407 return Parser
<arm64
>::parse(path
, std::move(file
), modTime
, ordinal
, opts
, indirectDylib
);
416 } // namespace textstub