]> git.saurik.com Git - apple/ld64.git/blame - src/ArchiveReader.hpp
ld64-85.2.2.tar.gz
[apple/ld64.git] / src / ArchiveReader.hpp
CommitLineData
d696c285
A
1/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
2 *
2f2f92e4 3 * Copyright (c) 2005-2008 Apple Inc. All rights reserved.
d696c285
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
2f2f92e4
A
25#ifndef __OBJECT_FILE_ARCHIVE__
26#define __OBJECT_FILE_ARCHIVE__
d696c285
A
27
28#include <stdint.h>
29#include <math.h>
30#include <unistd.h>
31#include <sys/param.h>
32#include <mach-o/ranlib.h>
33#include <ar.h>
34
35#include <vector>
36#include <set>
37#include <algorithm>
38#include <ext/hash_map>
39
40#include "MachOFileAbstraction.hpp"
41#include "ObjectFile.h"
42#include "MachOReaderRelocatable.hpp"
2f2f92e4
A
43#if LTO_SUPPORT
44 #include "LTOReader.hpp"
45#endif
46
d696c285
A
47namespace archive {
48
49typedef const struct ranlib* ConstRanLibPtr;
50
51template <typename A>
52class Reader : public ObjectFile::Reader
53{
54public:
55 static bool validFile(const uint8_t* fileContent, uint64_t fileLength);
a61fdf0a
A
56 Reader(const uint8_t fileContent[], uint64_t fileLength,
57 const char* path, time_t modTime,
58 const ObjectFile::ReaderOptions& options, uint32_t ordinalBase);
d696c285
A
59 virtual ~Reader() {}
60
61 virtual const char* getPath() { return fPath; }
a61fdf0a 62 virtual time_t getModificationTime(){ return fModTime; }
d696c285
A
63 virtual DebugInfoKind getDebugInfoKind() { return ObjectFile::Reader::kDebugInfoNone; }
64 virtual std::vector<class ObjectFile::Atom*>& getAtoms();
65 virtual std::vector<class ObjectFile::Atom*>* getJustInTimeAtomsFor(const char* name);
66 virtual std::vector<Stab>* getStabs() { return NULL; }
2f2f92e4 67 virtual void optimize(std::vector<ObjectFile::Atom*>&, std::vector<ObjectFile::Atom*>&,
9d2e0767 68 std::vector<const char*>&, uint32_t, ObjectFile::Reader* writer,
2f2f92e4
A
69 bool allGlobalsAReDeadStripRoots, int okind,
70 bool verbose, bool saveTemps, const char* outputFilePath,
71 bool pie, bool allowTextRelocs);
d696c285
A
72
73private:
2f2f92e4
A
74 static bool validMachOFile(const uint8_t* fileContent, uint64_t fileLength);
75 static bool validLTOFile(const uint8_t* fileContent, uint64_t fileLength);
76 static cpu_type_t architecture();
77
78
d696c285
A
79 class Entry : ar_hdr
80 {
81 public:
82 const char* getName() const;
83 time_t getModTime() const;
84 const uint8_t* getContent() const;
85 uint32_t getContentSize() const;
86 const Entry* getNext() const;
87 private:
88 bool hasLongName() const;
89 unsigned int getLongNameSpace() const;
90
91 };
92
93 class CStringEquals
94 {
95 public:
96 bool operator()(const char* left, const char* right) const { return (strcmp(left, right) == 0); }
97 };
98 typedef __gnu_cxx::hash_map<const char*, const struct ranlib*, __gnu_cxx::hash<const char*>, CStringEquals> NameToEntryMap;
99
100 typedef typename A::P P;
101 typedef typename A::P::E E;
102
d696c285
A
103 const struct ranlib* ranlibHashSearch(const char* name);
104 ObjectFile::Reader* makeObjectReaderForMember(const Entry* member);
105 void dumpTableOfContents();
106 void buildHashTable();
107
108 const char* fPath;
109 time_t fModTime;
110 const ObjectFile::ReaderOptions& fOptions;
a61fdf0a 111 uint32_t fOrdinalBase;
d696c285
A
112 const uint8_t* fFileContent;
113 uint64_t fFileLength;
114 const struct ranlib* fTableOfContents;
115 uint32_t fTableOfContentCount;
116 const char* fStringPool;
117 std::vector<class ObjectFile::Atom*> fAllAtoms;
2f2f92e4 118 std::vector<class ObjectFile::Reader*> fInstantiatedReaders;
d696c285
A
119 std::set<const class Entry*> fInstantiatedEntries;
120 std::set<const class Entry*> fPossibleEntries;
121 NameToEntryMap fHashTable;
122
123 static std::vector<class ObjectFile::Atom*> fgEmptyList;
124};
125
126template <typename A>
127std::vector<class ObjectFile::Atom*> Reader<A>::fgEmptyList;
128
129
130template <typename A>
131bool Reader<A>::Entry::hasLongName() const
132{
133 return ( strncmp(this->ar_name, AR_EFMT1, strlen(AR_EFMT1)) == 0 );
134}
135
136template <typename A>
137unsigned int Reader<A>::Entry::getLongNameSpace() const
138{
139 char* endptr;
140 long result = strtol(&this->ar_name[strlen(AR_EFMT1)], &endptr, 10);
141 return result;
142}
143
144template <typename A>
145const char* Reader<A>::Entry::getName() const
146{
147 if ( this->hasLongName() ) {
148 int len = this->getLongNameSpace();
149 static char longName[256];
150 strncpy(longName, ((char*)this)+sizeof(ar_hdr), len);
151 longName[len] = '\0';
152 return longName;
153 }
154 else {
155 static char shortName[20];
156 strncpy(shortName, this->ar_name, 16);
157 shortName[16] = '\0';
158 char* space = strchr(shortName, ' ');
159 if ( space != NULL )
160 *space = '\0';
161 return shortName;
162 }
163}
164
165template <typename A>
166time_t Reader<A>::Entry::getModTime() const
167{
168 char temp[14];
a61fdf0a 169 strncpy(temp, this->ar_date, 12);
d696c285
A
170 temp[12] = '\0';
171 char* endptr;
a61fdf0a 172 return (time_t)strtol(temp, &endptr, 10);
d696c285
A
173}
174
175
176template <typename A>
177const uint8_t* Reader<A>::Entry::getContent() const
178{
179 if ( this->hasLongName() )
180 return ((uint8_t*)this) + sizeof(ar_hdr) + this->getLongNameSpace();
181 else
182 return ((uint8_t*)this) + sizeof(ar_hdr);
183}
184
185
186template <typename A>
187uint32_t Reader<A>::Entry::getContentSize() const
188{
189 char temp[12];
190 strncpy(temp, this->ar_size, 10);
191 temp[10] = '\0';
192 char* endptr;
193 long size = strtol(temp, &endptr, 10);
194 // long name is included in ar_size
195 if ( this->hasLongName() )
196 size -= this->getLongNameSpace();
197 return size;
198}
199
200
201template <typename A>
202const class Reader<A>::Entry* Reader<A>::Entry::getNext() const
203{
204 const uint8_t* p = this->getContent() + getContentSize();
205 p = (const uint8_t*)(((uintptr_t)p+3) & (-4)); // 4-byte align
206 return (class Reader<A>::Entry*)p;
207}
208
2f2f92e4
A
209
210template <> cpu_type_t Reader<ppc>::architecture() { return CPU_TYPE_POWERPC; }
211template <> cpu_type_t Reader<ppc64>::architecture() { return CPU_TYPE_POWERPC64; }
212template <> cpu_type_t Reader<x86>::architecture() { return CPU_TYPE_I386; }
213template <> cpu_type_t Reader<x86_64>::architecture() { return CPU_TYPE_X86_64; }
214template <> cpu_type_t Reader<arm>::architecture() { return CPU_TYPE_ARM; }
215
216
217template <typename A>
218bool Reader<A>::validMachOFile(const uint8_t* fileContent, uint64_t fileLength)
219{
220 return mach_o::relocatable::Reader<A>::validFile(fileContent);
221}
222
223template <typename A>
224bool Reader<A>::validLTOFile(const uint8_t* fileContent, uint64_t fileLength)
225{
226#if LTO_SUPPORT
227 return lto::Reader::validFile(fileContent, fileLength, architecture());
228#else
229 return false;
230#endif
231}
232
233
234
d696c285
A
235template <typename A>
236bool Reader<A>::validFile(const uint8_t* fileContent, uint64_t fileLength)
237{
238 // must have valid archive header
239 if ( strncmp((const char*)fileContent, "!<arch>\n", 8) != 0 )
240 return false;
241
242 // peak at first .o file and verify it is correct architecture
243 const Entry* const start = (Entry*)&fileContent[8];
244 const Entry* const end = (Entry*)&fileContent[fileLength];
245 for (const Entry* p=start; p < end; p = p->getNext()) {
246 const char* memberName = p->getName();
247 // skip option table-of-content member
248 if ( (p==start) && ((strcmp(memberName, SYMDEF_SORTED) == 0) || (strcmp(memberName, SYMDEF) == 0)) )
249 continue;
250 // archive is valid if first .o file is valid
2f2f92e4 251 return (validMachOFile(p->getContent(), p->getContentSize()) || validLTOFile(p->getContent(), p->getContentSize()));
d696c285
A
252 }
253 // empty archive
254 return true;
255}
256
257template <typename A>
a61fdf0a
A
258Reader<A>::Reader(const uint8_t fileContent[], uint64_t fileLength, const char* path, time_t modTime,
259 const ObjectFile::ReaderOptions& options, uint32_t ordinalBase)
260 : fPath(NULL), fModTime(modTime), fOptions(options), fOrdinalBase(ordinalBase), fFileContent(NULL),
261 fTableOfContents(NULL), fTableOfContentCount(0), fStringPool(NULL)
d696c285
A
262{
263 fPath = strdup(path);
264 fFileContent = fileContent;
265 fFileLength = fileLength;
266
267 if ( strncmp((const char*)fileContent, "!<arch>\n", 8) != 0 )
268 throw "not an archive";
269
a61fdf0a
A
270 // write out path for -whatsloaded option
271 if ( options.fLogAllFiles )
272 printf("%s\n", path);
273
d696c285
A
274 if ( !options.fFullyLoadArchives ) {
275 const Entry* const firstMember = (Entry*)&fFileContent[8];
276 if ( (strcmp(firstMember->getName(), SYMDEF_SORTED) == 0) || (strcmp(firstMember->getName(), SYMDEF) == 0) ) {
277 const uint8_t* contents = firstMember->getContent();
278 uint32_t ranlibArrayLen = E::get32(*((uint32_t*)contents));
279 fTableOfContents = (const struct ranlib*)&contents[4];
280 fTableOfContentCount = ranlibArrayLen / sizeof(struct ranlib);
281 fStringPool = (const char*)&contents[ranlibArrayLen+8];
282 if ( ((uint8_t*)(&fTableOfContents[fTableOfContentCount]) > &fileContent[fileLength])
283 || ((uint8_t*)fStringPool > &fileContent[fileLength]) )
284 throw "malformed archive, perhaps wrong architecture";
285 this->buildHashTable();
286 }
287 else
288 throw "archive has no table of contents";
289 }
290}
291
292
293template <typename A>
294ObjectFile::Reader* Reader<A>::makeObjectReaderForMember(const Entry* member)
295{
296 const char* memberName = member->getName();
297 char memberPath[strlen(fPath) + strlen(memberName)+4];
298 strcpy(memberPath, fPath);
299 strcat(memberPath, "(");
300 strcat(memberPath, memberName);
301 strcat(memberPath, ")");
302 //fprintf(stderr, "using %s from %s\n", memberName, fPath);
303 try {
a61fdf0a
A
304 // offset the ordinals in this mach-o .o file, so that atoms layout in same order as in archive
305 uint32_t ordinalBase = fOrdinalBase + (uint8_t*)member - fFileContent;
2f2f92e4
A
306 if ( validMachOFile(member->getContent(), member->getContentSize()) ) {
307 return new typename mach_o::relocatable::Reader<A>::Reader(member->getContent(), memberPath, member->getModTime(), fOptions, ordinalBase);
308 }
309#if LTO_SUPPORT
310 else if ( validLTOFile(member->getContent(), member->getContentSize()) ) {
311 return new typename lto::Reader(member->getContent(), member->getContentSize(), memberPath, member->getModTime(), fOptions, architecture());
312 }
313#endif
314 throw "not a valid archive member";
d696c285
A
315 }
316 catch (const char* msg) {
317 throwf("in %s, %s", memberPath, msg);
318 }
319}
320
a61fdf0a 321
d696c285
A
322template <typename A>
323std::vector<class ObjectFile::Atom*>& Reader<A>::getAtoms()
324{
325 if ( fOptions.fFullyLoadArchives ) {
326 // build vector of all atoms from all .o files in this archive
327 const Entry* const start = (Entry*)&fFileContent[8];
328 const Entry* const end = (Entry*)&fFileContent[fFileLength];
d696c285
A
329 for (const Entry* p=start; p < end; p = p->getNext()) {
330 const char* memberName = p->getName();
331 if ( (p==start) && ((strcmp(memberName, SYMDEF_SORTED) == 0) || (strcmp(memberName, SYMDEF) == 0)) )
332 continue;
a61fdf0a
A
333 if ( fOptions.fWhyLoad )
334 printf("-all_load forced load of %s(%s)\n", this->getPath(), memberName);
d696c285 335 ObjectFile::Reader* r = this->makeObjectReaderForMember(p);
d696c285
A
336 std::vector<class ObjectFile::Atom*>& atoms = r->getAtoms();
337 fAllAtoms.insert(fAllAtoms.end(), atoms.begin(), atoms.end());
2f2f92e4 338 fInstantiatedReaders.push_back(r);
d696c285
A
339 }
340 return fAllAtoms;
341 }
a61fdf0a
A
342 else if ( fOptions.fLoadAllObjcObjectsFromArchives ) {
343 // build vector of all atoms from all .o files containing objc classes in this archive
344 for(class NameToEntryMap::iterator it = fHashTable.begin(); it != fHashTable.end(); ++it) {
345 if ( (strncmp(it->first, ".objc_c", 7) == 0) || (strncmp(it->first, "_OBJC_CLASS_$_", 14) == 0) ) {
346 const Entry* member = (Entry*)&fFileContent[E::get32(it->second->ran_off)];
347 if ( fInstantiatedEntries.count(member) == 0 ) {
348 if ( fOptions.fWhyLoad )
349 printf("-ObjC forced load of %s(%s)\n", this->getPath(), member->getName());
350 // only return these atoms once
351 fInstantiatedEntries.insert(member);
352 ObjectFile::Reader* r = makeObjectReaderForMember(member);
353 std::vector<class ObjectFile::Atom*>& atoms = r->getAtoms();
354 fAllAtoms.insert(fAllAtoms.end(), atoms.begin(), atoms.end());
2f2f92e4 355 fInstantiatedReaders.push_back(r);
a61fdf0a
A
356 }
357 }
358 }
359 return fAllAtoms;
360 }
d696c285
A
361 else {
362 // return nonthing for now, getJustInTimeAtomsFor() will return atoms as needed
363 return fgEmptyList;
364 }
365}
366
2f2f92e4
A
367template <typename A>
368void Reader<A>::optimize(std::vector<ObjectFile::Atom*>& allAtoms, std::vector<ObjectFile::Atom*>& newAtoms,
9d2e0767 369 std::vector<const char*>& additionalUndefines, uint32_t nextOrdinal, ObjectFile::Reader* writer,
2f2f92e4
A
370 bool allGlobalsAReDeadStripRoots, int okind,
371 bool verbose, bool saveTemps, const char* outputFilePath,
372 bool pie, bool allowTextRelocs)
373{
374 for(std::vector<ObjectFile::Reader*>::iterator it=fInstantiatedReaders.begin(); it != fInstantiatedReaders.end(); ++it) {
9d2e0767
A
375 (*it)->optimize(allAtoms, newAtoms, additionalUndefines, nextOrdinal, writer, allGlobalsAReDeadStripRoots,
376 okind, verbose, saveTemps, outputFilePath, pie, allowTextRelocs);
2f2f92e4
A
377 }
378}
379
380
d696c285 381
d696c285
A
382template <typename A>
383ConstRanLibPtr Reader<A>::ranlibHashSearch(const char* name)
384{
385 class NameToEntryMap::iterator pos = fHashTable.find(name);
386 if ( pos != fHashTable.end() )
387 return pos->second;
388 else
389 return NULL;
390}
391
392template <typename A>
393void Reader<A>::buildHashTable()
394{
395 // walk through list backwards, adding/overwriting entries
396 // this assures that with duplicates those earliest in the list will be found
397 for (int i = fTableOfContentCount-1; i >= 0; --i) {
398 const struct ranlib* entry = &fTableOfContents[i];
399 const char* entryName = &fStringPool[E::get32(entry->ran_un.ran_strx)];
400 const Entry* member = (Entry*)&fFileContent[E::get32(entry->ran_off)];
401 //fprintf(stderr, "adding hash %d, %s -> %p\n", i, entryName, entry);
402 fHashTable[entryName] = entry;
403 fPossibleEntries.insert(member);
404 }
405}
406
407template <typename A>
408void Reader<A>::dumpTableOfContents()
409{
410 for (unsigned int i=0; i < fTableOfContentCount; ++i) {
411 const struct ranlib* e = &fTableOfContents[i];
412 printf("%s in %s\n", &fStringPool[E::get32(e->ran_un.ran_strx)], ((Entry*)&fFileContent[E::get32(e->ran_off)])->getName());
413 }
414}
415
416template <typename A>
417std::vector<class ObjectFile::Atom*>* Reader<A>::getJustInTimeAtomsFor(const char* name)
418{
419 if ( fOptions.fFullyLoadArchives ) {
420 return NULL;
421 }
422 else {
423 const struct ranlib* result = NULL;
424 // do a hash search of table of contents looking for requested symbol
425 result = ranlibHashSearch(name);
426 if ( result != NULL ) {
427 const Entry* member = (Entry*)&fFileContent[E::get32(result->ran_off)];
428 if ( fInstantiatedEntries.count(member) == 0 ) {
69a49097
A
429 if ( fOptions.fWhyLoad )
430 printf("%s forced load of %s(%s)\n", name, this->getPath(), member->getName());
d696c285
A
431 // only return these atoms once
432 fInstantiatedEntries.insert(member);
433 ObjectFile::Reader* r = makeObjectReaderForMember(member);
2f2f92e4 434 fInstantiatedReaders.push_back(r);
d696c285
A
435 return new std::vector<class ObjectFile::Atom*>(r->getAtoms());
436 }
437 }
438 //fprintf(stderr, "%s NOT found in archive %s\n", name, fPath);
439 return NULL;
440 }
441}
442
443
444
445
446
447}; // namespace archive
d696c285
A
448
449
2f2f92e4 450#endif // __OBJECT_FILE_ARCHIVE__