]> git.saurik.com Git - apple/ld64.git/blob - src/ld/parsers/textstub_dylib_file.cpp
ld64-409.12.tar.gz
[apple/ld64.git] / src / ld / parsers / textstub_dylib_file.cpp
1 /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
2 *
3 * Copyright (c) 2015 Apple Inc. All rights reserved.
4 *
5 * @APPLE_LICENSE_HEADER_START@
6 *
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
12 * file.
13 *
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.
21 *
22 * @APPLE_LICENSE_HEADER_END@
23 */
24
25
26 #include <sys/param.h>
27 #include <sys/mman.h>
28 #include <tapi/tapi.h>
29 #include <vector>
30
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"
37
38
39 namespace textstub {
40 namespace dylib {
41
42 //
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.
46 //
47 template <typename A>
48 class File final : public generic::dylib::File<A>
49 {
50 using Base = generic::dylib::File<A>;
51
52 public:
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 {}
70
71 // overrides of generic::dylib::File
72 virtual void processIndirectLibraries(ld::dylib::File::DylibHandler*, bool addImplicitDylibs) override final;
73
74 private:
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();
81
82 const Options* _opts;
83 tapi::LinkerInterfaceFile* _interface;
84 };
85
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; }
89
90
91 static ld::VersionSet mapPlatform(tapi::Platform platform, bool useSimulatorVariant) {
92 ld::VersionSet platforms;
93 switch (platform) {
94 case tapi::Platform::Unknown:
95 break;
96 case tapi::Platform::OSX:
97 platforms.add({ld::kPlatform_macOS, 0});
98 break;
99 case tapi::Platform::iOS:
100 if (useSimulatorVariant)
101 platforms.add({ld::kPlatform_iOSSimulator, 0});
102 else
103 platforms.add({ld::kPlatform_iOS, 0});
104 break;
105 case tapi::Platform::watchOS:
106 if (useSimulatorVariant)
107 platforms.add({ld::kPlatform_watchOSSimulator, 0});
108 else
109 platforms.add({ld::kPlatform_watchOS, 0});
110 break;
111 case tapi::Platform::tvOS:
112 if (useSimulatorVariant)
113 platforms.add({ld::kPlatform_tvOSSimulator, 0});
114 else
115 platforms.add({ld::kPlatform_tvOS, 0});
116 break;
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});
120 break;
121 #endif
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});
125 break;
126 case tapi::Platform::zippered:
127 platforms.add({ld::kPlatform_macOS, 0});
128 platforms.add({ld::kPlatform_iOSMac, 0});
129 break;
130 #endif
131 }
132
133 return platforms;
134 }
135
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)
146 {
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;
156 });
157
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;
165
166 if (!allowWeakImports)
167 flags |= tapi::ParsingFlags::DisallowWeakImports;
168
169 _interface = tapi::LinkerInterfaceFile::create(
170 path, cpuType, cpuSubType, flags,
171 tapi::PackedVersion32(linkMinOSVersion), errorMessage);
172 } else
173 #endif
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;
179
180 if (!allowWeakImports)
181 flags |= tapi::ParsingFlags::DisallowWeakImports;
182
183 _interface = tapi::LinkerInterfaceFile::create(
184 path, fileContent, fileLength, cpuType, cpuSubType, flags,
185 tapi::PackedVersion32(linkMinOSVersion), errorMessage);
186 } else
187 #endif
188 #if (TAPI_API_VERSION_MAJOR >= 1)
189 {
190 auto matchingType = enforceDylibSubtypesMatch ?
191 tapi::CpuSubTypeMatching::Exact : tapi::CpuSubTypeMatching::ABI_Compatible;
192
193 _interface = tapi::LinkerInterfaceFile::create(
194 path, fileContent, fileLength, cpuType, cpuSubType, matchingType,
195 tapi::PackedVersion32(linkMinOSVersion), errorMessage);
196 }
197 #endif
198
199 if (!_interface)
200 throw strdup(errorMessage.c_str());
201
202 // unmap file - it is no longer needed.
203 munmap((caddr_t)fileContent, fileLength);
204
205 // write out path for -t option
206 if ( logAllFiles )
207 printf("%s\n", path);
208
209 init(_interface, opts, buildingForSimulator, indirectDylib, linkingFlatNamespace,
210 linkingMainExecutable, path, platforms, targetInstallPath, ignoreMismatchPlatform, usingBitcode);
211 }
212
213 template<typename A>
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)
224 {
225 init(_interface, opts, buildingForSimulator, indirectDylib, linkingFlatNamespace,
226 linkingMainExecutable, path, platforms, installPath, ignoreMismatchPlatform, usingBitcode);
227 }
228
229 template<typename A>
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) {
234 _opts = opts;
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();
245
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/");
253
254 if ( strstr(this->_dylibInstallPath, frname) != NULL )
255 this->_frameworkName = leafName;
256 }
257
258 for (auto &client : file->allowableClients())
259 this->_allowableClients.push_back(strdup(client.c_str()));
260
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());
263
264 for (const auto &client : file->allowableClients())
265 this->_allowableClients.emplace_back(strdup(client.c_str()));
266
267 ld::VersionSet lcPlatforms = mapPlatform(file->getPlatform(), useSimulatorVariant());
268 this->_platforms = lcPlatforms;
269
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) {
276 if ( usingBitcode )
277 throwf("building for %s simulator, but linking against dylib built for %s,",
278 platforms.to_str().c_str(), lcPlatforms.to_str().c_str());
279 else
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());
283 }
284 } else {
285 if ( usingBitcode )
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());
292 }
293 }
294 });
295
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);
300 }
301
302 for (const auto& symbol : file->ignoreExports())
303 this->_ignoreExports.insert(strdup(symbol.c_str()));
304
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);
314 }
315
316 // build hash table
317 buildExportHashTable(file);
318 }
319
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());
324
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();
329
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;
334 }
335 }
336
337 template <typename A>
338 void File<A>::processIndirectLibraries(ld::dylib::File::DylibHandler* handler, bool addImplicitDylibs) {
339 if (_interface)
340 _opts->addTAPIInterface(_interface, this->path());
341 Base::processIndirectLibraries(handler, addImplicitDylibs);
342 }
343
344 template <typename A>
345 class Parser
346 {
347 public:
348 using P = typename A::P;
349
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)
354 {
355 return new File<A>(path, fileContent, fileLength, &opts, mTime, ordinal,
356 opts.flatNamespace(),
357 opts.linkingMainExecutable(),
358 opts.implicitlyLinkIndirectPublicDylibs(),
359 opts.platforms(),
360 opts.allowWeakImports(),
361 architecture,
362 subArchitecture,
363 opts.enforceDylibSubtypesMatch(),
364 opts.allowSimulatorToLinkWithMacOSX(),
365 opts.addVersionLoadCommand(),
366 opts.targetIOSSimulator(),
367 opts.logAllFiles(),
368 opts.installPath(),
369 indirectDylib,
370 opts.outputKind() == Options::kPreload,
371 opts.bundleBitcode());
372 }
373
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)
377 {
378 return new File<A>(file, path, &opts, mTime, ordinal,
379 opts.flatNamespace(),
380 opts.linkingMainExecutable(),
381 opts.implicitlyLinkIndirectPublicDylibs(),
382 opts.platforms(),
383 opts.allowWeakImports(),
384 architecture,
385 subArchitecture,
386 opts.enforceDylibSubtypesMatch(),
387 opts.allowSimulatorToLinkWithMacOSX(),
388 opts.addVersionLoadCommand(),
389 opts.targetIOSSimulator(),
390 opts.logAllFiles(),
391 opts.installPath(),
392 indirectDylib,
393 opts.outputKind() == Options::kPreload,
394 opts.bundleBitcode());
395 }
396
397 };
398
399
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)
404 {
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);
409 #endif
410 #if SUPPORT_ARCH_i386
411 case CPU_TYPE_I386:
412 return Parser<x86>::parse(path, fileContent, fileLength, modTime, ordinal, opts, indirectDylib, architecture, subArchitecture);
413 #endif
414 #if SUPPORT_ARCH_arm_any
415 case CPU_TYPE_ARM:
416 return Parser<arm>::parse(path, fileContent, fileLength, modTime, ordinal, opts, indirectDylib, architecture, subArchitecture);
417 #endif
418 #if SUPPORT_ARCH_arm64
419 case CPU_TYPE_ARM64:
420 return Parser<arm64>::parse(path, fileContent, fileLength, modTime, ordinal, opts, indirectDylib, architecture, subArchitecture);
421 #endif
422 default:
423 throwf("unsupported architecture for tbd file");
424 }
425 assert(0 && "function should return valid pointer or throw");
426 }
427
428
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)
432 {
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);
437 #endif
438 #if SUPPORT_ARCH_i386
439 case CPU_TYPE_I386:
440 return Parser<x86>::parse(path, file, modTime, ordinal, opts, indirectDylib, architecture, subArchitecture);
441 #endif
442 #if SUPPORT_ARCH_arm_any
443 case CPU_TYPE_ARM:
444 return Parser<arm>::parse(path, file, modTime, ordinal, opts, indirectDylib, architecture, subArchitecture);
445 #endif
446 #if SUPPORT_ARCH_arm64
447 case CPU_TYPE_ARM64:
448 return Parser<arm64>::parse(path, file, modTime, ordinal, opts, indirectDylib, architecture, subArchitecture);
449 #endif
450 default:
451 throwf("unsupported architecture for tbd file");
452 }
453 assert(0 && "function should return valid pointer or throw");
454 }
455
456
457 //
458 // main function used by linker to instantiate ld::Files
459 //
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)
463 {
464 if (!tapi::LinkerInterfaceFile::isSupported(path, fileContent, fileLength))
465 return nullptr;
466
467 try {
468 return parseAsArchitecture(fileContent, fileLength, path, modtime, ordinal, opts, bundleLoader, indirectDylib, opts.architecture(), opts.subArchitecture());
469 } catch (...) {
470 if (!opts.fallbackArchitecture())
471 throw;
472 }
473
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());
476 }
477
478 ld::dylib::File *parse(const char *path, tapi::LinkerInterfaceFile* file, time_t modTime,
479 ld::File::Ordinal ordinal, const Options& opts, bool indirectDylib) {
480 try {
481 return parseAsArchitecture(path, file, modTime, ordinal, opts, indirectDylib, opts.architecture(), opts.subArchitecture());
482 } catch (...) {
483 if (!opts.fallbackArchitecture())
484 throw;
485 }
486
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());
489 }
490
491 } // namespace dylib
492 } // namespace textstub