LTOReader.hpp   [plain text]


/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
 *
 * Copyright (c) 2006-2008 Apple Inc. All rights reserved.
 *
 * @APPLE_LICENSE_HEADER_START@
 *
 * This file contains Original Code and/or Modifications of Original Code
 * as defined in and that are subject to the Apple Public Source License
 * Version 2.0 (the 'License'). You may not use this file except in
 * compliance with the License. Please obtain a copy of the License at
 * http://www.opensource.apple.com/apsl/ and read it before using this
 * file.
 *
 * The Original Code and all software distributed under the License are
 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
 * Please see the License for the specific language governing rights and
 * limitations under the License.
 *
 * @APPLE_LICENSE_HEADER_END@
 */

#ifndef __LTO_READER_H__
#define __LTO_READER_H__

#include <stdlib.h>
#include <mach-o/dyld.h>
#include <vector>
#include <ext/hash_set>
#include <ext/hash_map>

#include "MachOFileAbstraction.hpp"
#include "Architectures.hpp"
#include "ObjectFile.h"
#include "Options.h"

#include "llvm-c/lto.h"


namespace lto {


//
// Reference handles Atom references. These references facilitate 
// symbol resolution.
//

class Reference : public ObjectFile::Reference
{
public:
										Reference(const char* name) : fTargetName(name), fTargetAtom(NULL) { }
										Reference(ObjectFile::Atom& atom) : fTargetName(NULL), fTargetAtom(&atom) { }

	bool 								isTargetUnbound() const 			{ return fTargetAtom == NULL; }
	bool 								isFromTargetUnbound() const 		{ return true; }
	uint8_t 							getKind() const 					{ return 0; }
	uint64_t 							getFixUpOffset() const 				{ return 0; }
	const char * 						getTargetName() const 				{ return fTargetName; }
	ObjectFile::Atom& 					getTarget() const 					{ return *fTargetAtom; }
	uint64_t 							getTargetOffset() const 			{ return 0; }
	bool 								hasFromTarget() const 				{ return false; }
	ObjectFile::Atom& 					getFromTarget() const 				{ return *((ObjectFile::Atom*)NULL); }
	const char *						getFromTargetName() const 			{ return NULL; }
	uint64_t 							getFromTargetOffset() const 		{ return 0; }
	TargetBinding						getTargetBinding() const;
	TargetBinding						getFromTargetBinding() const  { return kDontBind; }
	void 								setTarget (ObjectFile::Atom& a, uint64_t offset) 
										{ fTargetAtom = &a; }
	void 								setFromTarget(ObjectFile::Atom &a) 	{ }
	const char *						getDescription() const;

private:
	const char *						fTargetName;
	ObjectFile::Atom *					fTargetAtom;
};


ObjectFile::Reference::TargetBinding Reference::getTargetBinding() const 
{ 
	if ( fTargetAtom == NULL ) 
		return kUnboundByName;
	else if ( fTargetName == NULL ) 
		return kBoundDirectly;
	else
		return kBoundByName;
}

const char* Reference::getDescription() const
{ 
	static char temp[256];
	strcpy(temp, "reference to ");
	if ( fTargetName != NULL )
		strcat(temp, fTargetName);
	else
		strcat(temp, fTargetAtom->getDisplayName());
	return temp; 
}


class Segment : public ObjectFile::Segment
{
public:
										Segment(const char* name, bool readable, bool writable, bool executable, bool fixedAddress)
											: fName(name), fReadable(readable), fWritable(writable), fExecutable(executable), fFixedAddress(fixedAddress) {}
	virtual const char*					getName() const						{ return fName; }
	virtual bool						isContentReadable() const			{ return fReadable; }
	virtual bool						isContentWritable() const			{ return fWritable; }
	virtual bool						isContentExecutable() const			{ return fExecutable; }
	virtual bool						hasFixedAddress() const				{ return fFixedAddress; }

