]> git.saurik.com Git - apple/ld64.git/blob - src/ld/parsers/textstub_dylib_file.cpp
27c456010e8fde56378dcca2a7bc52d81a7330ba
[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 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 {}
68
69 // overrides of generic::dylib::File
70 virtual void processIndirectLibraries(ld::dylib::File::DylibHandler*, bool addImplicitDylibs) override final;
71
72 private:
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);
77
78 const Options *_opts;
79 std::unique_ptr<tapi::LinkerInterfaceFile> _interface;
80 };
81
82 static ld::File::ObjcConstraint mapObjCConstraint(tapi::ObjCConstraint constraint) {
83 switch (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;
94 }
95
96 return ld::File::objcConstraintNone;
97 }
98
99 static Options::Platform mapPlatform(tapi::Platform platform) {
100 switch (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;
114 #endif
115 }
116
117 return Options::kPlatformUnknown;
118 }
119
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,
127 bool indirectDylib)
128 : Base(strdup(path), mTime, ord, platform, linkMinOSVersion, allowWeakImports, linkingFlatNamespace,
129 hoistImplicitPublicDylibs, allowSimToMacOSX, addVers)
130 {
131 std::string errorMessage;
132
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;
140
141 if (!allowWeakImports)
142 flags |= tapi::ParsingFlags::DisallowWeakImports;
143
144 _interface.reset(tapi::LinkerInterfaceFile::create(
145 path, cpuType, cpuSubType, flags,
146 tapi::PackedVersion32(linkMinOSVersion), errorMessage));
147 } else
148 #endif
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;
154
155 if (!allowWeakImports)
156 flags |= tapi::ParsingFlags::DisallowWeakImports;
157
158 _interface.reset(tapi::LinkerInterfaceFile::create(
159 path, fileContent, fileLength, cpuType, cpuSubType, flags,
160 tapi::PackedVersion32(linkMinOSVersion), errorMessage));
161 } else
162 #endif
163 #if (TAPI_API_VERSION_MAJOR >= 1)
164 {
165 auto matchingType = enforceDylibSubtypesMatch ?
166 tapi::CpuSubTypeMatching::Exact : tapi::CpuSubTypeMatching::ABI_Compatible;
167
168 _interface.reset(tapi::LinkerInterfaceFile::create(
169 path, fileContent, fileLength, cpuType, cpuSubType, matchingType,
170 tapi::PackedVersion32(linkMinOSVersion), errorMessage));
171 }
172 #endif
173
174 if (!_interface)
175 throw strdup(errorMessage.c_str());
176
177 // unmap file - it is no longer needed.
178 munmap((caddr_t)fileContent, fileLength);
179
180 // write out path for -t option
181 if ( logAllFiles )
182 printf("%s\n", path);
183
184 init(_interface.get(), opts, buildingForSimulator, indirectDylib, linkingFlatNamespace,
185 linkingMainExecutable, path, platform, targetInstallPath);
186 }
187
188 template<typename A>
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))
198 {
199 init(_interface.get(), opts, buildingForSimulator, indirectDylib, linkingFlatNamespace,
200 linkingMainExecutable, path, platform, installPath);
201 }
202
203 template<typename A>
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) {
207 _opts = opts;
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();
219
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/");
227
228 if ( strstr(this->_dylibInstallPath, frname) != NULL )
229 this->_frameworkName = leafName;
230 }
231
232 for (auto &client : file->allowableClients())
233 this->_allowableClients.push_back(strdup(client.c_str()));
234
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());
237
238 for (const auto &client : file->allowableClients())
239 this->_allowableClients.emplace_back(strdup(client.c_str()));
240
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);
249 } else {
250 throwf("building for %s, but linking against dylib built for %s (%s).",
251 Options::platformName(platform), Options::platformName(dylibPlatform), path);
252 }
253 }
254 }
255
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);
260 }
261
262 for (const auto& symbol : file->ignoreExports())
263 this->_ignoreExports.insert(strdup(symbol.c_str()));
264
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);
274 }
275
276 // build hash table
277 buildExportHashTable(file);
278 }
279
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());
284
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();
289
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;
294 }
295 }
296
297 template <typename A>
298 void File<A>::processIndirectLibraries(ld::dylib::File::DylibHandler* handler, bool addImplicitDylibs) {
299 if (_interface)
300 _opts->addTAPIInterface(_interface.get(), this->path());
301 Base::processIndirectLibraries(handler, addImplicitDylibs);
302 }
303
304 template <typename A>
305 class Parser
306 {
307 public:
308 using P = typename A::P;
309
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,
313 bool indirectDylib)
314 {
315 return new File<A>(path, fileContent, fileLength, &opts, mTime, ordinal,
316 opts.flatNamespace(),
317 opts.linkingMainExecutable(),
318 opts.implicitlyLinkIndirectPublicDylibs(),
319 opts.platform(),
320 opts.minOSversion(),
321 opts.allowWeakImports(),
322 opts.architecture(),
323 opts.subArchitecture(),
324 opts.enforceDylibSubtypesMatch(),
325 opts.allowSimulatorToLinkWithMacOSX(),
326 opts.addVersionLoadCommand(),
327 opts.targetIOSSimulator(),
328 opts.logAllFiles(),
329 opts.installPath(),
330 indirectDylib);
331 }
332
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,
335 bool indirectDylib)
336 {
337 return new File<A>(std::move(file), path, &opts, mTime, ordinal,
338 opts.flatNamespace(),
339 opts.linkingMainExecutable(),
340 opts.implicitlyLinkIndirectPublicDylibs(),
341 opts.platform(),
342 opts.minOSversion(),
343 opts.allowWeakImports(),
344 opts.architecture(),
345 opts.subArchitecture(),
346 opts.enforceDylibSubtypesMatch(),
347 opts.allowSimulatorToLinkWithMacOSX(),
348 opts.addVersionLoadCommand(),
349 opts.targetIOSSimulator(),
350 opts.logAllFiles(),
351 opts.installPath(),
352 indirectDylib);
353 }
354
355 };
356
357 //
358 // main function used by linker to instantiate ld::Files
359 //
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)
363 {
364
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);
370 #endif
371 #if SUPPORT_ARCH_i386
372 case CPU_TYPE_I386:
373 if (tapi::LinkerInterfaceFile::isSupported(path, fileContent, fileLength))
374 return Parser<x86>::parse(path, fileContent, fileLength, modTime, ordinal, opts, indirectDylib);
375 #endif
376 #if SUPPORT_ARCH_arm_any
377 case CPU_TYPE_ARM:
378 if (tapi::LinkerInterfaceFile::isSupported(path, fileContent, fileLength))
379 return Parser<arm>::parse(path, fileContent, fileLength, modTime, ordinal, opts, indirectDylib);
380 #endif
381 #if SUPPORT_ARCH_arm64
382 case CPU_TYPE_ARM64:
383 if (tapi::LinkerInterfaceFile::isSupported(path, fileContent, fileLength))
384 return Parser<arm64>::parse(path, fileContent, fileLength, modTime, ordinal, opts, indirectDylib);
385 #endif
386 }
387 return nullptr;
388 }
389
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);
396 #endif
397 #if SUPPORT_ARCH_i386
398 case CPU_TYPE_I386:
399 return Parser<x86>::parse(path, std::move(file), modTime, ordinal, opts, indirectDylib);
400 #endif
401 #if SUPPORT_ARCH_arm_any
402 case CPU_TYPE_ARM:
403 return Parser<arm>::parse(path, std::move(file), modTime, ordinal, opts, indirectDylib);
404 #endif
405 #if SUPPORT_ARCH_arm64
406 case CPU_TYPE_ARM64:
407 return Parser<arm64>::parse(path, std::move(file), modTime, ordinal, opts, indirectDylib);
408 #endif
409 }
410 return nullptr;
411
412 }
413
414
415 } // namespace dylib
416 } // namespace textstub