]>
Commit | Line | Data |
---|---|---|
d696c285 A |
1 | /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- |
2 | * | |
3 | * Copyright (c) 2005-2006 Apple Computer, 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 | #ifndef __OBJECT_FILE_ARCHIVE_MACH_O__ | |
26 | #define __OBJECT_FILE_ARCHIVE_MACH_O__ | |
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" | |
43 | ||
44 | ||
45 | namespace mach_o { | |
46 | namespace archive { | |
47 | ||
48 | typedef const struct ranlib* ConstRanLibPtr; | |
49 | ||
50 | template <typename A> | |
51 | class Reader : public ObjectFile::Reader | |
52 | { | |
53 | public: | |
54 | static bool validFile(const uint8_t* fileContent, uint64_t fileLength); | |
55 | static Reader<A>* make(const uint8_t* fileContent, uint64_t fileLength, const char* path, | |
56 | time_t modTime, const ObjectFile::ReaderOptions& options) | |
57 | { return new Reader<A>(fileContent, fileLength, path, modTime, options); } | |
58 | virtual ~Reader() {} | |
59 | ||
60 | virtual const char* getPath() { return fPath; } | |
61 | virtual time_t getModificationTime(){ return 0; } | |
62 | virtual DebugInfoKind getDebugInfoKind() { return ObjectFile::Reader::kDebugInfoNone; } | |
63 | virtual std::vector<class ObjectFile::Atom*>& getAtoms(); | |
64 | virtual std::vector<class ObjectFile::Atom*>* getJustInTimeAtomsFor(const char* name); | |
65 | virtual std::vector<Stab>* getStabs() { return NULL; } | |
66 | ||
67 | private: | |
68 | class Entry : ar_hdr | |
69 | { | |
70 | public: | |
71 | const char* getName() const; | |
72 | time_t getModTime() const; | |
73 | const uint8_t* getContent() const; | |
74 | uint32_t getContentSize() const; | |
75 | const Entry* getNext() const; | |
76 | private: | |
77 | bool hasLongName() const; | |
78 | unsigned int getLongNameSpace() const; | |
79 | ||
80 | }; | |
81 | ||
82 | class CStringEquals | |
83 | { | |
84 | public: | |
85 | bool operator()(const char* left, const char* right) const { return (strcmp(left, right) == 0); } | |
86 | }; | |
87 | typedef __gnu_cxx::hash_map<const char*, const struct ranlib*, __gnu_cxx::hash<const char*>, CStringEquals> NameToEntryMap; | |
88 | ||
89 | typedef typename A::P P; | |
90 | typedef typename A::P::E E; | |
91 | ||
92 | Reader(const uint8_t fileContent[], uint64_t fileLength, | |
93 | const char* path, time_t modTime, const ObjectFile::ReaderOptions& options); | |
94 | const struct ranlib* ranlibBinarySearch(const char* name); | |
95 | const struct ranlib* ranlibLinearSearch(const char* name); | |
96 | const struct ranlib* ranlibHashSearch(const char* name); | |
97 | ObjectFile::Reader* makeObjectReaderForMember(const Entry* member); | |
98 | void dumpTableOfContents(); | |
99 | void buildHashTable(); | |
100 | ||
101 | const char* fPath; | |
102 | time_t fModTime; | |
103 | const ObjectFile::ReaderOptions& fOptions; | |
104 | const uint8_t* fFileContent; | |
105 | uint64_t fFileLength; | |
106 | const struct ranlib* fTableOfContents; | |
107 | uint32_t fTableOfContentCount; | |
108 | const char* fStringPool; | |
109 | std::vector<class ObjectFile::Atom*> fAllAtoms; | |
110 | std::set<const class Entry*> fInstantiatedEntries; | |
111 | std::set<const class Entry*> fPossibleEntries; | |
112 | NameToEntryMap fHashTable; | |
113 | ||
114 | static std::vector<class ObjectFile::Atom*> fgEmptyList; | |
115 | }; | |
116 | ||
117 | template <typename A> | |
118 | std::vector<class ObjectFile::Atom*> Reader<A>::fgEmptyList; | |
119 | ||
120 | ||
121 | template <typename A> | |
122 | bool Reader<A>::Entry::hasLongName() const | |
123 | { | |
124 | return ( strncmp(this->ar_name, AR_EFMT1, strlen(AR_EFMT1)) == 0 ); | |
125 | } | |
126 | ||
127 | template <typename A> | |
128 | unsigned int Reader<A>::Entry::getLongNameSpace() const | |
129 | { | |
130 | char* endptr; | |
131 | long result = strtol(&this->ar_name[strlen(AR_EFMT1)], &endptr, 10); | |
132 | return result; | |
133 | } | |
134 | ||
135 | template <typename A> | |
136 | const char* Reader<A>::Entry::getName() const | |
137 | { | |
138 | if ( this->hasLongName() ) { | |
139 | int len = this->getLongNameSpace(); | |
140 | static char longName[256]; | |
141 | strncpy(longName, ((char*)this)+sizeof(ar_hdr), len); | |
142 | longName[len] = '\0'; | |
143 | return longName; | |
144 | } | |
145 | else { | |
146 | static char shortName[20]; | |
147 | strncpy(shortName, this->ar_name, 16); | |
148 | shortName[16] = '\0'; | |
149 | char* space = strchr(shortName, ' '); | |
150 | if ( space != NULL ) | |
151 | *space = '\0'; | |
152 | return shortName; | |
153 | } | |
154 | } | |
155 | ||
156 | template <typename A> | |
157 | time_t Reader<A>::Entry::getModTime() const | |
158 | { | |
159 | char temp[14]; | |
160 | strncpy(temp, this->ar_size, 12); | |
161 | temp[12] = '\0'; | |
162 | char* endptr; | |
163 | return (time_t)strtol(temp, &endptr, 12); | |
164 | } | |
165 | ||
166 | ||
167 | template <typename A> | |
168 | const uint8_t* Reader<A>::Entry::getContent() const | |
169 | { | |
170 | if ( this->hasLongName() ) | |
171 | return ((uint8_t*)this) + sizeof(ar_hdr) + this->getLongNameSpace(); | |
172 | else | |
173 | return ((uint8_t*)this) + sizeof(ar_hdr); | |
174 | } | |
175 | ||
176 | ||
177 | template <typename A> | |
178 | uint32_t Reader<A>::Entry::getContentSize() const | |
179 | { | |
180 | char temp[12]; | |
181 | strncpy(temp, this->ar_size, 10); | |
182 | temp[10] = '\0'; | |
183 | char* endptr; | |
184 | long size = strtol(temp, &endptr, 10); | |
185 | // long name is included in ar_size | |
186 | if ( this->hasLongName() ) | |
187 | size -= this->getLongNameSpace(); | |
188 | return size; | |
189 | } | |
190 | ||
191 | ||
192 | template <typename A> | |
193 | const class Reader<A>::Entry* Reader<A>::Entry::getNext() const | |
194 | { | |
195 | const uint8_t* p = this->getContent() + getContentSize(); | |
196 | p = (const uint8_t*)(((uintptr_t)p+3) & (-4)); // 4-byte align | |
197 | return (class Reader<A>::Entry*)p; | |
198 | } | |
199 | ||
200 | template <typename A> | |
201 | bool Reader<A>::validFile(const uint8_t* fileContent, uint64_t fileLength) | |
202 | { | |
203 | // must have valid archive header | |
204 | if ( strncmp((const char*)fileContent, "!<arch>\n", 8) != 0 ) | |
205 | return false; | |
206 | ||
207 | // peak at first .o file and verify it is correct architecture | |
208 | const Entry* const start = (Entry*)&fileContent[8]; | |
209 | const Entry* const end = (Entry*)&fileContent[fileLength]; | |
210 | for (const Entry* p=start; p < end; p = p->getNext()) { | |
211 | const char* memberName = p->getName(); | |
212 | // skip option table-of-content member | |
213 | if ( (p==start) && ((strcmp(memberName, SYMDEF_SORTED) == 0) || (strcmp(memberName, SYMDEF) == 0)) ) | |
214 | continue; | |
215 | // archive is valid if first .o file is valid | |
216 | return mach_o::relocatable::Reader<A>::validFile(p->getContent()); | |
217 | } | |
218 | // empty archive | |
219 | return true; | |
220 | } | |
221 | ||
222 | template <typename A> | |
223 | Reader<A>::Reader(const uint8_t fileContent[], uint64_t fileLength, const char* path, time_t modTime, const ObjectFile::ReaderOptions& options) | |
224 | : fPath(NULL), fModTime(modTime), fOptions(options), fFileContent(NULL), fTableOfContents(NULL), fTableOfContentCount(0), | |
225 | fStringPool(NULL) | |
226 | { | |
227 | fPath = strdup(path); | |
228 | fFileContent = fileContent; | |
229 | fFileLength = fileLength; | |
230 | ||
231 | if ( strncmp((const char*)fileContent, "!<arch>\n", 8) != 0 ) | |
232 | throw "not an archive"; | |
233 | ||
234 | if ( !options.fFullyLoadArchives ) { | |
235 | const Entry* const firstMember = (Entry*)&fFileContent[8]; | |
236 | if ( (strcmp(firstMember->getName(), SYMDEF_SORTED) == 0) || (strcmp(firstMember->getName(), SYMDEF) == 0) ) { | |
237 | const uint8_t* contents = firstMember->getContent(); | |
238 | uint32_t ranlibArrayLen = E::get32(*((uint32_t*)contents)); | |
239 | fTableOfContents = (const struct ranlib*)&contents[4]; | |
240 | fTableOfContentCount = ranlibArrayLen / sizeof(struct ranlib); | |
241 | fStringPool = (const char*)&contents[ranlibArrayLen+8]; | |
242 | if ( ((uint8_t*)(&fTableOfContents[fTableOfContentCount]) > &fileContent[fileLength]) | |
243 | || ((uint8_t*)fStringPool > &fileContent[fileLength]) ) | |
244 | throw "malformed archive, perhaps wrong architecture"; | |
245 | this->buildHashTable(); | |
246 | } | |
247 | else | |
248 | throw "archive has no table of contents"; | |
249 | } | |
250 | } | |
251 | ||
252 | ||
253 | template <typename A> | |
254 | ObjectFile::Reader* Reader<A>::makeObjectReaderForMember(const Entry* member) | |
255 | { | |
256 | const char* memberName = member->getName(); | |
257 | char memberPath[strlen(fPath) + strlen(memberName)+4]; | |
258 | strcpy(memberPath, fPath); | |
259 | strcat(memberPath, "("); | |
260 | strcat(memberPath, memberName); | |
261 | strcat(memberPath, ")"); | |
262 | //fprintf(stderr, "using %s from %s\n", memberName, fPath); | |
263 | try { | |
264 | ObjectFile::Reader* obj = mach_o::relocatable::Reader<A>::make(member->getContent(), memberPath, fModTime, fOptions); | |
265 | unsigned int objIndex = 0; | |
266 | for (class std::set<const class Entry*>::iterator it=fPossibleEntries.begin(); it != fPossibleEntries.end(); it++, objIndex++) { | |
267 | if ( *it == member ) | |
268 | break; | |
269 | } | |
270 | obj->setSortOrder((fSortOrder<<16) + objIndex); | |
271 | //fprintf(stderr, "%s order = 0x%08X, index=%u\n", memberPath, obj->getSortOrder(), objIndex); | |
272 | return obj; | |
273 | } | |
274 | catch (const char* msg) { | |
275 | throwf("in %s, %s", memberPath, msg); | |
276 | } | |
277 | } | |
278 | ||
279 | template <typename A> | |
280 | std::vector<class ObjectFile::Atom*>& Reader<A>::getAtoms() | |
281 | { | |
282 | if ( fOptions.fFullyLoadArchives ) { | |
283 | // build vector of all atoms from all .o files in this archive | |
284 | const Entry* const start = (Entry*)&fFileContent[8]; | |
285 | const Entry* const end = (Entry*)&fFileContent[fFileLength]; | |
286 | unsigned int objIndex = 0; | |
287 | for (const Entry* p=start; p < end; p = p->getNext()) { | |
288 | const char* memberName = p->getName(); | |
289 | if ( (p==start) && ((strcmp(memberName, SYMDEF_SORTED) == 0) || (strcmp(memberName, SYMDEF) == 0)) ) | |
290 | continue; | |
291 | ObjectFile::Reader* r = this->makeObjectReaderForMember(p); | |
292 | r->setSortOrder((fSortOrder<<16) + objIndex++); | |
293 | std::vector<class ObjectFile::Atom*>& atoms = r->getAtoms(); | |
294 | fAllAtoms.insert(fAllAtoms.end(), atoms.begin(), atoms.end()); | |
295 | } | |
296 | return fAllAtoms; | |
297 | } | |
298 | else { | |
299 | // return nonthing for now, getJustInTimeAtomsFor() will return atoms as needed | |
300 | return fgEmptyList; | |
301 | } | |
302 | } | |
303 | ||
304 | ||
305 | template <typename A> | |
306 | ConstRanLibPtr Reader<A>::ranlibBinarySearch(const char* key) | |
307 | { | |
308 | const struct ranlib* base = fTableOfContents; | |
309 | for (uint32_t n = fTableOfContentCount; n > 0; n /= 2) { | |
310 | const struct ranlib* pivot = &base[n/2]; | |
311 | const char* pivotStr = &fStringPool[E::get32(pivot->ran_un.ran_strx)]; | |
312 | int cmp = strcmp(key, pivotStr); | |
313 | if ( cmp == 0 ) | |
314 | return pivot; | |
315 | if ( cmp > 0 ) { | |
316 | // key > pivot | |
317 | // move base to symbol after pivot | |
318 | base = &pivot[1]; | |
319 | --n; | |
320 | } | |
321 | else { | |
322 | // key < pivot | |
323 | // keep same base | |
324 | } | |
325 | } | |
326 | return NULL; | |
327 | } | |
328 | ||
329 | template <typename A> | |
330 | ConstRanLibPtr Reader<A>::ranlibLinearSearch(const char* key) | |
331 | { | |
332 | for (uint32_t i = 0; i < fTableOfContentCount; ++i) { | |
333 | const struct ranlib* entry = &fTableOfContents[i]; | |
334 | const char* entryName = &fStringPool[E::get32(entry->ran_un.ran_strx)]; | |
335 | if ( strcmp(key, entryName) == 0 ) | |
336 | return entry; | |
337 | } | |
338 | return NULL; | |
339 | } | |
340 | ||
341 | template <typename A> | |
342 | ConstRanLibPtr Reader<A>::ranlibHashSearch(const char* name) | |
343 | { | |
344 | class NameToEntryMap::iterator pos = fHashTable.find(name); | |
345 | if ( pos != fHashTable.end() ) | |
346 | return pos->second; | |
347 | else | |
348 | return NULL; | |
349 | } | |
350 | ||
351 | template <typename A> | |
352 | void Reader<A>::buildHashTable() | |
353 | { | |
354 | // walk through list backwards, adding/overwriting entries | |
355 | // this assures that with duplicates those earliest in the list will be found | |
356 | for (int i = fTableOfContentCount-1; i >= 0; --i) { | |
357 | const struct ranlib* entry = &fTableOfContents[i]; | |
358 | const char* entryName = &fStringPool[E::get32(entry->ran_un.ran_strx)]; | |
359 | const Entry* member = (Entry*)&fFileContent[E::get32(entry->ran_off)]; | |
360 | //fprintf(stderr, "adding hash %d, %s -> %p\n", i, entryName, entry); | |
361 | fHashTable[entryName] = entry; | |
362 | fPossibleEntries.insert(member); | |
363 | } | |
364 | } | |
365 | ||
366 | template <typename A> | |
367 | void Reader<A>::dumpTableOfContents() | |
368 | { | |
369 | for (unsigned int i=0; i < fTableOfContentCount; ++i) { | |
370 | const struct ranlib* e = &fTableOfContents[i]; | |
371 | printf("%s in %s\n", &fStringPool[E::get32(e->ran_un.ran_strx)], ((Entry*)&fFileContent[E::get32(e->ran_off)])->getName()); | |
372 | } | |
373 | } | |
374 | ||
375 | template <typename A> | |
376 | std::vector<class ObjectFile::Atom*>* Reader<A>::getJustInTimeAtomsFor(const char* name) | |
377 | { | |
378 | if ( fOptions.fFullyLoadArchives ) { | |
379 | return NULL; | |
380 | } | |
381 | else { | |
382 | const struct ranlib* result = NULL; | |
383 | // do a hash search of table of contents looking for requested symbol | |
384 | result = ranlibHashSearch(name); | |
385 | if ( result != NULL ) { | |
386 | const Entry* member = (Entry*)&fFileContent[E::get32(result->ran_off)]; | |
387 | if ( fInstantiatedEntries.count(member) == 0 ) { | |
69a49097 A |
388 | if ( fOptions.fWhyLoad ) |
389 | printf("%s forced load of %s(%s)\n", name, this->getPath(), member->getName()); | |
d696c285 A |
390 | // only return these atoms once |
391 | fInstantiatedEntries.insert(member); | |
392 | ObjectFile::Reader* r = makeObjectReaderForMember(member); | |
d696c285 A |
393 | return new std::vector<class ObjectFile::Atom*>(r->getAtoms()); |
394 | } | |
395 | } | |
396 | //fprintf(stderr, "%s NOT found in archive %s\n", name, fPath); | |
397 | return NULL; | |
398 | } | |
399 | } | |
400 | ||
401 | ||
402 | ||
403 | ||
404 | ||
405 | }; // namespace archive | |
406 | }; // namespace mach_o | |
407 | ||
408 | ||
409 | #endif // __OBJECT_FILE_ARCHIVE_MACH_O__ |