	static Segment						fgBootstrapSegment;

private:
	const char*							fName;
	const bool							fReadable;
	const bool							fWritable;
	const bool							fExecutable;
	const bool							fFixedAddress;
};

Segment	Segment:: fgBootstrapSegment("__TEMP", true, false, false, false);
  
	
	  

//
// Atom acts as a proxy Atom for the symbols that are exported by LLVM bitcode file. Initially,
// Reader creates Atoms to allow linker proceed with usual symbol resolution phase. After
// optimization is performed, real Atoms are created for these symobls. However these real Atoms
// are not inserted into global symbol table. Atom holds real Atom and forwards appropriate
// methods to real atom.
//
class Atom : public ObjectFile::Atom
{
public:
												Atom(class Reader& owner, const char* name, Scope, DefinitionKind, uint8_t alignment, ObjectFile::Atom& internalAtom);
	
	ObjectFile::Reader*							getFile() const 			{ return (ObjectFile::Reader*)&fOwner; }
	bool 										getTranslationUnitSource (const char **dir, const char **name) const 
																			{ return fRealAtom->getTranslationUnitSource(dir, name); }
	const char *								getName () const 			{ return fName;	}
	const char *								getDisplayName() const 		{ return this->getName(); }
	Scope 										getScope() const 			{ return (fRealAtom ? fRealAtom->getScope() : fScope); }
	DefinitionKind 								getDefinitionKind() const	{ return (fRealAtom ? fRealAtom->getDefinitionKind() : fKind); }
	SymbolTableInclusion 						getSymbolTableInclusion() const 
																			{ return fRealAtom->getSymbolTableInclusion(); }
	bool 										dontDeadStrip() const 		{ return false; }
	bool 										isZeroFill() const 			{ return (fRealAtom ? fRealAtom->isZeroFill() : false); }
	bool										isThumb() const				{ return false; }
	uint64_t 									getSize() const 			{ return (fRealAtom ? fRealAtom->getSize() : 0); }
	std::vector<ObjectFile::Reference*>& 		getReferences() const 
																			{ return (fRealAtom ? fRealAtom->getReferences() : (std::vector<ObjectFile::Reference*>&)fReferences); }
	bool 										mustRemainInSection() const { return fRealAtom->mustRemainInSection(); }
	const char *								getSectionName() const 		{ return (fRealAtom ? fRealAtom->getSectionName() : NULL); }
	// Linker::optimize() sets section for this atom, not fRealAtom. Use this Atom's fSection.
	class ObjectFile::Section *					getSection() const 			{ return fSection; }
	ObjectFile::Segment& 						getSegment() const 			{ return (fRealAtom ? fRealAtom->getSegment() : Segment::fgBootstrapSegment); }
	uint32_t									getOrdinal() const			{ return (fRealAtom ? fRealAtom->getOrdinal() : 0); }
	ObjectFile::Atom& 							getFollowOnAtom() const 	{ return fRealAtom->getFollowOnAtom(); }
	std::vector<ObjectFile::LineInfo>* 			getLineInfo() const 		{ return (fRealAtom ? fRealAtom->getLineInfo() : NULL); }
	ObjectFile::Alignment						getAlignment() const		{ return (fRealAtom ? fRealAtom->getAlignment() : ObjectFile::Alignment(fAlignment)); }
	void 										copyRawContent(uint8_t buffer[]) const 
																			{ if (fRealAtom) fRealAtom->copyRawContent(buffer); }
	void 										setScope(Scope s) 			{ if (fRealAtom) fRealAtom->setScope(s); else fScope = s; }

	void 										setRealAtom (ObjectFile::Atom *atom) 
																			{ fRealAtom = atom; }
	ObjectFile::Atom *							getRealAtom() { return fRealAtom; }
	void 										addReference(ObjectFile::Reference *ref) 
																			{ fReferences.push_back(ref); }

