1 /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
3 * Copyright (c) 2005-2011 Apple Inc. All rights reserved.
5 * @APPLE_LICENSE_HEADER_START@
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
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.
22 * @APPLE_LICENSE_HEADER_END@
28 #include <sys/param.h>
29 #include <mach-o/ranlib.h>
36 #include <unordered_map>
38 #include "MachOFileAbstraction.hpp"
39 #include "Architectures.hpp"
41 #include "macho_relocatable_file.h"
43 #include "archive_file.h"
50 template <typename A
> class File
;
57 typedef typename
A::P P
;
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
,
72 class File
: public ld::archive::File
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
);
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
); }
87 // overrides of ld::archive::File
88 virtual bool justInTimeDataOnlyforEachAtom(const char* name
, ld::File::AtomHandler
& handler
) const;
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();
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;
106 bool hasLongName() const;
107 unsigned int getLongNameSpace() const;
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;
114 typedef std::unordered_map
<const char*, uint64_t, ld::CStringHash
, ld::CStringEquals
> NameToOffsetMap
;
116 typedef typename
A::P P
;
117 typedef typename
A::P::E E
;
119 typedef std::map
<const class Entry
*, MemberState
> MemberToStateMap
;
121 MemberState
& makeObjectFileForMember(const Entry
* member
) const;
122 bool memberHasObjCCategories(const Entry
* member
) const;
123 void dumpTableOfContents();
124 void buildHashTable();
126 void buildHashTable64();
128 const uint8_t* _archiveFileContent
;
129 uint64_t _archiveFilelength
;
130 const struct ranlib
* _tableOfContents
;
132 const struct ranlib_64
* _tableOfContents64
;
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
;
148 template <typename A
>
149 bool File
<A
>::Entry::hasLongName() const
151 return ( strncmp(this->ar_name
, AR_EFMT1
, strlen(AR_EFMT1
)) == 0 );
154 template <typename A
>
155 unsigned int File
<A
>::Entry::getLongNameSpace() const
158 long result
= strtol(&this->ar_name
[strlen(AR_EFMT1
)], &endptr
, 10);
162 template <typename A
>
163 void File
<A
>::Entry::getName(char *buf
, int bufsz
) const
165 if ( this->hasLongName() ) {
166 int len
= this->getLongNameSpace();
167 assert(bufsz
>= len
+1);
168 strncpy(buf
, ((char*)this)+sizeof(ar_hdr
), len
);
172 assert(bufsz
>= 16+1);
173 strncpy(buf
, this->ar_name
, 16);
175 char* space
= strchr(buf
, ' ');
181 template <typename A
>
182 time_t File
<A
>::Entry::modificationTime() const
185 strncpy(temp
, this->ar_date
, 12);
188 return (time_t)strtol(temp
, &endptr
, 10);
192 template <typename A
>
193 const uint8_t* File
<A
>::Entry::content() const
195 if ( this->hasLongName() )
196 return ((uint8_t*)this) + sizeof(ar_hdr
) + this->getLongNameSpace();
198 return ((uint8_t*)this) + sizeof(ar_hdr
);
202 template <typename A
>
203 uint32_t File
<A
>::Entry::contentSize() const
206 strncpy(temp
, this->ar_size
, 10);
209 long size
= strtol(temp
, &endptr
, 10);
210 // long name is included in ar_size
211 if ( this->hasLongName() )
212 size
-= this->getLongNameSpace();
217 template <typename A
>
218 const class File
<A
>::Entry
* File
<A
>::Entry::next() const
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
;
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
; }
231 template <typename A
>
232 bool File
<A
>::validMachOFile(const uint8_t* fileContent
, uint64_t fileLength
, const mach_o::relocatable::ParserOptions
& opts
)
234 return mach_o::relocatable::isObjectFile(fileContent
, fileLength
, opts
);
237 template <typename A
>
238 bool File
<A
>::validLTOFile(const uint8_t* fileContent
, uint64_t fileLength
, const mach_o::relocatable::ParserOptions
& opts
)
240 return lto::isObjectFile(fileContent
, fileLength
, opts
.architecture
, opts
.subType
);
245 template <typename A
>
246 bool File
<A
>::validFile(const uint8_t* fileContent
, uint64_t fileLength
, const mach_o::relocatable::ParserOptions
& opts
)
248 // must have valid archive header
249 if ( strncmp((const char*)fileContent
, "!<arch>\n", 8) != 0 )
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)) )
262 if ( (p
==start
) && ((strcmp(memberName
, SYMDEF_64_SORTED
) == 0) || (strcmp(memberName
, SYMDEF_64
) == 0)) )
265 // archive is valid if first .o file is valid
266 return (validMachOFile(p
->content(), p
->contentSize(), opts
) || validLTOFile(p
->content(), p
->contentSize(), opts
));
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
),
280 _tableOfContents64(NULL
),
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
)
287 if ( strncmp((const char*)fileContent
, "!<arch>\n", 8) != 0 )
288 throw "not an archive";
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();
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();
319 throw "archive has no table of contents";
324 bool File
<x86
>::memberHasObjCCategories(const Entry
* member
) const
327 // i386 for iOS simulator uses ObjC2 which has no global symbol for categories
328 return mach_o::relocatable::hasObjC2Categories(member
->content());
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());
339 template <typename A
>
340 bool File
<A
>::memberHasObjCCategories(const Entry
* member
) const
342 // x86_64 and ARM use ObjC2 which has no global symbol for categories
343 return mach_o::relocatable::hasObjC2Categories(member
->content());
347 template <typename A
>
348 typename File
<A
>::MemberState
& File
<A
>::makeObjectFileForMember(const Entry
* member
) const
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
357 if (_instantiatedEntries
.size() == 0) {
358 start
= (Entry
*)&_archiveFileContent
[8];
361 MemberState
&lastKnown
= _instantiatedEntries
.rbegin()->second
;
362 start
= lastKnown
.entry
->next();
363 index
= lastKnown
.index
+1;
365 for (const Entry
* p
=start
; p
<= member
; p
= p
->next(), index
++) {
366 MemberState state
= {NULL
, p
, false, false, index
};
367 _instantiatedEntries
[p
] = state
;
373 MemberState
& state
= pos
->second
;
376 memberIndex
= state
.index
;
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());
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(),
399 if ( result
!= NULL
) {
400 MemberState state
= {result
, member
, false, false, memberIndex
};
401 _instantiatedEntries
[member
] = state
;
402 return _instantiatedEntries
[member
];
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
];
414 throwf("archive member '%s' with length %d is not mach-o or llvm bitcode", memberName
, member
->contentSize());
416 catch (const char* msg
) {
417 throwf("in %s, %s", memberPath
, msg
);
422 template <typename A
>
423 bool File
<A
>::loadMember(MemberState
& state
, ld::File::AtomHandler
& handler
, const char *format
, ...) const
425 bool didSomething
= false;
427 if ( _verboseLoad
&& !state
.logged
) {
429 va_start(list
, format
);
430 vprintf(format
, list
);
435 didSomething
= state
.file
->forEachAtom(handler
);
441 template <typename A
>
442 bool File
<A
>::forEachAtom(ld::File::AtomHandler
& handler
) const
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)) )
455 if ( (p
==start
) && ((strcmp(memberName
, SYMDEF_64_SORTED
) == 0) || (strcmp(memberName
, SYMDEF_64
) == 0)) )
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
);
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
);
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()) {
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)) )
483 if ( (member
==start
) && ((strcmp(mname
, SYMDEF_64_SORTED
) == 0) || (strcmp(mname
, SYMDEF_64
) == 0)) )
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
);
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
);
514 template <typename A
>
515 bool File
<A
>::justInTimeforEachAtom(const char* name
, ld::File::AtomHandler
& handler
) const
517 // in force load case, all members already loaded
518 if ( _forceLoadAll
|| _forceLoadThis
)
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() )
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
);
534 class CheckIsDataSymbolHandler
: public ld::File::AtomHandler
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
)
544 virtual void doFile(const class ld::File
&) {}
545 bool symbolIsDataDefinition() { return _isData
; }
553 template <typename A
>
554 bool File
<A
>::justInTimeDataOnlyforEachAtom(const char* name
, ld::File::AtomHandler
& handler
) const
556 // in force load case, all members already loaded
557 if ( _forceLoadAll
|| _forceLoadThis
)
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() )
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
);
578 //fprintf(stderr, "%s NOT found in archive %s\n", name, this->path());
582 template <typename A
>
583 void File
<A
>::buildHashTable()
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
);
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
;
603 template <typename A
>
604 void File
<A
>::buildHashTable64()
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
);
617 //fprintf(stderr, "adding hash %d: %s -> 0x%0llX\n", i, entryName, offset);
618 _hashTable
[entryName
] = offset
;
623 template <typename A
>
624 void File
<A
>::dumpTableOfContents()
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());
634 // main function used by linker to instantiate archive files
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
)
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
);
646 #if SUPPORT_ARCH_i386
648 if ( archive::Parser
<x86
>::validFile(fileContent
, fileLength
, opts
.objOpts
) )
649 return archive::Parser
<x86
>::parse(fileContent
, fileLength
, path
, modTime
, ordinal
, opts
);
652 #if SUPPORT_ARCH_arm_any
654 if ( archive::Parser
<arm
>::validFile(fileContent
, fileLength
, opts
.objOpts
) )
655 return archive::Parser
<arm
>::parse(fileContent
, fileLength
, path
, modTime
, ordinal
, opts
);
658 #if SUPPORT_ARCH_arm64
660 if ( archive::Parser
<arm64
>::validFile(fileContent
, fileLength
, opts
.objOpts
) )
661 return archive::Parser
<arm64
>::parse(fileContent
, fileLength
, path
, modTime
, ordinal
, opts
);
670 }; // namespace archive