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