	void										setSectionOffset(uint64_t offset) { fSectionOffset = offset; if (fRealAtom) fRealAtom->setSectionOffset(offset); }
	void										setSection(class ObjectFile::Section* sect) { fSection = sect; if (fRealAtom) fRealAtom->setSection(sect); }
																			
private:
	class Reader&								fOwner;
	const char*									fName;
	ObjectFile::Atom::Scope						fScope;
	ObjectFile::Atom::DefinitionKind 			fKind;
	uint8_t										fAlignment;
	ObjectFile::Atom*							fRealAtom;
	std::vector<ObjectFile::Reference*> 		fReferences;
};


Atom::Atom(class Reader& owner, const char* name, Scope scope, DefinitionKind kind, uint8_t alignment, ObjectFile::Atom& internalAtom) 
: fOwner(owner), fName(name), fScope(scope), fKind(kind), fAlignment(alignment), fRealAtom(NULL)
{
	// every Atom references the InternalAtom for its reader
	fReferences.push_back(new Reference(internalAtom));
}


//
// ld64 only tracks non-internal symbols from an llvm bitcode file.  
// We model this by having an InternalAtom which represent all internal functions and data.
// All non-interal symbols from a bitcode file are represented by a Atom
// and each Atom has a reference to the InternalAtom.  The InternalAtom
// also has references to each symbol external to the bitcode file. 
//
class InternalAtom : public ObjectFile::Atom
{
public:
												InternalAtom(class Reader& owner) : fOwner(owner) {}

	ObjectFile::Reader *						getFile() const 			{ return (ObjectFile::Reader*)&fOwner; }
	bool 										getTranslationUnitSource (const char **dir, const char **name) const 
																			{ return false; }
	const char *								getName () const 			{ return "__llvm-internal-atom";	}
	const char *								getDisplayName() const 		{ return "llvm bitcode"; }
	Scope 										getScope() const 			{ return scopeTranslationUnit; }
	DefinitionKind 								getDefinitionKind() const   { return kRegularDefinition; }
	SymbolTableInclusion 						getSymbolTableInclusion() const { return kSymbolTableNotIn; }
	bool 										dontDeadStrip() const 		{ return false; }
	bool 										isZeroFill() const 			{ return false; }
	bool										isThumb() const				{ return false; }
	uint64_t 									getSize() const 			{ return 0; }
	std::vector<ObjectFile::Reference*>& 		getReferences() const   	{ return  (std::vector<ObjectFile::Reference*>&)fReferences; }
	bool 										mustRemainInSection() const { return false; }
	const char *								getSectionName() const 		{ return NULL; }
	class ObjectFile::Section *					getSection() const 			{ return NULL; }
	ObjectFile::Segment& 						getSegment() const 			{ return Segment::fgBootstrapSegment; }
	uint32_t									getOrdinal() const          { return 0; }
	ObjectFile::Atom& 							getFollowOnAtom() const 	{ return *((ObjectFile::Atom*)NULL); }
	std::vector<ObjectFile::LineInfo>* 			getLineInfo() const 		{ return NULL; }
	ObjectFile::Alignment						getAlignment() const 		{ return ObjectFile::Alignment(0); }
	void 										copyRawContent(uint8_t buffer[]) const { }
	void 										setScope(Scope s) 			{ }

	void 										addReference(const char* targetName);
	
private:
	class Reader&								fOwner;
	std::vector<ObjectFile::Reference*> 		fReferences;
};


void InternalAtom::addReference(const char* name)
{
	fReferences.push_back(new Reference(name));
}




class RemovableAtoms
{
public:
	RemovableAtoms(std::set<ObjectFile::Atom*>& iAtoms) : fAtoms(iAtoms) {}

	bool operator()(ObjectFile::Atom*& atom) const {
		return ( fAtoms.count(atom) != 0 );
	}

private:
	std::set<ObjectFile::Atom*>& fAtoms;
};



//
// LLVM bitcode file reader
//
class Reader : public ObjectFile::Reader
{
public:
	static bool										validFile(const uint8_t* fileContent, uint64_t fileLength, cpu_type_t architecture);
	static bool										loaded() { return (::lto_get_version() != NULL); }
													Reader(const uint8_t* fileContent, uint64_t fileLength, 
																const char* path, time_t modTime, 
																const ObjectFile::ReaderOptions&, cpu_type_t arch);
	virtual											~Reader();
	
