]> git.saurik.com Git - apple/ld64.git/blob - src/ld/ArchiveReader.hpp
b8f1a5c5518c3cd79eb70511e389ae6aea1d3924
[apple/ld64.git] / src / ld / 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 LibraryOptions& archiveOptions,
59 const ObjectFile::ReaderOptions& options, uint32_t ordinalBase);
60 virtual ~Reader() {}
61
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);
77
78 private:
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();
82
83
84 class Entry : ar_hdr
85 {
86 public:
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;
92 private:
93 bool hasLongName() const;
94 unsigned int getLongNameSpace() const;
95
96 };
97
98 class CStringEquals
99 {
100 public:
101 bool operator()(const char* left, const char* right) const { return (strcmp(left, right) == 0); }
102 };
103 typedef __gnu_cxx::hash_map<const char*, const struct ranlib*, __gnu_cxx::hash<const char*>, CStringEquals> NameToEntryMap;
104
105 typedef typename A::P P;
106 typedef typename A::P::E E;
107
108 const struct ranlib* ranlibHashSearch(const char* name);
109 ObjectFile::Reader* makeObjectReaderForMember(const Entry* member);
110 void dumpTableOfContents();
111 void buildHashTable();
112
113 const char* fPath;
114 time_t fModTime;
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;
127 bool fForceLoad;
128
129 static std::vector<class ObjectFile::Atom*> fgEmptyList;
130 };
131
132 template <typename A>
133 std::vector<class ObjectFile::Atom*> Reader<A>::fgEmptyList;
134
135
136 template <typename A>
137 bool Reader<A>::Entry::hasLongName() const
138 {
139 return ( strncmp(this->ar_name, AR_EFMT1, strlen(AR_EFMT1)) == 0 );
140 }
141
142 template <typename A>
143 unsigned int Reader<A>::Entry::getLongNameSpace() const
144 {
145 char* endptr;
146 long result = strtol(&this->ar_name[strlen(AR_EFMT1)], &endptr, 10);
147 return result;
148 }
149
150 template <typename A>
151 const char* Reader<A>::Entry::getName() const
152 {
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';
158 return longName;
159 }
160 else {
161 static char shortName[20];
162 strncpy(shortName, this->ar_name, 16);
163 shortName[16] = '\0';
164 char* space = strchr(shortName, ' ');
165 if ( space != NULL )
166 *space = '\0';
167 return shortName;
168 }
169 }
170
171 template <typename A>
172 time_t Reader<A>::Entry::getModTime() const
173 {
174 char temp[14];
175 strncpy(temp, this->ar_date, 12);
176 temp[12] = '\0';
177 char* endptr;
178 return (time_t)strtol(temp, &endptr, 10);
179 }
180
181
182 template <typename A>
183 const uint8_t* Reader<A>::Entry::getContent() const
184 {
185 if ( this->hasLongName() )
186 return ((uint8_t*)this) + sizeof(ar_hdr) + this->getLongNameSpace();
187 else
188 return ((uint8_t*)this) + sizeof(ar_hdr);
189 }
190
191
192 template <typename A>
193 uint32_t Reader<A>::Entry::getContentSize() const
194 {
195 char temp[12];
196 strncpy(temp, this->ar_size, 10);
197 temp[10] = '\0';
198 char* endptr;
199 long size = strtol(temp, &endptr, 10);
200 // long name is included in ar_size
201 if ( this->hasLongName() )
202 size -= this->getLongNameSpace();
203 return size;
204 }
205
206
207 template <typename A>
208 const class Reader<A>::Entry* Reader<A>::Entry::getNext() const
209 {
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;
213 }
214
215
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; }
221
222
223 template <typename A>
224 bool Reader<A>::validMachOFile(const uint8_t* fileContent, uint64_t fileLength)
225 {
226 return mach_o::relocatable::Reader<A>::validFile(fileContent);
227 }
228
229 template <typename A>
230 bool Reader<A>::validLTOFile(const uint8_t* fileContent, uint64_t fileLength)
231 {
232 #if LTO_SUPPORT
233 return lto::Reader::validFile(fileContent, fileLength, architecture());
234 #else
235 return false;
236 #endif
237 }
238
239
240
241 template <typename A>
242 bool Reader<A>::validFile(const uint8_t* fileContent, uint64_t fileLength)
243 {
244 // must have valid archive header
245 if ( strncmp((const char*)fileContent, "!<arch>\n", 8) != 0 )
246 return false;
247
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)) )
255 continue;
256 // archive is valid if first .o file is valid
257 return (validMachOFile(p->getContent(), p->getContentSize()) || validLTOFile(p->getContent(), p->getContentSize()));
258 }
259 // empty archive
260 return true;
261 }
262
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)
269 {
270 fPath = strdup(path);
271 fFileContent = fileContent;
272 fFileLength = fileLength;
273
274 if ( strncmp((const char*)fileContent, "!<arch>\n", 8) != 0 )
275 throw "not an archive";
276
277 // write out path for -whatsloaded option
278 if ( options.fLogAllFiles )
279 printf("%s\n", path);
280
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();
293 }
294 else
295 throw "archive has no table of contents";
296 }
297 }
298
299
300 template <typename A>
301 ObjectFile::Reader* Reader<A>::makeObjectReaderForMember(const Entry* member)
302 {
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);
310 try {
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);
315 }
316 #if LTO_SUPPORT
317 else if ( validLTOFile(member->getContent(), member->getContentSize()) ) {
318 return new typename lto::Reader(member->getContent(), member->getContentSize(), memberPath, member->getModTime(), fOptions, architecture());
319 }
320 #endif
321 throwf("archive member '%s' with length %d is not mach-o or bitcode", memberName, member->getContentSize());
322 }
323 catch (const char* msg) {
324 throwf("in %s, %s", memberPath, msg);
325 }
326 }
327
328
329 template <typename A>
330 std::vector<class ObjectFile::Atom*>& Reader<A>::getAtoms()
331 {
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)) )
339 continue;
340 if ( fOptions.fWhyLoad ) {
341 if ( fForceLoad )
342 printf("-force_load forced load of %s(%s)\n", this->getPath(), memberName);
343 else
344 printf("-all_load forced load of %s(%s)\n", this->getPath(), memberName);
345 }
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);
350 }
351 return fAllAtoms;
352 }
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);
367 }
368 }
369 }
370 return fAllAtoms;
371 }
372 else {
373 // return nonthing for now, getJustInTimeAtomsFor() will return atoms as needed
374 return fgEmptyList;
375 }
376 }
377
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)
387 {
388 bool result = false;
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);
393 }
394 return result;
395 }
396
397
398
399 template <typename A>
400 ConstRanLibPtr Reader<A>::ranlibHashSearch(const char* name)
401 {
402 class NameToEntryMap::iterator pos = fHashTable.find(name);
403 if ( pos != fHashTable.end() )
404 return pos->second;
405 else
406 return NULL;
407 }
408
409 template <typename A>
410 void Reader<A>::buildHashTable()
411 {
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);
421 }
422 }
423
424 template <typename A>
425 void Reader<A>::dumpTableOfContents()
426 {
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());
430 }
431 }
432
433 template <typename A>
434 std::vector<class ObjectFile::Atom*>* Reader<A>::getJustInTimeAtomsFor(const char* name)
435 {
436 if ( fOptions.fFullyLoadArchives || fForceLoad ) {
437 return NULL;
438 }
439 else {
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());
453 }
454 }
455 //fprintf(stderr, "%s NOT found in archive %s\n", name, fPath);
456 return NULL;
457 }
458 }
459
460
461
462
463
464 }; // namespace archive
465
466
467 #endif // __OBJECT_FILE_ARCHIVE__