1 /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
3 * Copyright (c) 2005-2008 Apple Inc. All rights reserved.
5 * @APPLE_LICENSE_HEADER_START@
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
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.
22 * @APPLE_LICENSE_HEADER_END@
25 #ifndef __OBJECT_FILE_ARCHIVE__
26 #define __OBJECT_FILE_ARCHIVE__
31 #include <sys/param.h>
32 #include <mach-o/ranlib.h>
38 #include <ext/hash_map>
40 #include "MachOFileAbstraction.hpp"
41 #include "ObjectFile.h"
42 #include "MachOReaderRelocatable.hpp"
44 #include "LTOReader.hpp"
49 typedef const struct ranlib* ConstRanLibPtr;
52 class Reader : public ObjectFile::Reader
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 LibraryOptions& archiveOptions,
59 const ObjectFile::ReaderOptions& options, uint32_t ordinalBase);
62 virtual const char* getPath() { return fPath; }
63 virtual time_t getModificationTime(){ return fModTime; }
64 virtual DebugInfoKind getDebugInfoKind() { return ObjectFile::Reader::kDebugInfoNone; }
65 virtual std::vector<class ObjectFile::Atom*>& getAtoms();
66 virtual std::vector<class ObjectFile::Atom*>* getJustInTimeAtomsFor(const char* name);
67 virtual std::vector<Stab>* getStabs() { return NULL; }
68 virtual bool optimize(const std::vector<ObjectFile::Atom*>&, std::vector<ObjectFile::Atom*>&,
69 std::vector<const char*>&, const std::set<ObjectFile::Atom*>&,
70 std::vector<ObjectFile::Atom*>& newDeadAtoms,
71 uint32_t, ObjectFile::Reader* writer,
72 ObjectFile::Atom* entryPointAtom,
73 const std::vector<const char*>& llvmOptions,
74 bool allGlobalsAReDeadStripRoots, int okind,
75 bool verbose, bool saveTemps, const char* outputFilePath,
76 bool pie, bool allowTextRelocs);
79 static bool validMachOFile(const uint8_t* fileContent, uint64_t fileLength);
80 static bool validLTOFile(const uint8_t* fileContent, uint64_t fileLength);
81 static cpu_type_t architecture();
87 const char* getName() const;
88 time_t getModTime() const;
89 const uint8_t* getContent() const;
90 uint32_t getContentSize() const;
91 const Entry* getNext() const;
93 bool hasLongName() const;
94 unsigned int getLongNameSpace() const;
101 bool operator()(const char* left, const char* right) const { return (strcmp(left, right) == 0); }
103 typedef __gnu_cxx::hash_map<const char*, const struct ranlib*, __gnu_cxx::hash<const char*>, CStringEquals> NameToEntryMap;
105 typedef typename A::P P;
106 typedef typename A::P::E E;
108 const struct ranlib* ranlibHashSearch(const char* name);
109 ObjectFile::Reader* makeObjectReaderForMember(const Entry* member);
110 void dumpTableOfContents();
111 void buildHashTable();
115 const ObjectFile::ReaderOptions& fOptions;
116 uint32_t fOrdinalBase;
117 const uint8_t* fFileContent;
118 uint64_t fFileLength;
119 const struct ranlib* fTableOfContents;
120 uint32_t fTableOfContentCount;
121 const char* fStringPool;
122 std::vector<class ObjectFile::Atom*> fAllAtoms;
123 std::vector<class ObjectFile::Reader*> fInstantiatedReaders;
124 std::set<const class Entry*> fInstantiatedEntries;
125 std::set<const class Entry*> fPossibleEntries;
126 NameToEntryMap fHashTable;
129 static std::vector<class ObjectFile::Atom*> fgEmptyList;
132 template <typename A>
133 std::vector<class ObjectFile::Atom*> Reader<A>::fgEmptyList;
136 template <typename A>
137 bool Reader<A>::Entry::hasLongName() const
139 return ( strncmp(this->ar_name, AR_EFMT1, strlen(AR_EFMT1)) == 0 );
142 template <typename A>
143 unsigned int Reader<A>::Entry::getLongNameSpace() const
146 long result = strtol(&this->ar_name[strlen(AR_EFMT1)], &endptr, 10);
150 template <typename A>
151 const char* Reader<A>::Entry::getName() const
153 if ( this->hasLongName() ) {
154 int len = this->getLongNameSpace();
155 static char longName[256];
156 strncpy(longName, ((char*)this)+sizeof(ar_hdr), len);
157 longName[len] = '\0';
161 static char shortName[20];
162 strncpy(shortName, this->ar_name, 16);
163 shortName[16] = '\0';
164 char* space = strchr(shortName, ' ');
171 template <typename A>
172 time_t Reader<A>::Entry::getModTime() const
175 strncpy(temp, this->ar_date, 12);
178 return (time_t)strtol(temp, &endptr, 10);
182 template <typename A>
183 const uint8_t* Reader<A>::Entry::getContent() const
185 if ( this->hasLongName() )
186 return ((uint8_t*)this) + sizeof(ar_hdr) + this->getLongNameSpace();
188 return ((uint8_t*)this) + sizeof(ar_hdr);
192 template <typename A>
193 uint32_t Reader<A>::Entry::getContentSize() const
196 strncpy(temp, this->ar_size, 10);
199 long size = strtol(temp, &endptr, 10);
200 // long name is included in ar_size
201 if ( this->hasLongName() )
202 size -= this->getLongNameSpace();
207 template <typename A>
208 const class Reader<A>::Entry* Reader<A>::Entry::getNext() const
210 const uint8_t* p = this->getContent() + getContentSize();
211 p = (const uint8_t*)(((uintptr_t)p+3) & (-4)); // 4-byte align
212 return (class Reader<A>::Entry*)p;
216 template <> cpu_type_t Reader<ppc>::architecture() { return CPU_TYPE_POWERPC; }
217 template <> cpu_type_t Reader<ppc64>::architecture() { return CPU_TYPE_POWERPC64; }
218 template <> cpu_type_t Reader<x86>::architecture() { return CPU_TYPE_I386; }
219 template <> cpu_type_t Reader<x86_64>::architecture() { return CPU_TYPE_X86_64; }
220 template <> cpu_type_t Reader<arm>::architecture() { return CPU_TYPE_ARM; }
223 template <typename A>
224 bool Reader<A>::validMachOFile(const uint8_t* fileContent, uint64_t fileLength)
226 return mach_o::relocatable::Reader<A>::validFile(fileContent);
229 template <typename A>
230 bool Reader<A>::validLTOFile(const uint8_t* fileContent, uint64_t fileLength)
233 return lto::Reader::validFile(fileContent, fileLength, architecture());
241 template <typename A>
242 bool Reader<A>::validFile(const uint8_t* fileContent, uint64_t fileLength)
244 // must have valid archive header
245 if ( strncmp((const char*)fileContent, "!<arch>\n", 8) != 0 )
248 // peak at first .o file and verify it is correct architecture
249 const Entry* const start = (Entry*)&fileContent[8];
250 const Entry* const end = (Entry*)&fileContent[fileLength];
251 for (const Entry* p=start; p < end; p = p->getNext()) {
252 const char* memberName = p->getName();
253 // skip option table-of-content member
254 if ( (p==start) && ((strcmp(memberName, SYMDEF_SORTED) == 0) || (strcmp(memberName, SYMDEF) == 0)) )
256 // archive is valid if first .o file is valid
257 return (validMachOFile(p->getContent(), p->getContentSize()) || validLTOFile(p->getContent(), p->getContentSize()));
263 template <typename A>
264 Reader<A>::Reader(const uint8_t fileContent[], uint64_t fileLength, const char* path, time_t modTime,
265 const LibraryOptions& archiveOptions,
266 const ObjectFile::ReaderOptions& options, uint32_t ordinalBase)
267 : fPath(NULL), fModTime(modTime), fOptions(options), fOrdinalBase(ordinalBase), fFileContent(NULL),
268 fTableOfContents(NULL), fTableOfContentCount(0), fStringPool(NULL), fForceLoad(archiveOptions.fForceLoad)
270 fPath = strdup(path);
271 fFileContent = fileContent;
272 fFileLength = fileLength;
274 if ( strncmp((const char*)fileContent, "!<arch>\n", 8) != 0 )
275 throw "not an archive";
277 // write out path for -whatsloaded option
278 if ( options.fLogAllFiles )
279 printf("%s\n", path);
281 if ( !options.fFullyLoadArchives && !fForceLoad ) {
282 const Entry* const firstMember = (Entry*)&fFileContent[8];
283 if ( (strcmp(firstMember->getName(), SYMDEF_SORTED) == 0) || (strcmp(firstMember->getName(), SYMDEF) == 0) ) {
284 const uint8_t* contents = firstMember->getContent();
285 uint32_t ranlibArrayLen = E::get32(*((uint32_t*)contents));
286 fTableOfContents = (const struct ranlib*)&contents[4];
287 fTableOfContentCount = ranlibArrayLen / sizeof(struct ranlib);
288 fStringPool = (const char*)&contents[ranlibArrayLen+8];
289 if ( ((uint8_t*)(&fTableOfContents[fTableOfContentCount]) > &fileContent[fileLength])
290 || ((uint8_t*)fStringPool > &fileContent[fileLength]) )
291 throw "malformed archive, perhaps wrong architecture";
292 this->buildHashTable();
295 throw "archive has no table of contents";
300 template <typename A>
301 ObjectFile::Reader* Reader<A>::makeObjectReaderForMember(const Entry* member)
303 const char* memberName = member->getName();
304 char memberPath[strlen(fPath) + strlen(memberName)+4];
305 strcpy(memberPath, fPath);
306 strcat(memberPath, "(");
307 strcat(memberPath, memberName);
308 strcat(memberPath, ")");
309 //fprintf(stderr, "using %s from %s\n", memberName, fPath);
311 // offset the ordinals in this mach-o .o file, so that atoms layout in same order as in archive
312 uint32_t ordinalBase = fOrdinalBase + (uint8_t*)member - fFileContent;
313 if ( validMachOFile(member->getContent(), member->getContentSize()) ) {
314 return new typename mach_o::relocatable::Reader<A>::Reader(member->getContent(), memberPath, member->getModTime(), fOptions, ordinalBase);
317 else if ( validLTOFile(member->getContent(), member->getContentSize()) ) {
318 return new typename lto::Reader(member->getContent(), member->getContentSize(), memberPath, member->getModTime(), fOptions, architecture());
321 throwf("archive member '%s' with length %d is not mach-o or bitcode", memberName, member->getContentSize());
323 catch (const char* msg) {
324 throwf("in %s, %s", memberPath, msg);
329 template <typename A>
330 std::vector<class ObjectFile::Atom*>& Reader<A>::getAtoms()
332 if ( fOptions.fFullyLoadArchives || fForceLoad ) {
333 // build vector of all atoms from all .o files in this archive
334 const Entry* const start = (Entry*)&fFileContent[8];
335 const Entry* const end = (Entry*)&fFileContent[fFileLength];
336 for (const Entry* p=start; p < end; p = p->getNext()) {
337 const char* memberName = p->getName();
338 if ( (p==start) && ((strcmp(memberName, SYMDEF_SORTED) == 0) || (strcmp(memberName, SYMDEF) == 0)) )
340 if ( fOptions.fWhyLoad ) {
342 printf("-force_load forced load of %s(%s)\n", this->getPath(), memberName);
344 printf("-all_load forced load of %s(%s)\n", this->getPath(), memberName);
346 ObjectFile::Reader* r = this->makeObjectReaderForMember(p);
347 std::vector<class ObjectFile::Atom*>& atoms = r->getAtoms();
348 fAllAtoms.insert(fAllAtoms.end(), atoms.begin(), atoms.end());
349 fInstantiatedReaders.push_back(r);
353 else if ( fOptions.fLoadAllObjcObjectsFromArchives ) {
354 // build vector of all atoms from all .o files containing objc classes in this archive
355 for(class NameToEntryMap::iterator it = fHashTable.begin(); it != fHashTable.end(); ++it) {
356 if ( (strncmp(it->first, ".objc_c", 7) == 0) || (strncmp(it->first, "_OBJC_CLASS_$_", 14) == 0) ) {
357 const Entry* member = (Entry*)&fFileContent[E::get32(it->second->ran_off)];
358 if ( fInstantiatedEntries.count(member) == 0 ) {
359 if ( fOptions.fWhyLoad )
360 printf("-ObjC forced load of %s(%s)\n", this->getPath(), member->getName());
361 // only return these atoms once
362 fInstantiatedEntries.insert(member);
363 ObjectFile::Reader* r = makeObjectReaderForMember(member);
364 std::vector<class ObjectFile::Atom*>& atoms = r->getAtoms();
365 fAllAtoms.insert(fAllAtoms.end(), atoms.begin(), atoms.end());
366 fInstantiatedReaders.push_back(r);
373 // return nonthing for now, getJustInTimeAtomsFor() will return atoms as needed
378 template <typename A>
379 bool Reader<A>::optimize(const std::vector<ObjectFile::Atom*>& allAtoms, std::vector<ObjectFile::Atom*>& newAtoms,
380 std::vector<const char*>& additionalUndefines, const std::set<ObjectFile::Atom*>& deadAtoms,
381 std::vector<ObjectFile::Atom*>& newDeadAtoms,
382 uint32_t nextOrdinal, ObjectFile::Reader* writer, ObjectFile::Atom* entryPointAtom,
383 const std::vector<const char*>& llvmOptions,
384 bool allGlobalsAReDeadStripRoots, int okind,
385 bool verbose, bool saveTemps, const char* outputFilePath,
386 bool pie, bool allowTextRelocs)
389 for(std::vector<ObjectFile::Reader*>::iterator it=fInstantiatedReaders.begin(); it != fInstantiatedReaders.end(); ++it) {
390 result |= (*it)->optimize(allAtoms, newAtoms, additionalUndefines, deadAtoms, newDeadAtoms, nextOrdinal,
391 writer, entryPointAtom, llvmOptions, allGlobalsAReDeadStripRoots, okind,
392 verbose, saveTemps, outputFilePath, pie, allowTextRelocs);
399 template <typename A>
400 ConstRanLibPtr Reader<A>::ranlibHashSearch(const char* name)
402 class NameToEntryMap::iterator pos = fHashTable.find(name);
403 if ( pos != fHashTable.end() )
409 template <typename A>
410 void Reader<A>::buildHashTable()
412 // walk through list backwards, adding/overwriting entries
413 // this assures that with duplicates those earliest in the list will be found
414 for (int i = fTableOfContentCount-1; i >= 0; --i) {
415 const struct ranlib* entry = &fTableOfContents[i];
416 const char* entryName = &fStringPool[E::get32(entry->ran_un.ran_strx)];
417 const Entry* member = (Entry*)&fFileContent[E::get32(entry->ran_off)];
418 //fprintf(stderr, "adding hash %d, %s -> %p\n", i, entryName, entry);
419 fHashTable[entryName] = entry;
420 fPossibleEntries.insert(member);
424 template <typename A>
425 void Reader<A>::dumpTableOfContents()
427 for (unsigned int i=0; i < fTableOfContentCount; ++i) {
428 const struct ranlib* e = &fTableOfContents[i];
429 printf("%s in %s\n", &fStringPool[E::get32(e->ran_un.ran_strx)], ((Entry*)&fFileContent[E::get32(e->ran_off)])->getName());
433 template <typename A>
434 std::vector<class ObjectFile::Atom*>* Reader<A>::getJustInTimeAtomsFor(const char* name)
436 if ( fOptions.fFullyLoadArchives || fForceLoad ) {
440 const struct ranlib* result = NULL;
441 // do a hash search of table of contents looking for requested symbol
442 result = ranlibHashSearch(name);
443 if ( result != NULL ) {
444 const Entry* member = (Entry*)&fFileContent[E::get32(result->ran_off)];
445 if ( fInstantiatedEntries.count(member) == 0 ) {
446 if ( fOptions.fWhyLoad )
447 printf("%s forced load of %s(%s)\n", name, this->getPath(), member->getName());
448 // only return these atoms once
449 fInstantiatedEntries.insert(member);
450 ObjectFile::Reader* r = makeObjectReaderForMember(member);
451 fInstantiatedReaders.push_back(r);
452 return new std::vector<class ObjectFile::Atom*>(r->getAtoms());
455 //fprintf(stderr, "%s NOT found in archive %s\n", name, fPath);
464 }; // namespace archive
467 #endif // __OBJECT_FILE_ARCHIVE__