]>
Commit | Line | Data |
---|---|---|
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 |
47 | namespace archive { |
48 | ||
49 | typedef const struct ranlib* ConstRanLibPtr; | |
50 | ||
51 | template <typename A> | |
52 | class Reader : public ObjectFile::Reader | |
53 | { | |
54 | public: | |
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 | |
73 | private: | |
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 | ||
126 | template <typename A> | |
127 | std::vector<class ObjectFile::Atom*> Reader<A>::fgEmptyList; | |
128 | ||
129 | ||
130 | template <typename A> | |
131 | bool Reader<A>::Entry::hasLongName() const | |
132 | { | |
133 | return ( strncmp(this->ar_name, AR_EFMT1, strlen(AR_EFMT1)) == 0 ); | |
134 | } | |
135 | ||
136 | template <typename A> | |
137 | unsigned 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 | ||
144 | template <typename A> | |
145 | const 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 | ||
165 | template <typename A> | |
166 | time_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 | ||
176 | template <typename A> | |
177 | const 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 | ||
186 | template <typename A> | |
187 | uint32_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 | ||
201 | template <typename A> | |
202 | const 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 | |
210 | template <> cpu_type_t Reader<ppc>::architecture() { return CPU_TYPE_POWERPC; } | |
211 | template <> cpu_type_t Reader<ppc64>::architecture() { return CPU_TYPE_POWERPC64; } | |
212 | template <> cpu_type_t Reader<x86>::architecture() { return CPU_TYPE_I386; } | |
213 | template <> cpu_type_t Reader<x86_64>::architecture() { return CPU_TYPE_X86_64; } | |
214 | template <> cpu_type_t Reader<arm>::architecture() { return CPU_TYPE_ARM; } | |
215 | ||
216 | ||
217 | template <typename A> | |
218 | bool Reader<A>::validMachOFile(const uint8_t* fileContent, uint64_t fileLength) | |
219 | { | |
220 | return mach_o::relocatable::Reader<A>::validFile(fileContent); | |
221 | } | |
222 | ||
223 | template <typename A> | |
224 | bool 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 |
235 | template <typename A> |
236 | bool 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 | ||
257 | template <typename A> | |
a61fdf0a A |
258 | Reader<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 | ||
293 | template <typename A> | |
294 | ObjectFile::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 |
322 | template <typename A> |
323 | std::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 |
367 | template <typename A> |
368 | void 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 |
382 | template <typename A> |
383 | ConstRanLibPtr 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 | ||
392 | template <typename A> | |
393 | void 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 | ||
407 | template <typename A> | |
408 | void 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 | ||
416 | template <typename A> | |
417 | std::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__ |