]> git.saurik.com Git - apple/ld64.git/blob - src/ld/parsers/archive_file.cpp
900453040e4a511e6a68468173cff94b5794fa9b
[apple/ld64.git] / src / ld / parsers / archive_file.cpp
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 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,
65 ld::File::Ordinal ordinal, const ParserOptions& opts) {
66 return new File<A>(fileContent, fileLength, path, mTime,
67 ordinal, opts);
68 }
69
70 };
71
72 template <typename A>
73 class File : public ld::archive::File
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,
80 ld::File::Ordinal ord, const ParserOptions& opts);
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
88 // overrides of ld::archive::File
89 virtual bool justInTimeDataOnlyforEachAtom(const char* name, ld::File::AtomHandler& handler) const;
90
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
98 class Entry : ar_hdr
99 {
100 public:
101 void getName(char *, int) const;
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
112 struct MemberState { ld::relocatable::File* file; const Entry *entry; bool logged; bool loaded; uint32_t index;};
113 bool loadMember(MemberState& state, ld::File::AtomHandler& handler, const char *format, ...) const;
114
115 typedef std::unordered_map<const char*, const struct ranlib*, ld::CStringHash, ld::CStringEquals> NameToEntryMap;
116
117 typedef typename A::P P;
118 typedef typename A::P::E E;
119
120 typedef std::map<const class Entry*, MemberState> MemberToStateMap;
121
122 const struct ranlib* ranlibHashSearch(const char* name) const;
123 MemberState& makeObjectFileForMember(const Entry* member) const;
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;
133 mutable MemberToStateMap _instantiatedEntries;
134 NameToEntryMap _hashTable;
135 const bool _forceLoadAll;
136 const bool _forceLoadObjC;
137 const bool _forceLoadThis;
138 const bool _objc2ABI;
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>
160 void File<A>::Entry::getName(char *buf, int bufsz) const
161 {
162 if ( this->hasLongName() ) {
163 int len = this->getLongNameSpace();
164 assert(bufsz >= len+1);
165 strncpy(buf, ((char*)this)+sizeof(ar_hdr), len);
166 buf[len] = '\0';
167 }
168 else {
169 assert(bufsz >= 16+1);
170 strncpy(buf, this->ar_name, 16);
171 buf[16] = '\0';
172 char* space = strchr(buf, ' ');
173 if ( space != NULL )
174 *space = '\0';
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
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; }
226 template <> cpu_type_t File<arm64>::architecture() { return CPU_TYPE_ARM64; }
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()) {
254 char memberName[256];
255 p->getName(memberName, sizeof(memberName));
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,
269 ld::File::Ordinal ord, const ParserOptions& opts)
270 : ld::archive::File(strdup(pth), modTime, ord),
271 _archiveFileContent(fileContent), _archiveFilelength(fileLength),
272 _tableOfContents(NULL), _tableOfContentCount(0), _tableOfContentStrings(NULL),
273 _forceLoadAll(opts.forceLoadAll), _forceLoadObjC(opts.forceLoadObjC),
274 _forceLoadThis(opts.forceLoadThisArchive), _objc2ABI(opts.objcABI2), _verboseLoad(opts.verboseLoad),
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];
282 char memberName[256];
283 firstMember->getName(memberName, sizeof(memberName));
284 if ( (strcmp(memberName, SYMDEF_SORTED) == 0) || (strcmp(memberName, SYMDEF) == 0) ) {
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 {
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
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());
311 }
312 }
313
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>
325 typename File<A>::MemberState& File<A>::makeObjectFileForMember(const Entry* member) const
326 {
327 uint32_t memberIndex = 0;
328 // in case member was instantiated earlier but not needed yet
329 typename MemberToStateMap::iterator pos = _instantiatedEntries.find(member);
330 if ( pos == _instantiatedEntries.end() ) {
331 // Have to find the index of this member
332 const Entry* start;
333 uint32_t index;
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));
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);
371 // see if member is mach-o file
372 ld::File::Ordinal ordinal = this->ordinal().archiveOrdinalWithMemberIndex(memberIndex);
373 ld::relocatable::File* result = mach_o::relocatable::parse(member->content(), member->contentSize(),
374 mPath, member->modificationTime(),
375 ordinal, _objOpts);
376 if ( result != NULL ) {
377 MemberState state = {result, member, false, false, memberIndex};
378 _instantiatedEntries[member] = state;
379 return _instantiatedEntries[member];
380 }
381 // see if member is llvm bitcode file
382 result = lto::parse(member->content(), member->contentSize(),
383 mPath, member->modificationTime(), ordinal,
384 _objOpts.architecture, _objOpts.subType, _logAllFiles, _objOpts.verboseOptimizationHints);
385 if ( result != NULL ) {
386 MemberState state = {result, member, false, false, memberIndex};
387 _instantiatedEntries[member] = state;
388 return _instantiatedEntries[member];
389 }
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
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
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()) {
427 char memberName[256];
428 p->getName(memberName, sizeof(memberName));
429 if ( (p==start) && ((strcmp(memberName, SYMDEF_SORTED) == 0) || (strcmp(memberName, SYMDEF) == 0)) )
430 continue;
431 MemberState& state = this->makeObjectFileForMember(p);
432 didSome |= loadMember(state, handler, "%s forced load of %s(%s)\n", _forceLoadThis ? "-force_load" : "-all_load", this->path(), memberName);
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)];
440 MemberState& state = this->makeObjectFileForMember(member);
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);
444 }
445 }
446 // ObjC2 has no symbols in .o files with categories but not classes, look deeper for those
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()) {
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 ) {
458 if ( this->memberHasObjCCategories(member) ) {
459 MemberState& state = this->makeObjectFileForMember(member);
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);
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)];
481 MemberState& state = this->makeObjectFileForMember(member);
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);
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() ) {
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);
529 }
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 //
581 ld::archive::File* parse(const uint8_t* fileContent, uint64_t fileLength,
582 const char* path, time_t modTime, ld::File::Ordinal ordinal, const ParserOptions& opts)
583 {
584 switch ( opts.objOpts.architecture ) {
585 #if SUPPORT_ARCH_x86_64
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;
590 #endif
591 #if SUPPORT_ARCH_i386
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;
596 #endif
597 #if SUPPORT_ARCH_arm_any
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;
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;
608 #endif
609 }
610 return NULL;
611 }
612
613
614
615 }; // namespace archive
616
617