]>
Commit | Line | Data |
---|---|---|
eaf282aa A |
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> | |
0a8dc3df | 28 | #include <tapi/tapi.h> |
eaf282aa A |
29 | #include <vector> |
30 | ||
31 | #include "Architectures.hpp" | |
32 | #include "bitcode.hpp" | |
33 | #include "MachOFileAbstraction.hpp" | |
34 | #include "MachOTrie.hpp" | |
ec29ba20 | 35 | #include "generic_dylib_file.hpp" |
eaf282aa A |
36 | #include "textstub_dylib_file.hpp" |
37 | ||
eaf282aa A |
38 | |
39 | namespace textstub { | |
40 | namespace dylib { | |
41 | ||
eaf282aa A |
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> | |
ec29ba20 | 48 | class File final : public generic::dylib::File<A> |
eaf282aa | 49 | { |
ec29ba20 A |
50 | using Base = generic::dylib::File<A>; |
51 | ||
eaf282aa | 52 | public: |
0a8dc3df | 53 | File(const char* path, const uint8_t* fileContent, uint64_t fileLength, |
eaf282aa | 54 | time_t mTime, ld::File::Ordinal ordinal, bool linkingFlatNamespace, |
0a8dc3df A |
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, | |
eaf282aa A |
58 | bool allowSimToMacOSX, bool addVers, bool buildingForSimulator, |
59 | bool logAllFiles, const char* installPath, bool indirectDylib); | |
ec29ba20 | 60 | virtual ~File() noexcept {} |
eaf282aa A |
61 | |
62 | private: | |
0a8dc3df | 63 | void buildExportHashTable(const tapi::LinkerInterfaceFile* file); |
ec29ba20 | 64 | }; |
eaf282aa | 65 | |
0a8dc3df A |
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; | |
bee7e226 A |
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 | |
0a8dc3df A |
99 | } |
100 | ||
101 | return Options::kPlatformUnknown; | |
102 | } | |
103 | ||
eaf282aa | 104 | template <typename A> |
0a8dc3df 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, | |
eaf282aa A |
110 | bool buildingForSimulator, bool logAllFiles, const char* targetInstallPath, |
111 | bool indirectDylib) | |
0a8dc3df A |
112 | : Base(strdup(path), mTime, ord, platform, linkMinOSVersion, allowWeakImports, linkingFlatNamespace, |
113 | hoistImplicitPublicDylibs, allowSimToMacOSX, addVers) | |
eaf282aa | 114 | { |
82b4b32b A |
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 | |
0a8dc3df | 141 | auto matchingType = enforceDylibSubtypesMatch ? |
82b4b32b | 142 | tapi::CpuSubTypeMatching::Exact : tapi::CpuSubTypeMatching::ABI_Compatible; |
0a8dc3df | 143 | |
82b4b32b A |
144 | file.reset(tapi::LinkerInterfaceFile::create( |
145 | path, fileContent, fileLength, cpuType, cpuSubType, matchingType, | |
146 | tapi::PackedVersion32(linkMinOSVersion), errorMessage)); | |
147 | #endif | |
0a8dc3df A |
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); | |
ec29ba20 | 154 | |
eaf282aa A |
155 | // write out path for -t option |
156 | if ( logAllFiles ) | |
157 | printf("%s\n", path); | |
158 | ||
0a8dc3df A |
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(); | |
ec29ba20 A |
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 | ||
0a8dc3df A |
183 | for (auto &client : file->allowableClients()) |
184 | this->_allowableClients.push_back(strdup(client.c_str())); | |
eaf282aa A |
185 | |
186 | // <rdar://problem/20659505> [TAPI] Don't hoist "public" (in /usr/lib/) dylibs that should not be directly linked | |
0a8dc3df A |
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())); | |
eaf282aa | 191 | |
0a8dc3df A |
192 | auto dylibPlatform = mapPlatform(file->getPlatform()); |
193 | if ( (dylibPlatform != platform) && (platform != Options::kPlatformUnknown) ) { | |
ec29ba20 A |
194 | this->_wrongOS = true; |
195 | if ( this->_addVersionLoadCommand && !indirectDylib ) { | |
eaf282aa | 196 | if ( buildingForSimulator ) { |
ec29ba20 | 197 | if ( !this->_allowSimToMacOSXLinking ) |
eaf282aa | 198 | throwf("building for %s simulator, but linking against dylib built for %s (%s).", |
0a8dc3df | 199 | Options::platformName(platform), Options::platformName(dylibPlatform), path); |
eaf282aa A |
200 | } else { |
201 | throwf("building for %s, but linking against dylib built for %s (%s).", | |
0a8dc3df | 202 | Options::platformName(platform), Options::platformName(dylibPlatform), path); |
eaf282aa A |
203 | } |
204 | } | |
205 | } | |
206 | ||
0a8dc3df A |
207 | for (const auto& reexport : file->reexportedLibraries()) { |
208 | const char *path = strdup(reexport.c_str()); | |
ec29ba20 A |
209 | if ( (targetInstallPath == nullptr) || (strcmp(targetInstallPath, path) != 0) ) |
210 | this->_dependentDylibs.emplace_back(path, true); | |
eaf282aa A |
211 | } |
212 | ||
0a8dc3df A |
213 | for (const auto& symbol : file->ignoreExports()) |
214 | this->_ignoreExports.insert(strdup(symbol.c_str())); | |
eaf282aa | 215 | |
0a8dc3df A |
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()); | |
eaf282aa A |
229 | } |
230 | ||
231 | template <typename A> | |
0a8dc3df | 232 | void File<A>::buildExportHashTable(const tapi::LinkerInterfaceFile* file) { |
ec29ba20 | 233 | if (this->_s_logHashtable ) |
eaf282aa A |
234 | fprintf(stderr, "ld: building hashtable from text-stub info in %s\n", this->path()); |
235 | ||
0a8dc3df A |
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(); | |
eaf282aa | 240 | |
0a8dc3df A |
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; | |
eaf282aa | 245 | } |
eaf282aa A |
246 | } |
247 | ||
eaf282aa A |
248 | template <typename A> |
249 | class Parser | |
250 | { | |
251 | public: | |
ec29ba20 | 252 | using P = typename A::P; |
eaf282aa | 253 | |
0a8dc3df A |
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, | |
ec29ba20 A |
257 | bool indirectDylib) |
258 | { | |
0a8dc3df | 259 | return new File<A>(path, fileContent, fileLength,mTime, ordinal, |
eaf282aa | 260 | opts.flatNamespace(), |
0a8dc3df | 261 | opts.linkingMainExecutable(), |
eaf282aa A |
262 | opts.implicitlyLinkIndirectPublicDylibs(), |
263 | opts.platform(), | |
eaf282aa | 264 | opts.minOSversion(), |
0a8dc3df A |
265 | opts.allowWeakImports(), |
266 | opts.architecture(), | |
267 | opts.subArchitecture(), | |
268 | opts.enforceDylibSubtypesMatch(), | |
eaf282aa A |
269 | opts.allowSimulatorToLinkWithMacOSX(), |
270 | opts.addVersionLoadCommand(), | |
271 | opts.targetIOSSimulator(), | |
272 | opts.logAllFiles(), | |
273 | opts.installPath(), | |
274 | indirectDylib); | |
275 | } | |
276 | }; | |
277 | ||
eaf282aa A |
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 | { | |
0a8dc3df | 285 | |
eaf282aa A |
286 | switch ( opts.architecture() ) { |
287 | #if SUPPORT_ARCH_x86_64 | |
288 | case CPU_TYPE_X86_64: | |
0a8dc3df A |
289 | if (tapi::LinkerInterfaceFile::isSupported(path, fileContent, fileLength)) |
290 | return Parser<x86_64>::parse(path, fileContent, fileLength, modTime, ordinal, opts, indirectDylib); | |
eaf282aa A |
291 | #endif |
292 | #if SUPPORT_ARCH_i386 | |
293 | case CPU_TYPE_I386: | |
0a8dc3df A |
294 | if (tapi::LinkerInterfaceFile::isSupported(path, fileContent, fileLength)) |
295 | return Parser<x86>::parse(path, fileContent, fileLength, modTime, ordinal, opts, indirectDylib); | |
eaf282aa A |
296 | #endif |
297 | #if SUPPORT_ARCH_arm_any | |
298 | case CPU_TYPE_ARM: | |
0a8dc3df A |
299 | if (tapi::LinkerInterfaceFile::isSupported(path, fileContent, fileLength)) |
300 | return Parser<arm>::parse(path, fileContent, fileLength, modTime, ordinal, opts, indirectDylib); | |
eaf282aa A |
301 | #endif |
302 | #if SUPPORT_ARCH_arm64 | |
303 | case CPU_TYPE_ARM64: | |
0a8dc3df A |
304 | if (tapi::LinkerInterfaceFile::isSupported(path, fileContent, fileLength)) |
305 | return Parser<arm64>::parse(path, fileContent, fileLength, modTime, ordinal, opts, indirectDylib); | |
eaf282aa A |
306 | #endif |
307 | } | |
308 | return nullptr; | |
309 | } | |
310 | ||
311 | ||
312 | } // namespace dylib | |
313 | } // namespace textstub |