	virtual std::vector<class ObjectFile::Atom*>&	getAtoms()				{ return (std::vector<class ObjectFile::Atom*>&)(fAtoms); }
	virtual std::vector<class ObjectFile::Atom*>*	getJustInTimeAtomsFor(const char* name) { return NULL; }
	virtual const char*								getPath()				{ return fPath; }
	virtual time_t									getModificationTime()	{ return fModTime; }
	virtual ObjectFile::Reader::DebugInfoKind		getDebugInfoKind()		{ return kDebugInfoNone; }
	virtual std::vector<Stab>*						getStabs()				{ return NULL; }
	virtual bool									optimize(const std::vector<ObjectFile::Atom*>& allAtoms, std::vector<ObjectFile::Atom*>& newAtoms, 
																std::vector<const char*>& additionalUndefines, const std::set<ObjectFile::Atom*>&,
																std::vector<ObjectFile::Atom*>& newDeadAtoms,
																uint32_t nextInputOrdinal, 
																ObjectFile::Reader* writer, ObjectFile::Atom* entryPointAtom,
																const std::vector<const char*>& llvmOptions,
																bool allGlobalsAReDeadStripRoots,
																int outputKind, bool verbose, bool saveTemps, const char* outputFilePath,
																bool pie, bool allowTextRelocs);

private:

	class CStringEquals
	{
	public:
		bool operator()(const char* left, const char* right) const { return (strcmp(left, right) == 0); }
	};
	typedef	__gnu_cxx::hash_set<const char*, __gnu_cxx::hash<const char*>, CStringEquals>  CStringSet;
	typedef __gnu_cxx::hash_map<const char*, Atom*, __gnu_cxx::hash<const char*>, CStringEquals> CStringToAtom;
	
	ObjectFile::Reader*								makeMachOReader(const uint8_t* p, size_t len, uint32_t nextInputOrdinal);
	static const char*								tripletPrefixForArch(cpu_type_t);

