]> git.saurik.com Git - apple/ld64.git/blame - src/ld/parsers/generic_dylib_file.hpp
ld64-409.12.tar.gz
[apple/ld64.git] / src / ld / parsers / generic_dylib_file.hpp
CommitLineData
ec29ba20
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#ifndef __GENERIC_DYLIB_FILE_H__
26#define __GENERIC_DYLIB_FILE_H__
27
28#include "ld.hpp"
29#include "Options.h"
30#include <unordered_map>
31#include <unordered_set>
32
33namespace generic {
34namespace dylib {
35
36// forward reference
37template <typename A> class File;
38
39//
40// An ExportAtom has no content. It exists so that the linker can track which
41// imported symbols came from which dynamic libraries.
42//
43template <typename A>
44class ExportAtom final : public ld::Atom
45{
46public:
47 ExportAtom(const File<A>& f, const char* nm, bool weakDef, bool tlv,
48 typename A::P::uint_t address)
49 : ld::Atom(f._importProxySection, ld::Atom::definitionProxy,
50 (weakDef ? ld::Atom::combineByName : ld::Atom::combineNever),
51 ld::Atom::scopeLinkageUnit,
52 (tlv ? ld::Atom::typeTLV : ld::Atom::typeUnclassified),
53 symbolTableNotIn, false, false, false, ld::Atom::Alignment(0)),
54 _file(f),
55 _name(nm),
56 _address(address)
57 {}
58
59 // overrides of ld::Atom
60 virtual const ld::File* file() const override final { return &_file; }
61 virtual const char* name() const override final { return _name; }
62 virtual uint64_t size() const override final { return 0; }
63 virtual uint64_t objectAddress() const override final { return _address; }
64 virtual void copyRawContent(uint8_t buffer[]) const override final { }
65
66 virtual void setScope(Scope) { }
67
68private:
69 using pint_t = typename A::P::uint_t;
70
71 virtual ~ExportAtom() {}
72
73 const File<A>& _file;
74 const char* _name;
75 pint_t _address;
76};
77
78
79//
80// An ImportAtom has no content. It exists so that when linking a main executable flat-namespace
81// the imports of all flat dylibs are checked
82//
83template <typename A>
84class ImportAtom final : public ld::Atom
85{
86public:
87 ImportAtom(File<A>& f, std::vector<const char*>& imports);
88
89 // overrides of ld::Atom
90 virtual ld::File* file() const override final { return &_file; }
91 virtual const char* name() const override final { return "import-atom"; }
92 virtual uint64_t size() const override final { return 0; }
93 virtual uint64_t objectAddress() const override final { return 0; }
94 virtual ld::Fixup::iterator fixupsBegin() const override final { return &_undefs[0]; }
95 virtual ld::Fixup::iterator fixupsEnd() const override final { return &_undefs[_undefs.size()]; }
96 virtual void copyRawContent(uint8_t buffer[]) const override final { }
97
98 virtual void setScope(Scope) { }
99
100private:
101 virtual ~ImportAtom() {}
102
103 File<A>& _file;
104 mutable std::vector<ld::Fixup> _undefs;
105};
106
107template <typename A>
108ImportAtom<A>::ImportAtom(File<A>& f, std::vector<const char*>& imports)
109 : ld::Atom(f._flatDummySection, ld::Atom::definitionRegular, ld::Atom::combineNever,
110 ld::Atom::scopeTranslationUnit, ld::Atom::typeUnclassified, symbolTableNotIn, false,
111 false, false, ld::Atom::Alignment(0)),
112 _file(f)
113{
114 for(auto *name : imports)
115 _undefs.emplace_back(0, ld::Fixup::k1of1, ld::Fixup::kindNone, false, strdup(name));
116}
117
118//
119// A generic representation for the dynamic library files we support (Mach-O and text-based stubs).
120// Common state and functionality is consolidated in this class.
121//
122template <typename A>
123class File : public ld::dylib::File
124{
125public:
e456bf10
A
126 File(const char* path, time_t mTime, ld::File::Ordinal ordinal, const ld::VersionSet& platforms,
127 bool allowWeakImports, bool linkingFlatNamespace, bool hoistImplicitPublicDylibs,
ec29ba20
A
128 bool allowSimToMacOSX, bool addVers);
129
130 // overrides of ld::File
131 virtual bool forEachAtom(ld::File::AtomHandler&) const override final;
132 virtual bool justInTimeforEachAtom(const char* name, ld::File::AtomHandler&) const override final;
ec29ba20 133 virtual uint8_t swiftVersion() const override final { return _swiftVersion; }
ec29ba20
A
134 virtual ld::Bitcode* getBitcode() const override final { return _bitcode.get(); }
135
136
137 // overrides of ld::dylib::File
f410558f 138 virtual void processIndirectLibraries(ld::dylib::File::DylibHandler*, bool addImplicitDylibs) override;
ec29ba20
A
139 virtual bool providedExportAtom() const override final { return _providedAtom; }
140 virtual const char* parentUmbrella() const override final { return _parentUmbrella; }
141 virtual const std::vector<const char*>* allowableClients() const override final { return _allowableClients.empty() ? nullptr : &_allowableClients; }
0a8dc3df 142 virtual const std::vector<const char*>& rpaths() const override final { return _rpaths; }
ec29ba20
A
143 virtual bool hasWeakExternals() const override final { return _hasWeakExports; }
144 virtual bool deadStrippable() const override final { return _deadStrippable; }
145 virtual bool hasWeakDefinition(const char* name) const override final;
146 virtual bool hasPublicInstallName() const override final { return _hasPublicInstallName; }
147 virtual bool allSymbolsAreWeakImported() const override final;
148 virtual bool installPathVersionSpecific() const override final { return _installPathOverride; }
149 virtual bool appExtensionSafe() const override final { return _appExtensionSafe; };
150
151
152 bool wrongOS() const { return _wrongOS; }
153
154private:
155 using pint_t = typename A::P::uint_t;
156
157 friend class ExportAtom<A>;
158 friend class ImportAtom<A>;
159
160 struct CStringHash {
161 std::size_t operator()(const char* __s) const {
162 unsigned long __h = 0;
163 for ( ; *__s; ++__s)
164 __h = 5 * __h + *__s;
165 return size_t(__h);
166 };
167 };
168
169protected:
170 struct AtomAndWeak { ld::Atom* atom; bool weakDef; bool tlv; pint_t address; };
171 struct Dependent {
172 const char* path;
173 File<A>* dylib;
174 bool reExport;
175
176 Dependent(const char* path, bool reExport)
177 : path(path), dylib(nullptr), reExport(reExport) {}
178 };
179 struct ReExportChain { ReExportChain* prev; const File* file; };
180
181private:
182 using NameToAtomMap = std::unordered_map<const char*, AtomAndWeak, ld::CStringHash, ld::CStringEquals>;
183 using NameSet = std::unordered_set<const char*, CStringHash, ld::CStringEquals>;
184
185 std::pair<bool, bool> hasWeakDefinitionImpl(const char* name) const;
186 bool containsOrReExports(const char* name, bool& weakDef, bool& tlv, pint_t& addr) const;
187 void assertNoReExportCycles(ReExportChain*) const;
188
189protected:
190 bool isPublicLocation(const char* path) const;
ec29ba20
A
191
192private:
193 ld::Section _importProxySection;
194 ld::Section _flatDummySection;
195 mutable bool _providedAtom;
196 bool _indirectDylibsProcessed;
197
198protected:
199 mutable NameToAtomMap _atoms;
200 NameSet _ignoreExports;
201 std::vector<Dependent> _dependentDylibs;
202 ImportAtom<A>* _importAtom;
203 std::vector<const char*> _allowableClients;
0a8dc3df 204 std::vector<const char*> _rpaths;
ec29ba20
A
205 const char* _parentUmbrella;
206 std::unique_ptr<ld::Bitcode> _bitcode;
e456bf10 207 ld::VersionSet _platforms;
ec29ba20
A
208 uint8_t _swiftVersion;
209 bool _wrongOS;
210 bool _linkingFlat;
211 bool _noRexports;
212 bool _explictReExportFound;
213 bool _implicitlyLinkPublicDylibs;
214 bool _installPathOverride;
215 bool _hasWeakExports;
216 bool _deadStrippable;
217 bool _hasPublicInstallName;
218 bool _appExtensionSafe;
e456bf10 219 const bool _allowWeakImports;
ec29ba20
A
220 const bool _allowSimToMacOSXLinking;
221 const bool _addVersionLoadCommand;
222
223 static bool _s_logHashtable;
224};
225
226template <typename A>
227bool File<A>::_s_logHashtable = false;
228
229template <typename A>
e456bf10
A
230File<A>::File(const char* path, time_t mTime, ld::File::Ordinal ord, const ld::VersionSet& platforms,
231 bool allowWeakImports, bool linkingFlatNamespace,
ec29ba20
A
232 bool hoistImplicitPublicDylibs,
233 bool allowSimToMacOSX, bool addVers)
234 : ld::dylib::File(path, mTime, ord),
235 _importProxySection("__TEXT", "__import", ld::Section::typeImportProxies, true),
236 _flatDummySection("__LINKEDIT", "__flat_dummy", ld::Section::typeLinkEdit, true),
237 _providedAtom(false),
238 _indirectDylibsProcessed(false),
239 _importAtom(nullptr),
240 _parentUmbrella(nullptr),
e456bf10 241 _platforms(platforms),
ec29ba20
A
242 _swiftVersion(0),
243 _wrongOS(false),
244 _linkingFlat(linkingFlatNamespace),
245 _noRexports(false),
246 _explictReExportFound(false),
247 _implicitlyLinkPublicDylibs(hoistImplicitPublicDylibs),
248 _installPathOverride(false),
249 _hasWeakExports(false),
250 _deadStrippable(false),
251 _hasPublicInstallName(false),
252 _appExtensionSafe(false),
e456bf10 253 _allowWeakImports(allowWeakImports),
ec29ba20
A
254 _allowSimToMacOSXLinking(allowSimToMacOSX),
255 _addVersionLoadCommand(addVers)
256{
257}
258
259template <typename A>
260std::pair<bool, bool> File<A>::hasWeakDefinitionImpl(const char* name) const
261{
262 const auto pos = _atoms.find(name);
263 if ( pos != this->_atoms.end() )
264 return std::make_pair(true, pos->second.weakDef);
265
266 // look in re-exported libraries.
267 for (const auto &dep : _dependentDylibs) {
268 if ( dep.reExport ) {
269 auto ret = dep.dylib->hasWeakDefinitionImpl(name);
270 if ( ret.first )
271 return ret;
272 }
273 }
274 return std::make_pair(false, false);
275}
276
277template <typename A>
278bool File<A>::hasWeakDefinition(const char* name) const
279{
280 // If we are supposed to ignore this export, then pretend we don't have it.
281 if ( _ignoreExports.count(name) != 0 )
282 return false;
283
284 return hasWeakDefinitionImpl(name).second;
285}
286
287template <typename A>
288bool File<A>::containsOrReExports(const char* name, bool& weakDef, bool& tlv, pint_t& addr) const
289{
290 if ( _ignoreExports.count(name) != 0 )
291 return false;
292
293 // check myself
294 const auto pos = _atoms.find(name);
295 if ( pos != _atoms.end() ) {
296 weakDef = pos->second.weakDef;
297 tlv = pos->second.tlv;
298 addr = pos->second.address;
299 return true;
300 }
301
302 // check dylibs I re-export
303 for (const auto& dep : _dependentDylibs) {
304 if ( dep.reExport && !dep.dylib->implicitlyLinked() ) {
305 if ( dep.dylib->containsOrReExports(name, weakDef, tlv, addr) )
306 return true;
307 }
308 }
309
310 return false;
311}
312
313template <typename A>
314bool File<A>::forEachAtom(ld::File::AtomHandler& handler) const
315{
316 handler.doFile(*this);
317
318 // if doing flatnamespace and need all this dylib's imports resolve
319 // add atom which references alls undefines in this dylib
320 if ( _importAtom != nullptr ) {
321 handler.doAtom(*_importAtom);
322 return true;
323 }
324 return false;
325}
326
327template <typename A>
328bool File<A>::justInTimeforEachAtom(const char* name, ld::File::AtomHandler& handler) const
329{
330 // If we are supposed to ignore this export, then pretend we don't have it.
331 if ( _ignoreExports.count(name) != 0 )
332 return false;
333
334
335 AtomAndWeak bucket;
336 if ( containsOrReExports(name, bucket.weakDef, bucket.tlv, bucket.address) ) {
337 bucket.atom = new ExportAtom<A>(*this, name, bucket.weakDef, bucket.tlv, bucket.address);
338 _atoms[name] = bucket;
339 _providedAtom = true;
340 if ( _s_logHashtable )
341 fprintf(stderr, "getJustInTimeAtomsFor: %s found in %s\n", name, this->path());
342 // call handler with new export atom
343 handler.doAtom(*bucket.atom);
344 return true;
345 }
346
347 return false;
348}
349
350template <typename A>
351void File<A>::assertNoReExportCycles(ReExportChain* prev) const
352{
353 // recursively check my re-exported dylibs
354 ReExportChain chain = { prev, this };
355 for (const auto &dep : _dependentDylibs) {
356 if ( dep.reExport ) {
357 auto* child = dep.dylib;
358 // check child is not already in chain
359 for (auto* p = prev; p != nullptr; p = p->prev) {
360 if ( p->file == child ) {
361 throwf("cycle in dylib re-exports with %s and %s", child->path(), this->path());
362 }
363 }
364 if ( dep.dylib != nullptr )
365 dep.dylib->assertNoReExportCycles(&chain);
366 }
367 }
368}
369
370template <typename A>
371void File<A>::processIndirectLibraries(ld::dylib::File::DylibHandler* handler, bool addImplicitDylibs)
372{
373 // only do this once
374 if ( _indirectDylibsProcessed )
375 return;
376
377 const static bool log = false;
378 if ( log )
379 fprintf(stderr, "processIndirectLibraries(%s)\n", this->installPath());
380 if ( _linkingFlat ) {
381 for (auto &dep : _dependentDylibs)
0a8dc3df 382 dep.dylib = (File<A>*)handler->findDylib(dep.path, this, false);
ec29ba20
A
383 }
384 else if ( _noRexports ) {
385 // MH_NO_REEXPORTED_DYLIBS bit set, then nothing to do
386 }
387 else {
388 // two-level, might have re-exports
389 for (auto &dep : this->_dependentDylibs) {
390 if ( dep.reExport ) {
391 if ( log )
392 fprintf(stderr, "processIndirectLibraries() parent=%s, child=%s\n", this->installPath(), dep.path);
393 // a LC_REEXPORT_DYLIB, LC_SUB_UMBRELLA or LC_SUB_LIBRARY says we re-export this child
0a8dc3df 394 dep.dylib = (File<A>*)handler->findDylib(dep.path, this, this->speculativelyLoaded());
ec29ba20
A
395 if ( dep.dylib->hasPublicInstallName() && !dep.dylib->wrongOS() ) {
396 // promote this child to be automatically added as a direct dependent if this already is
397 if ( (this->explicitlyLinked() || this->implicitlyLinked()) && (strcmp(dep.path, dep.dylib->installPath()) == 0) ) {
398 if ( log )
399 fprintf(stderr, "processIndirectLibraries() implicitly linking %s\n", dep.dylib->installPath());
400 dep.dylib->setImplicitlyLinked();
401 }
402 else if ( dep.dylib->explicitlyLinked() || dep.dylib->implicitlyLinked() ) {
403 if ( log )
404 fprintf(stderr, "processIndirectLibraries() parent is not directly linked, but child is, so no need to re-export child\n");
405 }
406 else {
407 if ( log )
408 fprintf(stderr, "processIndirectLibraries() parent is not directly linked, so parent=%s will re-export child=%s\n", this->installPath(), dep.path);
409 }
410 }
411 else {
412 // add all child's symbols to me
413 if ( log )
414 fprintf(stderr, "processIndirectLibraries() child is not public, so parent=%s will re-export child=%s\n", this->installPath(), dep.path);
415 }
416 }
417 else if ( !_explictReExportFound ) {
418 // see if child contains LC_SUB_FRAMEWORK with my name
0a8dc3df 419 dep.dylib = (File<A>*)handler->findDylib(dep.path, this, this->speculativelyLoaded());
ec29ba20
A
420 const char* parentUmbrellaName = dep.dylib->parentUmbrella();
421 if ( parentUmbrellaName != nullptr ) {
422 const char* parentName = this->path();
423 const char* lastSlash = strrchr(parentName, '/');
424 if ( (lastSlash != nullptr) && (strcmp(&lastSlash[1], parentUmbrellaName) == 0) ) {
425 // add all child's symbols to me
426 dep.reExport = true;
427 if ( log )
428 fprintf(stderr, "processIndirectLibraries() umbrella=%s will re-export child=%s\n", this->installPath(), dep.path);
429 }
430 }
431 }
432 }
433 }
434
435 // check for re-export cycles
436 ReExportChain chain = { nullptr, this };
437 this->assertNoReExportCycles(&chain);
438
439 _indirectDylibsProcessed = true;
440}
441
442template <typename A>
443bool File<A>::isPublicLocation(const char* path) const
444{
445 // -no_implicit_dylibs disables this optimization
446 if ( ! _implicitlyLinkPublicDylibs )
447 return false;
448
449 // /usr/lib is a public location
450 if ( (strncmp(path, "/usr/lib/", 9) == 0) && (strchr(&path[9], '/') == nullptr) )
451 return true;
452
453 // /System/Library/Frameworks/ is a public location
454 if ( strncmp(path, "/System/Library/Frameworks/", 27) == 0 ) {
455 const char* frameworkDot = strchr(&path[27], '.');
456 // but only top level framework
457 // /System/Library/Frameworks/Foo.framework/Versions/A/Foo ==> true
458 // /System/Library/Frameworks/Foo.framework/Resources/libBar.dylib ==> false
459 // /System/Library/Frameworks/Foo.framework/Frameworks/Bar.framework/Bar ==> false
460 // /System/Library/Frameworks/Foo.framework/Frameworks/Xfoo.framework/XFoo ==> false
461 if ( frameworkDot != nullptr ) {
462 int frameworkNameLen = frameworkDot - &path[27];
463 if ( strncmp(&path[strlen(path)-frameworkNameLen-1], &path[26], frameworkNameLen+1) == 0 )
464 return true;
465 }
466 }
467
468 return false;
469}
470
ec29ba20
A
471// <rdar://problem/5529626> If only weak_import symbols are used, linker should use LD_LOAD_WEAK_DYLIB
472template <typename A>
473bool File<A>::allSymbolsAreWeakImported() const
474{
475 bool foundNonWeakImport = false;
476 bool foundWeakImport = false;
477 //fprintf(stderr, "%s:\n", this->path());
478 for (const auto &it : _atoms) {
479 auto* atom = it.second.atom;
480 if ( atom != nullptr ) {
481 if ( atom->weakImported() )
482 foundWeakImport = true;
483 else
484 foundNonWeakImport = true;
485 //fprintf(stderr, " weak_import=%d, name=%s\n", atom->weakImported(), it->first);
486 }
487 }
488
489 // don't automatically weak link dylib with no imports
490 // so at least one weak import symbol and no non-weak-imported symbols must be found
491 return foundWeakImport && !foundNonWeakImport;
492}
493
494
495} // end namespace dylib
496} // end namespace generic
497
498#endif // __GENERIC_DYLIB_FILE_H__