1 /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
3 * Copyright (c) 2006-2007 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 __LLVM_READER_H__
26 #define __LLVM_READER_H__
30 #include "MachOFileAbstraction.hpp"
31 #include "Architectures.hpp"
32 #include "ObjectFile.h"
33 #include "llvm/LinkTimeOptimizer.h"
35 #define LLVMLinkTimeOptimizer "LLVMlto.dylib"
40 // LLVMReference handles LLVMAtom references. These references facilitate
44 class LLVMReference : public ObjectFile::Reference
47 LLVMReference (const char *n) : fName(n), fAtom(0), fFromAtom(0) { }
49 bool isTargetUnbound() const { return fAtom == 0; }
50 bool isFromTargetUnbound() const { return true; }
51 uint8_t getKind() const { return 0; }
52 uint64_t getFixUpOffset() const { return 0; }
53 const char * getTargetName() const { return fName; }
54 ObjectFile::Atom& getTarget() const { return *fAtom; }
55 uint64_t getTargetOffset() const { return 0; }
56 bool hasFromTarget() const { return false; }
57 ObjectFile::Atom& getFromTarget() const { return *fFromAtom; }
58 const char * getFromTargetName() const { return NULL; }
59 uint64_t getFromTargetOffset() const { return 0; }
60 TargetBinding getTargetBinding() const;
61 TargetBinding getFromTargetBinding() const { return kDontBind; }
62 void setTarget (ObjectFile::Atom &a, uint64_t offset)
64 void setFromTarget(ObjectFile::Atom &a) { }
65 const char * getDescription() const { return NULL; }
69 ObjectFile::Atom * fAtom;
70 ObjectFile::Atom * fFromAtom;
73 ObjectFile::Reference::TargetBinding LLVMReference::getTargetBinding() const
75 if (strncmp (fName, "__ld64.llvm", 11) == 0)
77 else return kUnboundByName;
81 // LLVMAtom acts as a proxy Atom for the symbols that are exported by LLVM bytecode file. Initially,
82 // LLVMReader creates LLVMAtoms to allow linker proceed with usual symbol resolution phase. After
83 // optimization is performed, real Atoms are created for these symobls. However these real Atoms
84 // are not inserted into global symbol table. LLVMAtom holds real Atom and forwards appropriate
85 // methods to real atom.
88 class LLVMAtom : public ObjectFile::Atom
91 ObjectFile::Reader * getFile() const { return fOwner; }
92 bool getTranslationUnitSource (const char **dir, const char **name) const
93 { return fRealAtom->getTranslationUnitSource (dir, name); }
94 const char * getName () const { return fAtomName; }
95 const char * getDisplayName() const { return this->getName(); }
96 Scope getScope() const { return fScope; }
97 DefinitionKind getDefinitionKind() const;
98 SymbolTableInclusion getSymbolTableInclusion() const
99 { return fRealAtom->getSymbolTableInclusion(); }
100 bool dontDeadStrip() const { return false; }
101 bool isZeroFill() const { return fRealAtom->isZeroFill(); }
102 uint64_t getSize() const { return fRealAtom->getSize(); }
103 std::vector<ObjectFile::Reference*>& getReferences() const
104 { return (fRealAtom ? fRealAtom->getReferences() : (std::vector<ObjectFile::Reference*>&)fReferences); }
105 bool mustRemainInSection() const { return fRealAtom->mustRemainInSection(); }
106 const char * getSectionName() const { return (fRealAtom ? fRealAtom->getSectionName() : NULL); }
107 // Linker::optimize() sets section for this atom, not fRealAtom. Use this Atom's fSection.
108 class ObjectFile::Section * getSection() const { return fSection; }
109 ObjectFile::Segment& getSegment() const { return fRealAtom->getSegment(); }
110 uint32_t getOrdinal() const { return (fRealAtom ? fRealAtom->getOrdinal() : 0); }
111 ObjectFile::Atom& getFollowOnAtom() const { return fRealAtom->getFollowOnAtom(); }
112 std::vector<ObjectFile::LineInfo>* getLineInfo() const { return fRealAtom->getLineInfo(); }
113 ObjectFile::Alignment getAlignment() const;
114 void copyRawContent(uint8_t buffer[]) const
115 { fRealAtom->copyRawContent(buffer); }
116 void setScope(Scope s) { if (fRealAtom) fRealAtom->setScope(s); }
118 LLVMAtom(ObjectFile::Reader *owner, const char *n, llvm::LLVMSymbol *ls);
120 void setRealAtom (ObjectFile::Atom *atom)
121 { fRealAtom = atom; }
122 void addReference(ObjectFile::Reference *ref)
123 { fReferences.push_back(ref); }
125 void setSectionOffset(uint64_t offset) { fSectionOffset = offset; if (fRealAtom) fRealAtom->setSectionOffset(offset); }
126 void setSection(class ObjectFile::Section* sect) { fSection = sect; if (fRealAtom) fRealAtom->setSection(sect); }
129 ObjectFile::Reader * fOwner;
130 const char * fAtomName;
131 llvm::LLVMSymbol * fLLVMSymbol;
132 ObjectFile::Atom * fRealAtom;
133 std::vector<ObjectFile::Reference*> fReferences;
134 ObjectFile::Atom::Scope fScope;
135 ObjectFile::Atom::DefinitionKind fDefKind;
138 ObjectFile::Atom::DefinitionKind LLVMAtom::getDefinitionKind() const
141 return fRealAtom->getDefinitionKind();
146 LLVMAtom::LLVMAtom(ObjectFile::Reader *owner, const char *n, llvm::LLVMSymbol *ls) : fOwner(owner), fAtomName(n), fLLVMSymbol(ls), fRealAtom(0)
151 switch (ls->getLinkage()) {
152 case llvm::LTOExternalLinkage:
153 fScope = scopeGlobal;
154 fDefKind = kRegularDefinition;
156 case llvm::LTOLinkOnceLinkage:
157 case llvm::LTOWeakLinkage:
158 // ??? How to differentiate between this two linkage types ?
159 fScope = scopeGlobal;
160 fDefKind = kWeakDefinition;
163 throw "Unexpected LLVM Symbol Linkage info\n";
168 ObjectFile::Alignment LLVMAtom::getAlignment() const
171 return fRealAtom->getAlignment();
173 ObjectFile::Alignment alignment(fLLVMSymbol->getAlignment());
179 // LLVMReader does not expose internal symbols defined and used inside bytecode file. However,
180 // these symbols may refere other external symbols. IntercessorAtom facilitate by acting as a
181 // orignator of such references during pre-optimization symbol resoultion phase. These atoms
182 // are immediately removed after optimization.
185 class IntercessorAtom : public ObjectFile::Atom
188 ObjectFile::Reader * getFile() const { return fOwner; }
189 bool getTranslationUnitSource (const char **dir, const char **name) const
191 const char * getName () const { return fAtomName; }
192 const char * getDisplayName() const { return this->getName(); }
193 Scope getScope() const { return scopeGlobal; }
194 DefinitionKind getDefinitionKind() const { return kRegularDefinition; }
195 SymbolTableInclusion getSymbolTableInclusion() const
196 { return kSymbolTableNotIn; }
197 bool dontDeadStrip() const { return false; }
198 bool isZeroFill() const { return false; }
199 uint64_t getSize() const { return 0; }
200 std::vector<ObjectFile::Reference*>& getReferences() const { return (std::vector<ObjectFile::Reference*>&)fReferences; }
201 bool mustRemainInSection() const { return false; }
202 const char * getSectionName() const { return NULL; }
203 class ObjectFile::Section * getSection() const { return NULL; }
204 ObjectFile::Segment& getSegment() const { return this->getSegment(); }
205 uint32_t getOrdinal() const { return 0; }
206 ObjectFile::Atom& getFollowOnAtom() const { return this->getFollowOnAtom(); }
207 std::vector<ObjectFile::LineInfo>* getLineInfo() const { return NULL; }
208 ObjectFile::Alignment getAlignment() const { ObjectFile::Alignment a(0); return a; }
209 void copyRawContent(uint8_t buffer[]) const
211 void setScope(Scope s) { }
214 IntercessorAtom(ObjectFile::Reader *owner, std::set<std::string> &references);
216 void addReference(ObjectFile::Reference *ref)
217 { fReferences.push_back(ref); }
218 void addReferences(std::set<std::string> &references);
220 ObjectFile::Reader * fOwner;
222 std::vector<ObjectFile::Reference*> fReferences;
223 ObjectFile::Atom::Scope fScope;
224 ObjectFile::Atom::DefinitionKind fDefKind;
227 IntercessorAtom::IntercessorAtom(ObjectFile::Reader *owner, std::set<std::string> &references)
229 static int sCount = 0;
231 fAtomName = (char *) malloc (sizeof(char)*20);
232 sprintf (fAtomName,"__ld64.llvm%d__",sCount++);
234 for (std::set<std::string>::iterator it = references.begin(); it != references.end(); it++) {
236 this->addReference(new LLVMReference(r.c_str()));
240 void IntercessorAtom::addReferences(std::set<std::string> &references)
242 for (std::set<std::string>::iterator it = references.begin(); it != references.end(); it++) {
244 this->addReference(new LLVMReference(r.c_str()));
248 class InIntercessorSet
251 InIntercessorSet(std::set<ObjectFile::Atom*>& iAtoms) : fIntercessorAtoms(iAtoms) {}
253 bool operator()(ObjectFile::Atom*& atom) const {
254 return ( fIntercessorAtoms.count(atom) != 0 );
258 std::set<ObjectFile::Atom*>& fIntercessorAtoms;
262 // LLVMOptimizer class is responsible for communicating with LLVM LTO library.
263 // One LLVMOptimizer object is created per Linker invocation. All LLVMReaders share this
264 // one single optimizer object.
270 LLVMOptimizer(Options &opt);
271 ~LLVMOptimizer() { if (fLLVMHandle) dlclose(fLLVMHandle); }
274 void optimize(std::vector<ObjectFile::Atom *>&, std::vector<ObjectFile::Atom*>&, uint32_t);
275 void read(ObjectFile::Reader *, const char *, std::set<std::string>&, std::vector<ObjectFile::Atom*>&, const char *);
276 void reconcileOptimizedAtoms(std::vector<class ObjectFile::Atom*>&, std::vector<class ObjectFile::Atom*>&);
277 void addIntercessor(IntercessorAtom * atom) { fIntercessorAtoms.insert(atom); }
278 void addReader(ObjectFile::Reader *reader) { fLLVMReaders[reader->getPath()] = reader; }
279 cpu_type_t getCpuType(std::string &targetTriple);
280 bool validArchitecture(const char *path, cpu_type_t architecture);
284 bool operator()(const char* left, const char* right) const { return (strcmp(left, right) == 0); }
286 typedef hash_map<const char*, LLVMAtom*, hash<const char*>, LCStringEquals> LLVMAtomToNameMapper;
287 typedef hash_map<const char*, ObjectFile::Reader*, hash<const char*>, LCStringEquals> ReaderToPathMapper;
289 typedef llvm::LinkTimeOptimizer * (*createLLVMOptimizer_func_t) ();
292 llvm::LinkTimeOptimizer *fOptimizer;
294 LLVMAtomToNameMapper fLLVMSymbols;
296 std::set<ObjectFile::Atom*> fIntercessorAtoms;
297 ReaderToPathMapper fLLVMReaders;
300 LLVMOptimizer::LLVMOptimizer(Options &opts) : fOptions(opts)
302 fLLVMHandle = (llvm::LinkTimeOptimizer *) dlopen (LLVMLinkTimeOptimizer, RTLD_LAZY);
304 throwf("Unable to load LLVM library: \n", dlerror());
306 createLLVMOptimizer_func_t createLLVMOptimizer_fp = (createLLVMOptimizer_func_t)dlsym(fLLVMHandle, "createLLVMOptimizer");
307 if (createLLVMOptimizer_fp == NULL)
308 throwf("couldn't find \"createLLVMOptimizer\" ", dlerror());
309 fOptimizer = createLLVMOptimizer_fp();
313 cpu_type_t LLVMOptimizer::getCpuType(std::string &targetTriple)
315 if ( strncmp (targetTriple.c_str(), "powerpc-", 8) == 0)
316 return CPU_TYPE_POWERPC;
317 else if ( strncmp (targetTriple.c_str(), "powerpc64-", 10))
318 return CPU_TYPE_POWERPC64;
319 // match "i[3-9]86-*".
320 else if ( targetTriple.size() >= 5 && targetTriple[0] == 'i' && targetTriple[2] == '8' && targetTriple[3] == '6' && targetTriple[4] == '-' && targetTriple[1] - '3' < 6 )
321 return CPU_TYPE_I386;
326 bool LLVMOptimizer::validArchitecture(const char *path, cpu_type_t architecture)
328 std::string targetTriple;
329 fOptimizer->getTargetTriple(path, targetTriple);
330 if (architecture != getCpuType(targetTriple)) {
331 fOptimizer->removeModule(path);
338 void LLVMOptimizer::optimize(std::vector<ObjectFile::Atom*> &allAtoms, std::vector<ObjectFile::Atom*> &newAtoms, uint32_t nextInputOrdinal)
343 char * tmp = "/tmp/ld64XXXXXXXX";
344 char * bigOfile = (char *) malloc (strlen (tmp) + 3);
346 throw "Unable to create temp file name";
347 strcpy (bigOfile, tmp);
349 strcat (bigOfile, ".o");
351 std::vector <const char *> exportList;
352 for (std::vector<ObjectFile::Atom*>::iterator it = allAtoms.begin(); it != allAtoms.end(); ++it) {
353 ObjectFile::Atom *atom = *it;
354 if (atom->getName()) {
355 ReaderToPathMapper::iterator pos = fLLVMReaders.find(atom->getFile()->getPath());
356 if (pos != fLLVMReaders.end())
357 exportList.push_back(atom->getName());
362 std::string targetTriple;
363 llvm::LTOStatus status = fOptimizer->optimizeModules(bigOfile, exportList, targetTriple, fOptions.saveTempFiles(), fOptions.getOutputFilePath());
364 if (status != llvm::LTO_OPT_SUCCESS) {
365 if (status == llvm::LTO_WRITE_FAILURE)
366 throw "Unable to write optimized output file";
367 if (status == llvm::LTO_ASM_FAILURE)
368 throw "Unable to assemble optimized output file";
369 if (status == llvm::LTO_MODULE_MERGE_FAILURE)
370 throw "Unable to merge bytecode files";
371 if (status == llvm::LTO_NO_TARGET)
372 throw "Unable to load target optimizer";
376 Options::FileInfo info = fOptions.findFile (bigOfile);
377 ObjectFile::Reader* nr = NULL;
378 int fd = ::open(info.path, O_RDONLY, 0);
380 throwf("can't open file, errno=%d", errno);
381 if ( info.fileLen < 20 )
382 throw "file too small";
384 uint8_t* p = (uint8_t*)::mmap(NULL, info.fileLen, PROT_READ, MAP_FILE | MAP_PRIVATE, fd, 0);
385 if ( p == (uint8_t*)(-1) )
386 throwf("can't map file, errno=%d", errno);
388 cpu_type_t cpt = getCpuType(targetTriple);
390 case CPU_TYPE_POWERPC:
391 if ( mach_o::relocatable::Reader<ppc>::validFile(p) )
392 nr = new mach_o::relocatable::Reader<ppc>(p, info.path, info.modTime, fOptions.readerOptions(), nextInputOrdinal);
394 case CPU_TYPE_POWERPC64:
395 if ( mach_o::relocatable::Reader<ppc64>::validFile(p) )
396 nr = new mach_o::relocatable::Reader<ppc64>(p, info.path, info.modTime, fOptions.readerOptions(), nextInputOrdinal);
399 if ( mach_o::relocatable::Reader<x86>::validFile(p) )
400 nr = new mach_o::relocatable::Reader<x86>(p, info.path, info.modTime, fOptions.readerOptions(), nextInputOrdinal);
403 throw "file is not of required architecture";
407 std::vector<class ObjectFile::Atom*> optimizedAtoms;
408 optimizedAtoms = nr->getAtoms();
409 reconcileOptimizedAtoms(optimizedAtoms, newAtoms);
411 allAtoms.erase(std::remove_if(allAtoms.begin(), allAtoms.end(), InIntercessorSet(fIntercessorAtoms)), allAtoms.end());
416 void LLVMOptimizer::read(ObjectFile::Reader *reader, const char *path, std::set<std::string> &references, std::vector<ObjectFile::Atom*> &atoms, const char *intercessorName)
418 llvm::LinkTimeOptimizer::NameToSymbolMap symbols;
419 llvm::LTOStatus status = fOptimizer->readLLVMObjectFile (path, symbols, references);
420 if (status != llvm::LTO_READ_SUCCESS)
421 throw "Unable to read LLVM bytecode file";
423 for (llvm::LinkTimeOptimizer::NameToSymbolMap::iterator itr = symbols.begin();
424 itr != symbols.end(); itr++) {
425 const char *name = itr->first;
426 llvm::LLVMSymbol *ls = itr->second;
427 LLVMAtom *a = new LLVMAtom(reader, name, ls);
429 LLVMAtomToNameMapper::iterator pos = fLLVMSymbols.find(name);
430 bool insertNewAtom = true;
431 if (pos != fLLVMSymbols.end()) {
432 LLVMAtom *existingAtom = pos->second;
433 ObjectFile::Atom::DefinitionKind newDefKind = a->getDefinitionKind();
434 ObjectFile::Atom::DefinitionKind existingDefKind = existingAtom->getDefinitionKind();
435 if (newDefKind == ObjectFile::Atom::kRegularDefinition
436 && existingDefKind == ObjectFile::Atom::kRegularDefinition)
437 throwf ("duplicate symbol %s in %s and %s\n", name, a->getFile()->getPath(), existingAtom->getFile()->getPath());
438 else if (newDefKind == ObjectFile::Atom::kWeakDefinition
439 && existingDefKind == ObjectFile::Atom::kRegularDefinition)
440 insertNewAtom = false;
441 else if (newDefKind == ObjectFile::Atom::kWeakDefinition
442 && existingDefKind == ObjectFile::Atom::kWeakDefinition)
444 insertNewAtom = false;
445 else if (newDefKind == ObjectFile::Atom::kRegularDefinition
446 && existingDefKind == ObjectFile::Atom::kWeakDefinition)
447 insertNewAtom = true;
451 fLLVMSymbols[name] = a;
452 a->addReference(new LLVMReference (intercessorName));
457 void LLVMOptimizer::reconcileOptimizedAtoms(std::vector<class ObjectFile::Atom*>& optimizedAtoms,
458 std::vector<class ObjectFile::Atom*>& newAtoms)
460 for (std::vector<ObjectFile::Atom *>::iterator itr = optimizedAtoms.begin();
461 itr != optimizedAtoms.end(); ++itr) {
463 ObjectFile::Atom* atom = *itr;
464 if (!atom->getName()) {
465 newAtoms.push_back(atom);
469 LLVMAtomToNameMapper::iterator pos = fLLVMSymbols.find(atom->getName());
470 if ( pos != fLLVMSymbols.end() ) {
472 LLVMAtom *la = fLLVMSymbols[atom->getName()];
473 la->setRealAtom(atom);
477 newAtoms.push_back(atom);
482 // LLVM bytecode file reader
485 class LLVMReader : public ObjectFile::Reader
488 static bool validFile(const uint8_t* fileContent, const char *path, cpu_type_t architecture, Options &opts);
489 static LLVMReader* make(const uint8_t* fileContent, const char* path, time_t modTime, Options& options)
490 { return new LLVMReader(fileContent, path, modTime, options); }
491 virtual ~LLVMReader();
492 virtual std::vector<class ObjectFile::Atom*>& getAtoms() { return (std::vector<class ObjectFile::Atom*>&)(fAtoms); }
493 virtual std::vector<class ObjectFile::Atom*>* getJustInTimeAtomsFor(const char* name) { return NULL; }
494 virtual const char* getPath() { return fPath; }
495 virtual time_t getModificationTime() { return fModTime; }
496 virtual ObjectFile::Reader::DebugInfoKind getDebugInfoKind() { return kDebugInfoNone; }
497 virtual std::vector<Stab>* getStabs() { return NULL; }
499 ObjectFile::Atom * retriveIntercessorAtom() { fAtoms.pop_back();return fIntercessorAtom; }
500 ObjectFile::Atom * getIntercessorAtom() { return fIntercessorAtom; }
504 LLVMReader(const uint8_t* fileContent, const char* path, time_t modTime, Options& options);
505 void optimize(std::vector<ObjectFile::Atom*>& allAtoms, std::vector<ObjectFile::Atom*> &newAtoms, uint32_t);
509 std::vector<ObjectFile::Atom*> fAtoms;
510 IntercessorAtom * fIntercessorAtom;
511 static LLVMOptimizer *fOptimizer;
512 std::set<std::string> fLLVMReferences;
515 LLVMOptimizer *LLVMReader::fOptimizer = NULL;
517 LLVMReader::~LLVMReader()
523 LLVMReader::LLVMReader (const uint8_t* fileContent, const char *path, time_t modTime, Options& options)
528 fIntercessorAtom = new IntercessorAtom(this, fLLVMReferences);
529 fOptimizer->read(this, path, fLLVMReferences, fAtoms, fIntercessorAtom->getName());
530 fIntercessorAtom->addReferences(fLLVMReferences);
531 fAtoms.push_back(fIntercessorAtom);
532 fOptimizer->addIntercessor(fIntercessorAtom);
533 fOptimizer->addReader(this);
536 bool LLVMReader::validFile(const uint8_t* fileContent, const char *path, cpu_type_t architecture, Options &opts)
538 if (fileContent[0] == 'l'
539 && fileContent[1] == 'l'
540 && fileContent[2] == 'v'
541 && (fileContent[3] == 'c' || fileContent[3] == 'm')) {
545 fOptimizer = new LLVMOptimizer(opts);
547 if (fOptimizer->validArchitecture(path, architecture))
554 void LLVMReader::optimize(std::vector<ObjectFile::Atom *> &allAtoms, std::vector<ObjectFile::Atom*> &newAtoms, uint32_t nextInputOrdinal)
557 fOptimizer->optimize(allAtoms, newAtoms, nextInputOrdinal);