]>
Commit | Line | Data |
---|---|---|
a61fdf0a | 1 | /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-* |
a645023d | 2 | * |
b2fa67a8 | 3 | * Copyright (c) 2005-2011 Apple Inc. All rights reserved. |
c2646906 A |
4 | * |
5 | * @APPLE_LICENSE_HEADER_START@ | |
d696c285 | 6 | * |
c2646906 A |
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. | |
d696c285 | 13 | * |
c2646906 A |
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. | |
d696c285 | 21 | * |
c2646906 A |
22 | * @APPLE_LICENSE_HEADER_END@ |
23 | */ | |
2f2f92e4 A |
24 | |
25 | // start temp HACK for cross builds | |
26 | extern "C" double log2 ( double ); | |
a645023d | 27 | //#define __MATH__ |
2f2f92e4 A |
28 | // end temp HACK for cross builds |
29 | ||
30 | ||
c2646906 A |
31 | #include <stdlib.h> |
32 | #include <sys/types.h> | |
33 | #include <sys/stat.h> | |
34 | #include <sys/mman.h> | |
d696c285 | 35 | #include <sys/sysctl.h> |
c2646906 | 36 | #include <fcntl.h> |
6e880c60 | 37 | #include <errno.h> |
69a49097 | 38 | #include <limits.h> |
d696c285 | 39 | #include <unistd.h> |
a645023d | 40 | #include <execinfo.h> |
d696c285 A |
41 | #include <mach/mach_time.h> |
42 | #include <mach/vm_statistics.h> | |
43 | #include <mach/mach_init.h> | |
44 | #include <mach/mach_host.h> | |
a61fdf0a | 45 | #include <dlfcn.h> |
a645023d A |
46 | #include <mach-o/dyld.h> |
47 | #include <dlfcn.h> | |
48 | #include <AvailabilityMacros.h> | |
d696c285 | 49 | |
c2646906 | 50 | #include <string> |
74cfe461 | 51 | #include <map> |
c2646906 A |
52 | #include <set> |
53 | #include <string> | |
54 | #include <vector> | |
55 | #include <list> | |
56 | #include <algorithm> | |
d425e388 | 57 | #include <unordered_map> |
a645023d | 58 | #include <cxxabi.h> |
c2646906 A |
59 | |
60 | #include "Options.h" | |
61 | ||
a645023d A |
62 | #include "MachOFileAbstraction.hpp" |
63 | #include "Architectures.hpp" | |
64 | #include "ld.hpp" | |
d696c285 | 65 | |
a645023d A |
66 | #include "InputFiles.h" |
67 | #include "Resolver.h" | |
68 | #include "OutputFile.h" | |
ebf6f434 | 69 | #include "Snapshot.h" |
d696c285 | 70 | |
a645023d A |
71 | #include "passes/stubs/make_stubs.h" |
72 | #include "passes/dtrace_dof.h" | |
73 | #include "passes/got.h" | |
74 | #include "passes/tlvp.h" | |
75 | #include "passes/huge.h" | |
76 | #include "passes/compact_unwind.h" | |
b2fa67a8 | 77 | #include "passes/order.h" |
a645023d A |
78 | #include "passes/branch_island.h" |
79 | #include "passes/branch_shim.h" | |
80 | #include "passes/objc.h" | |
81 | #include "passes/dylibs.h" | |
eaf282aa | 82 | #include "passes/bitcode_bundle.h" |
c2646906 | 83 | |
a645023d A |
84 | #include "parsers/archive_file.h" |
85 | #include "parsers/macho_relocatable_file.h" | |
86 | #include "parsers/macho_dylib_file.h" | |
87 | #include "parsers/lto_file.h" | |
88 | #include "parsers/opaque_section_file.h" | |
c2646906 | 89 | |
c2646906 | 90 | |
b2fa67a8 A |
91 | struct PerformanceStatistics { |
92 | uint64_t startTool; | |
93 | uint64_t startInputFileProcessing; | |
94 | uint64_t startResolver; | |
95 | uint64_t startDylibs; | |
96 | uint64_t startPasses; | |
97 | uint64_t startOutput; | |
98 | uint64_t startDone; | |
99 | vm_statistics_data_t vmStart; | |
100 | vm_statistics_data_t vmEnd; | |
101 | }; | |
102 | ||
103 | ||
a645023d | 104 | class InternalState : public ld::Internal |
c2646906 A |
105 | { |
106 | public: | |
b2fa67a8 | 107 | InternalState(const Options& opts) : _options(opts), _atomsOrderedInSections(false) { } |
a645023d A |
108 | virtual ld::Internal::FinalSection* addAtom(const ld::Atom& atom); |
109 | virtual ld::Internal::FinalSection* getFinalSection(const ld::Section&); | |
599556ff | 110 | ld::Internal::FinalSection* getFinalSection(const char* seg, const char* sect, ld::Section::Type type); |
a645023d | 111 | |
9543cb2f A |
112 | uint64_t assignFileOffsets(); |
113 | void setSectionSizesAndAlignments(); | |
a645023d | 114 | void sortSections(); |
b2fa67a8 | 115 | void markAtomsOrdered() { _atomsOrderedInSections = true; } |
eaf282aa A |
116 | bool hasReferenceToWeakExternal(const ld::Atom& atom); |
117 | ||
a645023d | 118 | virtual ~InternalState() {} |
c2646906 | 119 | private: |
c2646906 | 120 | |
a645023d A |
121 | class FinalSection : public ld::Internal::FinalSection |
122 | { | |
123 | public: | |
599556ff | 124 | FinalSection(const ld::Section& sect, uint32_t sectionsSeen, const Options&); |
a645023d | 125 | static int sectionComparer(const void* l, const void* r); |
afe874b1 | 126 | static const ld::Section& outputSection(const ld::Section& sect, bool mergeZeroFill); |
9543cb2f | 127 | static const ld::Section& objectOutputSection(const ld::Section& sect, const Options&); |
a645023d A |
128 | private: |
129 | friend class InternalState; | |
599556ff A |
130 | static uint32_t sectionOrder(const ld::Section& sect, uint32_t sectionsSeen, const Options& options); |
131 | static uint32_t segmentOrder(const ld::Section& sect, const Options& options); | |
a645023d A |
132 | uint32_t _segmentOrder; |
133 | uint32_t _sectionOrder; | |
134 | ||
135 | static std::vector<const char*> _s_segmentsSeen; | |
136 | static ld::Section _s_DATA_data; | |
137 | static ld::Section _s_DATA_const; | |
138 | static ld::Section _s_TEXT_text; | |
139 | static ld::Section _s_TEXT_const; | |
140 | static ld::Section _s_DATA_nl_symbol_ptr; | |
141 | static ld::Section _s_DATA_common; | |
afe874b1 | 142 | static ld::Section _s_DATA_zerofill; |
eaf282aa A |
143 | static ld::Section _s_DATA_DIRTY_data; |
144 | static ld::Section _s_DATA_CONST_const; | |
c2646906 | 145 | }; |
55e3d2f6 | 146 | |
9543cb2f A |
147 | bool hasZeroForFileOffset(const ld::Section* sect); |
148 | uint64_t pageAlign(uint64_t addr); | |
149 | uint64_t pageAlign(uint64_t addr, uint64_t pageSize); | |
55e3d2f6 | 150 | |
a645023d A |
151 | struct SectionHash { |
152 | size_t operator()(const ld::Section*) const; | |
153 | }; | |
154 | struct SectionEquals { | |
155 | bool operator()(const ld::Section* left, const ld::Section* right) const; | |
156 | }; | |
d425e388 | 157 | typedef std::unordered_map<const ld::Section*, FinalSection*, SectionHash, SectionEquals> SectionInToOut; |
55e3d2f6 | 158 | |
d696c285 | 159 | |
a645023d A |
160 | SectionInToOut _sectionInToFinalMap; |
161 | const Options& _options; | |
b2fa67a8 | 162 | bool _atomsOrderedInSections; |
a645023d | 163 | }; |
d696c285 | 164 | |
a645023d A |
165 | ld::Section InternalState::FinalSection::_s_DATA_data( "__DATA", "__data", ld::Section::typeUnclassified); |
166 | ld::Section InternalState::FinalSection::_s_DATA_const("__DATA", "__const", ld::Section::typeUnclassified); | |
167 | ld::Section InternalState::FinalSection::_s_TEXT_text( "__TEXT", "__text", ld::Section::typeCode); | |
168 | ld::Section InternalState::FinalSection::_s_TEXT_const("__TEXT", "__const", ld::Section::typeUnclassified); | |
169 | ld::Section InternalState::FinalSection::_s_DATA_nl_symbol_ptr("__DATA", "__nl_symbol_ptr", ld::Section::typeNonLazyPointer); | |
170 | ld::Section InternalState::FinalSection::_s_DATA_common("__DATA", "__common", ld::Section::typeZeroFill); | |
afe874b1 | 171 | ld::Section InternalState::FinalSection::_s_DATA_zerofill("__DATA", "__zerofill", ld::Section::typeZeroFill); |
eaf282aa A |
172 | ld::Section InternalState::FinalSection::_s_DATA_DIRTY_data( "__DATA_DIRTY", "__data", ld::Section::typeUnclassified); |
173 | ld::Section InternalState::FinalSection::_s_DATA_CONST_const( "__DATA_CONST", "__const", ld::Section::typeUnclassified); | |
174 | ||
a645023d | 175 | std::vector<const char*> InternalState::FinalSection::_s_segmentsSeen; |
a61fdf0a | 176 | |
c2646906 | 177 | |
a645023d | 178 | size_t InternalState::SectionHash::operator()(const ld::Section* sect) const |
c2646906 | 179 | { |
a645023d | 180 | size_t hash = 0; |
d425e388 | 181 | ld::CStringHash temp; |
a645023d A |
182 | hash += temp.operator()(sect->segmentName()); |
183 | hash += temp.operator()(sect->sectionName()); | |
184 | return hash; | |
c2646906 A |
185 | } |
186 | ||
a645023d | 187 | bool InternalState::SectionEquals::operator()(const ld::Section* left, const ld::Section* right) const |
c2646906 | 188 | { |
a645023d | 189 | return (*left == *right); |
c2646906 A |
190 | } |
191 | ||
d696c285 | 192 | |
599556ff | 193 | InternalState::FinalSection::FinalSection(const ld::Section& sect, uint32_t sectionsSeen, const Options& opts) |
a645023d | 194 | : ld::Internal::FinalSection(sect), |
599556ff A |
195 | _segmentOrder(segmentOrder(sect, opts)), |
196 | _sectionOrder(sectionOrder(sect, sectionsSeen, opts)) | |
a645023d A |
197 | { |
198 | //fprintf(stderr, "FinalSection(%s, %s) _segmentOrder=%d, _sectionOrder=%d\n", | |
199 | // this->segmentName(), this->sectionName(), _segmentOrder, _sectionOrder); | |
c2646906 A |
200 | } |
201 | ||
afe874b1 | 202 | const ld::Section& InternalState::FinalSection::outputSection(const ld::Section& sect, bool mergeZeroFill) |
c2646906 | 203 | { |
a645023d A |
204 | // merge sections in final linked image |
205 | switch ( sect.type() ) { | |
206 | case ld::Section::typeLiteral4: | |
207 | case ld::Section::typeLiteral8: | |
208 | case ld::Section::typeLiteral16: | |
599556ff A |
209 | if ( strcmp(sect.segmentName(), "__TEXT") == 0 ) |
210 | return _s_TEXT_const; | |
211 | break; | |
a645023d A |
212 | case ld::Section::typeUnclassified: |
213 | if ( strcmp(sect.segmentName(), "__DATA") == 0 ) { | |
214 | if ( strcmp(sect.sectionName(), "__datacoal_nt") == 0 ) | |
215 | return _s_DATA_data; | |
216 | if ( strcmp(sect.sectionName(), "__const_coal") == 0 ) | |
217 | return _s_DATA_const; | |
218 | } | |
219 | else if ( strcmp(sect.segmentName(), "__TEXT") == 0 ) { | |
220 | if ( strcmp(sect.sectionName(), "__const_coal") == 0 ) | |
221 | return _s_TEXT_const; | |
222 | } | |
eaf282aa A |
223 | else if ( strcmp(sect.segmentName(), "__DATA_DIRTY") == 0 ) { |
224 | if ( strcmp(sect.sectionName(), "__datacoal_nt") == 0 ) | |
225 | return _s_DATA_DIRTY_data; | |
226 | } | |
227 | else if ( strcmp(sect.segmentName(), "__DATA_CONST") == 0 ) { | |
228 | if ( strcmp(sect.sectionName(), "__const_coal") == 0 ) | |
229 | return _s_DATA_CONST_const; | |
230 | } | |
69a49097 | 231 | break; |
afe874b1 A |
232 | case ld::Section::typeZeroFill: |
233 | if ( mergeZeroFill ) | |
234 | return _s_DATA_zerofill; | |
235 | break; | |
a645023d A |
236 | case ld::Section::typeCode: |
237 | if ( strcmp(sect.segmentName(), "__TEXT") == 0 ) { | |
238 | if ( strcmp(sect.sectionName(), "__textcoal_nt") == 0 ) | |
239 | return _s_TEXT_text; | |
240 | else if ( strcmp(sect.sectionName(), "__StaticInit") == 0 ) | |
241 | return _s_TEXT_text; | |
242 | } | |
69a49097 | 243 | break; |
a645023d A |
244 | case ld::Section::typeNonLazyPointer: |
245 | if ( strcmp(sect.segmentName(), "__DATA") == 0 ) { | |
246 | if ( strcmp(sect.sectionName(), "__nl_symbol_ptr") == 0 ) | |
247 | return _s_DATA_nl_symbol_ptr; | |
248 | } | |
249 | else if ( strcmp(sect.segmentName(), "__IMPORT") == 0 ) { | |
250 | if ( strcmp(sect.sectionName(), "__pointers") == 0 ) | |
251 | return _s_DATA_nl_symbol_ptr; | |
c211e7c9 | 252 | } |
2f2f92e4 | 253 | break; |
a645023d | 254 | case ld::Section::typeTentativeDefs: |
599556ff A |
255 | if ( (strcmp(sect.segmentName(), "__DATA") == 0) && (strcmp(sect.sectionName(), "__comm/tent") == 0) ) { |
256 | if ( mergeZeroFill ) | |
257 | return _s_DATA_zerofill; | |
258 | else | |
259 | return _s_DATA_common; | |
260 | } | |
a645023d A |
261 | break; |
262 | // FIX ME: more | |
69a49097 | 263 | default: |
69a49097 | 264 | break; |
d696c285 | 265 | } |
a645023d | 266 | return sect; |
c2646906 A |
267 | } |
268 | ||
9543cb2f | 269 | const ld::Section& InternalState::FinalSection::objectOutputSection(const ld::Section& sect, const Options& options) |
69a49097 | 270 | { |
a645023d | 271 | // in -r mode the only section that ever changes is __tenative -> __common with -d option |
9543cb2f | 272 | if ( (sect.type() == ld::Section::typeTentativeDefs) && options.makeTentativeDefinitionsReal()) |
a645023d A |
273 | return _s_DATA_common; |
274 | return sect; | |
69a49097 A |
275 | } |
276 | ||
599556ff | 277 | uint32_t InternalState::FinalSection::segmentOrder(const ld::Section& sect, const Options& options) |
69a49097 | 278 | { |
599556ff A |
279 | if ( options.outputKind() == Options::kPreload ) { |
280 | if ( strcmp(sect.segmentName(), "__HEADER") == 0 ) | |
281 | return 0; | |
282 | const std::vector<const char*>& order = options.segmentOrder(); | |
283 | for (size_t i=0; i != order.size(); ++i) { | |
284 | if ( strcmp(sect.segmentName(), order[i]) == 0 ) | |
285 | return i+1; | |
286 | } | |
287 | if ( strcmp(sect.segmentName(), "__TEXT") == 0 ) | |
288 | return order.size()+1; | |
289 | if ( strcmp(sect.segmentName(), "__DATA") == 0 ) | |
290 | return order.size()+2; | |
291 | } | |
292 | else { | |
293 | if ( strcmp(sect.segmentName(), "__PAGEZERO") == 0 ) | |
294 | return 0; | |
295 | if ( strcmp(sect.segmentName(), "__TEXT") == 0 ) | |
296 | return 1; | |
297 | // in -r mode, want __DATA last so zerofill sections are at end | |
298 | if ( strcmp(sect.segmentName(), "__DATA") == 0 ) | |
299 | return (options.outputKind() == Options::kObjectFile) ? 5 : 2; | |
300 | if ( strcmp(sect.segmentName(), "__OBJC") == 0 ) | |
301 | return 3; | |
302 | if ( strcmp(sect.segmentName(), "__IMPORT") == 0 ) | |
303 | return 4; | |
304 | } | |
305 | // layout non-standard segments in order seen (+100 to shift beyond standard segments) | |
a645023d A |
306 | for (uint32_t i=0; i < _s_segmentsSeen.size(); ++i) { |
307 | if ( strcmp(_s_segmentsSeen[i], sect.segmentName()) == 0 ) | |
599556ff | 308 | return i+100; |
a645023d A |
309 | } |
310 | _s_segmentsSeen.push_back(sect.segmentName()); | |
599556ff | 311 | return _s_segmentsSeen.size()-1+100; |
69a49097 A |
312 | } |
313 | ||
599556ff | 314 | uint32_t InternalState::FinalSection::sectionOrder(const ld::Section& sect, uint32_t sectionsSeen, const Options& options) |
d696c285 | 315 | { |
a645023d A |
316 | if ( sect.type() == ld::Section::typeFirstSection ) |
317 | return 0; | |
318 | if ( sect.type() == ld::Section::typeMachHeader ) | |
319 | return 1; | |
320 | if ( sect.type() == ld::Section::typeLastSection ) | |
321 | return INT_MAX; | |
599556ff | 322 | const std::vector<const char*>* sectionList = options.sectionOrder(sect.segmentName()); |
eaf282aa | 323 | if ( ((options.outputKind() == Options::kPreload) || (options.outputKind() == Options::kDyld)) && (sectionList != NULL) ) { |
599556ff A |
324 | uint32_t count = 10; |
325 | for (std::vector<const char*>::const_iterator it=sectionList->begin(); it != sectionList->end(); ++it, ++count) { | |
326 | if ( strcmp(*it, sect.sectionName()) == 0 ) | |
327 | return count; | |
328 | } | |
329 | } | |
a645023d A |
330 | if ( strcmp(sect.segmentName(), "__TEXT") == 0 ) { |
331 | switch ( sect.type() ) { | |
332 | case ld::Section::typeCode: | |
333 | // <rdar://problem/8346444> make __text always be first "code" section | |
334 | if ( strcmp(sect.sectionName(), "__text") == 0 ) | |
335 | return 10; | |
336 | else | |
337 | return 11; | |
338 | case ld::Section::typeStub: | |
339 | return 12; | |
340 | case ld::Section::typeStubHelper: | |
341 | return 13; | |
342 | case ld::Section::typeLSDA: | |
343 | return INT_MAX-3; | |
344 | case ld::Section::typeUnwindInfo: | |
345 | return INT_MAX-2; | |
346 | case ld::Section::typeCFI: | |
347 | return INT_MAX-1; | |
348 | case ld::Section::typeStubClose: | |
349 | return INT_MAX; | |
350 | default: | |
351 | return sectionsSeen+20; | |
352 | } | |
353 | } | |
eaf282aa | 354 | else if ( strncmp(sect.segmentName(), "__DATA", 6) == 0 ) { |
a645023d A |
355 | switch ( sect.type() ) { |
356 | case ld::Section::typeLazyPointerClose: | |
357 | return 8; | |
358 | case ld::Section::typeDyldInfo: | |
359 | return 9; | |
360 | case ld::Section::typeNonLazyPointer: | |
361 | return 10; | |
362 | case ld::Section::typeLazyPointer: | |
363 | return 11; | |
364 | case ld::Section::typeInitializerPointers: | |
365 | return 12; | |
366 | case ld::Section::typeTerminatorPointers: | |
367 | return 13; | |
368 | case ld::Section::typeTLVInitialValues: | |
369 | return INT_MAX-4; // need TLV zero-fill to follow TLV init values | |
370 | case ld::Section::typeTLVZeroFill: | |
371 | return INT_MAX-3; | |
372 | case ld::Section::typeZeroFill: | |
373 | // make sure __huge is always last zerofill section | |
374 | if ( strcmp(sect.sectionName(), "__huge") == 0 ) | |
375 | return INT_MAX-1; | |
376 | else | |
377 | return INT_MAX-2; | |
378 | default: | |
f80fe69f A |
379 | // <rdar://problem/14348664> __DATA,__const section should be near __mod_init_func not __data |
380 | if ( strcmp(sect.sectionName(), "__const") == 0 ) | |
381 | return 14; | |
599556ff A |
382 | // <rdar://problem/17125893> Linker should put __cfstring near __const |
383 | if ( strcmp(sect.sectionName(), "__cfstring") == 0 ) | |
384 | return 15; | |
a645023d | 385 | // <rdar://problem/7435296> Reorder sections to reduce page faults in object files |
f80fe69f | 386 | else if ( strcmp(sect.sectionName(), "__objc_classlist") == 0 ) |
a645023d A |
387 | return 20; |
388 | else if ( strcmp(sect.sectionName(), "__objc_nlclslist") == 0 ) | |
389 | return 21; | |
390 | else if ( strcmp(sect.sectionName(), "__objc_catlist") == 0 ) | |
391 | return 22; | |
599556ff | 392 | else if ( strcmp(sect.sectionName(), "__objc_nlcatlist") == 0 ) |
a645023d | 393 | return 23; |
599556ff | 394 | else if ( strcmp(sect.sectionName(), "__objc_protolist") == 0 ) |
a645023d | 395 | return 24; |
599556ff | 396 | else if ( strcmp(sect.sectionName(), "__objc_imageinfo") == 0 ) |
a645023d | 397 | return 25; |
599556ff | 398 | else if ( strcmp(sect.sectionName(), "__objc_const") == 0 ) |
a645023d | 399 | return 26; |
599556ff | 400 | else if ( strcmp(sect.sectionName(), "__objc_selrefs") == 0 ) |
a645023d | 401 | return 27; |
599556ff | 402 | else if ( strcmp(sect.sectionName(), "__objc_msgrefs") == 0 ) |
a645023d | 403 | return 28; |
599556ff | 404 | else if ( strcmp(sect.sectionName(), "__objc_protorefs") == 0 ) |
a645023d | 405 | return 29; |
599556ff | 406 | else if ( strcmp(sect.sectionName(), "__objc_classrefs") == 0 ) |
a645023d | 407 | return 30; |
599556ff | 408 | else if ( strcmp(sect.sectionName(), "__objc_superrefs") == 0 ) |
a645023d | 409 | return 31; |
599556ff A |
410 | else if ( strcmp(sect.sectionName(), "__objc_ivar") == 0 ) |
411 | return 32; | |
412 | else if ( strcmp(sect.sectionName(), "__objc_data") == 0 ) | |
413 | return 33; | |
a645023d A |
414 | else |
415 | return sectionsSeen+40; | |
416 | } | |
417 | } | |
418 | // make sure zerofill in any other section is at end of segment | |
419 | if ( sect.type() == ld::Section::typeZeroFill ) | |
420 | return INT_MAX-1; | |
421 | return sectionsSeen+20; | |
422 | } | |
423 | ||
424 | #ifndef NDEBUG | |
425 | static void validateFixups(const ld::Atom& atom) | |
426 | { | |
427 | //fprintf(stderr, "validateFixups %s\n", atom.name()); | |
428 | bool lastWasClusterEnd = true; | |
429 | ld::Fixup::Cluster lastClusterSize = ld::Fixup::k1of1; | |
430 | uint32_t curClusterOffsetInAtom = 0; | |
431 | for (ld::Fixup::iterator fit=atom.fixupsBegin(); fit != atom.fixupsEnd(); ++fit) { | |
432 | //fprintf(stderr, " fixup offset=%d, cluster=%d\n", fit->offsetInAtom, fit->clusterSize); | |
b1f7435d | 433 | assert((fit->offsetInAtom <= atom.size()) || (fit->offsetInAtom == 0)); |
a645023d A |
434 | if ( fit->firstInCluster() ) { |
435 | assert(lastWasClusterEnd); | |
436 | curClusterOffsetInAtom = fit->offsetInAtom; | |
437 | lastWasClusterEnd = (fit->clusterSize == ld::Fixup::k1of1); | |
438 | } | |
439 | else { | |
440 | assert(!lastWasClusterEnd); | |
441 | assert(fit->offsetInAtom == curClusterOffsetInAtom); | |
442 | switch ((ld::Fixup::Cluster)fit->clusterSize) { | |
443 | case ld::Fixup::k1of1: | |
444 | case ld::Fixup::k1of2: | |
445 | case ld::Fixup::k1of3: | |
446 | case ld::Fixup::k1of4: | |
447 | case ld::Fixup::k1of5: | |
448 | lastWasClusterEnd = false; | |
449 | break; | |
450 | case ld::Fixup::k2of2: | |
451 | assert(lastClusterSize = ld::Fixup::k1of2); | |
452 | lastWasClusterEnd = true; | |
453 | break; | |
454 | case ld::Fixup::k2of3: | |
455 | assert(lastClusterSize = ld::Fixup::k1of3); | |
456 | lastWasClusterEnd = false; | |
457 | break; | |
458 | case ld::Fixup::k2of4: | |
459 | assert(lastClusterSize = ld::Fixup::k1of4); | |
460 | lastWasClusterEnd = false; | |
461 | break; | |
462 | case ld::Fixup::k2of5: | |
463 | assert(lastClusterSize = ld::Fixup::k1of5); | |
464 | lastWasClusterEnd = false; | |
465 | break; | |
466 | case ld::Fixup::k3of3: | |
467 | assert(lastClusterSize = ld::Fixup::k2of3); | |
468 | lastWasClusterEnd = true; | |
469 | break; | |
470 | case ld::Fixup::k3of4: | |
471 | assert(lastClusterSize = ld::Fixup::k2of4); | |
472 | lastWasClusterEnd = false; | |
473 | break; | |
474 | case ld::Fixup::k3of5: | |
475 | assert(lastClusterSize = ld::Fixup::k2of5); | |
476 | lastWasClusterEnd = false; | |
477 | break; | |
478 | case ld::Fixup::k4of4: | |
479 | assert(lastClusterSize = ld::Fixup::k3of4); | |
480 | lastWasClusterEnd = true; | |
481 | break; | |
482 | case ld::Fixup::k4of5: | |
483 | assert(lastClusterSize = ld::Fixup::k3of5); | |
484 | lastWasClusterEnd = false; | |
485 | break; | |
486 | case ld::Fixup::k5of5: | |
487 | assert(lastClusterSize = ld::Fixup::k4of5); | |
488 | lastWasClusterEnd = true; | |
489 | break; | |
d696c285 A |
490 | } |
491 | } | |
a645023d A |
492 | lastClusterSize = fit->clusterSize; |
493 | if ( fit->binding == ld::Fixup::bindingDirectlyBound ) { | |
494 | assert(fit->u.target != NULL); | |
495 | } | |
d696c285 | 496 | } |
a645023d A |
497 | switch (lastClusterSize) { |
498 | case ld::Fixup::k1of1: | |
499 | case ld::Fixup::k2of2: | |
500 | case ld::Fixup::k3of3: | |
501 | case ld::Fixup::k4of4: | |
502 | case ld::Fixup::k5of5: | |
503 | break; | |
504 | default: | |
505 | assert(0 && "last fixup was not end of cluster"); | |
506 | break; | |
69a49097 A |
507 | } |
508 | } | |
a645023d | 509 | #endif |
69a49097 | 510 | |
eaf282aa A |
511 | bool InternalState::hasReferenceToWeakExternal(const ld::Atom& atom) |
512 | { | |
513 | // if __DATA,__const atom has pointer to weak external symbol, don't move to __DATA_CONST | |
514 | const ld::Atom* target = NULL; | |
515 | for (ld::Fixup::iterator fit=atom.fixupsBegin(); fit != atom.fixupsEnd(); ++fit) { | |
516 | if ( fit->firstInCluster() ) { | |
517 | target = NULL; | |
518 | } | |
519 | switch ( fit->binding ) { | |
520 | case ld::Fixup::bindingNone: | |
521 | case ld::Fixup::bindingByNameUnbound: | |
522 | break; | |
523 | case ld::Fixup::bindingByContentBound: | |
524 | case ld::Fixup::bindingDirectlyBound: | |
525 | target = fit->u.target; | |
526 | break; | |
527 | case ld::Fixup::bindingsIndirectlyBound: | |
528 | target = indirectBindingTable[fit->u.bindingIndex]; | |
529 | break; | |
530 | } | |
531 | if ( (target != NULL) && (target->definition() == ld::Atom::definitionRegular) | |
532 | && (target->combine() == ld::Atom::combineByName) && (target->scope() == ld::Atom::scopeGlobal) ) { | |
533 | return true; | |
534 | } | |
535 | } | |
536 | return false; | |
537 | } | |
538 | ||
a645023d A |
539 | ld::Internal::FinalSection* InternalState::addAtom(const ld::Atom& atom) |
540 | { | |
599556ff A |
541 | ld::Internal::FinalSection* fs = NULL; |
542 | const char* sectName = atom.section().sectionName(); | |
543 | ld::Section::Type sectType = atom.section().type(); | |
544 | const ld::File* f = atom.file(); | |
545 | const char* path = (f != NULL) ? f->path() : NULL; | |
546 | if ( atom.section().type() == ld::Section::typeTentativeDefs ) { | |
547 | // tentative defintions don't have a real section name yet | |
548 | sectType = ld::Section::typeZeroFill; | |
549 | if ( _options.mergeZeroFill() ) | |
550 | sectName = FinalSection::_s_DATA_zerofill.sectionName(); | |
551 | else | |
552 | sectName = FinalSection::_s_DATA_common.sectionName(); | |
553 | } | |
554 | // Support for -move_to_r._segment | |
555 | if ( atom.symbolTableInclusion() == ld::Atom::symbolTableIn ) { | |
556 | const char* dstSeg; | |
557 | //fprintf(stderr, "%s\n", atom.name()); | |
558 | bool wildCardMatch; | |
559 | if ( _options.moveRwSymbol(atom.name(), path, dstSeg, wildCardMatch) ) { | |
560 | if ( (sectType != ld::Section::typeZeroFill) | |
561 | && (sectType != ld::Section::typeUnclassified) | |
eaf282aa A |
562 | && (sectType != ld::Section::typeTentativeDefs) |
563 | && (sectType != ld::Section::typeDyldInfo) ) { | |
599556ff A |
564 | if ( !wildCardMatch ) |
565 | warning("cannot move symbol '%s' from file %s to segment '%s' because symbol is not data (is %d)", atom.name(), path, dstSeg, sectType); | |
566 | } | |
567 | else { | |
568 | if ( _options.traceSymbolLayout() ) | |
569 | printf("symbol '%s', -move_to_rw_segment mapped it to %s/%s\n", atom.name(), dstSeg, sectName); | |
570 | fs = this->getFinalSection(dstSeg, sectName, sectType); | |
571 | } | |
572 | } | |
573 | if ( (fs == NULL) && _options.moveRoSymbol(atom.name(), path, dstSeg, wildCardMatch) ) { | |
eaf282aa A |
574 | if ( (sectType != ld::Section::typeCode) |
575 | && (sectType != ld::Section::typeUnclassified) ) { | |
599556ff A |
576 | if ( !wildCardMatch ) |
577 | warning("cannot move symbol '%s' from file %s to segment '%s' because symbol is not code (is %d)", atom.name(), path, dstSeg, sectType); | |
578 | } | |
579 | else { | |
580 | if ( _options.traceSymbolLayout() ) | |
581 | printf("symbol '%s', -move_to_ro_segment mapped it to %s/%s\n", atom.name(), dstSeg, sectName); | |
582 | fs = this->getFinalSection(dstSeg, sectName, ld::Section::typeCode); | |
583 | } | |
584 | } | |
585 | } | |
586 | // support for -rename_section and -rename_segment | |
587 | if ( fs == NULL ) { | |
588 | const std::vector<Options::SectionRename>& sectRenames = _options.sectionRenames(); | |
589 | const std::vector<Options::SegmentRename>& segRenames = _options.segmentRenames(); | |
590 | for ( std::vector<Options::SectionRename>::const_iterator it=sectRenames.begin(); it != sectRenames.end(); ++it) { | |
591 | if ( (strcmp(sectName, it->fromSection) == 0) && (strcmp(atom.section().segmentName(), it->fromSegment) == 0) ) { | |
eaf282aa A |
592 | if ( _options.useDataConstSegment() && (strcmp(sectName, "__const") == 0) |
593 | && (strcmp(atom.section().segmentName(), "__DATA") == 0) && hasReferenceToWeakExternal(atom) ) { | |
594 | // if __DATA,__const atom has pointer to weak external symbol, don't move to __DATA_CONST | |
595 | fs = this->getFinalSection("__DATA", "__const_weak", sectType); | |
596 | if ( _options.traceSymbolLayout() ) | |
597 | printf("symbol '%s', contains pointers to weak symbols, so mapped it to __DATA/_const_weak\n", atom.name()); | |
598 | } | |
599 | else if ( _options.useDataConstSegment() && (sectType == ld::Section::typeNonLazyPointer) && hasReferenceToWeakExternal(atom) ) { | |
600 | // if __DATA,__nl_symbol_ptr atom has pointer to weak external symbol, don't move to __DATA_CONST | |
601 | fs = this->getFinalSection("__DATA", "__got_weak", sectType); | |
602 | if ( _options.traceSymbolLayout() ) | |
603 | printf("symbol '%s', contains pointers to weak symbols, so mapped it to __DATA/__got_weak\n", atom.name()); | |
604 | } | |
605 | else { | |
606 | fs = this->getFinalSection(it->toSegment, it->toSection, sectType); | |
607 | if ( _options.traceSymbolLayout() ) | |
608 | printf("symbol '%s', -rename_section mapped it to %s/%s\n", atom.name(), fs->segmentName(), fs->sectionName()); | |
609 | } | |
599556ff A |
610 | } |
611 | } | |
612 | if ( fs == NULL ) { | |
613 | for ( std::vector<Options::SegmentRename>::const_iterator it=segRenames.begin(); it != segRenames.end(); ++it) { | |
614 | if ( strcmp(atom.section().segmentName(), it->fromSegment) == 0 ) { | |
615 | if ( _options.traceSymbolLayout() ) | |
616 | printf("symbol '%s', -rename_segment mapped it to %s/%s\n", atom.name(), it->toSegment, sectName); | |
617 | fs = this->getFinalSection(it->toSegment, sectName, sectType); | |
618 | } | |
619 | } | |
620 | } | |
621 | } | |
eaf282aa | 622 | |
599556ff A |
623 | // if no override, use default location |
624 | if ( fs == NULL ) { | |
625 | fs = this->getFinalSection(atom.section()); | |
626 | if ( _options.traceSymbolLayout() && (atom.symbolTableInclusion() == ld::Atom::symbolTableIn) ) | |
627 | printf("symbol '%s', use default mapping to %s/%s\n", atom.name(), fs->segmentName(), fs->sectionName()); | |
628 | } | |
629 | ||
a645023d A |
630 | //fprintf(stderr, "InternalState::doAtom(%p), name=%s, sect=%s, finalsect=%p\n", &atom, atom.name(), atom.section().sectionName(), fs); |
631 | #ifndef NDEBUG | |
632 | validateFixups(atom); | |
633 | #endif | |
b2fa67a8 A |
634 | if ( _atomsOrderedInSections ) { |
635 | // make sure this atom is placed before any trailing section$end$ atom | |
636 | if ( (fs->atoms.size() > 1) && (fs->atoms.back()->contentType() == ld::Atom::typeSectionEnd) ) { | |
637 | // last atom in section$end$ atom, insert before it | |
638 | const ld::Atom* endAtom = fs->atoms.back(); | |
639 | fs->atoms.pop_back(); | |
640 | fs->atoms.push_back(&atom); | |
641 | fs->atoms.push_back(endAtom); | |
642 | } | |
643 | else { | |
644 | // not end atom, just append new atom | |
645 | fs->atoms.push_back(&atom); | |
646 | } | |
647 | } | |
648 | else { | |
649 | // normal case | |
650 | fs->atoms.push_back(&atom); | |
651 | } | |
eaf282aa | 652 | this->atomToSection[&atom] = fs; |
a645023d | 653 | return fs; |
55e3d2f6 | 654 | } |
2f2f92e4 | 655 | |
599556ff A |
656 | |
657 | ||
658 | ld::Internal::FinalSection* InternalState::getFinalSection(const char* seg, const char* sect, ld::Section::Type type) | |
659 | { | |
660 | for (std::vector<ld::Internal::FinalSection*>::iterator it=sections.begin(); it != sections.end(); ++it) { | |
661 | if ( (strcmp((*it)->segmentName(),seg) == 0) && (strcmp((*it)->sectionName(),sect) == 0) ) | |
662 | return *it; | |
663 | } | |
664 | return this->getFinalSection(*new ld::Section(seg, sect, type, false)); | |
665 | } | |
666 | ||
a645023d A |
667 | ld::Internal::FinalSection* InternalState::getFinalSection(const ld::Section& inputSection) |
668 | { | |
669 | const ld::Section* baseForFinalSection = &inputSection; | |
670 | ||
671 | // see if input section already has a FinalSection | |
672 | SectionInToOut::iterator pos = _sectionInToFinalMap.find(&inputSection); | |
673 | if ( pos != _sectionInToFinalMap.end() ) { | |
674 | return pos->second; | |
675 | } | |
55e3d2f6 | 676 | |
a645023d | 677 | // otherwise, create a new final section |
a645023d A |
678 | switch ( _options.outputKind() ) { |
679 | case Options::kStaticExecutable: | |
680 | case Options::kDynamicExecutable: | |
681 | case Options::kDynamicLibrary: | |
682 | case Options::kDynamicBundle: | |
683 | case Options::kDyld: | |
684 | case Options::kKextBundle: | |
685 | case Options::kPreload: | |
686 | { | |
687 | // coalesce some sections | |
afe874b1 | 688 | const ld::Section& outSect = FinalSection::outputSection(inputSection, _options.mergeZeroFill()); |
a645023d A |
689 | pos = _sectionInToFinalMap.find(&outSect); |
690 | if ( pos != _sectionInToFinalMap.end() ) { | |
691 | _sectionInToFinalMap[&inputSection] = pos->second; | |
692 | //fprintf(stderr, "_sectionInToFinalMap[%p] = %p\n", &inputSection, pos->second); | |
693 | return pos->second; | |
55e3d2f6 | 694 | } |
a645023d A |
695 | else if ( outSect != inputSection ) { |
696 | // new output section created, but not in map | |
697 | baseForFinalSection = &outSect; | |
55e3d2f6 A |
698 | } |
699 | } | |
a645023d A |
700 | break; |
701 | case Options::kObjectFile: | |
9543cb2f | 702 | baseForFinalSection = &FinalSection::objectOutputSection(inputSection, _options); |
a645023d A |
703 | pos = _sectionInToFinalMap.find(baseForFinalSection); |
704 | if ( pos != _sectionInToFinalMap.end() ) { | |
705 | _sectionInToFinalMap[&inputSection] = pos->second; | |
706 | //fprintf(stderr, "_sectionInToFinalMap[%p] = %p\n", &inputSection, pos->second); | |
707 | return pos->second; | |
55e3d2f6 | 708 | } |
a645023d | 709 | break; |
2f2f92e4 | 710 | } |
d696c285 | 711 | |
a645023d | 712 | InternalState::FinalSection* result = new InternalState::FinalSection(*baseForFinalSection, |
599556ff | 713 | _sectionInToFinalMap.size(), _options); |
a645023d | 714 | _sectionInToFinalMap[baseForFinalSection] = result; |
599556ff | 715 | //fprintf(stderr, "_sectionInToFinalMap[%p(%s)] = %p\n", baseForFinalSection, baseForFinalSection->sectionName(), result); |
a645023d A |
716 | sections.push_back(result); |
717 | return result; | |
d696c285 A |
718 | } |
719 | ||
a645023d A |
720 | |
721 | int InternalState::FinalSection::sectionComparer(const void* l, const void* r) | |
d696c285 | 722 | { |
a645023d A |
723 | const FinalSection* left = *(FinalSection**)l; |
724 | const FinalSection* right = *(FinalSection**)r; | |
725 | if ( left->_segmentOrder != right->_segmentOrder ) | |
726 | return (left->_segmentOrder - right->_segmentOrder); | |
727 | return (left->_sectionOrder - right->_sectionOrder); | |
d696c285 A |
728 | } |
729 | ||
a645023d | 730 | void InternalState::sortSections() |
d696c285 | 731 | { |
a645023d A |
732 | //fprintf(stderr, "UNSORTED final sections:\n"); |
733 | //for (std::vector<ld::Internal::FinalSection*>::iterator it = sections.begin(); it != sections.end(); ++it) { | |
734 | // fprintf(stderr, "final section %p %s/%s\n", (*it), (*it)->segmentName(), (*it)->sectionName()); | |
735 | //} | |
736 | qsort(§ions[0], sections.size(), sizeof(FinalSection*), &InternalState::FinalSection::sectionComparer); | |
737 | //fprintf(stderr, "SORTED final sections:\n"); | |
738 | //for (std::vector<ld::Internal::FinalSection*>::iterator it = sections.begin(); it != sections.end(); ++it) { | |
739 | // fprintf(stderr, "final section %p %s/%s\n", (*it), (*it)->segmentName(), (*it)->sectionName()); | |
740 | //} | |
741 | assert((sections[0]->type() == ld::Section::typeMachHeader) | |
742 | || ((sections[0]->type() == ld::Section::typeFirstSection) && (sections[1]->type() == ld::Section::typeMachHeader)) | |
743 | || ((sections[0]->type() == ld::Section::typePageZero) && (sections[1]->type() == ld::Section::typeMachHeader)) | |
744 | || ((sections[0]->type() == ld::Section::typePageZero) && (sections[1]->type() == ld::Section::typeFirstSection) && (sections[2]->type() == ld::Section::typeMachHeader)) ); | |
745 | ||
d696c285 A |
746 | } |
747 | ||
9543cb2f A |
748 | |
749 | bool InternalState::hasZeroForFileOffset(const ld::Section* sect) | |
750 | { | |
751 | switch ( sect->type() ) { | |
752 | case ld::Section::typeZeroFill: | |
753 | case ld::Section::typeTLVZeroFill: | |
754 | return _options.optimizeZeroFill(); | |
755 | case ld::Section::typePageZero: | |
756 | case ld::Section::typeStack: | |
757 | case ld::Section::typeTentativeDefs: | |
758 | return true; | |
759 | default: | |
760 | break; | |
761 | } | |
762 | return false; | |
763 | } | |
764 | ||
765 | uint64_t InternalState::pageAlign(uint64_t addr) | |
766 | { | |
767 | const uint64_t alignment = _options.segmentAlignment(); | |
768 | return ((addr+alignment-1) & (-alignment)); | |
769 | } | |
770 | ||
771 | uint64_t InternalState::pageAlign(uint64_t addr, uint64_t pageSize) | |
772 | { | |
773 | return ((addr+pageSize-1) & (-pageSize)); | |
774 | } | |
775 | ||
776 | void InternalState::setSectionSizesAndAlignments() | |
777 | { | |
778 | for (std::vector<ld::Internal::FinalSection*>::iterator sit = sections.begin(); sit != sections.end(); ++sit) { | |
779 | ld::Internal::FinalSection* sect = *sit; | |
780 | if ( sect->type() == ld::Section::typeAbsoluteSymbols ) { | |
781 | // absolute symbols need their finalAddress() to their value | |
782 | for (std::vector<const ld::Atom*>::iterator ait = sect->atoms.begin(); ait != sect->atoms.end(); ++ait) { | |
783 | const ld::Atom* atom = *ait; | |
784 | (const_cast<ld::Atom*>(atom))->setSectionOffset(atom->objectAddress()); | |
785 | } | |
786 | } | |
787 | else { | |
788 | uint16_t maxAlignment = 0; | |
789 | uint64_t offset = 0; | |
790 | for (std::vector<const ld::Atom*>::iterator ait = sect->atoms.begin(); ait != sect->atoms.end(); ++ait) { | |
791 | const ld::Atom* atom = *ait; | |
792 | bool pagePerAtom = false; | |
793 | uint32_t atomAlignmentPowerOf2 = atom->alignment().powerOf2; | |
794 | uint32_t atomModulus = atom->alignment().modulus; | |
eaf282aa | 795 | if ( _options.pageAlignDataAtoms() && ( strncmp(atom->section().segmentName(), "__DATA", 6) == 0) ) { |
9543cb2f A |
796 | // most objc sections cannot be padded |
797 | bool contiguousObjCSection = ( strncmp(atom->section().sectionName(), "__objc_", 7) == 0 ); | |
798 | if ( strcmp(atom->section().sectionName(), "__objc_const") == 0 ) | |
799 | contiguousObjCSection = false; | |
800 | if ( strcmp(atom->section().sectionName(), "__objc_data") == 0 ) | |
801 | contiguousObjCSection = false; | |
802 | switch ( atom->section().type() ) { | |
803 | case ld::Section::typeUnclassified: | |
804 | case ld::Section::typeTentativeDefs: | |
805 | case ld::Section::typeZeroFill: | |
806 | if ( contiguousObjCSection ) | |
807 | break; | |
808 | pagePerAtom = true; | |
809 | if ( atomAlignmentPowerOf2 < 12 ) { | |
810 | atomAlignmentPowerOf2 = 12; | |
811 | atomModulus = 0; | |
812 | } | |
813 | break; | |
814 | default: | |
815 | break; | |
816 | } | |
817 | } | |
818 | if ( atomAlignmentPowerOf2 > maxAlignment ) | |
819 | maxAlignment = atomAlignmentPowerOf2; | |
820 | // calculate section offset for this atom | |
821 | uint64_t alignment = 1 << atomAlignmentPowerOf2; | |
822 | uint64_t currentModulus = (offset % alignment); | |
823 | uint64_t requiredModulus = atomModulus; | |
824 | if ( currentModulus != requiredModulus ) { | |
825 | if ( requiredModulus > currentModulus ) | |
826 | offset += requiredModulus-currentModulus; | |
827 | else | |
828 | offset += requiredModulus+alignment-currentModulus; | |
829 | } | |
830 | // LINKEDIT atoms are laid out later | |
831 | if ( sect->type() != ld::Section::typeLinkEdit ) { | |
832 | (const_cast<ld::Atom*>(atom))->setSectionOffset(offset); | |
833 | offset += atom->size(); | |
834 | if ( pagePerAtom ) { | |
835 | offset = (offset + 4095) & (-4096); // round up to end of page | |
836 | } | |
837 | } | |
838 | if ( (atom->scope() == ld::Atom::scopeGlobal) | |
839 | && (atom->definition() == ld::Atom::definitionRegular) | |
840 | && (atom->combine() == ld::Atom::combineByName) | |
841 | && ((atom->symbolTableInclusion() == ld::Atom::symbolTableIn) | |
842 | || (atom->symbolTableInclusion() == ld::Atom::symbolTableInAndNeverStrip)) ) { | |
843 | this->hasWeakExternalSymbols = true; | |
844 | if ( _options.warnWeakExports() ) | |
845 | warning("weak external symbol: %s", atom->name()); | |
846 | } | |
847 | } | |
848 | sect->size = offset; | |
849 | // section alignment is that of a contained atom with the greatest alignment | |
850 | sect->alignment = maxAlignment; | |
851 | // unless -sectalign command line option overrides | |
852 | if ( _options.hasCustomSectionAlignment(sect->segmentName(), sect->sectionName()) ) | |
853 | sect->alignment = _options.customSectionAlignment(sect->segmentName(), sect->sectionName()); | |
854 | // each atom in __eh_frame has zero alignment to assure they pack together, | |
855 | // but compilers usually make the CFIs pointer sized, so we want whole section | |
856 | // to start on pointer sized boundary. | |
857 | if ( sect->type() == ld::Section::typeCFI ) | |
858 | sect->alignment = 3; | |
859 | if ( sect->type() == ld::Section::typeTLVDefs ) | |
860 | this->hasThreadLocalVariableDefinitions = true; | |
861 | } | |
862 | } | |
863 | } | |
864 | ||
865 | uint64_t InternalState::assignFileOffsets() | |
866 | { | |
867 | const bool log = false; | |
868 | const bool hiddenSectionsOccupyAddressSpace = ((_options.outputKind() != Options::kObjectFile) | |
869 | && (_options.outputKind() != Options::kPreload)); | |
870 | const bool segmentsArePageAligned = (_options.outputKind() != Options::kObjectFile); | |
871 | ||
872 | uint64_t address = 0; | |
873 | const char* lastSegName = ""; | |
874 | uint64_t floatingAddressStart = _options.baseAddress(); | |
eaf282aa | 875 | bool haveFixedSegments = false; |
9543cb2f | 876 | |
eaf282aa A |
877 | // mark all sections as not having an address yet |
878 | for (std::vector<ld::Internal::FinalSection*>::iterator it = sections.begin(); it != sections.end(); ++it) { | |
879 | ld::Internal::FinalSection* sect = *it; | |
880 | sect->alignmentPaddingBytes = 0; | |
881 | sect->address = ULLONG_MAX; | |
882 | } | |
883 | ||
9543cb2f A |
884 | // first pass, assign addresses to sections in segments with fixed start addresses |
885 | if ( log ) fprintf(stderr, "Fixed address segments:\n"); | |
886 | for (std::vector<ld::Internal::FinalSection*>::iterator it = sections.begin(); it != sections.end(); ++it) { | |
887 | ld::Internal::FinalSection* sect = *it; | |
888 | if ( ! _options.hasCustomSegmentAddress(sect->segmentName()) ) | |
889 | continue; | |
eaf282aa | 890 | haveFixedSegments = true; |
9543cb2f A |
891 | if ( segmentsArePageAligned ) { |
892 | if ( strcmp(lastSegName, sect->segmentName()) != 0 ) { | |
893 | address = _options.customSegmentAddress(sect->segmentName()); | |
894 | lastSegName = sect->segmentName(); | |
895 | } | |
896 | } | |
897 | // adjust section address based on alignment | |
898 | uint64_t unalignedAddress = address; | |
899 | uint64_t alignment = (1 << sect->alignment); | |
900 | address = ( (unalignedAddress+alignment-1) & (-alignment) ); | |
901 | ||
902 | // update section info | |
903 | sect->address = address; | |
904 | sect->alignmentPaddingBytes = (address - unalignedAddress); | |
905 | ||
906 | // sanity check size | |
907 | if ( ((address + sect->size) > _options.maxAddress()) && (_options.outputKind() != Options::kObjectFile) | |
908 | && (_options.outputKind() != Options::kStaticExecutable) ) | |
909 | throwf("section %s (address=0x%08llX, size=%llu) would make the output executable exceed available address range", | |
910 | sect->sectionName(), address, sect->size); | |
911 | ||
912 | if ( log ) fprintf(stderr, " address=0x%08llX, hidden=%d, alignment=%02d, section=%s,%s\n", | |
913 | sect->address, sect->isSectionHidden(), sect->alignment, sect->segmentName(), sect->sectionName()); | |
914 | // update running totals | |
915 | if ( !sect->isSectionHidden() || hiddenSectionsOccupyAddressSpace ) | |
916 | address += sect->size; | |
917 | ||
918 | // if TEXT segment address is fixed, then flow other segments after it | |
919 | if ( strcmp(sect->segmentName(), "__TEXT") == 0 ) { | |
920 | floatingAddressStart = address; | |
921 | } | |
922 | } | |
eaf282aa A |
923 | |
924 | // second pass, assign section addresses to sections in segments that are ordered after a segment with a fixed address | |
925 | if ( haveFixedSegments && !_options.segmentOrder().empty() ) { | |
926 | if ( log ) fprintf(stderr, "After Fixed address segments:\n"); | |
927 | lastSegName = ""; | |
928 | ld::Internal::FinalSection* lastSect = NULL; | |
929 | for (std::vector<ld::Internal::FinalSection*>::iterator it = sections.begin(); it != sections.end(); ++it) { | |
930 | ld::Internal::FinalSection* sect = *it; | |
931 | if ( (sect->address == ULLONG_MAX) && _options.segmentOrderAfterFixedAddressSegment(sect->segmentName()) ) { | |
932 | address = lastSect->address + lastSect->size; | |
933 | if ( (strcmp(lastSegName, sect->segmentName()) != 0) && segmentsArePageAligned ) { | |
934 | // round up size of last segment | |
935 | address = pageAlign(address, _options.segPageSize(lastSegName)); | |
936 | } | |
937 | // adjust section address based on alignment | |
938 | uint64_t unalignedAddress = address; | |
939 | uint64_t alignment = (1 << sect->alignment); | |
940 | address = ( (unalignedAddress+alignment-1) & (-alignment) ); | |
941 | sect->alignmentPaddingBytes = (address - unalignedAddress); | |
942 | sect->address = address; | |
943 | if ( log ) fprintf(stderr, " address=0x%08llX, hidden=%d, alignment=%02d, section=%s,%s\n", | |
944 | sect->address, sect->isSectionHidden(), sect->alignment, sect->segmentName(), sect->sectionName()); | |
945 | // update running totals | |
946 | if ( !sect->isSectionHidden() || hiddenSectionsOccupyAddressSpace ) | |
947 | address += sect->size; | |
948 | } | |
949 | lastSegName = sect->segmentName(); | |
950 | lastSect = sect; | |
951 | } | |
952 | } | |
953 | ||
954 | // last pass, assign addresses to remaining sections | |
9543cb2f A |
955 | address = floatingAddressStart; |
956 | lastSegName = ""; | |
957 | ld::Internal::FinalSection* overlappingFixedSection = NULL; | |
958 | ld::Internal::FinalSection* overlappingFlowSection = NULL; | |
959 | if ( log ) fprintf(stderr, "Regular layout segments:\n"); | |
960 | for (std::vector<ld::Internal::FinalSection*>::iterator it = sections.begin(); it != sections.end(); ++it) { | |
961 | ld::Internal::FinalSection* sect = *it; | |
eaf282aa | 962 | if ( sect->address != ULLONG_MAX ) |
9543cb2f A |
963 | continue; |
964 | if ( (_options.outputKind() == Options::kPreload) && (sect->type() == ld::Section::typeMachHeader) ) { | |
965 | sect->alignmentPaddingBytes = 0; | |
966 | continue; | |
967 | } | |
968 | if ( segmentsArePageAligned ) { | |
969 | if ( strcmp(lastSegName, sect->segmentName()) != 0 ) { | |
970 | // round up size of last segment if needed | |
971 | if ( *lastSegName != '\0' ) { | |
972 | address = pageAlign(address, _options.segPageSize(lastSegName)); | |
973 | } | |
974 | // set segment address based on end of last segment | |
975 | address = pageAlign(address); | |
976 | lastSegName = sect->segmentName(); | |
977 | } | |
978 | } | |
eaf282aa | 979 | |
9543cb2f A |
980 | // adjust section address based on alignment |
981 | uint64_t unalignedAddress = address; | |
982 | uint64_t alignment = (1 << sect->alignment); | |
983 | address = ( (unalignedAddress+alignment-1) & (-alignment) ); | |
984 | ||
985 | // update section info | |
986 | sect->address = address; | |
987 | sect->alignmentPaddingBytes = (address - unalignedAddress); | |
988 | ||
989 | // sanity check size | |
990 | if ( ((address + sect->size) > _options.maxAddress()) && (_options.outputKind() != Options::kObjectFile) | |
991 | && (_options.outputKind() != Options::kStaticExecutable) ) | |
992 | throwf("section %s (address=0x%08llX, size=%llu) would make the output executable exceed available address range", | |
993 | sect->sectionName(), address, sect->size); | |
994 | ||
995 | // sanity check it does not overlap a fixed address segment | |
996 | for (std::vector<ld::Internal::FinalSection*>::iterator sit = sections.begin(); sit != sections.end(); ++sit) { | |
997 | ld::Internal::FinalSection* otherSect = *sit; | |
998 | if ( ! _options.hasCustomSegmentAddress(otherSect->segmentName()) ) | |
999 | continue; | |
599556ff A |
1000 | if ( otherSect->size == 0 ) |
1001 | continue; | |
1002 | if ( sect->size == 0 ) | |
1003 | continue; | |
9543cb2f A |
1004 | if ( sect->address > otherSect->address ) { |
1005 | if ( (otherSect->address+otherSect->size) > sect->address ) { | |
1006 | overlappingFixedSection = otherSect; | |
1007 | overlappingFlowSection = sect; | |
1008 | } | |
1009 | } | |
1010 | else { | |
1011 | if ( (sect->address+sect->size) > otherSect->address ) { | |
1012 | overlappingFixedSection = otherSect; | |
1013 | overlappingFlowSection = sect; | |
1014 | } | |
1015 | } | |
1016 | } | |
1017 | ||
1018 | if ( log ) fprintf(stderr, " address=0x%08llX, size=0x%08llX, hidden=%d, alignment=%02d, padBytes=%d, section=%s,%s\n", | |
1019 | sect->address, sect->size, sect->isSectionHidden(), sect->alignment, sect->alignmentPaddingBytes, | |
1020 | sect->segmentName(), sect->sectionName()); | |
1021 | // update running totals | |
1022 | if ( !sect->isSectionHidden() || hiddenSectionsOccupyAddressSpace ) | |
1023 | address += sect->size; | |
1024 | } | |
1025 | if ( overlappingFixedSection != NULL ) { | |
1026 | fprintf(stderr, "Section layout:\n"); | |
1027 | for (std::vector<ld::Internal::FinalSection*>::iterator it = sections.begin(); it != sections.end(); ++it) { | |
1028 | ld::Internal::FinalSection* sect = *it; | |
599556ff A |
1029 | //if ( sect->isSectionHidden() ) |
1030 | // continue; | |
9543cb2f A |
1031 | fprintf(stderr, " address:0x%08llX, alignment:2^%d, size:0x%08llX, padBytes:%d, section:%s/%s\n", |
1032 | sect->address, sect->alignment, sect->size, sect->alignmentPaddingBytes, | |
1033 | sect->segmentName(), sect->sectionName()); | |
1034 | ||
1035 | } | |
1036 | throwf("Section (%s/%s) overlaps fixed address section (%s/%s)", | |
1037 | overlappingFlowSection->segmentName(), overlappingFlowSection->sectionName(), | |
1038 | overlappingFixedSection->segmentName(), overlappingFixedSection->sectionName()); | |
1039 | } | |
1040 | ||
1041 | ||
1042 | // third pass, assign section file offsets | |
1043 | uint64_t fileOffset = 0; | |
1044 | lastSegName = ""; | |
1045 | if ( log ) fprintf(stderr, "All segments with file offsets:\n"); | |
1046 | for (std::vector<ld::Internal::FinalSection*>::iterator it = sections.begin(); it != sections.end(); ++it) { | |
1047 | ld::Internal::FinalSection* sect = *it; | |
1048 | if ( hasZeroForFileOffset(sect) ) { | |
1049 | // fileoff of zerofill sections is moot, but historically it is set to zero | |
1050 | sect->fileOffset = 0; | |
1051 | ||
1052 | // <rdar://problem/10445047> align file offset with address layout | |
1053 | fileOffset += sect->alignmentPaddingBytes; | |
1054 | } | |
1055 | else { | |
1056 | // page align file offset at start of each segment | |
1057 | if ( segmentsArePageAligned && (*lastSegName != '\0') && (strcmp(lastSegName, sect->segmentName()) != 0) ) { | |
1058 | fileOffset = pageAlign(fileOffset, _options.segPageSize(lastSegName)); | |
1059 | } | |
1060 | lastSegName = sect->segmentName(); | |
1061 | ||
1062 | // align file offset with address layout | |
1063 | fileOffset += sect->alignmentPaddingBytes; | |
1064 | ||
1065 | // update section info | |
1066 | sect->fileOffset = fileOffset; | |
1067 | ||
1068 | // update running total | |
1069 | fileOffset += sect->size; | |
1070 | } | |
1071 | ||
1072 | if ( log ) fprintf(stderr, " fileoffset=0x%08llX, address=0x%08llX, hidden=%d, size=%lld, alignment=%02d, section=%s,%s\n", | |
1073 | sect->fileOffset, sect->address, sect->isSectionHidden(), sect->size, sect->alignment, | |
1074 | sect->segmentName(), sect->sectionName()); | |
1075 | } | |
1076 | ||
1077 | #if 0 | |
1078 | // for encrypted iPhoneOS apps | |
1079 | if ( _options.makeEncryptable() ) { | |
1080 | // remember end of __TEXT for later use by load command | |
1081 | for (std::vector<ld::Internal::FinalSection*>::iterator it = state.sections.begin(); it != state.sections.end(); ++it) { | |
1082 | ld::Internal::FinalSection* sect = *it; | |
1083 | if ( strcmp(sect->segmentName(), "__TEXT") == 0 ) { | |
1084 | _encryptedTEXTendOffset = pageAlign(sect->fileOffset + sect->size); | |
1085 | } | |
1086 | } | |
1087 | } | |
1088 | #endif | |
1089 | ||
1090 | // return total file size | |
1091 | return fileOffset; | |
1092 | } | |
1093 | ||
b2fa67a8 A |
1094 | static char* commatize(uint64_t in, char* out) |
1095 | { | |
1096 | char* result = out; | |
1097 | char rawNum[30]; | |
1098 | sprintf(rawNum, "%llu", in); | |
1099 | const int rawNumLen = strlen(rawNum); | |
1100 | for(int i=0; i < rawNumLen-1; ++i) { | |
1101 | *out++ = rawNum[i]; | |
1102 | if ( ((rawNumLen-i) % 3) == 1 ) | |
1103 | *out++ = ','; | |
1104 | } | |
1105 | *out++ = rawNum[rawNumLen-1]; | |
1106 | *out = '\0'; | |
1107 | return result; | |
1108 | } | |
1109 | ||
1110 | static void printTime(const char* msg, uint64_t partTime, uint64_t totalTime) | |
1111 | { | |
1112 | static uint64_t sUnitsPerSecond = 0; | |
1113 | if ( sUnitsPerSecond == 0 ) { | |
1114 | struct mach_timebase_info timeBaseInfo; | |
f80fe69f A |
1115 | if ( mach_timebase_info(&timeBaseInfo) != KERN_SUCCESS ) |
1116 | return; | |
1117 | sUnitsPerSecond = 1000000000ULL * timeBaseInfo.denom / timeBaseInfo.numer; | |
b2fa67a8 A |
1118 | } |
1119 | if ( partTime < sUnitsPerSecond ) { | |
1120 | uint32_t milliSecondsTimeTen = (partTime*10000)/sUnitsPerSecond; | |
1121 | uint32_t milliSeconds = milliSecondsTimeTen/10; | |
1122 | uint32_t percentTimesTen = (partTime*1000)/totalTime; | |
1123 | uint32_t percent = percentTimesTen/10; | |
1124 | fprintf(stderr, "%24s: % 4d.%d milliseconds (% 4d.%d%%)\n", msg, milliSeconds, milliSecondsTimeTen-milliSeconds*10, percent, percentTimesTen-percent*10); | |
1125 | } | |
1126 | else { | |
1127 | uint32_t secondsTimeTen = (partTime*10)/sUnitsPerSecond; | |
1128 | uint32_t seconds = secondsTimeTen/10; | |
1129 | uint32_t percentTimesTen = (partTime*1000)/totalTime; | |
1130 | uint32_t percent = percentTimesTen/10; | |
1131 | fprintf(stderr, "%24s: % 4d.%d seconds (% 4d.%d%%)\n", msg, seconds, secondsTimeTen-seconds*10, percent, percentTimesTen-percent*10); | |
1132 | } | |
1133 | } | |
1134 | ||
1135 | ||
a645023d | 1136 | static void getVMInfo(vm_statistics_data_t& info) |
d696c285 A |
1137 | { |
1138 | mach_msg_type_number_t count = sizeof(vm_statistics_data_t) / sizeof(natural_t); | |
1139 | kern_return_t error = host_statistics(mach_host_self(), HOST_VM_INFO, | |
1140 | (host_info_t)&info, &count); | |
1141 | if (error != KERN_SUCCESS) { | |
1142 | bzero(&info, sizeof(vm_statistics_data_t)); | |
1143 | } | |
1144 | } | |
1145 | ||
ebf6f434 A |
1146 | |
1147 | ||
1148 | static const char* sOverridePathlibLTO = NULL; | |
1149 | ||
1150 | // | |
1151 | // This is magic glue that overrides the default behaviour | |
1152 | // of lazydylib1.o which is used to lazily load libLTO.dylib. | |
1153 | // | |
1154 | extern "C" const char* dyld_lazy_dylib_path_fix(const char* path); | |
1155 | const char* dyld_lazy_dylib_path_fix(const char* path) | |
1156 | { | |
1157 | if ( sOverridePathlibLTO != NULL ) | |
1158 | return sOverridePathlibLTO; | |
1159 | else | |
1160 | return path; | |
1161 | } | |
1162 | ||
1163 | ||
1164 | ||
a645023d | 1165 | int main(int argc, const char* argv[]) |
d696c285 | 1166 | { |
a645023d A |
1167 | const char* archName = NULL; |
1168 | bool showArch = false; | |
1169 | bool archInferred = false; | |
1170 | try { | |
b2fa67a8 A |
1171 | PerformanceStatistics statistics; |
1172 | statistics.startTool = mach_absolute_time(); | |
1173 | ||
a645023d A |
1174 | // create object to track command line arguments |
1175 | Options options(argc, argv); | |
b2fa67a8 | 1176 | InternalState state(options); |
a645023d | 1177 | |
ebf6f434 A |
1178 | // allow libLTO to be overridden by command line -lto_library |
1179 | sOverridePathlibLTO = options.overridePathlibLTO(); | |
1180 | ||
b2fa67a8 | 1181 | // gather vm stats |
a645023d | 1182 | if ( options.printStatistics() ) |
b2fa67a8 | 1183 | getVMInfo(statistics.vmStart); |
a645023d A |
1184 | |
1185 | // update strings for error messages | |
1186 | showArch = options.printArchPrefix(); | |
1187 | archName = options.architectureName(); | |
1188 | archInferred = (options.architecture() == 0); | |
1189 | ||
1190 | // open and parse input files | |
b2fa67a8 | 1191 | statistics.startInputFileProcessing = mach_absolute_time(); |
a645023d A |
1192 | ld::tool::InputFiles inputFiles(options, &archName); |
1193 | ||
1194 | // load and resolve all references | |
b2fa67a8 | 1195 | statistics.startResolver = mach_absolute_time(); |
a645023d A |
1196 | ld::tool::Resolver resolver(options, inputFiles, state); |
1197 | resolver.resolve(); | |
ebf6f434 | 1198 | |
a645023d | 1199 | // add dylibs used |
b2fa67a8 | 1200 | statistics.startDylibs = mach_absolute_time(); |
a645023d A |
1201 | inputFiles.dylibs(state); |
1202 | ||
1203 | // do initial section sorting so passes have rough idea of the layout | |
1204 | state.sortSections(); | |
1205 | ||
1206 | // run passes | |
b2fa67a8 | 1207 | statistics.startPasses = mach_absolute_time(); |
a645023d A |
1208 | ld::passes::objc::doPass(options, state); |
1209 | ld::passes::stubs::doPass(options, state); | |
1210 | ld::passes::huge::doPass(options, state); | |
1211 | ld::passes::got::doPass(options, state); | |
1212 | ld::passes::tlvp::doPass(options, state); | |
1213 | ld::passes::dylibs::doPass(options, state); // must be after stubs and GOT passes | |
b2fa67a8 A |
1214 | ld::passes::order::doPass(options, state); |
1215 | state.markAtomsOrdered(); | |
a645023d | 1216 | ld::passes::branch_shim::doPass(options, state); // must be after stubs |
b2fa67a8 | 1217 | ld::passes::branch_island::doPass(options, state); // must be after stubs and order pass |
a645023d | 1218 | ld::passes::dtrace::doPass(options, state); |
b2fa67a8 | 1219 | ld::passes::compact_unwind::doPass(options, state); // must be after order pass |
eaf282aa A |
1220 | ld::passes::bitcode_bundle::doPass(options, state); // must be after dylib |
1221 | ||
a645023d A |
1222 | // sort final sections |
1223 | state.sortSections(); | |
1224 | ||
1225 | // write output file | |
b2fa67a8 | 1226 | statistics.startOutput = mach_absolute_time(); |
a645023d A |
1227 | ld::tool::OutputFile out(options); |
1228 | out.write(state); | |
b2fa67a8 | 1229 | statistics.startDone = mach_absolute_time(); |
a645023d A |
1230 | |
1231 | // print statistics | |
1232 | //mach_o::relocatable::printCounts(); | |
1233 | if ( options.printStatistics() ) { | |
b2fa67a8 A |
1234 | getVMInfo(statistics.vmEnd); |
1235 | uint64_t totalTime = statistics.startDone - statistics.startTool; | |
1236 | printTime("ld total time", totalTime, totalTime); | |
1237 | printTime(" option parsing time", statistics.startInputFileProcessing - statistics.startTool, totalTime); | |
1238 | printTime(" object file processing", statistics.startResolver - statistics.startInputFileProcessing,totalTime); | |
1239 | printTime(" resolve symbols", statistics.startDylibs - statistics.startResolver, totalTime); | |
1240 | printTime(" build atom list", statistics.startPasses - statistics.startDylibs, totalTime); | |
1241 | printTime(" passess", statistics.startOutput - statistics.startPasses, totalTime); | |
1242 | printTime(" write output", statistics.startDone - statistics.startOutput, totalTime); | |
1243 | fprintf(stderr, "pageins=%u, pageouts=%u, faults=%u\n", | |
1244 | statistics.vmEnd.pageins-statistics.vmStart.pageins, | |
1245 | statistics.vmEnd.pageouts-statistics.vmStart.pageouts, | |
1246 | statistics.vmEnd.faults-statistics.vmStart.faults); | |
1247 | char temp[40]; | |
1248 | fprintf(stderr, "processed %3u object files, totaling %15s bytes\n", inputFiles._totalObjectLoaded, commatize(inputFiles._totalObjectSize, temp)); | |
1249 | fprintf(stderr, "processed %3u archive files, totaling %15s bytes\n", inputFiles._totalArchivesLoaded, commatize(inputFiles._totalArchiveSize, temp)); | |
1250 | fprintf(stderr, "processed %3u dylib files\n", inputFiles._totalDylibsLoaded); | |
1251 | fprintf(stderr, "wrote output file totaling %15s bytes\n", commatize(out.fileSize(), temp)); | |
1252 | } | |
1253 | // <rdar://problem/6780050> Would like linker warning to be build error. | |
1254 | if ( options.errorBecauseOfWarnings() ) { | |
1255 | fprintf(stderr, "ld: fatal warning(s) induced error (-fatal_warnings)\n"); | |
1256 | return 1; | |
a645023d A |
1257 | } |
1258 | } | |
1259 | catch (const char* msg) { | |
1260 | if ( archInferred ) | |
1261 | fprintf(stderr, "ld: %s for inferred architecture %s\n", msg, archName); | |
1262 | else if ( showArch ) | |
1263 | fprintf(stderr, "ld: %s for architecture %s\n", msg, archName); | |
1264 | else | |
1265 | fprintf(stderr, "ld: %s\n", msg); | |
1266 | return 1; | |
d696c285 | 1267 | } |
a645023d A |
1268 | |
1269 | return 0; | |
c2646906 A |
1270 | } |
1271 | ||
a645023d A |
1272 | |
1273 | #ifndef NDEBUG | |
1274 | // implement assert() function to print out a backtrace before aborting | |
1275 | void __assert_rtn(const char* func, const char* file, int line, const char* failedexpr) | |
c2646906 | 1276 | { |
ebf6f434 A |
1277 | Snapshot *snapshot = Snapshot::globalSnapshot; |
1278 | ||
1279 | snapshot->setSnapshotMode(Snapshot::SNAPSHOT_DEBUG); | |
1280 | snapshot->createSnapshot(); | |
1281 | snapshot->recordAssertionMessage("Assertion failed: (%s), function %s, file %s, line %d.\n", failedexpr, func, file, line); | |
d696c285 | 1282 | |
a645023d A |
1283 | void* callStack[128]; |
1284 | int depth = ::backtrace(callStack, 128); | |
1285 | char* buffer = (char*)malloc(1024); | |
1286 | for(int i=0; i < depth-1; ++i) { | |
1287 | Dl_info info; | |
1288 | dladdr(callStack[i], &info); | |
1289 | const char* symboName = info.dli_sname; | |
1290 | if ( (symboName != NULL) && (strncmp(symboName, "_Z", 2) == 0) ) { | |
1291 | size_t bufLen = 1024; | |
1292 | int result; | |
1293 | char* unmangled = abi::__cxa_demangle(symboName, buffer, &bufLen, &result); | |
1294 | if ( unmangled != NULL ) | |
1295 | symboName = unmangled; | |
c2646906 | 1296 | } |
a645023d A |
1297 | long offset = (uintptr_t)callStack[i] - (uintptr_t)info.dli_saddr; |
1298 | fprintf(stderr, "%d %p %s + %ld\n", i, callStack[i], symboName, offset); | |
ebf6f434 | 1299 | snapshot->recordAssertionMessage("%d %p %s + %ld\n", i, callStack[i], symboName, offset); |
69a49097 | 1300 | } |
ebf6f434 A |
1301 | fprintf(stderr, "A linker snapshot was created at:\n\t%s\n", snapshot->rootDir()); |
1302 | fprintf(stderr, "ld: Assertion failed: (%s), function %s, file %s, line %d.\n", failedexpr, func, file, line); | |
a645023d A |
1303 | exit(1); |
1304 | } | |
1305 | #endif | |
d696c285 | 1306 | |
d696c285 | 1307 |