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