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
,
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 virtual ~File() noexcept {}
63 void buildExportHashTable(const tapi::LinkerInterfaceFile
* file
);
66 static ld::File::ObjcConstraint
mapObjCConstraint(tapi::ObjCConstraint constraint
) {
68 case tapi::ObjCConstraint::None
:
69 return ld::File::objcConstraintNone
;
70 case tapi::ObjCConstraint::Retain_Release
:
71 return ld::File::objcConstraintRetainRelease
;
72 case tapi::ObjCConstraint::Retain_Release_For_Simulator
:
73 return ld::File::objcConstraintRetainReleaseForSimulator
;
74 case tapi::ObjCConstraint::Retain_Release_Or_GC
:
75 return ld::File::objcConstraintRetainReleaseOrGC
;
76 case tapi::ObjCConstraint::GC
:
77 return ld::File::objcConstraintGC
;
80 return ld::File::objcConstraintNone
;
83 static Options::Platform
mapPlatform(tapi::Platform platform
) {
85 case tapi::Platform::Unknown
:
86 return Options::kPlatformUnknown
;
87 case tapi::Platform::OSX
:
88 return Options::kPlatformOSX
;
89 case tapi::Platform::iOS
:
90 return Options::kPlatformiOS
;
91 case tapi::Platform::watchOS
:
92 return Options::kPlatformWatchOS
;
93 case tapi::Platform::tvOS
:
94 return Options::kPlatform_tvOS
;
97 return Options::kPlatformUnknown
;
100 template <typename A
>
101 File
<A
>::File(const char* path
, const uint8_t* fileContent
, uint64_t fileLength
,
102 time_t mTime
, ld::File::Ordinal ord
, bool linkingFlatNamespace
,
103 bool linkingMainExecutable
, bool hoistImplicitPublicDylibs
, Options::Platform platform
,
104 uint32_t linkMinOSVersion
, bool allowWeakImports
, cpu_type_t cpuType
, cpu_subtype_t cpuSubType
,
105 bool enforceDylibSubtypesMatch
, bool allowSimToMacOSX
, bool addVers
,
106 bool buildingForSimulator
, bool logAllFiles
, const char* targetInstallPath
,
108 : Base(strdup(path
), mTime
, ord
, platform
, linkMinOSVersion
, allowWeakImports
, linkingFlatNamespace
,
109 hoistImplicitPublicDylibs
, allowSimToMacOSX
, addVers
)
111 auto matchingType
= enforceDylibSubtypesMatch
?
112 tapi::CpuSubTypeMatching::Exact
: tapi::CpuSubTypeMatching::ABI_Compatible
;
114 std::string errorMessage
;
115 auto file
= std::unique_ptr
<tapi::LinkerInterfaceFile
>(
116 tapi::LinkerInterfaceFile::create(path
, fileContent
, fileLength
, cpuType
,
117 cpuSubType
, matchingType
,
118 tapi::PackedVersion32(linkMinOSVersion
), errorMessage
));
121 throw strdup(errorMessage
.c_str());
123 // unmap file - it is no longer needed.
124 munmap((caddr_t
)fileContent
, fileLength
);
126 // write out path for -t option
128 printf("%s\n", path
);
130 this->_bitcode
= std::unique_ptr
<ld::Bitcode
>(new ld::Bitcode(nullptr, 0));
131 this->_noRexports
= !file
->hasReexportedLibraries();
132 this->_hasWeakExports
= file
->hasWeakDefinedExports();
133 this->_dylibInstallPath
= strdup(file
->getInstallName().c_str());
134 this->_installPathOverride
= file
->isInstallNameVersionSpecific();
135 this->_dylibCurrentVersion
= file
->getCurrentVersion();
136 this->_dylibCompatibilityVersion
= file
->getCompatibilityVersion();
137 this->_swiftVersion
= file
->getSwiftVersion();
138 this->_objcConstraint
= mapObjCConstraint(file
->getObjCConstraint());
139 this->_parentUmbrella
= file
->getParentFrameworkName().empty() ? nullptr : strdup(file
->getParentFrameworkName().c_str());
140 this->_appExtensionSafe
= file
->isApplicationExtensionSafe();
142 // if framework, capture framework name
143 const char* lastSlash
= strrchr(this->_dylibInstallPath
, '/');
144 if ( lastSlash
!= NULL
) {
145 const char* leafName
= lastSlash
+1;
146 char frname
[strlen(leafName
)+32];
147 strcpy(frname
, leafName
);
148 strcat(frname
, ".framework/");
150 if ( strstr(this->_dylibInstallPath
, frname
) != NULL
)
151 this->_frameworkName
= leafName
;
154 for (auto &client
: file
->allowableClients())
155 this->_allowableClients
.push_back(strdup(client
.c_str()));
157 // <rdar://problem/20659505> [TAPI] Don't hoist "public" (in /usr/lib/) dylibs that should not be directly linked
158 this->_hasPublicInstallName
= file
->hasAllowableClients() ? false : this->isPublicLocation(file
->getInstallName().c_str());
160 for (const auto &client
: file
->allowableClients())
161 this->_allowableClients
.emplace_back(strdup(client
.c_str()));
163 auto dylibPlatform
= mapPlatform(file
->getPlatform());
164 if ( (dylibPlatform
!= platform
) && (platform
!= Options::kPlatformUnknown
) ) {
165 this->_wrongOS
= true;
166 if ( this->_addVersionLoadCommand
&& !indirectDylib
) {
167 if ( buildingForSimulator
) {
168 if ( !this->_allowSimToMacOSXLinking
)
169 throwf("building for %s simulator, but linking against dylib built for %s (%s).",
170 Options::platformName(platform
), Options::platformName(dylibPlatform
), path
);
172 throwf("building for %s, but linking against dylib built for %s (%s).",
173 Options::platformName(platform
), Options::platformName(dylibPlatform
), path
);
178 for (const auto& reexport
: file
->reexportedLibraries()) {
179 const char *path
= strdup(reexport
.c_str());
180 if ( (targetInstallPath
== nullptr) || (strcmp(targetInstallPath
, path
) != 0) )
181 this->_dependentDylibs
.emplace_back(path
, true);
184 for (const auto& symbol
: file
->ignoreExports())
185 this->_ignoreExports
.insert(strdup(symbol
.c_str()));
187 // if linking flat and this is a flat dylib, create one atom that references all imported symbols.
188 if ( linkingFlatNamespace
&& linkingMainExecutable
&& (file
->hasTwoLevelNamespace() == false) ) {
189 std::vector
<const char*> importNames
;
190 importNames
.reserve(file
->undefineds().size());
191 // We do not need to strdup the name, because that will be done by the
192 // ImportAtom constructor.
193 for (const auto &sym
: file
->undefineds())
194 importNames
.emplace_back(sym
.getName().c_str());
195 this->_importAtom
= new generic::dylib::ImportAtom
<A
>(*this, importNames
);
199 buildExportHashTable(file
.get());
202 template <typename A
>
203 void File
<A
>::buildExportHashTable(const tapi::LinkerInterfaceFile
* file
) {
204 if (this->_s_logHashtable
)
205 fprintf(stderr
, "ld: building hashtable from text-stub info in %s\n", this->path());
207 for (const auto &sym
: file
->exports()) {
208 const char* name
= sym
.getName().c_str();
209 bool weakDef
= sym
.isWeakDefined();
210 bool tlv
= sym
.isThreadLocalValue();
212 typename
Base::AtomAndWeak bucket
= { nullptr, weakDef
, tlv
, 0 };
213 if ( this->_s_logHashtable
)
214 fprintf(stderr
, " adding %s to hash table for %s\n", name
, this->path());
215 this->_atoms
[strdup(name
)] = bucket
;
219 template <typename A
>
223 using P
= typename
A::P
;
225 static ld::dylib::File
* parse(const char* path
, const uint8_t* fileContent
,
226 uint64_t fileLength
, time_t mTime
,
227 ld::File::Ordinal ordinal
, const Options
& opts
,
230 return new File
<A
>(path
, fileContent
, fileLength
,mTime
, ordinal
,
231 opts
.flatNamespace(),
232 opts
.linkingMainExecutable(),
233 opts
.implicitlyLinkIndirectPublicDylibs(),
236 opts
.allowWeakImports(),
238 opts
.subArchitecture(),
239 opts
.enforceDylibSubtypesMatch(),
240 opts
.allowSimulatorToLinkWithMacOSX(),
241 opts
.addVersionLoadCommand(),
242 opts
.targetIOSSimulator(),
250 // main function used by linker to instantiate ld::Files
252 ld::dylib::File
* parse(const uint8_t* fileContent
, uint64_t fileLength
, const char* path
,
253 time_t modTime
, const Options
& opts
, ld::File::Ordinal ordinal
,
254 bool bundleLoader
, bool indirectDylib
)
257 switch ( opts
.architecture() ) {
258 #if SUPPORT_ARCH_x86_64
259 case CPU_TYPE_X86_64
:
260 if (tapi::LinkerInterfaceFile::isSupported(path
, fileContent
, fileLength
))
261 return Parser
<x86_64
>::parse(path
, fileContent
, fileLength
, modTime
, ordinal
, opts
, indirectDylib
);
263 #if SUPPORT_ARCH_i386
265 if (tapi::LinkerInterfaceFile::isSupported(path
, fileContent
, fileLength
))
266 return Parser
<x86
>::parse(path
, fileContent
, fileLength
, modTime
, ordinal
, opts
, indirectDylib
);
268 #if SUPPORT_ARCH_arm_any
270 if (tapi::LinkerInterfaceFile::isSupported(path
, fileContent
, fileLength
))
271 return Parser
<arm
>::parse(path
, fileContent
, fileLength
, modTime
, ordinal
, opts
, indirectDylib
);
273 #if SUPPORT_ARCH_arm64
275 if (tapi::LinkerInterfaceFile::isSupported(path
, fileContent
, fileLength
))
276 return Parser
<arm64
>::parse(path
, fileContent
, fileLength
, modTime
, ordinal
, opts
, indirectDylib
);
284 } // namespace textstub