]>
Commit | Line | Data |
---|---|---|
1 | /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- | |
2 | * | |
3 | * Copyright (c) 2005-2011 Apple Inc. All rights reserved. | |
4 | * | |
5 | * @APPLE_LICENSE_HEADER_START@ | |
6 | * | |
7 | * This file contains Original Code and/or Modifications of Original Code | |
8 | * as defined in and that are subject to the Apple Public Source License | |
9 | * Version 2.0 (the 'License'). You may not use this file except in | |
10 | * compliance with the License. Please obtain a copy of the License at | |
11 | * http://www.opensource.apple.com/apsl/ and read it before using this | |
12 | * file. | |
13 | * | |
14 | * The Original Code and all software distributed under the License are | |
15 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER | |
16 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, | |
17 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
18 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. | |
19 | * Please see the License for the specific language governing rights and | |
20 | * limitations under the License. | |
21 | * | |
22 | * @APPLE_LICENSE_HEADER_END@ | |
23 | */ | |
24 | ||
25 | #include <stdint.h> | |
26 | #include <math.h> | |
27 | #include <unistd.h> | |
28 | #include <sys/param.h> | |
29 | #include <mach-o/ranlib.h> | |
30 | #include <ar.h> | |
31 | ||
32 | #include <vector> | |
33 | #include <set> | |
34 | #include <map> | |
35 | #include <algorithm> | |
36 | #include <unordered_map> | |
37 | ||
38 | #include "MachOFileAbstraction.hpp" | |
39 | #include "Architectures.hpp" | |
40 | ||
41 | #include "macho_relocatable_file.h" | |
42 | #include "lto_file.h" | |
43 | #include "archive_file.h" | |
44 | ||
45 | ||
46 | namespace archive { | |
47 | ||
48 | ||
49 | // forward reference | |
50 | template <typename A> class File; | |
51 | ||
52 | ||
53 | template <typename A> | |
54 | class Parser | |
55 | { | |
56 | public: | |
57 | typedef typename A::P P; | |
58 | ||
59 | static bool validFile(const uint8_t* fileContent, uint64_t fileLength, | |
60 | const mach_o::relocatable::ParserOptions& opts) { | |
61 | return File<A>::validFile(fileContent, fileLength, opts); } | |
62 | static File<A>* parse(const uint8_t* fileContent, uint64_t fileLength, | |
63 | const char* path, time_t mTime, | |
64 | ld::File::Ordinal ordinal, const ParserOptions& opts) { | |
65 | return new File<A>(fileContent, fileLength, path, mTime, | |
66 | ordinal, opts); | |
67 | } | |
68 | ||
69 | }; | |
70 | ||
71 | template <typename A> | |
72 | class File : public ld::archive::File | |
73 | { | |
74 | public: | |
75 | static bool validFile(const uint8_t* fileContent, uint64_t fileLength, | |
76 | const mach_o::relocatable::ParserOptions& opts); | |
77 | File(const uint8_t* fileContent, uint64_t fileLength, | |
78 | const char* pth, time_t modTime, | |
79 | ld::File::Ordinal ord, const ParserOptions& opts); | |
80 | virtual ~File() {} | |
81 | ||
82 | // overrides of ld::File | |
83 | virtual bool forEachAtom(ld::File::AtomHandler&) const; | |
84 | virtual bool justInTimeforEachAtom(const char* name, ld::File::AtomHandler&) const; | |
85 | virtual uint32_t subFileCount() const { return _archiveFilelength/sizeof(ar_hdr); } | |
86 | ||
87 | // overrides of ld::archive::File | |
88 | virtual bool justInTimeDataOnlyforEachAtom(const char* name, ld::File::AtomHandler& handler) const; | |
89 | ||
90 | private: | |
91 | static bool validMachOFile(const uint8_t* fileContent, uint64_t fileLength, | |
92 | const mach_o::relocatable::ParserOptions& opts); | |
93 | static bool validLTOFile(const uint8_t* fileContent, uint64_t fileLength, | |
94 | const mach_o::relocatable::ParserOptions& opts); | |
95 | static cpu_type_t architecture(); | |
96 | ||
97 | class Entry : ar_hdr | |
98 | { | |
99 | public: | |
100 | void getName(char *, int) const; | |
101 | time_t modificationTime() const; | |
102 | const uint8_t* content() const; | |
103 | uint32_t contentSize() const; | |
104 | const Entry* next() const; | |
105 | private: | |
106 | bool hasLongName() const; | |
107 | unsigned int getLongNameSpace() const; | |
108 | ||
109 | }; | |
110 | ||
111 | struct MemberState { ld::relocatable::File* file; const Entry *entry; bool logged; bool loaded; uint32_t index;}; | |
112 | bool loadMember(MemberState& state, ld::File::AtomHandler& handler, const char *format, ...) const; | |
113 | ||
114 | typedef std::unordered_map<const char*, uint64_t, ld::CStringHash, ld::CStringEquals> NameToOffsetMap; | |
115 | ||
116 | typedef typename A::P P; | |
117 | typedef typename A::P::E E; | |
118 | ||
119 | typedef std::map<const class Entry*, MemberState> MemberToStateMap; | |
120 | ||
121 | MemberState& makeObjectFileForMember(const Entry* member) const; | |
122 | bool memberHasObjCCategories(const Entry* member) const; | |
123 | void dumpTableOfContents(); | |
124 | void buildHashTable(); | |
125 | #ifdef SYMDEF_64 | |
126 | void buildHashTable64(); | |
127 | #endif | |
128 | const uint8_t* _archiveFileContent; | |
129 | uint64_t _archiveFilelength; | |
130 | const struct ranlib* _tableOfContents; | |
131 | #ifdef SYMDEF_64 | |
132 | const struct ranlib_64* _tableOfContents64; | |
133 | #endif | |
134 | uint32_t _tableOfContentCount; | |
135 | const char* _tableOfContentStrings; | |
136 | mutable MemberToStateMap _instantiatedEntries; | |
137 | NameToOffsetMap _hashTable; | |
138 | const bool _forceLoadAll; | |
139 | const bool _forceLoadObjC; | |
140 | const bool _forceLoadThis; | |
141 | const bool _objc2ABI; | |
142 | const bool _verboseLoad; | |
143 | const bool _logAllFiles; | |
144 | const mach_o::relocatable::ParserOptions _objOpts; | |
145 | }; | |
146 | ||
147 | ||
148 | template <typename A> | |
149 | bool File<A>::Entry::hasLongName() const | |
150 | { | |
151 | return ( strncmp(this->ar_name, AR_EFMT1, strlen(AR_EFMT1)) == 0 ); | |
152 | } | |
153 | ||
154 | template <typename A> | |
155 | unsigned int File<A>::Entry::getLongNameSpace() const | |
156 | { | |
157 | char* endptr; | |
158 | long result = strtol(&this->ar_name[strlen(AR_EFMT1)], &endptr, 10); | |
159 | return result; | |
160 | } | |
161 | ||
162 | template <typename A> | |
163 | void File<A>::Entry::getName(char *buf, int bufsz) const | |
164 | { | |
165 | if ( this->hasLongName() ) { | |
166 | int len = this->getLongNameSpace(); | |
167 | assert(bufsz >= len+1); | |
168 | strncpy(buf, ((char*)this)+sizeof(ar_hdr), len); | |
169 | buf[len] = '\0'; | |
170 | } | |
171 | else { | |
172 | assert(bufsz >= 16+1); | |
173 | strncpy(buf, this->ar_name, 16); | |
174 | buf[16] = '\0'; | |
175 | char* space = strchr(buf, ' '); | |
176 | if ( space != NULL ) | |
177 | *space = '\0'; | |
178 | } | |
179 | } | |
180 | ||
181 | template <typename A> | |
182 | time_t File<A>::Entry::modificationTime() const | |
183 | { | |
184 | char temp[14]; | |
185 | strncpy(temp, this->ar_date, 12); | |
186 | temp[12] = '\0'; | |
187 | char* endptr; | |
188 | return (time_t)strtol(temp, &endptr, 10); | |
189 | } | |
190 | ||
191 | ||
192 | template <typename A> | |
193 | const uint8_t* File<A>::Entry::content() const | |
194 | { | |
195 | if ( this->hasLongName() ) | |
196 | return ((uint8_t*)this) + sizeof(ar_hdr) + this->getLongNameSpace(); | |
197 | else | |
198 | return ((uint8_t*)this) + sizeof(ar_hdr); | |
199 | } | |
200 | ||
201 | ||
202 | template <typename A> | |
203 | uint32_t File<A>::Entry::contentSize() const | |
204 | { | |
205 | char temp[12]; | |
206 | strncpy(temp, this->ar_size, 10); | |
207 | temp[10] = '\0'; | |
208 | char* endptr; | |
209 | long size = strtol(temp, &endptr, 10); | |
210 | // long name is included in ar_size | |
211 | if ( this->hasLongName() ) | |
212 | size -= this->getLongNameSpace(); | |
213 | return size; | |
214 | } | |
215 | ||
216 | ||
217 | template <typename A> | |
218 | const class File<A>::Entry* File<A>::Entry::next() const | |
219 | { | |
220 | const uint8_t* p = this->content() + contentSize(); | |
221 | p = (const uint8_t*)(((uintptr_t)p+3) & (-4)); // 4-byte align | |
222 | return (class File<A>::Entry*)p; | |
223 | } | |
224 | ||
225 | ||
226 | template <> cpu_type_t File<x86>::architecture() { return CPU_TYPE_I386; } | |
227 | template <> cpu_type_t File<x86_64>::architecture() { return CPU_TYPE_X86_64; } | |
228 | template <> cpu_type_t File<arm>::architecture() { return CPU_TYPE_ARM; } | |
229 | template <> cpu_type_t File<arm64>::architecture() { return CPU_TYPE_ARM64; } | |
230 | ||
231 | template <typename A> | |
232 | bool File<A>::validMachOFile(const uint8_t* fileContent, uint64_t fileLength, const mach_o::relocatable::ParserOptions& opts) | |
233 | { | |
234 | return mach_o::relocatable::isObjectFile(fileContent, fileLength, opts); | |
235 | } | |
236 | ||
237 | template <typename A> | |
238 | bool File<A>::validLTOFile(const uint8_t* fileContent, uint64_t fileLength, const mach_o::relocatable::ParserOptions& opts) | |
239 | { | |
240 | return lto::isObjectFile(fileContent, fileLength, opts.architecture, opts.subType); | |
241 | } | |
242 | ||
243 | ||
244 | ||
245 | template <typename A> | |
246 | bool File<A>::validFile(const uint8_t* fileContent, uint64_t fileLength, const mach_o::relocatable::ParserOptions& opts) | |
247 | { | |
248 | // must have valid archive header | |
249 | if ( strncmp((const char*)fileContent, "!<arch>\n", 8) != 0 ) | |
250 | return false; | |
251 | ||
252 | // peak at first .o file and verify it is correct architecture | |
253 | const Entry* const start = (Entry*)&fileContent[8]; | |
254 | const Entry* const end = (Entry*)&fileContent[fileLength]; | |
255 | for (const Entry* p=start; p < end; p = p->next()) { | |
256 | char memberName[256]; | |
257 | p->getName(memberName, sizeof(memberName)); | |
258 | // skip option table-of-content member | |
259 | if ( (p==start) && ((strcmp(memberName, SYMDEF_SORTED) == 0) || (strcmp(memberName, SYMDEF) == 0)) ) | |
260 | continue; | |
261 | #ifdef SYMDEF_64 | |
262 | if ( (p==start) && ((strcmp(memberName, SYMDEF_64_SORTED) == 0) || (strcmp(memberName, SYMDEF_64) == 0)) ) | |
263 | continue; | |
264 | #endif | |
265 | // archive is valid if first .o file is valid | |
266 | return (validMachOFile(p->content(), p->contentSize(), opts) || validLTOFile(p->content(), p->contentSize(), opts)); | |
267 | } | |
268 | // empty archive | |
269 | return true; | |
270 | } | |
271 | ||
272 | ||
273 | template <typename A> | |
274 | File<A>::File(const uint8_t fileContent[], uint64_t fileLength, const char* pth, time_t modTime, | |
275 | ld::File::Ordinal ord, const ParserOptions& opts) | |
276 | : ld::archive::File(strdup(pth), modTime, ord), | |
277 | _archiveFileContent(fileContent), _archiveFilelength(fileLength), | |
278 | _tableOfContents(NULL), | |
279 | #ifdef SYMDEF_64 | |
280 | _tableOfContents64(NULL), | |
281 | #endif | |
282 | _tableOfContentCount(0), _tableOfContentStrings(NULL), | |
283 | _forceLoadAll(opts.forceLoadAll), _forceLoadObjC(opts.forceLoadObjC), | |
284 | _forceLoadThis(opts.forceLoadThisArchive), _objc2ABI(opts.objcABI2), _verboseLoad(opts.verboseLoad), | |
285 | _logAllFiles(opts.logAllFiles), _objOpts(opts.objOpts) | |
286 | { | |
287 | if ( strncmp((const char*)fileContent, "!<arch>\n", 8) != 0 ) | |
288 | throw "not an archive"; | |
289 | ||
290 | if ( !_forceLoadAll ) { | |
291 | const Entry* const firstMember = (Entry*)&_archiveFileContent[8]; | |
292 | char memberName[256]; | |
293 | firstMember->getName(memberName, sizeof(memberName)); | |
294 | if ( (strcmp(memberName, SYMDEF_SORTED) == 0) || (strcmp(memberName, SYMDEF) == 0) ) { | |
295 | const uint8_t* contents = firstMember->content(); | |
296 | uint32_t ranlibArrayLen = E::get32(*((uint32_t*)contents)); | |
297 | _tableOfContents = (const struct ranlib*)&contents[4]; | |
298 | _tableOfContentCount = ranlibArrayLen / sizeof(struct ranlib); | |
299 | _tableOfContentStrings = (const char*)&contents[ranlibArrayLen+8]; | |
300 | if ( ((uint8_t*)(&_tableOfContents[_tableOfContentCount]) > &fileContent[fileLength]) | |
301 | || ((uint8_t*)_tableOfContentStrings > &fileContent[fileLength]) ) | |
302 | throw "malformed archive, perhaps wrong architecture"; | |
303 | this->buildHashTable(); | |
304 | } | |
305 | #ifdef SYMDEF_64 | |
306 | else if ( (strcmp(memberName, SYMDEF_64_SORTED) == 0) || (strcmp(memberName, SYMDEF_64) == 0) ) { | |
307 | const uint8_t* contents = firstMember->content(); | |
308 | uint64_t ranlibArrayLen = E::get64(*((uint64_t*)contents)); | |
309 | _tableOfContents64 = (const struct ranlib_64*)&contents[8]; | |
310 | _tableOfContentCount = ranlibArrayLen / sizeof(struct ranlib_64); | |
311 | _tableOfContentStrings = (const char*)&contents[ranlibArrayLen+16]; | |
312 | if ( ((uint8_t*)(&_tableOfContents[_tableOfContentCount]) > &fileContent[fileLength]) | |
313 | || ((uint8_t*)_tableOfContentStrings > &fileContent[fileLength]) ) | |
314 | throw "malformed archive, perhaps wrong architecture"; | |
315 | this->buildHashTable64(); | |
316 | } | |
317 | #endif | |
318 | else | |
319 | throw "archive has no table of contents"; | |
320 | } | |
321 | } | |
322 | ||
323 | template <> | |
324 | bool File<x86>::memberHasObjCCategories(const Entry* member) const | |
325 | { | |
326 | if ( _objc2ABI ) { | |
327 | // i386 for iOS simulator uses ObjC2 which has no global symbol for categories | |
328 | return mach_o::relocatable::hasObjC2Categories(member->content()); | |
329 | } | |
330 | else { | |
331 | // i386 uses ObjC1 ABI which has .objc_category* global symbols | |
332 | // <rdar://problem/11342022> strip -S on i386 pulls out .objc_category_name symbols from static frameworks | |
333 | return mach_o::relocatable::hasObjC1Categories(member->content()); | |
334 | } | |
335 | } | |
336 | ||
337 | ||
338 | ||
339 | template <typename A> | |
340 | bool File<A>::memberHasObjCCategories(const Entry* member) const | |
341 | { | |
342 | // x86_64 and ARM use ObjC2 which has no global symbol for categories | |
343 | return mach_o::relocatable::hasObjC2Categories(member->content()); | |
344 | } | |
345 | ||
346 | ||
347 | template <typename A> | |
348 | typename File<A>::MemberState& File<A>::makeObjectFileForMember(const Entry* member) const | |
349 | { | |
350 | uint32_t memberIndex = 0; | |
351 | // in case member was instantiated earlier but not needed yet | |
352 | typename MemberToStateMap::iterator pos = _instantiatedEntries.find(member); | |
353 | if ( pos == _instantiatedEntries.end() ) { | |
354 | // Have to find the index of this member | |
355 | const Entry* start; | |
356 | uint32_t index; | |
357 | if (_instantiatedEntries.size() == 0) { | |
358 | start = (Entry*)&_archiveFileContent[8]; | |
359 | index = 1; | |
360 | } else { | |
361 | MemberState &lastKnown = _instantiatedEntries.rbegin()->second; | |
362 | start = lastKnown.entry->next(); | |
363 | index = lastKnown.index+1; | |
364 | } | |
365 | for (const Entry* p=start; p <= member; p = p->next(), index++) { | |
366 | MemberState state = {NULL, p, false, false, index}; | |
367 | _instantiatedEntries[p] = state; | |
368 | if (member == p) { | |
369 | memberIndex = index; | |
370 | } | |
371 | } | |
372 | } else { | |
373 | MemberState& state = pos->second; | |
374 | if (state.file) | |
375 | return state; | |
376 | memberIndex = state.index; | |
377 | } | |
378 | assert(memberIndex != 0); | |
379 | char memberName[256]; | |
380 | member->getName(memberName, sizeof(memberName)); | |
381 | char memberPath[strlen(this->path()) + strlen(memberName)+4]; | |
382 | strcpy(memberPath, this->path()); | |
383 | strcat(memberPath, "("); | |
384 | strcat(memberPath, memberName); | |
385 | strcat(memberPath, ")"); | |
386 | //fprintf(stderr, "using %s from %s\n", memberName, this->path()); | |
387 | try { | |
388 | // range check | |
389 | if ( member > (Entry*)(_archiveFileContent+_archiveFilelength) ) | |
390 | throwf("corrupt archive, member starts past end of file"); | |
391 | if ( (member->content() + member->contentSize()) > (_archiveFileContent+_archiveFilelength) ) | |
392 | throwf("corrupt archive, member contents extends past end of file"); | |
393 | const char* mPath = strdup(memberPath); | |
394 | // see if member is mach-o file | |
395 | ld::File::Ordinal ordinal = this->ordinal().archiveOrdinalWithMemberIndex(memberIndex); | |
396 | ld::relocatable::File* result = mach_o::relocatable::parse(member->content(), member->contentSize(), | |
397 | mPath, member->modificationTime(), | |
398 | ordinal, _objOpts); | |
399 | if ( result != NULL ) { | |
400 | MemberState state = {result, member, false, false, memberIndex}; | |
401 | _instantiatedEntries[member] = state; | |
402 | return _instantiatedEntries[member]; | |
403 | } | |
404 | // see if member is llvm bitcode file | |
405 | result = lto::parse(member->content(), member->contentSize(), | |
406 | mPath, member->modificationTime(), ordinal, | |
407 | _objOpts.architecture, _objOpts.subType, _logAllFiles, _objOpts.verboseOptimizationHints); | |
408 | if ( result != NULL ) { | |
409 | MemberState state = {result, member, false, false, memberIndex}; | |
410 | _instantiatedEntries[member] = state; | |
411 | return _instantiatedEntries[member]; | |
412 | } | |
413 | ||
414 | throwf("archive member '%s' with length %d is not mach-o or llvm bitcode", memberName, member->contentSize()); | |
415 | } | |
416 | catch (const char* msg) { | |
417 | throwf("in %s, %s", memberPath, msg); | |
418 | } | |
419 | } | |
420 | ||
421 | ||
422 | template <typename A> | |
423 | bool File<A>::loadMember(MemberState& state, ld::File::AtomHandler& handler, const char *format, ...) const | |
424 | { | |
425 | bool didSomething = false; | |
426 | if (!state.loaded) { | |
427 | if ( _verboseLoad && !state.logged ) { | |
428 | va_list list; | |
429 | va_start(list, format); | |
430 | vprintf(format, list); | |
431 | va_end(list); | |
432 | state.logged = true; | |
433 | } | |
434 | state.loaded = true; | |
435 | didSomething = state.file->forEachAtom(handler); | |
436 | } | |
437 | return didSomething; | |
438 | } | |
439 | ||
440 | ||
441 | template <typename A> | |
442 | bool File<A>::forEachAtom(ld::File::AtomHandler& handler) const | |
443 | { | |
444 | bool didSome = false; | |
445 | if ( _forceLoadAll || _forceLoadThis ) { | |
446 | // call handler on all .o files in this archive | |
447 | const Entry* const start = (Entry*)&_archiveFileContent[8]; | |
448 | const Entry* const end = (Entry*)&_archiveFileContent[_archiveFilelength]; | |
449 | for (const Entry* p=start; p < end; p = p->next()) { | |
450 | char memberName[256]; | |
451 | p->getName(memberName, sizeof(memberName)); | |
452 | if ( (p==start) && ((strcmp(memberName, SYMDEF_SORTED) == 0) || (strcmp(memberName, SYMDEF) == 0)) ) | |
453 | continue; | |
454 | #ifdef SYMDEF_64 | |
455 | if ( (p==start) && ((strcmp(memberName, SYMDEF_64_SORTED) == 0) || (strcmp(memberName, SYMDEF_64) == 0)) ) | |
456 | continue; | |
457 | #endif | |
458 | MemberState& state = this->makeObjectFileForMember(p); | |
459 | didSome |= loadMember(state, handler, "%s forced load of %s(%s)\n", _forceLoadThis ? "-force_load" : "-all_load", this->path(), memberName); | |
460 | } | |
461 | } | |
462 | else if ( _forceLoadObjC ) { | |
463 | // call handler on all .o files in this archive containing objc classes | |
464 | for (const auto& entry : _hashTable) { | |
465 | if ( (strncmp(entry.first, ".objc_c", 7) == 0) || (strncmp(entry.first, "_OBJC_CLASS_$_", 14) == 0) ) { | |
466 | const Entry* member = (Entry*)&_archiveFileContent[entry.second]; | |
467 | MemberState& state = this->makeObjectFileForMember(member); | |
468 | char memberName[256]; | |
469 | member->getName(memberName, sizeof(memberName)); | |
470 | didSome |= loadMember(state, handler, "-ObjC forced load of %s(%s)\n", this->path(), memberName); | |
471 | } | |
472 | } | |
473 | // ObjC2 has no symbols in .o files with categories but not classes, look deeper for those | |
474 | const Entry* const start = (Entry*)&_archiveFileContent[8]; | |
475 | const Entry* const end = (Entry*)&_archiveFileContent[_archiveFilelength]; | |
476 | for (const Entry* member=start; member < end; member = member->next()) { | |
477 | char mname[256]; | |
478 | member->getName(mname, sizeof(mname)); | |
479 | // skip table-of-content member | |
480 | if ( (member==start) && ((strcmp(mname, SYMDEF_SORTED) == 0) || (strcmp(mname, SYMDEF) == 0)) ) | |
481 | continue; | |
482 | #ifdef SYMDEF_64 | |
483 | if ( (member==start) && ((strcmp(mname, SYMDEF_64_SORTED) == 0) || (strcmp(mname, SYMDEF_64) == 0)) ) | |
484 | continue; | |
485 | #endif | |
486 | if ( validMachOFile(member->content(), member->contentSize(), _objOpts) ) { | |
487 | MemberState& state = this->makeObjectFileForMember(member); | |
488 | // only look at files not already loaded | |
489 | if ( ! state.loaded ) { | |
490 | if ( this->memberHasObjCCategories(member) ) { | |
491 | MemberState& state = this->makeObjectFileForMember(member); | |
492 | char memberName[256]; | |
493 | member->getName(memberName, sizeof(memberName)); | |
494 | didSome |= loadMember(state, handler, "-ObjC forced load of %s(%s)\n", this->path(), memberName); | |
495 | } | |
496 | } | |
497 | } | |
498 | else if ( validLTOFile(member->content(), member->contentSize(), _objOpts) ) { | |
499 | if ( lto::hasObjCCategory(member->content(), member->contentSize()) ) { | |
500 | MemberState& state = this->makeObjectFileForMember(member); | |
501 | // only look at files not already loaded | |
502 | if ( ! state.loaded ) { | |
503 | char memberName[256]; | |
504 | member->getName(memberName, sizeof(memberName)); | |
505 | didSome |= loadMember(state, handler, "-ObjC forced load of %s(%s)\n", this->path(), memberName); | |
506 | } | |
507 | } | |
508 | } | |
509 | } | |
510 | } | |
511 | return didSome; | |
512 | } | |
513 | ||
514 | template <typename A> | |
515 | bool File<A>::justInTimeforEachAtom(const char* name, ld::File::AtomHandler& handler) const | |
516 | { | |
517 | // in force load case, all members already loaded | |
518 | if ( _forceLoadAll || _forceLoadThis ) | |
519 | return false; | |
520 | ||
521 | // do a hash search of table of contents looking for requested symbol | |
522 | const auto& pos = _hashTable.find(name); | |
523 | if ( pos == _hashTable.end() ) | |
524 | return false; | |
525 | ||
526 | // do a hash search of table of contents looking for requested symbol | |
527 | const Entry* member = (Entry*)&_archiveFileContent[pos->second]; | |
528 | MemberState& state = this->makeObjectFileForMember(member); | |
529 | char memberName[256]; | |
530 | member->getName(memberName, sizeof(memberName)); | |
531 | return loadMember(state, handler, "%s forced load of %s(%s)\n", name, this->path(), memberName); | |
532 | } | |
533 | ||
534 | class CheckIsDataSymbolHandler : public ld::File::AtomHandler | |
535 | { | |
536 | public: | |
537 | CheckIsDataSymbolHandler(const char* n) : _name(n), _isData(false) {} | |
538 | virtual void doAtom(const class ld::Atom& atom) { | |
539 | if ( strcmp(atom.name(), _name) == 0 ) { | |
540 | if ( atom.section().type() != ld::Section::typeCode ) | |
541 | _isData = true; | |
542 | } | |
543 | } | |
544 | virtual void doFile(const class ld::File&) {} | |
545 | bool symbolIsDataDefinition() { return _isData; } | |
546 | ||
547 | private: | |
548 | const char* _name; | |
549 | bool _isData; | |
550 | ||
551 | }; | |
552 | ||
553 | template <typename A> | |
554 | bool File<A>::justInTimeDataOnlyforEachAtom(const char* name, ld::File::AtomHandler& handler) const | |
555 | { | |
556 | // in force load case, all members already loaded | |
557 | if ( _forceLoadAll || _forceLoadThis ) | |
558 | return false; | |
559 | ||
560 | // do a hash search of table of contents looking for requested symbol | |
561 | const auto& pos = _hashTable.find(name); | |
562 | if ( pos == _hashTable.end() ) | |
563 | return false; | |
564 | ||
565 | const Entry* member = (Entry*)&_archiveFileContent[pos->second]; | |
566 | MemberState& state = this->makeObjectFileForMember(member); | |
567 | // only call handler for each member once | |
568 | if ( ! state.loaded ) { | |
569 | CheckIsDataSymbolHandler checker(name); | |
570 | state.file->forEachAtom(checker); | |
571 | if ( checker.symbolIsDataDefinition() ) { | |
572 | char memberName[256]; | |
573 | member->getName(memberName, sizeof(memberName)); | |
574 | return loadMember(state, handler, "%s forced load of %s(%s)\n", name, this->path(), memberName); | |
575 | } | |
576 | } | |
577 | ||
578 | //fprintf(stderr, "%s NOT found in archive %s\n", name, this->path()); | |
579 | return false; | |
580 | } | |
581 | ||
582 | template <typename A> | |
583 | void File<A>::buildHashTable() | |
584 | { | |
585 | // walk through list backwards, adding/overwriting entries | |
586 | // this assures that with duplicates those earliest in the list will be found | |
587 | for (int i = _tableOfContentCount-1; i >= 0; --i) { | |
588 | const struct ranlib* entry = &_tableOfContents[i]; | |
589 | const char* entryName = &_tableOfContentStrings[E::get32(entry->ran_un.ran_strx)]; | |
590 | uint64_t offset = E::get32(entry->ran_off); | |
591 | if ( offset > _archiveFilelength ) { | |
592 | throwf("malformed archive TOC entry for %s, offset %d is beyond end of file %lld\n", | |
593 | entryName, entry->ran_off, _archiveFilelength); | |
594 | } | |
595 | ||
596 | //const Entry* member = (Entry*)&_archiveFileContent[E::get32(entry->ran_off)]; | |
597 | //fprintf(stderr, "adding hash %d, %s -> %p\n", i, entryName, entry); | |
598 | _hashTable[entryName] = offset; | |
599 | } | |
600 | } | |
601 | ||
602 | #ifdef SYMDEF_64 | |
603 | template <typename A> | |
604 | void File<A>::buildHashTable64() | |
605 | { | |
606 | // walk through list backwards, adding/overwriting entries | |
607 | // this assures that with duplicates those earliest in the list will be found | |
608 | for (int i = _tableOfContentCount-1; i >= 0; --i) { | |
609 | const struct ranlib_64* entry = &_tableOfContents64[i]; | |
610 | const char* entryName = &_tableOfContentStrings[E::get64(entry->ran_un.ran_strx)]; | |
611 | uint64_t offset = E::get64(entry->ran_off); | |
612 | if ( offset > _archiveFilelength ) { | |
613 | throwf("malformed archive TOC entry for %s, offset %lld is beyond end of file %lld\n", | |
614 | entryName, entry->ran_off, _archiveFilelength); | |
615 | } | |
616 | ||
617 | //fprintf(stderr, "adding hash %d: %s -> 0x%0llX\n", i, entryName, offset); | |
618 | _hashTable[entryName] = offset; | |
619 | } | |
620 | } | |
621 | #endif | |
622 | ||
623 | template <typename A> | |
624 | void File<A>::dumpTableOfContents() | |
625 | { | |
626 | for (unsigned int i=0; i < _tableOfContentCount; ++i) { | |
627 | const struct ranlib* e = &_tableOfContents[i]; | |
628 | printf("%s in %s\n", &_tableOfContentStrings[E::get32(e->ran_un.ran_strx)], ((Entry*)&_archiveFileContent[E::get32(e->ran_off)])->name()); | |
629 | } | |
630 | } | |
631 | ||
632 | ||
633 | // | |
634 | // main function used by linker to instantiate archive files | |
635 | // | |
636 | ld::archive::File* parse(const uint8_t* fileContent, uint64_t fileLength, | |
637 | const char* path, time_t modTime, ld::File::Ordinal ordinal, const ParserOptions& opts) | |
638 | { | |
639 | switch ( opts.objOpts.architecture ) { | |
640 | #if SUPPORT_ARCH_x86_64 | |
641 | case CPU_TYPE_X86_64: | |
642 | if ( archive::Parser<x86_64>::validFile(fileContent, fileLength, opts.objOpts) ) | |
643 | return archive::Parser<x86_64>::parse(fileContent, fileLength, path, modTime, ordinal, opts); | |
644 | break; | |
645 | #endif | |
646 | #if SUPPORT_ARCH_i386 | |
647 | case CPU_TYPE_I386: | |
648 | if ( archive::Parser<x86>::validFile(fileContent, fileLength, opts.objOpts) ) | |
649 | return archive::Parser<x86>::parse(fileContent, fileLength, path, modTime, ordinal, opts); | |
650 | break; | |
651 | #endif | |
652 | #if SUPPORT_ARCH_arm_any | |
653 | case CPU_TYPE_ARM: | |
654 | if ( archive::Parser<arm>::validFile(fileContent, fileLength, opts.objOpts) ) | |
655 | return archive::Parser<arm>::parse(fileContent, fileLength, path, modTime, ordinal, opts); | |
656 | break; | |
657 | #endif | |
658 | #if SUPPORT_ARCH_arm64 | |
659 | case CPU_TYPE_ARM64: | |
660 | if ( archive::Parser<arm64>::validFile(fileContent, fileLength, opts.objOpts) ) | |
661 | return archive::Parser<arm64>::parse(fileContent, fileLength, path, modTime, ordinal, opts); | |
662 | break; | |
663 | #endif | |
664 | } | |
665 | return NULL; | |
666 | } | |
667 | ||
668 | ||
669 | ||
670 | }; // namespace archive | |
671 | ||
672 |