]> git.saurik.com Git - apple/ld64.git/blob - src/MachOReaderArchive.hpp
ld64-47.2.tar.gz
[apple/ld64.git] / src / MachOReaderArchive.hpp
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 ) {
388 // only return these atoms once
389 fInstantiatedEntries.insert(member);
390 ObjectFile::Reader* r = makeObjectReaderForMember(member);
391 //fprintf(stderr, "%s found in %s(%s)\n", name, this->getPath(), member->getName());
392 return new std::vector<class ObjectFile::Atom*>(r->getAtoms());
393 }
394 }
395 //fprintf(stderr, "%s NOT found in archive %s\n", name, fPath);
396 return NULL;
397 }
398 }
399
400
401
402
403
404 }; // namespace archive
405 }; // namespace mach_o
406
407
408 #endif // __OBJECT_FILE_ARCHIVE_MACH_O__