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