]> git.saurik.com Git - apple/ld64.git/blob - src/MachOReaderArchive.hpp
ld64-77.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 Reader(const uint8_t fileContent[], uint64_t fileLength,
56 const char* path, time_t modTime,
57 const ObjectFile::ReaderOptions& options, uint32_t ordinalBase);
58 virtual ~Reader() {}
59
60 virtual const char* getPath() { return fPath; }
61 virtual time_t getModificationTime(){ return fModTime; }
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 const struct ranlib* ranlibHashSearch(const char* name);
93 ObjectFile::Reader* makeObjectReaderForMember(const Entry* member);
94 void dumpTableOfContents();
95 void buildHashTable();
96
97 const char* fPath;
98 time_t fModTime;
99 const ObjectFile::ReaderOptions& fOptions;
100 uint32_t fOrdinalBase;
101 const uint8_t* fFileContent;
102 uint64_t fFileLength;
103 const struct ranlib* fTableOfContents;
104 uint32_t fTableOfContentCount;
105 const char* fStringPool;
106 std::vector<class ObjectFile::Atom*> fAllAtoms;
107 std::set<const class Entry*> fInstantiatedEntries;
108 std::set<const class Entry*> fPossibleEntries;
109 NameToEntryMap fHashTable;
110
111 static std::vector<class ObjectFile::Atom*> fgEmptyList;
112 };
113
114 template <typename A>
115 std::vector<class ObjectFile::Atom*> Reader<A>::fgEmptyList;
116
117
118 template <typename A>
119 bool Reader<A>::Entry::hasLongName() const
120 {
121 return ( strncmp(this->ar_name, AR_EFMT1, strlen(AR_EFMT1)) == 0 );
122 }
123
124 template <typename A>
125 unsigned int Reader<A>::Entry::getLongNameSpace() const
126 {
127 char* endptr;
128 long result = strtol(&this->ar_name[strlen(AR_EFMT1)], &endptr, 10);
129 return result;
130 }
131
132 template <typename A>
133 const char* Reader<A>::Entry::getName() const
134 {
135 if ( this->hasLongName() ) {
136 int len = this->getLongNameSpace();
137 static char longName[256];
138 strncpy(longName, ((char*)this)+sizeof(ar_hdr), len);
139 longName[len] = '\0';
140 return longName;
141 }
142 else {
143 static char shortName[20];
144 strncpy(shortName, this->ar_name, 16);
145 shortName[16] = '\0';
146 char* space = strchr(shortName, ' ');
147 if ( space != NULL )
148 *space = '\0';
149 return shortName;
150 }
151 }
152
153 template <typename A>
154 time_t Reader<A>::Entry::getModTime() const
155 {
156 char temp[14];
157 strncpy(temp, this->ar_date, 12);
158 temp[12] = '\0';
159 char* endptr;
160 return (time_t)strtol(temp, &endptr, 10);
161 }
162
163
164 template <typename A>
165 const uint8_t* Reader<A>::Entry::getContent() const
166 {
167 if ( this->hasLongName() )
168 return ((uint8_t*)this) + sizeof(ar_hdr) + this->getLongNameSpace();
169 else
170 return ((uint8_t*)this) + sizeof(ar_hdr);
171 }
172
173
174 template <typename A>
175 uint32_t Reader<A>::Entry::getContentSize() const
176 {
177 char temp[12];
178 strncpy(temp, this->ar_size, 10);
179 temp[10] = '\0';
180 char* endptr;
181 long size = strtol(temp, &endptr, 10);
182 // long name is included in ar_size
183 if ( this->hasLongName() )
184 size -= this->getLongNameSpace();
185 return size;
186 }
187
188
189 template <typename A>
190 const class Reader<A>::Entry* Reader<A>::Entry::getNext() const
191 {
192 const uint8_t* p = this->getContent() + getContentSize();
193 p = (const uint8_t*)(((uintptr_t)p+3) & (-4)); // 4-byte align
194 return (class Reader<A>::Entry*)p;
195 }
196
197 template <typename A>
198 bool Reader<A>::validFile(const uint8_t* fileContent, uint64_t fileLength)
199 {
200 // must have valid archive header
201 if ( strncmp((const char*)fileContent, "!<arch>\n", 8) != 0 )
202 return false;
203
204 // peak at first .o file and verify it is correct architecture
205 const Entry* const start = (Entry*)&fileContent[8];
206 const Entry* const end = (Entry*)&fileContent[fileLength];
207 for (const Entry* p=start; p < end; p = p->getNext()) {
208 const char* memberName = p->getName();
209 // skip option table-of-content member
210 if ( (p==start) && ((strcmp(memberName, SYMDEF_SORTED) == 0) || (strcmp(memberName, SYMDEF) == 0)) )
211 continue;
212 // archive is valid if first .o file is valid
213 return mach_o::relocatable::Reader<A>::validFile(p->getContent());
214 }
215 // empty archive
216 return true;
217 }
218
219 template <typename A>
220 Reader<A>::Reader(const uint8_t fileContent[], uint64_t fileLength, const char* path, time_t modTime,
221 const ObjectFile::ReaderOptions& options, uint32_t ordinalBase)
222 : fPath(NULL), fModTime(modTime), fOptions(options), fOrdinalBase(ordinalBase), fFileContent(NULL),
223 fTableOfContents(NULL), fTableOfContentCount(0), fStringPool(NULL)
224 {
225 fPath = strdup(path);
226 fFileContent = fileContent;
227 fFileLength = fileLength;
228
229 if ( strncmp((const char*)fileContent, "!<arch>\n", 8) != 0 )
230 throw "not an archive";
231
232 // write out path for -whatsloaded option
233 if ( options.fLogAllFiles )
234 printf("%s\n", path);
235
236 if ( !options.fFullyLoadArchives ) {
237 const Entry* const firstMember = (Entry*)&fFileContent[8];
238 if ( (strcmp(firstMember->getName(), SYMDEF_SORTED) == 0) || (strcmp(firstMember->getName(), SYMDEF) == 0) ) {
239 const uint8_t* contents = firstMember->getContent();
240 uint32_t ranlibArrayLen = E::get32(*((uint32_t*)contents));
241 fTableOfContents = (const struct ranlib*)&contents[4];
242 fTableOfContentCount = ranlibArrayLen / sizeof(struct ranlib);
243 fStringPool = (const char*)&contents[ranlibArrayLen+8];
244 if ( ((uint8_t*)(&fTableOfContents[fTableOfContentCount]) > &fileContent[fileLength])
245 || ((uint8_t*)fStringPool > &fileContent[fileLength]) )
246 throw "malformed archive, perhaps wrong architecture";
247 this->buildHashTable();
248 }
249 else
250 throw "archive has no table of contents";
251 }
252 }
253
254
255 template <typename A>
256 ObjectFile::Reader* Reader<A>::makeObjectReaderForMember(const Entry* member)
257 {
258 const char* memberName = member->getName();
259 char memberPath[strlen(fPath) + strlen(memberName)+4];
260 strcpy(memberPath, fPath);
261 strcat(memberPath, "(");
262 strcat(memberPath, memberName);
263 strcat(memberPath, ")");
264 //fprintf(stderr, "using %s from %s\n", memberName, fPath);
265 try {
266 // offset the ordinals in this mach-o .o file, so that atoms layout in same order as in archive
267 uint32_t ordinalBase = fOrdinalBase + (uint8_t*)member - fFileContent;
268 return new typename mach_o::relocatable::Reader<A>::Reader(member->getContent(), memberPath, member->getModTime(), fOptions, ordinalBase);
269 }
270 catch (const char* msg) {
271 throwf("in %s, %s", memberPath, msg);
272 }
273 }
274
275
276 template <typename A>
277 std::vector<class ObjectFile::Atom*>& Reader<A>::getAtoms()
278 {
279 if ( fOptions.fFullyLoadArchives ) {
280 // build vector of all atoms from all .o files in this archive
281 const Entry* const start = (Entry*)&fFileContent[8];
282 const Entry* const end = (Entry*)&fFileContent[fFileLength];
283 for (const Entry* p=start; p < end; p = p->getNext()) {
284 const char* memberName = p->getName();
285 if ( (p==start) && ((strcmp(memberName, SYMDEF_SORTED) == 0) || (strcmp(memberName, SYMDEF) == 0)) )
286 continue;
287 if ( fOptions.fWhyLoad )
288 printf("-all_load forced load of %s(%s)\n", this->getPath(), memberName);
289 ObjectFile::Reader* r = this->makeObjectReaderForMember(p);
290 std::vector<class ObjectFile::Atom*>& atoms = r->getAtoms();
291 fAllAtoms.insert(fAllAtoms.end(), atoms.begin(), atoms.end());
292 }
293 return fAllAtoms;
294 }
295 else if ( fOptions.fLoadAllObjcObjectsFromArchives ) {
296 // build vector of all atoms from all .o files containing objc classes in this archive
297 for(class NameToEntryMap::iterator it = fHashTable.begin(); it != fHashTable.end(); ++it) {
298 if ( (strncmp(it->first, ".objc_c", 7) == 0) || (strncmp(it->first, "_OBJC_CLASS_$_", 14) == 0) ) {
299 const Entry* member = (Entry*)&fFileContent[E::get32(it->second->ran_off)];
300 if ( fInstantiatedEntries.count(member) == 0 ) {
301 if ( fOptions.fWhyLoad )
302 printf("-ObjC forced load of %s(%s)\n", this->getPath(), member->getName());
303 // only return these atoms once
304 fInstantiatedEntries.insert(member);
305 ObjectFile::Reader* r = makeObjectReaderForMember(member);
306 std::vector<class ObjectFile::Atom*>& atoms = r->getAtoms();
307 fAllAtoms.insert(fAllAtoms.end(), atoms.begin(), atoms.end());
308 }
309 }
310 }
311 return fAllAtoms;
312 }
313 else {
314 // return nonthing for now, getJustInTimeAtomsFor() will return atoms as needed
315 return fgEmptyList;
316 }
317 }
318
319
320 template <typename A>
321 ConstRanLibPtr Reader<A>::ranlibHashSearch(const char* name)
322 {
323 class NameToEntryMap::iterator pos = fHashTable.find(name);
324 if ( pos != fHashTable.end() )
325 return pos->second;
326 else
327 return NULL;
328 }
329
330 template <typename A>
331 void Reader<A>::buildHashTable()
332 {
333 // walk through list backwards, adding/overwriting entries
334 // this assures that with duplicates those earliest in the list will be found
335 for (int i = fTableOfContentCount-1; i >= 0; --i) {
336 const struct ranlib* entry = &fTableOfContents[i];
337 const char* entryName = &fStringPool[E::get32(entry->ran_un.ran_strx)];
338 const Entry* member = (Entry*)&fFileContent[E::get32(entry->ran_off)];
339 //fprintf(stderr, "adding hash %d, %s -> %p\n", i, entryName, entry);
340 fHashTable[entryName] = entry;
341 fPossibleEntries.insert(member);
342 }
343 }
344
345 template <typename A>
346 void Reader<A>::dumpTableOfContents()
347 {
348 for (unsigned int i=0; i < fTableOfContentCount; ++i) {
349 const struct ranlib* e = &fTableOfContents[i];
350 printf("%s in %s\n", &fStringPool[E::get32(e->ran_un.ran_strx)], ((Entry*)&fFileContent[E::get32(e->ran_off)])->getName());
351 }
352 }
353
354 template <typename A>
355 std::vector<class ObjectFile::Atom*>* Reader<A>::getJustInTimeAtomsFor(const char* name)
356 {
357 if ( fOptions.fFullyLoadArchives ) {
358 return NULL;
359 }
360 else {
361 const struct ranlib* result = NULL;
362 // do a hash search of table of contents looking for requested symbol
363 result = ranlibHashSearch(name);
364 if ( result != NULL ) {
365 const Entry* member = (Entry*)&fFileContent[E::get32(result->ran_off)];
366 if ( fInstantiatedEntries.count(member) == 0 ) {
367 if ( fOptions.fWhyLoad )
368 printf("%s forced load of %s(%s)\n", name, this->getPath(), member->getName());
369 // only return these atoms once
370 fInstantiatedEntries.insert(member);
371 ObjectFile::Reader* r = makeObjectReaderForMember(member);
372 return new std::vector<class ObjectFile::Atom*>(r->getAtoms());
373 }
374 }
375 //fprintf(stderr, "%s NOT found in archive %s\n", name, fPath);
376 return NULL;
377 }
378 }
379
380
381
382
383
384 }; // namespace archive
385 }; // namespace mach_o
386
387
388 #endif // __OBJECT_FILE_ARCHIVE_MACH_O__