]> git.saurik.com Git - apple/ld64.git/blob - src/ld/parsers/textstub_dylib_file.cpp
076736e714929eb6bafe0c297d3c7c382cb282d8
[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,
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 {}
61
62 private:
63 void buildExportHashTable(const tapi::LinkerInterfaceFile* file);
64 };
65
66 static ld::File::ObjcConstraint mapObjCConstraint(tapi::ObjCConstraint constraint) {
67 switch (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;
78 }
79
80 return ld::File::objcConstraintNone;
81 }
82
83 static Options::Platform mapPlatform(tapi::Platform platform) {
84 switch (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;
98 #endif
99 }
100
101 return Options::kPlatformUnknown;
102 }
103
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,
111 bool indirectDylib)
112 : Base(strdup(path), mTime, ord, platform, linkMinOSVersion, allowWeakImports, linkingFlatNamespace,
113 hoistImplicitPublicDylibs, allowSimToMacOSX, addVers)
114 {
115 std::unique_ptr<tapi::LinkerInterfaceFile> file;
116 std::string errorMessage;
117
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;
125
126 if (!allowWeakImports)
127 flags |= tapi::ParsingFlags::DisallowWeakImports;
128
129 file.reset(tapi::LinkerInterfaceFile::create(
130 path, fileContent, fileLength, cpuType, cpuSubType, flags,
131 tapi::PackedVersion32(linkMinOSVersion), errorMessage));
132 } else {
133 auto matchingType = enforceDylibSubtypesMatch ?
134 tapi::CpuSubTypeMatching::Exact : tapi::CpuSubTypeMatching::ABI_Compatible;
135
136 file.reset(tapi::LinkerInterfaceFile::create(
137 path, fileContent, fileLength, cpuType, cpuSubType, matchingType,
138 tapi::PackedVersion32(linkMinOSVersion), errorMessage));
139 }
140 #else
141 auto matchingType = enforceDylibSubtypesMatch ?
142 tapi::CpuSubTypeMatching::Exact : tapi::CpuSubTypeMatching::ABI_Compatible;
143
144 file.reset(tapi::LinkerInterfaceFile::create(
145 path, fileContent, fileLength, cpuType, cpuSubType, matchingType,
146 tapi::PackedVersion32(linkMinOSVersion), errorMessage));
147 #endif
148
149 if (file == nullptr)
150 throw strdup(errorMessage.c_str());
151
152 // unmap file - it is no longer needed.
153 munmap((caddr_t)fileContent, fileLength);
154
155 // write out path for -t option
156 if ( logAllFiles )
157 printf("%s\n", path);
158
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();
170
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/");
178
179 if ( strstr(this->_dylibInstallPath, frname) != NULL )
180 this->_frameworkName = leafName;
181 }
182
183 for (auto &client : file->allowableClients())
184 this->_allowableClients.push_back(strdup(client.c_str()));
185
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());
188
189 for (const auto &client : file->allowableClients())
190 this->_allowableClients.emplace_back(strdup(client.c_str()));
191
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);
200 } else {
201 throwf("building for %s, but linking against dylib built for %s (%s).",
202 Options::platformName(platform), Options::platformName(dylibPlatform), path);
203 }
204 }
205 }
206
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);
211 }
212
213 for (const auto& symbol : file->ignoreExports())
214 this->_ignoreExports.insert(strdup(symbol.c_str()));
215
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);
225 }
226
227 // build hash table
228 buildExportHashTable(file.get());
229 }
230
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());
235
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();
240
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;
245 }
246 }
247
248 template <typename A>
249 class Parser
250 {
251 public:
252 using P = typename A::P;
253
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,
257 bool indirectDylib)
258 {
259 return new File<A>(path, fileContent, fileLength,mTime, ordinal,
260 opts.flatNamespace(),
261 opts.linkingMainExecutable(),
262 opts.implicitlyLinkIndirectPublicDylibs(),
263 opts.platform(),
264 opts.minOSversion(),
265 opts.allowWeakImports(),
266 opts.architecture(),
267 opts.subArchitecture(),
268 opts.enforceDylibSubtypesMatch(),
269 opts.allowSimulatorToLinkWithMacOSX(),
270 opts.addVersionLoadCommand(),
271 opts.targetIOSSimulator(),
272 opts.logAllFiles(),
273 opts.installPath(),
274 indirectDylib);
275 }
276 };
277
278 //
279 // main function used by linker to instantiate ld::Files
280 //
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)
284 {
285
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);
291 #endif
292 #if SUPPORT_ARCH_i386
293 case CPU_TYPE_I386:
294 if (tapi::LinkerInterfaceFile::isSupported(path, fileContent, fileLength))
295 return Parser<x86>::parse(path, fileContent, fileLength, modTime, ordinal, opts, indirectDylib);
296 #endif
297 #if SUPPORT_ARCH_arm_any
298 case CPU_TYPE_ARM:
299 if (tapi::LinkerInterfaceFile::isSupported(path, fileContent, fileLength))
300 return Parser<arm>::parse(path, fileContent, fileLength, modTime, ordinal, opts, indirectDylib);
301 #endif
302 #if SUPPORT_ARCH_arm64
303 case CPU_TYPE_ARM64:
304 if (tapi::LinkerInterfaceFile::isSupported(path, fileContent, fileLength))
305 return Parser<arm64>::parse(path, fileContent, fileLength, modTime, ordinal, opts, indirectDylib);
306 #endif
307 }
308 return nullptr;
309 }
310
311
312 } // namespace dylib
313 } // namespace textstub