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