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
;
95 #if ((TAPI_API_VERSION_MAJOR == 1 && TAPI_API_VERSION_MINOR >= 2) || (TAPI_API_VERSION_MAJOR > 1))
96 case tapi::Platform::bridgeOS
:
97 return Options::kPlatform_bridgeOS
;
101 return Options::kPlatformUnknown
;
104 template <typename A
>
105 File
<A
>::File(const char* path
, const uint8_t* fileContent
, uint64_t fileLength
,
106 time_t mTime
, ld::File::Ordinal ord
, bool linkingFlatNamespace
,
107 bool linkingMainExecutable
, bool hoistImplicitPublicDylibs
, Options::Platform platform
,
108 uint32_t linkMinOSVersion
, bool allowWeakImports
, cpu_type_t cpuType
, cpu_subtype_t cpuSubType
,
109 bool enforceDylibSubtypesMatch
, bool allowSimToMacOSX
, bool addVers
,
110 bool buildingForSimulator
, bool logAllFiles
, const char* targetInstallPath
,
112 : Base(strdup(path
), mTime
, ord
, platform
, linkMinOSVersion
, allowWeakImports
, linkingFlatNamespace
,
113 hoistImplicitPublicDylibs
, allowSimToMacOSX
, addVers
)
115 std::unique_ptr
<tapi::LinkerInterfaceFile
> file
;
116 std::string errorMessage
;
118 // <rdar://problem/29038544> Support $ld$weak symbols in .tbd files
119 #if ((TAPI_API_VERSION_MAJOR == 1 && TAPI_API_VERSION_MINOR >= 1) || (TAPI_API_VERSION_MAJOR > 1))
120 // Check if the library supports the new create API.
121 if (tapi::APIVersion::isAtLeast(1, 1)) {
122 tapi::ParsingFlags flags
= tapi::ParsingFlags::None
;
123 if (enforceDylibSubtypesMatch
)
124 flags
|= tapi::ParsingFlags::ExactCpuSubType
;
126 if (!allowWeakImports
)
127 flags
|= tapi::ParsingFlags::DisallowWeakImports
;
129 file
.reset(tapi::LinkerInterfaceFile::create(
130 path
, fileContent
, fileLength
, cpuType
, cpuSubType
, flags
,
131 tapi::PackedVersion32(linkMinOSVersion
), errorMessage
));
133 auto matchingType
= enforceDylibSubtypesMatch
?
134 tapi::CpuSubTypeMatching::Exact
: tapi::CpuSubTypeMatching::ABI_Compatible
;
136 file
.reset(tapi::LinkerInterfaceFile::create(
137 path
, fileContent
, fileLength
, cpuType
, cpuSubType
, matchingType
,
138 tapi::PackedVersion32(linkMinOSVersion
), errorMessage
));
141 auto matchingType
= enforceDylibSubtypesMatch
?
142 tapi::CpuSubTypeMatching::Exact
: tapi::CpuSubTypeMatching::ABI_Compatible
;
144 file
.reset(tapi::LinkerInterfaceFile::create(
145 path
, fileContent
, fileLength
, cpuType
, cpuSubType
, matchingType
,
146 tapi::PackedVersion32(linkMinOSVersion
), errorMessage
));
150 throw strdup(errorMessage
.c_str());
152 // unmap file - it is no longer needed.
153 munmap((caddr_t
)fileContent
, fileLength
);
155 // write out path for -t option
157 printf("%s\n", path
);
159 this->_bitcode
= std::unique_ptr
<ld::Bitcode
>(new ld::Bitcode(nullptr, 0));
160 this->_noRexports
= !file
->hasReexportedLibraries();
161 this->_hasWeakExports
= file
->hasWeakDefinedExports();
162 this->_dylibInstallPath
= strdup(file
->getInstallName().c_str());
163 this->_installPathOverride
= file
->isInstallNameVersionSpecific();
164 this->_dylibCurrentVersion
= file
->getCurrentVersion();
165 this->_dylibCompatibilityVersion
= file
->getCompatibilityVersion();
166 this->_swiftVersion
= file
->getSwiftVersion();
167 this->_objcConstraint
= mapObjCConstraint(file
->getObjCConstraint());
168 this->_parentUmbrella
= file
->getParentFrameworkName().empty() ? nullptr : strdup(file
->getParentFrameworkName().c_str());
169 this->_appExtensionSafe
= file
->isApplicationExtensionSafe();
171 // if framework, capture framework name
172 const char* lastSlash
= strrchr(this->_dylibInstallPath
, '/');
173 if ( lastSlash
!= NULL
) {
174 const char* leafName
= lastSlash
+1;
175 char frname
[strlen(leafName
)+32];
176 strcpy(frname
, leafName
);
177 strcat(frname
, ".framework/");
179 if ( strstr(this->_dylibInstallPath
, frname
) != NULL
)
180 this->_frameworkName
= leafName
;
183 for (auto &client
: file
->allowableClients())
184 this->_allowableClients
.push_back(strdup(client
.c_str()));
186 // <rdar://problem/20659505> [TAPI] Don't hoist "public" (in /usr/lib/) dylibs that should not be directly linked
187 this->_hasPublicInstallName
= file
->hasAllowableClients() ? false : this->isPublicLocation(file
->getInstallName().c_str());
189 for (const auto &client
: file
->allowableClients())
190 this->_allowableClients
.emplace_back(strdup(client
.c_str()));
192 auto dylibPlatform
= mapPlatform(file
->getPlatform());
193 if ( (dylibPlatform
!= platform
) && (platform
!= Options::kPlatformUnknown
) ) {
194 this->_wrongOS
= true;
195 if ( this->_addVersionLoadCommand
&& !indirectDylib
) {
196 if ( buildingForSimulator
) {
197 if ( !this->_allowSimToMacOSXLinking
)
198 throwf("building for %s simulator, but linking against dylib built for %s (%s).",
199 Options::platformName(platform
), Options::platformName(dylibPlatform
), path
);
201 throwf("building for %s, but linking against dylib built for %s (%s).",
202 Options::platformName(platform
), Options::platformName(dylibPlatform
), path
);
207 for (const auto& reexport
: file
->reexportedLibraries()) {
208 const char *path
= strdup(reexport
.c_str());
209 if ( (targetInstallPath
== nullptr) || (strcmp(targetInstallPath
, path
) != 0) )
210 this->_dependentDylibs
.emplace_back(path
, true);
213 for (const auto& symbol
: file
->ignoreExports())
214 this->_ignoreExports
.insert(strdup(symbol
.c_str()));
216 // if linking flat and this is a flat dylib, create one atom that references all imported symbols.
217 if ( linkingFlatNamespace
&& linkingMainExecutable
&& (file
->hasTwoLevelNamespace() == false) ) {
218 std::vector
<const char*> importNames
;
219 importNames
.reserve(file
->undefineds().size());
220 // We do not need to strdup the name, because that will be done by the
221 // ImportAtom constructor.
222 for (const auto &sym
: file
->undefineds())
223 importNames
.emplace_back(sym
.getName().c_str());
224 this->_importAtom
= new generic::dylib::ImportAtom
<A
>(*this, importNames
);
228 buildExportHashTable(file
.get());
231 template <typename A
>
232 void File
<A
>::buildExportHashTable(const tapi::LinkerInterfaceFile
* file
) {
233 if (this->_s_logHashtable
)
234 fprintf(stderr
, "ld: building hashtable from text-stub info in %s\n", this->path());
236 for (const auto &sym
: file
->exports()) {
237 const char* name
= sym
.getName().c_str();
238 bool weakDef
= sym
.isWeakDefined();
239 bool tlv
= sym
.isThreadLocalValue();
241 typename
Base::AtomAndWeak bucket
= { nullptr, weakDef
, tlv
, 0 };
242 if ( this->_s_logHashtable
)
243 fprintf(stderr
, " adding %s to hash table for %s\n", name
, this->path());
244 this->_atoms
[strdup(name
)] = bucket
;
248 template <typename A
>
252 using P
= typename
A::P
;
254 static ld::dylib::File
* parse(const char* path
, const uint8_t* fileContent
,
255 uint64_t fileLength
, time_t mTime
,
256 ld::File::Ordinal ordinal
, const Options
& opts
,
259 return new File
<A
>(path
, fileContent
, fileLength
,mTime
, ordinal
,
260 opts
.flatNamespace(),
261 opts
.linkingMainExecutable(),
262 opts
.implicitlyLinkIndirectPublicDylibs(),
265 opts
.allowWeakImports(),
267 opts
.subArchitecture(),
268 opts
.enforceDylibSubtypesMatch(),
269 opts
.allowSimulatorToLinkWithMacOSX(),
270 opts
.addVersionLoadCommand(),
271 opts
.targetIOSSimulator(),
279 // main function used by linker to instantiate ld::Files
281 ld::dylib::File
* parse(const uint8_t* fileContent
, uint64_t fileLength
, const char* path
,
282 time_t modTime
, const Options
& opts
, ld::File::Ordinal ordinal
,
283 bool bundleLoader
, bool indirectDylib
)
286 switch ( opts
.architecture() ) {
287 #if SUPPORT_ARCH_x86_64
288 case CPU_TYPE_X86_64
:
289 if (tapi::LinkerInterfaceFile::isSupported(path
, fileContent
, fileLength
))
290 return Parser
<x86_64
>::parse(path
, fileContent
, fileLength
, modTime
, ordinal
, opts
, indirectDylib
);
292 #if SUPPORT_ARCH_i386
294 if (tapi::LinkerInterfaceFile::isSupported(path
, fileContent
, fileLength
))
295 return Parser
<x86
>::parse(path
, fileContent
, fileLength
, modTime
, ordinal
, opts
, indirectDylib
);
297 #if SUPPORT_ARCH_arm_any
299 if (tapi::LinkerInterfaceFile::isSupported(path
, fileContent
, fileLength
))
300 return Parser
<arm
>::parse(path
, fileContent
, fileLength
, modTime
, ordinal
, opts
, indirectDylib
);
302 #if SUPPORT_ARCH_arm64
304 if (tapi::LinkerInterfaceFile::isSupported(path
, fileContent
, fileLength
))
305 return Parser
<arm64
>::parse(path
, fileContent
, fileLength
, modTime
, ordinal
, opts
, indirectDylib
);
313 } // namespace textstub