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