]> git.saurik.com Git - apple/ld64.git/blame_incremental - src/ld/parsers/archive_file.cpp
ld64-409.12.tar.gz
[apple/ld64.git] / src / ld / parsers / archive_file.cpp
... / ...
CommitLineData
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
46namespace archive {
47
48
49// forward reference
50template <typename A> class File;
51
52
53template <typename A>
54class Parser
55{
56public:
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
71template <typename A>
72class File : public ld::archive::File
73{
74public:
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
90private:
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
148template <typename A>
149bool File<A>::Entry::hasLongName() const
150{
151 return ( strncmp(this->ar_name, AR_EFMT1, strlen(AR_EFMT1)) == 0 );
152}
153
154template <typename A>
155unsigned 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
162template <typename A>
163void 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
181template <typename A>
182time_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
192template <typename A>
193const 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
202template <typename A>
203uint32_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
217template <typename A>
218const 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
226template <> cpu_type_t File<x86>::architecture() { return CPU_TYPE_I386; }
227template <> cpu_type_t File<x86_64>::architecture() { return CPU_TYPE_X86_64; }
228template <> cpu_type_t File<arm>::architecture() { return CPU_TYPE_ARM; }
229template <> cpu_type_t File<arm64>::architecture() { return CPU_TYPE_ARM64; }
230
231template <typename A>
232bool 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
237template <typename A>
238bool 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
245template <typename A>
246bool 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
273template <typename A>
274File<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
323template <>
324bool 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
339template <typename A>
340bool 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
347template <typename A>
348typename 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
422template <typename A>
423bool 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
441template <typename A>
442bool 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
514template <typename A>
515bool 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
534class CheckIsDataSymbolHandler : public ld::File::AtomHandler
535{
536public:
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
547private:
548 const char* _name;
549 bool _isData;
550
551};
552
553template <typename A>
554bool 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
582template <typename A>
583void 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
603template <typename A>
604void 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
623template <typename A>
624void 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//
636ld::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