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 const ld::VersionSet
& platforms
, 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 bool ignoreMismatchPlatform
, bool usingBitcode
);
61 File(tapi::LinkerInterfaceFile
* file
, const char *path
, const Options
*opts
,
62 time_t mTime
, ld::File::Ordinal ordinal
, bool linkingFlatNamespace
,
63 bool linkingMainExecutable
, bool hoistImplicitPublicDylibs
,
64 const ld::VersionSet
& platforms
, bool allowWeakImports
,
65 cpu_type_t cpuType
, cpu_subtype_t cpuSubType
, bool enforceDylibSubtypesMatch
,
66 bool allowSimToMacOSX
, bool addVers
, bool buildingForSimulator
,
67 bool logAllFiles
, const char* installPath
, bool indirectDylib
,
68 bool ignoreMismatchPlatform
, bool usingBitcode
);
69 virtual ~File() noexcept {}
71 // overrides of generic::dylib::File
72 virtual void processIndirectLibraries(ld::dylib::File::DylibHandler
*, bool addImplicitDylibs
) override final
;
75 void init(tapi::LinkerInterfaceFile
* file
, const Options
*opts
, bool buildingForSimulator
,
76 bool indirectDylib
, bool linkingFlatNamespace
, bool linkingMainExecutable
,
77 const char *path
, const ld::VersionSet
& platforms
, const char *targetInstallPath
,
78 bool ignoreMismatchPlatform
, bool usingBitcode
);
79 void buildExportHashTable(const tapi::LinkerInterfaceFile
* file
);
80 static bool useSimulatorVariant();
83 tapi::LinkerInterfaceFile
* _interface
;
86 template <> bool File
<x86
>::useSimulatorVariant() { return true; }
87 template <> bool File
<x86_64
>::useSimulatorVariant() { return true; }
88 template <typename A
> bool File
<A
>::useSimulatorVariant() { return false; }
91 static ld::VersionSet
mapPlatform(tapi::Platform platform
, bool useSimulatorVariant
) {
92 ld::VersionSet platforms
;
94 case tapi::Platform::Unknown
:
96 case tapi::Platform::OSX
:
97 platforms
.add({ld::kPlatform_macOS
, 0});
99 case tapi::Platform::iOS
:
100 if (useSimulatorVariant
)
101 platforms
.add({ld::kPlatform_iOSSimulator
, 0});
103 platforms
.add({ld::kPlatform_iOS
, 0});
105 case tapi::Platform::watchOS
:
106 if (useSimulatorVariant
)
107 platforms
.add({ld::kPlatform_watchOSSimulator
, 0});
109 platforms
.add({ld::kPlatform_watchOS
, 0});
111 case tapi::Platform::tvOS
:
112 if (useSimulatorVariant
)
113 platforms
.add({ld::kPlatform_tvOSSimulator
, 0});
115 platforms
.add({ld::kPlatform_tvOS
, 0});
117 #if ((TAPI_API_VERSION_MAJOR == 1 && TAPI_API_VERSION_MINOR >= 2) || (TAPI_API_VERSION_MAJOR > 1))
118 case tapi::Platform::bridgeOS
:
119 platforms
.add({ld::kPlatform_bridgeOS
, 0});
122 #if ((TAPI_API_VERSION_MAJOR == 1 && TAPI_API_VERSION_MINOR >= 4) || (TAPI_API_VERSION_MAJOR > 1))
123 case tapi::Platform::iOSMac
:
124 platforms
.add({ld::kPlatform_iOSMac
, 0});
126 case tapi::Platform::zippered
:
127 platforms
.add({ld::kPlatform_macOS
, 0});
128 platforms
.add({ld::kPlatform_iOSMac
, 0});
136 template <typename A
>
137 File
<A
>::File(const char* path
, const uint8_t* fileContent
, uint64_t fileLength
, const Options
*opts
,
138 time_t mTime
, ld::File::Ordinal ord
, bool linkingFlatNamespace
,
139 bool linkingMainExecutable
, bool hoistImplicitPublicDylibs
, const ld::VersionSet
& platforms
,
140 bool allowWeakImports
, cpu_type_t cpuType
, cpu_subtype_t cpuSubType
,
141 bool enforceDylibSubtypesMatch
, bool allowSimToMacOSX
, bool addVers
,
142 bool buildingForSimulator
, bool logAllFiles
, const char* targetInstallPath
,
143 bool indirectDylib
, bool ignoreMismatchPlatform
, bool usingBitcode
)
144 : Base(strdup(path
), mTime
, ord
, platforms
, allowWeakImports
, linkingFlatNamespace
,
145 hoistImplicitPublicDylibs
, allowSimToMacOSX
, addVers
)
147 std::unique_ptr
<tapi::LinkerInterfaceFile
> file
;
148 std::string errorMessage
;
149 __block
uint32_t linkMinOSVersion
= 0;
150 //FIXME handle this correctly once we have multi-platfrom TAPI
151 platforms
.forEach(^(ld::Platform platform
, uint32_t version
, bool &stop
) {
152 if (linkMinOSVersion
== 0)
153 linkMinOSVersion
= version
;
154 if (platform
== ld::kPlatform_macOS
)
155 linkMinOSVersion
= version
;
158 // <rdar://problem/29038544> Support $ld$weak symbols in .tbd files
159 #if ((TAPI_API_VERSION_MAJOR == 1 && TAPI_API_VERSION_MINOR >= 3) || (TAPI_API_VERSION_MAJOR > 1))
160 // Check if the library supports the new create API.
161 if (tapi::APIVersion::isAtLeast(1, 3)) {
162 tapi::ParsingFlags flags
= tapi::ParsingFlags::None
;
163 if (enforceDylibSubtypesMatch
)
164 flags
|= tapi::ParsingFlags::ExactCpuSubType
;
166 if (!allowWeakImports
)
167 flags
|= tapi::ParsingFlags::DisallowWeakImports
;
169 _interface
= tapi::LinkerInterfaceFile::create(
170 path
, cpuType
, cpuSubType
, flags
,
171 tapi::PackedVersion32(linkMinOSVersion
), errorMessage
);
174 #if ((TAPI_API_VERSION_MAJOR == 1 && TAPI_API_VERSION_MINOR >= 1) || (TAPI_API_VERSION_MAJOR > 1))
175 if (tapi::APIVersion::isAtLeast(1, 1)) {
176 tapi::ParsingFlags flags
= tapi::ParsingFlags::None
;
177 if (enforceDylibSubtypesMatch
)
178 flags
|= tapi::ParsingFlags::ExactCpuSubType
;
180 if (!allowWeakImports
)
181 flags
|= tapi::ParsingFlags::DisallowWeakImports
;
183 _interface
= tapi::LinkerInterfaceFile::create(
184 path
, fileContent
, fileLength
, cpuType
, cpuSubType
, flags
,
185 tapi::PackedVersion32(linkMinOSVersion
), errorMessage
);
188 #if (TAPI_API_VERSION_MAJOR >= 1)
190 auto matchingType
= enforceDylibSubtypesMatch
?
191 tapi::CpuSubTypeMatching::Exact
: tapi::CpuSubTypeMatching::ABI_Compatible
;
193 _interface
= tapi::LinkerInterfaceFile::create(
194 path
, fileContent
, fileLength
, cpuType
, cpuSubType
, matchingType
,
195 tapi::PackedVersion32(linkMinOSVersion
), errorMessage
);
200 throw strdup(errorMessage
.c_str());
202 // unmap file - it is no longer needed.
203 munmap((caddr_t
)fileContent
, fileLength
);
205 // write out path for -t option
207 printf("%s\n", path
);
209 init(_interface
, opts
, buildingForSimulator
, indirectDylib
, linkingFlatNamespace
,
210 linkingMainExecutable
, path
, platforms
, targetInstallPath
, ignoreMismatchPlatform
, usingBitcode
);
214 File
<A
>::File(tapi::LinkerInterfaceFile
* file
, const char* path
, const Options
*opts
,
215 time_t mTime
, ld::File::Ordinal ordinal
, bool linkingFlatNamespace
,
216 bool linkingMainExecutable
, bool hoistImplicitPublicDylibs
,
217 const ld::VersionSet
& platforms
, bool allowWeakImports
,
218 cpu_type_t cpuType
, cpu_subtype_t cpuSubType
, bool enforceDylibSubtypesMatch
,
219 bool allowSimToMacOSX
, bool addVers
, bool buildingForSimulator
,
220 bool logAllFiles
, const char* installPath
, bool indirectDylib
,
221 bool ignoreMismatchPlatform
, bool usingBitcode
)
222 : Base(strdup(path
), mTime
, ordinal
, platforms
, allowWeakImports
, linkingFlatNamespace
,
223 hoistImplicitPublicDylibs
, allowSimToMacOSX
, addVers
), _interface(file
)
225 init(_interface
, opts
, buildingForSimulator
, indirectDylib
, linkingFlatNamespace
,
226 linkingMainExecutable
, path
, platforms
, installPath
, ignoreMismatchPlatform
, usingBitcode
);
230 void File
<A
>::init(tapi::LinkerInterfaceFile
* file
, const Options
*opts
, bool buildingForSimulator
,
231 bool indirectDylib
, bool linkingFlatNamespace
, bool linkingMainExecutable
,
232 const char *path
, const ld::VersionSet
& platforms
, const char *targetInstallPath
,
233 bool ignoreMismatchPlatform
, bool usingBitcode
) {
235 this->_bitcode
= std::unique_ptr
<ld::Bitcode
>(new ld::Bitcode(nullptr, 0));
236 this->_noRexports
= !file
->hasReexportedLibraries();
237 this->_hasWeakExports
= file
->hasWeakDefinedExports();
238 this->_dylibInstallPath
= strdup(file
->getInstallName().c_str());
239 this->_installPathOverride
= file
->isInstallNameVersionSpecific();
240 this->_dylibCurrentVersion
= file
->getCurrentVersion();
241 this->_dylibCompatibilityVersion
= file
->getCompatibilityVersion();
242 this->_swiftVersion
= file
->getSwiftVersion();
243 this->_parentUmbrella
= file
->getParentFrameworkName().empty() ? nullptr : strdup(file
->getParentFrameworkName().c_str());
244 this->_appExtensionSafe
= file
->isApplicationExtensionSafe();
246 // if framework, capture framework name
247 const char* lastSlash
= strrchr(this->_dylibInstallPath
, '/');
248 if ( lastSlash
!= NULL
) {
249 const char* leafName
= lastSlash
+1;
250 char frname
[strlen(leafName
)+32];
251 strcpy(frname
, leafName
);
252 strcat(frname
, ".framework/");
254 if ( strstr(this->_dylibInstallPath
, frname
) != NULL
)
255 this->_frameworkName
= leafName
;
258 for (auto &client
: file
->allowableClients())
259 this->_allowableClients
.push_back(strdup(client
.c_str()));
261 // <rdar://problem/20659505> [TAPI] Don't hoist "public" (in /usr/lib/) dylibs that should not be directly linked
262 this->_hasPublicInstallName
= file
->hasAllowableClients() ? false : this->isPublicLocation(file
->getInstallName().c_str());
264 for (const auto &client
: file
->allowableClients())
265 this->_allowableClients
.emplace_back(strdup(client
.c_str()));
267 ld::VersionSet lcPlatforms
= mapPlatform(file
->getPlatform(), useSimulatorVariant());
268 this->_platforms
= lcPlatforms
;
270 // check cross-linking
271 platforms
.forEach(^(ld::Platform platform
, uint32_t version
, bool &stop
) {
272 if (!lcPlatforms
.contains(platform
) ) {
273 this->_wrongOS
= true;
274 if ( this->_addVersionLoadCommand
&& !indirectDylib
&& !ignoreMismatchPlatform
) {
275 if (buildingForSimulator
&& !this->_allowSimToMacOSXLinking
) {
277 throwf("building for %s simulator, but linking against dylib built for %s,",
278 platforms
.to_str().c_str(), lcPlatforms
.to_str().c_str());
280 warning("URGENT: building for %s simulator, but linking against dylib (%s) built for %s. "
281 "Note: This will be an error in the future.",
282 platforms
.to_str().c_str(), path
, lcPlatforms
.to_str().c_str());
286 throwf("building for %s, but linking against dylib built for %s,",
287 platforms
.to_str().c_str(), lcPlatforms
.to_str().c_str());
288 else if ( (getenv("RC_XBS") != NULL
) && (getenv("RC_BUILDIT") == NULL
) ) // FIXME: remove after platform bringup
289 warning("URGENT: building for %s, but linking against dylib (%s) built for %s. "
290 "Note: This will be an error in the future.",
291 platforms
.to_str().c_str(), path
, lcPlatforms
.to_str().c_str());
296 for (const auto& reexport
: file
->reexportedLibraries()) {
297 const char *path
= strdup(reexport
.c_str());
298 if ( (targetInstallPath
== nullptr) || (strcmp(targetInstallPath
, path
) != 0) )
299 this->_dependentDylibs
.emplace_back(path
, true);
302 for (const auto& symbol
: file
->ignoreExports())
303 this->_ignoreExports
.insert(strdup(symbol
.c_str()));
305 // if linking flat and this is a flat dylib, create one atom that references all imported symbols.
306 if ( linkingFlatNamespace
&& linkingMainExecutable
&& (file
->hasTwoLevelNamespace() == false) ) {
307 std::vector
<const char*> importNames
;
308 importNames
.reserve(file
->undefineds().size());
309 // We do not need to strdup the name, because that will be done by the
310 // ImportAtom constructor.
311 for (const auto &sym
: file
->undefineds())
312 importNames
.emplace_back(sym
.getName().c_str());
313 this->_importAtom
= new generic::dylib::ImportAtom
<A
>(*this, importNames
);
317 buildExportHashTable(file
);
320 template <typename A
>
321 void File
<A
>::buildExportHashTable(const tapi::LinkerInterfaceFile
* file
) {
322 if (this->_s_logHashtable
)
323 fprintf(stderr
, "ld: building hashtable from text-stub info in %s\n", this->path());
325 for (const auto &sym
: file
->exports()) {
326 const char* name
= sym
.getName().c_str();
327 bool weakDef
= sym
.isWeakDefined();
328 bool tlv
= sym
.isThreadLocalValue();
330 typename
Base::AtomAndWeak bucket
= { nullptr, weakDef
, tlv
, 0 };
331 if ( this->_s_logHashtable
)
332 fprintf(stderr
, " adding %s to hash table for %s\n", name
, this->path());
333 this->_atoms
[strdup(name
)] = bucket
;
337 template <typename A
>
338 void File
<A
>::processIndirectLibraries(ld::dylib::File::DylibHandler
* handler
, bool addImplicitDylibs
) {
340 _opts
->addTAPIInterface(_interface
, this->path());
341 Base::processIndirectLibraries(handler
, addImplicitDylibs
);
344 template <typename A
>
348 using P
= typename
A::P
;
350 static ld::dylib::File
* parse(const char* path
, const uint8_t* fileContent
,
351 uint64_t fileLength
, time_t mTime
,
352 ld::File::Ordinal ordinal
, const Options
& opts
,
353 bool indirectDylib
, cpu_type_t architecture
, cpu_subtype_t subArchitecture
)
355 return new File
<A
>(path
, fileContent
, fileLength
, &opts
, mTime
, ordinal
,
356 opts
.flatNamespace(),
357 opts
.linkingMainExecutable(),
358 opts
.implicitlyLinkIndirectPublicDylibs(),
360 opts
.allowWeakImports(),
363 opts
.enforceDylibSubtypesMatch(),
364 opts
.allowSimulatorToLinkWithMacOSX(),
365 opts
.addVersionLoadCommand(),
366 opts
.targetIOSSimulator(),
370 opts
.outputKind() == Options::kPreload
,
371 opts
.bundleBitcode());
374 static ld::dylib::File
* parse(const char* path
, tapi::LinkerInterfaceFile
* file
, time_t mTime
,
375 ld::File::Ordinal ordinal
, const Options
& opts
,
376 bool indirectDylib
, cpu_type_t architecture
, cpu_subtype_t subArchitecture
)
378 return new File
<A
>(file
, path
, &opts
, mTime
, ordinal
,
379 opts
.flatNamespace(),
380 opts
.linkingMainExecutable(),
381 opts
.implicitlyLinkIndirectPublicDylibs(),
383 opts
.allowWeakImports(),
386 opts
.enforceDylibSubtypesMatch(),
387 opts
.allowSimulatorToLinkWithMacOSX(),
388 opts
.addVersionLoadCommand(),
389 opts
.targetIOSSimulator(),
393 opts
.outputKind() == Options::kPreload
,
394 opts
.bundleBitcode());
400 static ld::dylib::File
* parseAsArchitecture(const uint8_t* fileContent
, uint64_t fileLength
, const char* path
,
401 time_t modTime
, ld::File::Ordinal ordinal
, const Options
& opts
,
402 bool bundleLoader
, bool indirectDylib
,
403 cpu_type_t architecture
, cpu_subtype_t subArchitecture
)
405 switch ( architecture
) {
406 #if SUPPORT_ARCH_x86_64
407 case CPU_TYPE_X86_64
:
408 return Parser
<x86_64
>::parse(path
, fileContent
, fileLength
, modTime
, ordinal
, opts
, indirectDylib
, architecture
, subArchitecture
);
410 #if SUPPORT_ARCH_i386
412 return Parser
<x86
>::parse(path
, fileContent
, fileLength
, modTime
, ordinal
, opts
, indirectDylib
, architecture
, subArchitecture
);
414 #if SUPPORT_ARCH_arm_any
416 return Parser
<arm
>::parse(path
, fileContent
, fileLength
, modTime
, ordinal
, opts
, indirectDylib
, architecture
, subArchitecture
);
418 #if SUPPORT_ARCH_arm64
420 return Parser
<arm64
>::parse(path
, fileContent
, fileLength
, modTime
, ordinal
, opts
, indirectDylib
, architecture
, subArchitecture
);
423 throwf("unsupported architecture for tbd file");
425 assert(0 && "function should return valid pointer or throw");
429 static ld::dylib::File
*parseAsArchitecture(const char *path
, tapi::LinkerInterfaceFile
* file
, time_t modTime
,
430 ld::File::Ordinal ordinal
, const Options
& opts
, bool indirectDylib
,
431 cpu_type_t architecture
, cpu_subtype_t subArchitecture
)
433 switch ( architecture
) {
434 #if SUPPORT_ARCH_x86_64
435 case CPU_TYPE_X86_64
:
436 return Parser
<x86_64
>::parse(path
, file
, modTime
, ordinal
, opts
, indirectDylib
, architecture
, subArchitecture
);
438 #if SUPPORT_ARCH_i386
440 return Parser
<x86
>::parse(path
, file
, modTime
, ordinal
, opts
, indirectDylib
, architecture
, subArchitecture
);
442 #if SUPPORT_ARCH_arm_any
444 return Parser
<arm
>::parse(path
, file
, modTime
, ordinal
, opts
, indirectDylib
, architecture
, subArchitecture
);
446 #if SUPPORT_ARCH_arm64
448 return Parser
<arm64
>::parse(path
, file
, modTime
, ordinal
, opts
, indirectDylib
, architecture
, subArchitecture
);
451 throwf("unsupported architecture for tbd file");
453 assert(0 && "function should return valid pointer or throw");
458 // main function used by linker to instantiate ld::Files
460 ld::dylib::File
* parse(const uint8_t* fileContent
, uint64_t fileLength
, const char* path
,
461 time_t modtime
, const Options
& opts
, ld::File::Ordinal ordinal
,
462 bool bundleLoader
, bool indirectDylib
)
464 if (!tapi::LinkerInterfaceFile::isSupported(path
, fileContent
, fileLength
))
468 return parseAsArchitecture(fileContent
, fileLength
, path
, modtime
, ordinal
, opts
, bundleLoader
, indirectDylib
, opts
.architecture(), opts
.subArchitecture());
470 if (!opts
.fallbackArchitecture())
474 warning("architecture %s not present in TBD %s, attempting fallback", opts
.architectureName(), path
);
475 return parseAsArchitecture(fileContent
, fileLength
, path
, modtime
, ordinal
, opts
, bundleLoader
, indirectDylib
, opts
.fallbackArchitecture(), opts
.fallbackSubArchitecture());
478 ld::dylib::File
*parse(const char *path
, tapi::LinkerInterfaceFile
* file
, time_t modTime
,
479 ld::File::Ordinal ordinal
, const Options
& opts
, bool indirectDylib
) {
481 return parseAsArchitecture(path
, file
, modTime
, ordinal
, opts
, indirectDylib
, opts
.architecture(), opts
.subArchitecture());
483 if (!opts
.fallbackArchitecture())
487 warning("architecture %s not present in TBD %s, attempting fallback", opts
.architectureName(), path
);
488 return parseAsArchitecture(path
, file
, modTime
, ordinal
, opts
, indirectDylib
, opts
.fallbackArchitecture(), opts
.fallbackSubArchitecture());
492 } // namespace textstub