	cpu_type_t										fArchitecture;
	const char*										fPath;
	time_t											fModTime;
	lto_module_t									fModule;
	std::vector<ObjectFile::Atom*>					fAtoms;
	InternalAtom									fInternalAtom;
	const ObjectFile::ReaderOptions&				fReaderOptions;
	static std::set<Reader*>						fgReaders;
	static bool										fgOptimized;
};

bool					Reader::fgOptimized = false;
std::set<Reader*>		Reader::fgReaders;


Reader::~Reader()
{
	if ( fModule != NULL )
		::lto_module_dispose(fModule);
}

Reader::Reader(const uint8_t* fileContent, uint64_t fileLength, const char* path, time_t modTime, 
				const ObjectFile::ReaderOptions& options, cpu_type_t arch)
	: fArchitecture(arch), fPath(strdup(path)), fModTime(modTime), fInternalAtom(*this), fReaderOptions(options)
{
	fgReaders.insert(this);

	fModule = ::lto_module_create_from_memory(fileContent, fileLength);
    if ( fModule == NULL )
		throwf("could not parse object file %s: %s", path, lto_get_error_message());
	
	fAtoms.push_back(&fInternalAtom);
	
	uint32_t count = ::lto_module_get_num_symbols(fModule);
	for (uint32_t i=0; i < count; ++i) {
		const char* name = ::lto_module_get_symbol_name(fModule, i);
		lto_symbol_attributes attr = lto_module_get_symbol_attribute(fModule, i);

		// <rdar://problem/6378110> LTO doesn't like dtrace symbols
		// ignore dtrace static probes for now
		// later when codegen is done and a mach-o file is produces the probes will be processed
		if ( (strncmp(name, "___dtrace_probe$", 16) == 0) || (strncmp(name, "___dtrace_isenabled$", 20) == 0) )
			continue;
				
		ObjectFile::Atom::DefinitionKind kind;
		switch ( attr & LTO_SYMBOL_DEFINITION_MASK ) {
			case LTO_SYMBOL_DEFINITION_REGULAR:
				kind = ObjectFile::Atom::kRegularDefinition;
				break;
			case LTO_SYMBOL_DEFINITION_TENTATIVE:
				kind = ObjectFile::Atom::kTentativeDefinition;
				break;
			case LTO_SYMBOL_DEFINITION_WEAK:
				kind = ObjectFile::Atom::kWeakDefinition;
				break;
			case LTO_SYMBOL_DEFINITION_UNDEFINED:
			case LTO_SYMBOL_DEFINITION_WEAKUNDEF:
				kind = ObjectFile::Atom::kExternalDefinition;
				break;
			default:
				throwf("unknown definition kind for symbol %s in bitcode file %s", name, path);
		}

		// make LLVM atoms for definitions and a reference for undefines
		if ( kind != ObjectFile::Atom::kExternalDefinition ) {
			ObjectFile::Atom::Scope scope;
			switch ( attr & LTO_SYMBOL_SCOPE_MASK) {
				case LTO_SYMBOL_SCOPE_INTERNAL:
					scope = ObjectFile::Atom::scopeTranslationUnit;
					break;
				case LTO_SYMBOL_SCOPE_HIDDEN:
					scope = ObjectFile::Atom::scopeLinkageUnit;
					break;
				case LTO_SYMBOL_SCOPE_DEFAULT:
					scope = ObjectFile::Atom::scopeGlobal;
					break;
				default:
					throwf("unknown scope for symbol %s in bitcode file %s", name, path);
			}
			// only make atoms for non-internal symbols 
			if ( scope == ObjectFile::Atom::scopeTranslationUnit )
				continue;
			uint8_t alignment = (attr & LTO_SYMBOL_ALIGNMENT_MASK);
			// make Atom
			fAtoms.push_back(new Atom(*this, name, scope, kind, alignment, fInternalAtom));
		}
		else {
			// add to list of external references
			fInternalAtom.addReference(name);
		}
	}
}

const char* Reader::tripletPrefixForArch(cpu_type_t arch)
{
	switch (arch) {
		case CPU_TYPE_POWERPC:
			return "powerpc-";
		case CPU_TYPE_POWERPC64:
			return "powerpc64-";
		case CPU_TYPE_I386:
			return "i386-";
		case CPU_TYPE_X86_64:
			return "x86_64-";
		case CPU_TYPE_ARM:
			return "arm";
	}
	return "";
}

bool Reader::validFile(const uint8_t* fileContent, uint64_t fileLength, cpu_type_t architecture)
{
	return ::lto_module_is_object_file_in_memory_for_target(fileContent, fileLength, tripletPrefixForArch(architecture));
}

bool Reader::optimize(const std::vector<ObjectFile::Atom *>& allAtoms, std::vector<ObjectFile::Atom*>& newAtoms, 
							std::vector<const char*>& additionalUndefines, const std::set<ObjectFile::Atom*>& deadAtoms,
							std::vector<ObjectFile::Atom*>& newlyDeadAtoms,
							uint32_t nextInputOrdinal,  ObjectFile::Reader* writer, ObjectFile::Atom* entryPointAtom,
							const std::vector<const char*>& llvmOptions,
							bool allGlobalsAReDeadStripRoots,
							int okind, bool verbose, bool saveTemps, const char* outputFilePath,
							bool pie, bool allowTextRelocs)
{ 
	// this method is call on all Readers.  We want the first call to trigger optimization
	// across all Readers and the subsequent calls to do nothing.
	if ( fgOptimized ) 
		return false;
	fgOptimized = true;
	
	Options::OutputKind outputKind = (Options::OutputKind)okind; // HACK to work around upward dependency

	// print out LTO version string if -v was used
	if ( verbose )
		fprintf(stderr, "%s\n", lto_get_version());
	
	// create optimizer and add each Reader
	lto_code_gen_t generator = ::lto_codegen_create();
	for (std::set<Reader*>::iterator it=fgReaders.begin(); it != fgReaders.end(); ++it) {
		if ( ::lto_codegen_add_module(generator, (*it)->fModule) )
			throwf("lto: could not merge in %s because %s", (*it)->fPath, ::lto_get_error_message());
	}

	// add any -mllvm command line options
	for (std::vector<const char*>::const_iterator it=llvmOptions.begin(); it != llvmOptions.end(); ++it) {
		::lto_codegen_debug_options(generator, *it);
	}

	// The atom graph uses directed edges (references). Collect all references where 
	// originating atom is not part of any LTO Reader. This allows optimizer to optimize an 
	// external (i.e. not originated from same .o file) reference if all originating atoms are also 
	// defined in llvm bitcode file.
	CStringSet nonLLVMRefs;
	CStringToAtom llvmAtoms;
    bool hasNonllvmAtoms = false;
	for (std::vector<ObjectFile::Atom*>::const_iterator it = allAtoms.begin(); it != allAtoms.end(); ++it) {
		ObjectFile::Atom* atom = *it;
		// only look at references come from an atom that is not an llvm atom
		if ( fgReaders.count((Reader*)(atom->getFile())) == 0 ) {
				// remember if we've seen any atoms not from an llvm reader and not from the writer
				if ( atom->getFile() != writer )
					hasNonllvmAtoms = true;
				std::vector<ObjectFile::Reference*>& refs = atom->getReferences();
				for (std::vector<ObjectFile::Reference*>::iterator ri=refs.begin(), re=refs.end(); ri != re; ++ri) {
					ObjectFile::Reference* ref = *ri;
					// add target name to set if target is an llvm atom
					if ( (ref->getTargetName() != NULL) && (fgReaders.count((Reader*)(ref->getTarget().getFile())) != 0) ) {
						nonLLVMRefs.insert(ref->getTargetName());
				}
			}
		}
		else {
			const char* name = atom->getName();
			if ( name != NULL )
				llvmAtoms[name] = (Atom*)atom;
		}
	}
	// if entry  point is in a llvm bitcode file, it must be preserved by LTO
	if ( entryPointAtom != NULL ) {
		if ( fgReaders.count((Reader*)(entryPointAtom->getFile())) != 0 ) 
			nonLLVMRefs.insert(entryPointAtom->getName());
	}
	
	// deadAtoms are the atoms that the linker coalesced.  For instance weak or tentative definitions
	// overriden by another atom.  If any of these deadAtoms are llvm atoms and they were replaced
	// with a mach-o atom, we need to tell the lto engine to preserve (not optimize away) its dead 
	// atom so that the linker can replace it with the mach-o one later.
	CStringToAtom deadllvmAtoms;
	for (std::set<ObjectFile::Atom*>::iterator it = deadAtoms.begin(); it != deadAtoms.end(); ++it) {
		ObjectFile::Atom* atom = *it;
		if ( fgReaders.count((Reader*)(atom->getFile())) != 0 ) {
			const char* name = atom->getName();
			::lto_codegen_add_must_preserve_symbol(generator, name);
			deadllvmAtoms[name] = (Atom*)atom;
		}
	}

	
	// tell code generator about symbols that must be preserved
	for (CStringToAtom::iterator it = llvmAtoms.begin(); it != llvmAtoms.end(); ++it) {
		const char* name = it->first;
		Atom* atom = it->second;
		// Include llvm Symbol in export list if it meets one of following two conditions
		// 1 - atom scope is global (and not linkage unit).
		// 2 - included in nonLLVMRefs set.
		// If a symbol is not listed in exportList then LTO is free to optimize it away.
		if ( (atom->getScope() == ObjectFile::Atom::scopeGlobal) ) 
			::lto_codegen_add_must_preserve_symbol(generator, name);
		else if ( nonLLVMRefs.find(name) != nonLLVMRefs.end() ) 
			::lto_codegen_add_must_preserve_symbol(generator, name);
	}
	
    // special case running ld -r on all bitcode files to produce another bitcode file (instead of mach-o)
    if ( (outputKind == Options::kObjectFile) && !hasNonllvmAtoms ) {
		if ( ! ::lto_codegen_write_merged_modules(generator, outputFilePath) ) {
			// HACK, no good way to tell linker we are all done, so just quit
			exit(0);
		}
		warning("could not produce merged bitcode file");
    }
    
    // if requested, save off merged bitcode file
    if ( saveTemps ) {
        char tempBitcodePath[MAXPATHLEN];
        strcpy(tempBitcodePath, outputFilePath);
        strcat(tempBitcodePath, ".lto.bc");
        ::lto_codegen_write_merged_modules(generator, tempBitcodePath);
    }

	// set code-gen model
	lto_codegen_model model = LTO_CODEGEN_PIC_MODEL_DYNAMIC;
	switch ( outputKind ) {
		case Options::kDynamicExecutable:
		case Options::kPreload:
			if ( pie )
				model = LTO_CODEGEN_PIC_MODEL_DYNAMIC;
			else
				model = LTO_CODEGEN_PIC_MODEL_DYNAMIC_NO_PIC;
			break;
		case Options::kDynamicLibrary:
		case Options::kDynamicBundle:
		case Options::kObjectFile: // ?? Is this appropriate ?
		case Options::kDyld:
		case Options::kKextBundle:
			if ( allowTextRelocs )
				model = LTO_CODEGEN_PIC_MODEL_DYNAMIC_NO_PIC;
			else
				model = LTO_CODEGEN_PIC_MODEL_DYNAMIC;
			break;
		case Options::kStaticExecutable:
			model = LTO_CODEGEN_PIC_MODEL_STATIC;
			break;
	}
	if ( ::lto_codegen_set_pic_model(generator, model) )
		throwf("could not create set codegen model: %s", lto_get_error_message());

	// run code generator
	size_t machOFileLen;
	const uint8_t* machOFile = (uint8_t*)::lto_codegen_compile(generator, &machOFileLen);
	if ( machOFile == NULL ) 
		throwf("could not do LTO codegen: %s", ::lto_get_error_message());
	
    // if requested, save off temp mach-o file
    if ( saveTemps ) {
        char tempMachoPath[MAXPATHLEN];
        strcpy(tempMachoPath, outputFilePath);
        strcat(tempMachoPath, ".lto.o");
        int fd = ::open(tempMachoPath, O_CREAT | O_WRONLY | O_TRUNC, 0666);
		if ( fd != -1) {
			::write(fd, machOFile, machOFileLen);
			::close(fd);
		}
    }

	// parse generated mach-o file into a MachOReader
	ObjectFile::Reader* machoReader = this->makeMachOReader(machOFile, machOFileLen, nextInputOrdinal);
	
	// sync generated mach-o atoms with existing atoms ld knows about
	std::vector<ObjectFile::Atom*> machoAtoms = machoReader->getAtoms();
	for (std::vector<ObjectFile::Atom *>::iterator it = machoAtoms.begin(); it != machoAtoms.end(); ++it) {
		ObjectFile::Atom* atom = *it;
		const char* name = atom->getName();
		if ( name != NULL ) {
			CStringToAtom::iterator pos = llvmAtoms.find(name);
			if ( pos != llvmAtoms.end() ) {
				// turn Atom into a proxy for this mach-o atom
				pos->second->setRealAtom(atom);
			}
			else {
				// an atom of this name was not in the allAtoms list the linker gave us
				if ( deadllvmAtoms.find(name) != deadllvmAtoms.end() ) {
					// this corresponding to an atom that the linker coalesced away.  Ignore it
					// Make sure there any dependent atoms are also marked dead
					std::vector<ObjectFile::Reference*>& refs = atom->getReferences();
					for (std::vector<ObjectFile::Reference*>::iterator ri=refs.begin(), re=refs.end(); ri != re; ++ri) {
						ObjectFile::Reference* ref = *ri;
						if ( ref->getKind() == 2 /*kGroupSubordinate*/ ) {	// FIX FIX
							ObjectFile::Atom* targ = &ref->getTarget();
							deadllvmAtoms[targ->getName()] = (Atom*)atom;
						}
					}
				}
				else
				{
					// this is something new that lto conjured up, tell ld its new
					newAtoms.push_back(atom);
				}
			}
		}
		else {
			// ld only knew about named atoms, so this one must be new
			newAtoms.push_back(atom);
		}
		std::vector<class ObjectFile::Reference*>& references = atom->getReferences();
		for (std::vector<ObjectFile::Reference*>::iterator rit=references.begin(); rit != references.end(); ++rit) {
			ObjectFile::Reference* ref = *rit;
			const char* targetName = ref->getTargetName();
			CStringToAtom::iterator pos;
			if (targetName != NULL) {
				switch ( ref->getTargetBinding() ) {
					case ObjectFile::Reference::kUnboundByName:
						// accumulate unbounded references so that ld can bound them.
						additionalUndefines.push_back(targetName);
						break;
					case ObjectFile::Reference::kBoundDirectly:
					case ObjectFile::Reference::kBoundByName:
						// If mach-o atom is referencing another mach-o atom then 
						// reference is not going through Atom proxy. Fix it here to ensure that all
						// llvm symbol references always go through Atom proxy.
						pos = llvmAtoms.find(targetName);
						if ( pos != llvmAtoms.end() )
							ref->setTarget(*pos->second, ref->getTargetOffset());
						break;
					case ObjectFile::Reference::kDontBind:
						break;
				}
			}
		}
	}
		
	// Remove InternalAtoms from ld
	for (std::set<Reader*>::iterator it=fgReaders.begin(); it != fgReaders.end(); ++it) {
		newlyDeadAtoms.push_back(&((*it)->fInternalAtom));
	}
	// Remove Atoms from ld if code generator optimized them away
	for (CStringToAtom::iterator li = llvmAtoms.begin(), le = llvmAtoms.end(); li != le; ++li) {
		// check if setRealAtom() called on this Atom
		if ( li->second->getRealAtom() == NULL )
			newlyDeadAtoms.push_back(li->second);
	}
	
	return true;
}


ObjectFile::Reader* Reader::makeMachOReader(const uint8_t* p, size_t len, uint32_t nextInputOrdinal) 
{
	switch ( fArchitecture ) {
		case CPU_TYPE_POWERPC:
			if ( mach_o::relocatable::Reader<ppc>::validFile(p) )
				return new mach_o::relocatable::Reader<ppc>(p, "/tmp/lto.o", 0, fReaderOptions, nextInputOrdinal);
			break;
		case CPU_TYPE_POWERPC64:
			if ( mach_o::relocatable::Reader<ppc64>::validFile(p) )
				return new mach_o::relocatable::Reader<ppc64>(p, "/tmp/lto.o", 0, fReaderOptions, nextInputOrdinal);
			break;
		case CPU_TYPE_I386:
			if ( mach_o::relocatable::Reader<x86>::validFile(p) )
				return new mach_o::relocatable::Reader<x86>(p, "/tmp/lto.o", 0, fReaderOptions, nextInputOrdinal);
			break;
		case CPU_TYPE_X86_64:
			if ( mach_o::relocatable::Reader<x86_64>::validFile(p) )
				return new mach_o::relocatable::Reader<x86_64>(p, "/tmp/lto.o", 0, fReaderOptions, nextInputOrdinal);
			break;
		case CPU_TYPE_ARM:
			if ( mach_o::relocatable::Reader<arm>::validFile(p) )
				return new mach_o::relocatable::Reader<arm>(p, "/tmp/lto.o", 0, fReaderOptions, nextInputOrdinal);
			break;
	}
	throw "LLVM LTO, file is not of required architecture";
}

}; // namespace lto

extern void printLTOVersion(Options &opts);

void printLTOVersion(Options &opts) {
	const char* vers = lto_get_version();
	if ( vers != NULL )
		fprintf(stderr, "%s\n", vers);
}


#endif