ExecutableFileMachO.cpp   [plain text]


/*
 * Copyright (c) 2005 Apple Computer, 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@
 */



namespace ExecutableFileMachO {

class Writer : public ExecutableFile::Writer
{ 
public:
								Writer(const char* path, Options& options, std::vector<ExecutableFile::DyLibUsed>& dynamicLibraries);
	virtual						~Writer();

	virtual const char*								getPath();
	virtual std::vector<class ObjectFile::Atom*>&	getAtoms();
	virtual std::vector<class ObjectFile::Atom*>*	getJustInTimeAtomsFor(const char* name);
	virtual std::vector<ObjectFile::StabsInfo>*		getStabsDebugInfo();

	virtual class ObjectFile::Atom*					getUndefinedProxyAtom(const char* name);
	virtual void									write(std::vector<class ObjectFile::Atom*>& atoms, class ObjectFile::Atom* entryPointAtom);
	
private:
	void						assignFileOffsets();
	void						partitionIntoSections();
	void						adjustLoadCommandsAndPadding();
	void						createDynamicLinkerCommand();
	void						createDylibCommands();
	void						buildLinkEdit();
	void						writeAtoms();
	void						collectExportedAndImportedAndLocalAtoms();
	void						setNlistRange(std::vector<class ObjectFile::Atom*>& atoms, uint32_t startIndex, uint32_t count);
	void						buildSymbolTable();
	void						setExportNlist(const ObjectFile::Atom* atom, macho_nlist* entry);
	void						setImportNlist(const ObjectFile::Atom* atom, macho_nlist* entry);
	void						setLocalNlist(const ObjectFile::Atom* atom, macho_nlist* entry);
	uint64_t					getAtomLoadAddress(const ObjectFile::Atom* atom);
	uint8_t						ordinalForLibrary(ObjectFile::Reader* file);
	bool						shouldExport(ObjectFile::Atom& atom);
	void						buildFixups();
	void						adjustLinkEditSections();
	void						buildObjectFileFixups();
	void						buildExecutableFixups();
	uint32_t					symbolIndex(ObjectFile::Atom& atom);
	uint32_t					addRelocs(ObjectFile::Atom* atom, ObjectFile::Reference* ref);
	unsigned int				collectStabs();
	macho_uintptr_t				valueForStab(const ObjectFile::StabsInfo& stab, const ObjectFile::Atom* atom);
	void						addStabs(uint32_t startIndex, uint32_t count);

	
	class SectionInfo : public ObjectFile::Section {
	public:
											SectionInfo();
		void								setIndex(unsigned int index) { fIndex=index; }
		std::vector<ObjectFile::Atom*>		fAtoms;
		char								fSegmentName[20];
		char								fSectionName[20];
		uint64_t							fFileOffset;
		uint64_t							fSize;
		uint32_t							fRelocCount;
		uint32_t							fRelocOffset;
		uint32_t							fIndirectSymbolOffset;
		uint8_t								fAlignment;
		bool								fAllLazyPointers;
		bool								fAllNonLazyPointers;
		bool								fAllZeroFill;
		bool								fVirtualSection;
	};
	
	class SegmentInfo 
	{
	public:
											SegmentInfo();
		std::vector<class SectionInfo*>		fSections;
		char								fName[20];
		uint32_t							fInitProtection;
		uint32_t							fMaxProtection;
		uint64_t							fFileOffset;
		uint64_t							fFileSize;
		uint64_t							fBaseAddress;
		uint64_t							fSize;
	};

	
	struct DirectLibrary {
		class ObjectFile::Reader*		fLibrary;
		bool						fWeak;
		bool						fReExport;	
	};
	
	struct IndirectEntry {
		uint32_t	indirectIndex;	
		uint32_t	symbolIndex;
	};

	struct StabChunks {
		ObjectFile::Atom*					fAtom;
		ObjectFile::Reader*					fReader;
		unsigned int						fOrderInReader;
		std::vector<ObjectFile::StabsInfo>*	fStabs;
	};

	static bool						stabChunkCompare(const StabChunks& lhs, const StabChunks& rhs);

	friend class WriterAtom;
	friend class PageZeroAtom;
	friend class CustomStackAtom;
	friend class MachHeaderAtom;
	friend class SegmentLoadCommandsAtom;
	friend class SymbolTableLoadCommandsAtom;
	friend class ThreadsLoadCommandsAtom;
	friend class DylibIDLoadCommandsAtom;
	friend class RoutinesLoadCommandsAtom;
	friend class DyldLoadCommandsAtom;
	friend class LinkEditAtom;
	friend class LocalRelocationsLinkEditAtom;
	friend class ExternalRelocationsLinkEditAtom;
	friend class SymbolTableLinkEditAtom;
	friend class IndirectTableLinkEditAtom;
	friend class StringsLinkEditAtom;

	const char*										fFilePath;
	Options&										fOptions;
	int												fFileDescriptor;
	std::vector<class ObjectFile::Atom*>*			fAllAtoms;
	class SectionInfo*								fLoadCommandsSection;
	class SegmentInfo*								fLoadCommandsSegment;
	class SegmentLoadCommandsAtom*					fSegmentCommands;
	class SymbolTableLoadCommandsAtom*				fSymbolTableCommands;
	class LoadCommandsPaddingAtom*					fHeaderPadding;
	std::vector<class ObjectFile::Atom*>			fWriterSynthesizedAtoms;
	std::vector<SegmentInfo*>						fSegmentInfos;
	class ObjectFile::Atom*							fEntryPoint;
	std::vector<DirectLibrary>						fDirectLibraries;
	std::map<class ObjectFile::Reader*, uint32_t>	fLibraryToOrdinal;
	std::vector<StabChunks>							fStabChunks;
	std::vector<class ObjectFile::Atom*>			fExportedAtoms;
	std::vector<class ObjectFile::Atom*>			fImportedAtoms;
	std::vector<class ObjectFile::Atom*>			fLocalSymbolAtoms;	
	LocalRelocationsLinkEditAtom*					fLocalRelocationsAtom;
	ExternalRelocationsLinkEditAtom*				fExternalRelocationsAtom;
	SymbolTableLinkEditAtom*						fSymbolTableAtom;
	IndirectTableLinkEditAtom*						fIndirectTableAtom;
	StringsLinkEditAtom*							fStringsAtom;
	macho_nlist*									fSymbolTable;
	//char*											fStringPool;
	//uint32_t										fStringPoolUsed;
	//uint32_t										fStringPoolSize;
	std::vector<macho_relocation_info>				fInternalRelocs;
	std::vector<macho_relocation_info>				fExternalRelocs;
	std::vector<IndirectEntry>						fIndirectSymbolTable;
	uint32_t										fSymbolTableCount;			
	uint32_t										fSymbolTableStabsCount;			
	uint32_t										fSymbolTableStabsStartIndex;			
	uint32_t										fSymbolTableLocalCount;			
	uint32_t										fSymbolTableLocalStartIndex;			
	uint32_t										fSymbolTableExportCount;			
	uint32_t										fSymbolTableExportStartIndex;			
	uint32_t										fSymbolTableImportCount;			
	uint32_t										fSymbolTableImportStartIndex;	
	bool											fEmitVirtualSections;
	bool											fHasWeakExports;
	bool											fReferencesWeakImports;
};


class WriterAtom : public ObjectFile::Atom 
{
protected:
	class Segment;
public:
	enum Kind { zeropage, machHeaderApp, machHeaderDylib, machHeaderBundle, machHeaderObject, loadCommands, undefinedProxy };
											WriterAtom(Writer& writer, class WriterAtom::Segment& segment) : fWriter(writer), fSegment(segment) {}
											
	virtual ObjectFile::Reader*				getFile() const					{ return &fWriter; }
	virtual const char*						getName() const					{ return NULL; }
	virtual const char*						getDisplayName() const			{ return this->getName(); }
	virtual Scope							getScope() const				{ return ObjectFile::Atom::scopeTranslationUnit; }
	virtual bool							isTentativeDefinition() const	{ return false; }
	virtual bool							isWeakDefinition() const		{ return false; }
	virtual bool							isCoalesableByName() const		{ return false; }
	virtual bool							isCoalesableByValue() const		{ return false; }
	virtual bool							isZeroFill() const				{ return false; }
	virtual bool							dontDeadStrip() const			{ return true; }
	virtual bool							dontStripName() const			{ return false; }
	virtual bool							isImportProxy() const			{ return false; }
	virtual std::vector<ObjectFile::Reference*>&  getReferences() const		{ return fgEmptyReferenceList; }
	virtual bool							mustRemainInSection() const		{ return true; }
	virtual ObjectFile::Segment&			getSegment() const				{ return fSegment; }
	virtual bool							requiresFollowOnAtom() const	{ return false; } 
	virtual ObjectFile::Atom&				getFollowOnAtom() const			{ return *((ObjectFile::Atom*)NULL); }
	virtual std::vector<ObjectFile::StabsInfo>*	getStabsDebugInfo() const	{ return NULL; }
	virtual uint8_t							getAlignment() const			{ return 2; }
	virtual WeakImportSetting				getImportWeakness() const		{ return Atom::kWeakUnset; }
	virtual void							copyRawContent(uint8_t buffer[]) const { throw "don't use copyRawContent"; }
	virtual void							setScope(Scope)					{ }
	virtual void							setImportWeakness(bool weakImport) { }


protected:
	virtual									~WriterAtom() {}
	
	class Segment : public ObjectFile::Segment
	{
	public:
									Segment(const char* name, bool readable, bool writable, bool executable)
												 : fName(name), fReadable(readable), fWritable(writable), fExecutable(executable) {}
		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; }
	private:
		const char*					fName;
		const bool					fReadable;
		const bool					fWritable;
		const bool					fExecutable;
	};
	
	static std::vector<ObjectFile::Reference*>	fgEmptyReferenceList;
	static Segment								fgTextSegment;
	static Segment								fgPageZeroSegment;
	static Segment								fgLinkEditSegment;
	static Segment								fgStackSegment;
	
	
	Writer&									fWriter;
	Segment&								fSegment;
};


WriterAtom::Segment						WriterAtom::fgPageZeroSegment("__PAGEZERO", false, false, false);
WriterAtom::Segment						WriterAtom::fgTextSegment("__TEXT", true, false, true);
WriterAtom::Segment						WriterAtom::fgLinkEditSegment("__LINKEDIT", true, false, false);
WriterAtom::Segment						WriterAtom::fgStackSegment("__UNIXSTACK", true, true, false);
std::vector<ObjectFile::Reference*>		WriterAtom::fgEmptyReferenceList;

class PageZeroAtom : public WriterAtom 
{
public:
											PageZeroAtom(Writer& writer) : WriterAtom(writer, fgPageZeroSegment) {}
	virtual const char*						getDisplayName() const	{ return "page zero content"; }
	virtual bool							isZeroFill() const		{ return true; }
	virtual uint64_t						getSize() const			{ return fWriter.fOptions.zeroPageSize(); }
	virtual const char*						getSectionName() const	{ return "._zeropage"; }
	virtual uint8_t							getAlignment() const	{ return 12; }
	virtual void							writeContent(bool finalLinkedImage, ObjectFile::ContentWriter&) const {}
};

class MachHeaderAtom : public WriterAtom 
{
public:
											MachHeaderAtom(Writer& writer)  : WriterAtom(writer, fgTextSegment) {}
	virtual const char*						getName() const;
	virtual const char*						getDisplayName() const;
	virtual Scope							getScope() const;
	virtual bool							dontStripName() const;
	virtual uint64_t						getSize() const;
	virtual uint8_t							getAlignment() const		{ return 12; }
	virtual const char*						getSectionName() const		{ return "._mach_header"; }
	virtual void							writeContent(bool finalLinkedImage, ObjectFile::ContentWriter&) const;
};

class CustomStackAtom : public WriterAtom 
{
public:
											CustomStackAtom(Writer& writer);
	virtual const char*						getDisplayName() const	{ return "custom stack content"; }
	virtual bool							isZeroFill() const		{ return true; }
	virtual uint64_t						getSize() const			{ return fWriter.fOptions.customStackSize(); }
	virtual const char*						getSectionName() const	{ return "._stack"; }
	virtual uint8_t							getAlignment() const	{ return 12; }
	virtual void							writeContent(bool finalLinkedImage, ObjectFile::ContentWriter&) const {}
};

class SegmentLoadCommandsAtom : public WriterAtom 
{
public:
											SegmentLoadCommandsAtom(Writer& writer)  : WriterAtom(writer, fgTextSegment), fCommandCount(0), fSize(0) { writer.fSegmentCommands = this; }
	virtual const char*						getDisplayName() const	{ return "segment load commands"; }
	virtual uint64_t						getSize() const			{ return fSize; }
	virtual uint8_t							getAlignment() const	{ return 2; }
	virtual const char*						getSectionName() const	{ return "._load_commands"; }
	virtual void							writeContent(bool finalLinkedImage, ObjectFile::ContentWriter&) const;
	
	void									computeSize();
	void									setup();
	unsigned int							commandCount()			{ return fCommandCount; }
	void									assignFileOffsets();
private:
	unsigned int							fCommandCount;
	uint32_t								fSize;
};

class SymbolTableLoadCommandsAtom : public WriterAtom 
{
public:
											SymbolTableLoadCommandsAtom(Writer&);
	virtual const char*						getDisplayName() const { return "symbol table load commands"; }
	virtual uint64_t						getSize() const;
	virtual uint8_t							getAlignment() const	{ return 2; }
	virtual const char*						getSectionName() const	{ return "._load_commands"; }
	virtual void							writeContent(bool finalLinkedImage, ObjectFile::ContentWriter&) const;
	
private:
	macho_symtab_command					fSymbolTable;
	macho_dysymtab_command					fDynamicSymbolTable;
};

class ThreadsLoadCommandsAtom : public WriterAtom 
{
public:
											ThreadsLoadCommandsAtom(Writer& writer) : WriterAtom(writer, fgTextSegment) {}
	virtual const char*						getDisplayName() const { return "thread load commands"; }
	virtual uint64_t						getSize() const;
	virtual uint8_t							getAlignment() const	{ return 2; }
	virtual const char*						getSectionName() const	{ return "._load_commands"; }
	virtual void							writeContent(bool finalLinkedImage, ObjectFile::ContentWriter&) const;
private:
	uint8_t*								fBuffer;
	uint32_t								fBufferSize;
};

class DyldLoadCommandsAtom : public WriterAtom 
{
public:
											DyldLoadCommandsAtom(Writer& writer)  : WriterAtom(writer, fgTextSegment) {}
	virtual const char*						getDisplayName() const	{ return "dyld load command"; }
	virtual uint64_t						getSize() const;
	virtual uint8_t							getAlignment() const	{ return 2; }
	virtual const char*						getSectionName() const	{ return "._load_commands"; }
	virtual void							writeContent(bool finalLinkedImage, ObjectFile::ContentWriter&) const;
};

class DylibLoadCommandsAtom : public WriterAtom 
{
public:
											DylibLoadCommandsAtom(Writer& writer, ExecutableFile::DyLibUsed& info)  : WriterAtom(writer, fgTextSegment), fInfo(info) {}
	virtual const char*						getDisplayName() const	{ return "dylib load command"; }
	virtual uint64_t						getSize() const;
	virtual uint8_t							getAlignment() const	{ return 2; }
	virtual const char*						getSectionName() const	{ return "._load_commands"; }
	virtual void							writeContent(bool finalLinkedImage, ObjectFile::ContentWriter&) const;
private:
	ExecutableFile::DyLibUsed&				fInfo;
};

class DylibIDLoadCommandsAtom : public WriterAtom 
{
public:
											DylibIDLoadCommandsAtom(Writer& writer) : WriterAtom(writer, fgTextSegment) {}
	virtual const char*						getDisplayName() const { return "dylib ID load command"; }
	virtual uint64_t						getSize() const;
	virtual uint8_t							getAlignment() const	{ return 2; }
	virtual const char*						getSectionName() const	{ return "._load_commands"; }
	virtual void							writeContent(bool finalLinkedImage, ObjectFile::ContentWriter&) const;
};

class RoutinesLoadCommandsAtom : public WriterAtom 
{
public:
											RoutinesLoadCommandsAtom(Writer& writer) : WriterAtom(writer, fgTextSegment) {}
	virtual const char*						getDisplayName() const { return "routines load command"; }
	virtual uint64_t						getSize() const;
	virtual uint8_t							getAlignment() const	{ return 2; }
	virtual const char*						getSectionName() const	{ return "._load_commands"; }
	virtual void							writeContent(bool finalLinkedImage, ObjectFile::ContentWriter&) const;
};

class SubUmbrellaLoadCommandsAtom : public WriterAtom 
{
public:
											SubUmbrellaLoadCommandsAtom(Writer& writer, const char* name)  : WriterAtom(writer, fgTextSegment), fName(name) {}
	virtual const char*						getDisplayName() const	{ return "sub-umbrella load command"; }
	virtual uint64_t						getSize() const;
	virtual uint8_t							getAlignment() const	{ return 2; }
	virtual const char*						getSectionName() const	{ return "._load_commands"; }
	virtual void							writeContent(bool finalLinkedImage, ObjectFile::ContentWriter&) const;
private:
	const char*								fName;
};

class SubLibraryLoadCommandsAtom : public WriterAtom 
{
public:
											SubLibraryLoadCommandsAtom(Writer& writer,  const char* nameStart, int nameLen)  
													: WriterAtom(writer, fgTextSegment), fNameStart(nameStart), fNameLength(nameLen) {}
	virtual const char*						getDisplayName() const	{ return "sub-library load command"; }
	virtual uint64_t						getSize() const;
	virtual uint8_t							getAlignment() const	{ return 2; }
	virtual const char*						getSectionName() const	{ return "._load_commands"; }
	virtual void							writeContent(bool finalLinkedImage, ObjectFile::ContentWriter&) const;
private:
	const char*								fNameStart;
	int										fNameLength;
};

class UmbrellaLoadCommandsAtom : public WriterAtom 
{
public:
											UmbrellaLoadCommandsAtom(Writer& writer, const char* name)  
													: WriterAtom(writer, fgTextSegment), fName(name) {}
	virtual const char*						getDisplayName() const	{ return "umbrella load command"; }
	virtual uint64_t						getSize() const;
	virtual uint8_t							getAlignment() const	{ return 2; }
	virtual const char*						getSectionName() const	{ return "._load_commands"; }
	virtual void							writeContent(bool finalLinkedImage, ObjectFile::ContentWriter&) const;
private:
	const char*								fName;
};

class LoadCommandsPaddingAtom : public WriterAtom 
{
public:
											LoadCommandsPaddingAtom(Writer& writer)  
													: WriterAtom(writer, fgTextSegment), fSize(0) {}
	virtual const char*						getDisplayName() const	{ return "header padding"; }
	virtual uint64_t						getSize() const;
	virtual uint8_t							getAlignment() const	{ return 2; }
	virtual const char*						getSectionName() const	{ return "._load_cmds_pad"; }
	virtual void							writeContent(bool finalLinkedImage, ObjectFile::ContentWriter&) const;
	
	void									setSize(uint64_t newSize) { fSize = newSize; }
private:
	uint64_t								fSize;
};

class LinkEditAtom : public WriterAtom 
{
public:
											LinkEditAtom(Writer& writer) : WriterAtom(writer, fgLinkEditSegment) {}
	uint64_t								getFileOffset() const;
};

class LocalRelocationsLinkEditAtom : public LinkEditAtom 
{
public:
											LocalRelocationsLinkEditAtom(Writer& writer) : LinkEditAtom(writer) { }
	virtual const char*						getDisplayName() const	{ return "local relocations"; }
	virtual uint64_t						getSize() const;
	virtual uint8_t							getAlignment() const	{ return 3; }
	virtual const char*						getSectionName() const	{ return "._local_relocs"; }
	virtual void							writeContent(bool finalLinkedImage, ObjectFile::ContentWriter&) const;
};

class SymbolTableLinkEditAtom : public LinkEditAtom 
{
public:
											SymbolTableLinkEditAtom(Writer& writer) : LinkEditAtom(writer) { }
	virtual const char*						getDisplayName() const	{ return "symbol table"; }
	virtual uint64_t						getSize() const;
	virtual uint8_t							getAlignment() const	{ return 2; }
	virtual const char*						getSectionName() const	{ return "._symbol_table"; }
	virtual void							writeContent(bool finalLinkedImage, ObjectFile::ContentWriter&) const;
};

class ExternalRelocationsLinkEditAtom : public LinkEditAtom 
{
public:
											ExternalRelocationsLinkEditAtom(Writer& writer) : LinkEditAtom(writer) { }
	virtual const char*						getDisplayName() const	{ return "external relocations"; }
	virtual uint64_t						getSize() const;
	virtual uint8_t							getAlignment() const	{ return 3; }
	virtual const char*						getSectionName() const	{ return "._extern_relocs"; }
	virtual void							writeContent(bool finalLinkedImage, ObjectFile::ContentWriter&) const;
};

class IndirectTableLinkEditAtom : public LinkEditAtom 
{
public:
											IndirectTableLinkEditAtom(Writer& writer) : LinkEditAtom(writer) { }
	virtual const char*						getDisplayName() const	{ return "indirect symbol table"; }
	virtual uint64_t						getSize() const;
	virtual uint8_t							getAlignment() const	{ return 2; }
	virtual const char*						getSectionName() const	{ return "._indirect_syms"; }
	virtual void							writeContent(bool finalLinkedImage, ObjectFile::ContentWriter&) const;
};

class StringsLinkEditAtom : public LinkEditAtom 
{
public:
											StringsLinkEditAtom(Writer& writer);
	virtual const char*						getDisplayName() const	{ return "string pool"; }
	virtual uint64_t						getSize() const;
	virtual uint8_t							getAlignment() const	{ return 2; }
	virtual const char*						getSectionName() const	{ return "._string_pool"; }
	virtual void							writeContent(bool finalLinkedImage, ObjectFile::ContentWriter&) const;
	
	int32_t									add(const char* name);
	int32_t									emptyString();
	
private:
	enum { kBufferSize = 0x01000000 };

	std::vector<char*>						fFullBuffers;
	char*									fCurrentBuffer;
	uint32_t								fCurrentBufferUsed;
};



class UndefinedSymbolProxyAtom : public WriterAtom 
{
public:
											UndefinedSymbolProxyAtom(Writer& writer, const char* name) : WriterAtom(writer, fgLinkEditSegment), fName(name), fWeakImportSetting(Atom::kWeakUnset) {}
	virtual const char*						getName() const				{ return fName; }
	virtual Scope							getScope() const			{ return ObjectFile::Atom::scopeGlobal; }
	virtual uint64_t						getSize() const				{ return 0; }
	virtual bool							isWeakDefinition() const	{ return true; }
	virtual bool							isImportProxy() const		{ return true; }
	virtual const char*						getSectionName() const		{ return "._imports"; }
	virtual void							writeContent(bool finalLinkedImage, ObjectFile::ContentWriter&) const {}
	virtual WeakImportSetting				getImportWeakness() const	{ return fWeakImportSetting; }
	virtual void							setImportWeakness(bool weakImport) { fWeakImportSetting = weakImport ? kWeakImport : kNonWeakImport; }
private:
	const char*								fName;
	WeakImportSetting						fWeakImportSetting;
};



struct ExportSorter
{
     bool operator()(ObjectFile::Atom* left, ObjectFile::Atom* right)
     {
          return (strcmp(left->getName(), right->getName()) < 0);
     }
};


ExecutableFile::Writer* MakeWriter(const char* path, Options& options, std::vector<ExecutableFile::DyLibUsed>& dynamicLibraries)
{
	return new Writer(path, options, dynamicLibraries);
}

Writer::SectionInfo::SectionInfo()
 : fFileOffset(0), fSize(0), fRelocCount(0), fRelocOffset(0), fIndirectSymbolOffset(0), fAlignment(0),
	fAllLazyPointers(false), fAllNonLazyPointers(false), fAllZeroFill(false), fVirtualSection(false)
{
	fSegmentName[0] = '\0';
	fSectionName[0] = '\0';
}
	
Writer::SegmentInfo::SegmentInfo()
 : fInitProtection(0), fMaxProtection(0), fFileOffset(0), fFileSize(0), fBaseAddress(0), fSize(0)
{
	fName[0] = '\0';
}


Writer::Writer(const char* path, Options& options, std::vector<ExecutableFile::DyLibUsed>& dynamicLibraries)
 : ExecutableFile::Writer(dynamicLibraries), fFilePath(strdup(path)), fOptions(options), fLoadCommandsSection(NULL), 
	fLoadCommandsSegment(NULL),
	//fStringPool(NULL), fStringPoolUsed(0), fStringPoolSize(0), 
	fEmitVirtualSections(false), fHasWeakExports(false), fReferencesWeakImports(false)
{
	int permissions = 0777;
	if ( fOptions.outputKind() == Options::kObjectFile )
		permissions = 0666;
	// Calling unlink first assures the file is gone so that open creates it with correct permissions
	// It also handles the case where fFilePath file is not writeable but its directory is
	// And it means we don't have to truncate the file when done writing (in case new is smaller than old)
	(void)unlink(fFilePath);
	fFileDescriptor = open(fFilePath, O_CREAT | O_WRONLY | O_TRUNC, permissions);	
	if ( fFileDescriptor == -1 ) {
		throw "can't open file for writing";
	}

	switch ( fOptions.outputKind() ) {
		case Options::kDynamicExecutable:
		case Options::kStaticExecutable:
			fWriterSynthesizedAtoms.push_back(new PageZeroAtom(*this));
			fWriterSynthesizedAtoms.push_back(new MachHeaderAtom(*this));
			fWriterSynthesizedAtoms.push_back(new SegmentLoadCommandsAtom(*this));
			fWriterSynthesizedAtoms.push_back(new SymbolTableLoadCommandsAtom(*this));
			if ( fOptions.outputKind() == Options::kDynamicExecutable )
				fWriterSynthesizedAtoms.push_back(new DyldLoadCommandsAtom(*this));
			fWriterSynthesizedAtoms.push_back(new ThreadsLoadCommandsAtom(*this));
			if ( fOptions.hasCustomStack() )
				fWriterSynthesizedAtoms.push_back(new CustomStackAtom(*this));
			fWriterSynthesizedAtoms.push_back(fHeaderPadding = new LoadCommandsPaddingAtom(*this));
			fWriterSynthesizedAtoms.push_back(fLocalRelocationsAtom = new LocalRelocationsLinkEditAtom(*this));
			fWriterSynthesizedAtoms.push_back(fSymbolTableAtom = new SymbolTableLinkEditAtom(*this));
			fWriterSynthesizedAtoms.push_back(fExternalRelocationsAtom = new ExternalRelocationsLinkEditAtom(*this));
			fWriterSynthesizedAtoms.push_back(fIndirectTableAtom = new IndirectTableLinkEditAtom(*this));
			fWriterSynthesizedAtoms.push_back(fStringsAtom = new StringsLinkEditAtom(*this));
			break;
		case Options::kDynamicLibrary:
		case Options::kDynamicBundle:
		case Options::kObjectFile:
			fWriterSynthesizedAtoms.push_back(new MachHeaderAtom(*this));
			fWriterSynthesizedAtoms.push_back(new SegmentLoadCommandsAtom(*this));
			if ( fOptions.outputKind() == Options::kDynamicLibrary ) {
				fWriterSynthesizedAtoms.push_back(new DylibIDLoadCommandsAtom(*this));
				if ( fOptions.initFunctionName() != NULL )
					fWriterSynthesizedAtoms.push_back(new RoutinesLoadCommandsAtom(*this));
			}
			fWriterSynthesizedAtoms.push_back(new SymbolTableLoadCommandsAtom(*this));
			fWriterSynthesizedAtoms.push_back(fHeaderPadding = new LoadCommandsPaddingAtom(*this));
			fWriterSynthesizedAtoms.push_back(fLocalRelocationsAtom = new LocalRelocationsLinkEditAtom(*this));
			fWriterSynthesizedAtoms.push_back(fSymbolTableAtom = new SymbolTableLinkEditAtom(*this));
			fWriterSynthesizedAtoms.push_back(fExternalRelocationsAtom = new ExternalRelocationsLinkEditAtom(*this));
			fWriterSynthesizedAtoms.push_back(fIndirectTableAtom = new IndirectTableLinkEditAtom(*this));
			fWriterSynthesizedAtoms.push_back(fStringsAtom = new StringsLinkEditAtom(*this));
			break;
		case Options::kDyld:
			fWriterSynthesizedAtoms.push_back(new MachHeaderAtom(*this));
			fWriterSynthesizedAtoms.push_back(new SegmentLoadCommandsAtom(*this));
			fWriterSynthesizedAtoms.push_back(new SymbolTableLoadCommandsAtom(*this));
			fWriterSynthesizedAtoms.push_back(new DyldLoadCommandsAtom(*this));
			fWriterSynthesizedAtoms.push_back(new ThreadsLoadCommandsAtom(*this));
			fWriterSynthesizedAtoms.push_back(fHeaderPadding = new LoadCommandsPaddingAtom(*this));
			fWriterSynthesizedAtoms.push_back(fLocalRelocationsAtom = new LocalRelocationsLinkEditAtom(*this));
			fWriterSynthesizedAtoms.push_back(fSymbolTableAtom = new SymbolTableLinkEditAtom(*this));
			fWriterSynthesizedAtoms.push_back(fExternalRelocationsAtom = new ExternalRelocationsLinkEditAtom(*this));
			fWriterSynthesizedAtoms.push_back(fIndirectTableAtom = new IndirectTableLinkEditAtom(*this));
			fWriterSynthesizedAtoms.push_back(fStringsAtom = new StringsLinkEditAtom(*this));
			break;
	}
	
	// add extra commmands
	uint8_t ordinal = 1;
	switch ( fOptions.outputKind() ) {
		case Options::kDynamicExecutable:
		case Options::kDynamicLibrary:
		case Options::kDynamicBundle:
			{
				// add dylib load command atoms for all dynamic libraries
				const unsigned int libCount = dynamicLibraries.size();
				for (unsigned int i=0; i < libCount; ++i) {
					ExecutableFile::DyLibUsed& dylibInfo = dynamicLibraries[i];
					if ( dylibInfo.indirect ) {
						// find ordinal of direct reader
						if ( fOptions.nameSpace() == Options::kTwoLevelNameSpace ) {
							bool found = false;
							for (std::map<class ObjectFile::Reader*, uint32_t>::iterator it = fLibraryToOrdinal.begin(); it != fLibraryToOrdinal.end(); ++it) {
								if ( it->first == dylibInfo.directReader ) {
									//fprintf(stderr, "ordinal %d for indirect %s\n", it->second, dylibInfo.reader->getPath());
									fLibraryToOrdinal[dylibInfo.reader] = it->second;
									found = true;
									break;
								}
							}
							if ( ! found ) 
								fprintf(stderr, "ld64 warning: ordinal not found for %s, parent %s\n", dylibInfo.reader->getPath(), dylibInfo.directReader != NULL ? dylibInfo.directReader->getPath() : NULL);
						}
					}
					else {
						// see if a DylibLoadCommandsAtom has already been created for this install path
						bool newDylib = true;
						const char* dylibInstallPath = dylibInfo.reader->getInstallPath();
						if ( dylibInfo.options.fInstallPathOverride != NULL ) 
							dylibInstallPath = dylibInfo.options.fInstallPathOverride;
						for (unsigned int seenLib=0; seenLib < i; ++seenLib) {
							ExecutableFile::DyLibUsed& seenDylibInfo = dynamicLibraries[seenLib];
							if ( !seenDylibInfo.indirect ) {
								const char* seenDylibInstallPath = seenDylibInfo.reader->getInstallPath();
								if ( seenDylibInfo.options.fInstallPathOverride != NULL ) 
									seenDylibInstallPath = dylibInfo.options.fInstallPathOverride;
								if ( strcmp(seenDylibInstallPath, dylibInstallPath) == 0 ) {
									fLibraryToOrdinal[dylibInfo.reader] = fLibraryToOrdinal[seenDylibInfo.reader];
									newDylib = false;
									break;
								}
							}
						}
						
						if ( newDylib ) {
							// assign new ordinal and check for other paired load commands
							fLibraryToOrdinal[dylibInfo.reader] = ordinal++;
							fWriterSynthesizedAtoms.push_back(new DylibLoadCommandsAtom(*this, dylibInfo));
							if ( dylibInfo.options.fReExport ) {
								// this dylib also needs a sub_x load command
								bool isFrameworkReExport = false;
								const char* lastSlash = strrchr(dylibInstallPath, '/');
								if ( lastSlash != NULL ) {
									char frameworkName[strlen(lastSlash)+20];
									sprintf(frameworkName, "/%s.framework/", &lastSlash[1]);
									isFrameworkReExport = (strstr(dylibInstallPath, frameworkName) != NULL);
								}
								if ( isFrameworkReExport ) {
									// needs a LC_SUB_UMBRELLA command
									fWriterSynthesizedAtoms.push_back(new SubUmbrellaLoadCommandsAtom(*this, &lastSlash[1]));
								}
								else {
									// needs a LC_SUB_LIBRARY command
									const char* nameStart = &lastSlash[1];
									if ( lastSlash == NULL )
										nameStart = dylibInstallPath;
									int len = strlen(nameStart);
									const char* dot = strchr(nameStart, '.');
									if ( dot != NULL )
										len = dot - nameStart;
									fWriterSynthesizedAtoms.push_back(new SubLibraryLoadCommandsAtom(*this, nameStart, len));
								}
							}
						}
					}
				}
				// add umbrella command if needed
				if ( fOptions.umbrellaName() != NULL ) {
					fWriterSynthesizedAtoms.push_back(new UmbrellaLoadCommandsAtom(*this, fOptions.umbrellaName()));
				}
			}
			break;
		case Options::kStaticExecutable:
		case Options::kObjectFile:
		case Options::kDyld:
			break;
	}
	
	//fprintf(stderr, "ordinals table:\n");
	//for (std::map<class ObjectFile::Reader*, uint32_t>::iterator it = fLibraryToOrdinal.begin(); it != fLibraryToOrdinal.end(); ++it) {
	//	fprintf(stderr, "%d <== %s\n", it->second, it->first->getPath());
	//}
}

Writer::~Writer()
{
	if ( fFilePath != NULL )
		free((void*)fFilePath);
	if ( fSymbolTable != NULL )
		delete [] fSymbolTable;
	//if ( fStringPool != NULL )
	//	delete [] fStringPool;
}

const char*	 Writer::getPath()
{
	return fFilePath;
}


std::vector<class ObjectFile::Atom*>& Writer::getAtoms()
{
	return fWriterSynthesizedAtoms;
}

std::vector<class ObjectFile::Atom*>* Writer::getJustInTimeAtomsFor(const char* name)
{
	return NULL;
}

std::vector<ObjectFile::StabsInfo>*	Writer::getStabsDebugInfo()
{
	return NULL;
}

ObjectFile::Atom* Writer::getUndefinedProxyAtom(const char* name)
{
	if ( (fOptions.outputKind() == Options::kObjectFile)
		|| (fOptions.undefinedTreatment() != Options::kUndefinedError) )
		return new UndefinedSymbolProxyAtom(*this, name);
	else
		return NULL;
}

uint8_t Writer::ordinalForLibrary(ObjectFile::Reader* lib)
{
	// flat namespace images use zero for all ordinals
	if (  fOptions.nameSpace() != Options::kTwoLevelNameSpace )
		return 0;

	// is an UndefinedSymbolProxyAtom
	if ( lib == this )
		if ( fOptions.nameSpace() == Options::kTwoLevelNameSpace )
			return DYNAMIC_LOOKUP_ORDINAL;

	std::map<class ObjectFile::Reader*, uint32_t>::iterator pos = fLibraryToOrdinal.find(lib);
	if ( pos != fLibraryToOrdinal.end() )
		return pos->second;
		
	throw "can't find ordinal for imported symbol";
}

 
void Writer::write(std::vector<class ObjectFile::Atom*>& atoms, class ObjectFile::Atom* entryPointAtom)
{
	fAllAtoms =  &atoms;
	fEntryPoint = entryPointAtom;

	// create SegmentInfo and SectionInfo objects and assign all atoms to a section
	partitionIntoSections();
	
	// segment load command can now be sized and padding can be set
	adjustLoadCommandsAndPadding();
	
	// assign each section a file offset
	assignFileOffsets();
	
	// build symbol table and relocations
	buildLinkEdit();
	
	// write everything
	writeAtoms();
}

void Writer::buildLinkEdit()
{
	this->collectExportedAndImportedAndLocalAtoms();
	this->buildSymbolTable();
	this->buildFixups();
	this->adjustLinkEditSections();
}



uint64_t Writer::getAtomLoadAddress(const ObjectFile::Atom* atom)
{
	return atom->getAddress();
//	SectionInfo* info = (SectionInfo*)atom->getSection();
//	return info->getBaseAddress() + atom->getSectionOffset();
}

void Writer::setExportNlist(const ObjectFile::Atom* atom, macho_nlist* entry)
{
	// set n_type
	entry->set_n_type(N_EXT | N_SECT);
	if ( (atom->getScope() == ObjectFile::Atom::scopeLinkageUnit) && fOptions.keepPrivateExterns() && (fOptions.outputKind() == Options::kObjectFile) )
		entry->set_n_type(N_EXT | N_SECT | N_PEXT);
	
	// set n_sect (section number of implementation )
	uint8_t sectionIndex = atom->getSection()->getIndex();
	entry->set_n_sect(sectionIndex);
	
	// the __mh_execute_header is magic and must be an absolute symbol  
	if ( (fOptions.outputKind() == Options::kDynamicExecutable) && (sectionIndex==0) && atom->dontStripName())
		entry->set_n_type(N_EXT | N_ABS);

	// set n_desc
	uint16_t desc = 0;
	if ( atom->dontStripName() )
		desc |= REFERENCED_DYNAMICALLY;
	if ( atom->isWeakDefinition() && (strcmp(atom->getSectionName(), "__common") != 0) ) {
		desc |= N_WEAK_DEF;
		fHasWeakExports = true;
	}
	entry->set_n_desc(desc);
	
	// set n_value ( address this symbol will be at if this executable is loaded at it preferred address )
	entry->set_n_value(this->getAtomLoadAddress(atom));
}

void Writer::setImportNlist(const ObjectFile::Atom* atom, macho_nlist* entry)
{
	// set n_type
	entry->set_n_type(N_UNDF | N_EXT);
	
	// set n_sect
	entry->set_n_sect(0);
	
	uint16_t desc = 0;
	if ( fOptions.outputKind() != Options::kObjectFile ) {
		// set n_desc ( high byte is library ordinal, low byte is reference type )
		desc = REFERENCE_FLAG_UNDEFINED_LAZY; // FIXME
		try {
			uint8_t ordinal = this->ordinalForLibrary(atom->getFile());
			SET_LIBRARY_ORDINAL(desc, ordinal);
		}
		catch (const char* msg) {
			throwf("%s %s from %s", msg, atom->getDisplayName(), atom->getFile()->getPath());
		}
	}
	if ( atom->dontStripName() )
		desc |= REFERENCED_DYNAMICALLY;
	// an import proxy is always weak (overridden by definition in .o files)
	// so we ask its reader if the exported symbol in its dylib is weak
	if ( ( fOptions.outputKind() != Options::kObjectFile) && atom->getFile()->isDefinitionWeak(*atom) ) {
		desc |= N_REF_TO_WEAK;
		fReferencesWeakImports = true;
	}
	// set weak_import attribute
	if ( atom->getImportWeakness() == ObjectFile::Atom::kWeakImport ) 
		desc |= N_WEAK_REF;
	entry->set_n_desc(desc);
	
	// set n_value, zero for import proxy and size for tentative definition
	entry->set_n_value(atom->getSize());
}

void Writer::setLocalNlist(const ObjectFile::Atom* atom, macho_nlist* entry)
{
	// set n_type
	uint8_t type = N_SECT;
	if ( atom->getScope() == ObjectFile::Atom::scopeLinkageUnit )
		type |= N_PEXT;
	entry->set_n_type(type);
	
	// set n_sect (section number of implementation )
	uint8_t sectIndex = atom->getSection()->getIndex();
	if ( sectIndex == 0 ) {
		// see <mach-o/ldsyms.h> synthesized lable for mach_header needs special section number...
		if ( strcmp(atom->getSectionName(), "._mach_header") == 0 )
			sectIndex = 1;
	}
	entry->set_n_sect(sectIndex);
	
	// set n_desc
	uint16_t desc = 0;
	if ( atom->isWeakDefinition() && (strcmp(atom->getSectionName(), "__common") != 0) ) // commons on not weak
		desc |= N_WEAK_DEF;
	entry->set_n_desc(desc);
	
	// set n_value ( address this symbol will be at if this executable is loaded at it preferred address )
	entry->set_n_value(this->getAtomLoadAddress(atom));
}


void Writer::setNlistRange(std::vector<class ObjectFile::Atom*>& atoms, uint32_t startIndex, uint32_t count)
{
	macho_nlist* entry = &fSymbolTable[startIndex];
	for (uint32_t i=0; i < count; ++i, ++entry) {
		ObjectFile::Atom* atom = atoms[i];
		entry->set_n_strx(this->fStringsAtom->add(atom->getName()));
		if ( &atoms == &fExportedAtoms ) {
			this->setExportNlist(atom, entry);
		}
		else if ( &atoms == &fImportedAtoms ) {
			this->setImportNlist(atom, entry);
		}
		else {
			this->setLocalNlist(atom, entry);
		}
	}
}

void Writer::buildSymbolTable()
{
	fSymbolTableStabsStartIndex		= 0;
	fSymbolTableStabsCount			= this->collectStabs();
	fSymbolTableLocalStartIndex		= fSymbolTableStabsStartIndex + fSymbolTableStabsCount;
	fSymbolTableLocalCount			= fLocalSymbolAtoms.size();
	fSymbolTableExportStartIndex	= fSymbolTableLocalStartIndex + fSymbolTableLocalCount;
	fSymbolTableExportCount			= fExportedAtoms.size();
	fSymbolTableImportStartIndex	= fSymbolTableExportStartIndex + fSymbolTableExportCount;
	fSymbolTableImportCount			= fImportedAtoms.size();
	
	// allocate symbol table
	fSymbolTableCount = fSymbolTableStabsCount + fSymbolTableLocalCount + fSymbolTableExportCount + fSymbolTableImportCount;
	fSymbolTable = new macho_nlist[fSymbolTableCount];
	
	// fill in symbol table and string pool (do stabs last so strings are at end of pool)
	setNlistRange(fLocalSymbolAtoms, fSymbolTableLocalStartIndex,  fSymbolTableLocalCount);
	setNlistRange(fExportedAtoms,    fSymbolTableExportStartIndex, fSymbolTableExportCount);
	setNlistRange(fImportedAtoms,    fSymbolTableImportStartIndex, fSymbolTableImportCount);
	addStabs(fSymbolTableStabsStartIndex, fSymbolTableStabsCount);
}



bool Writer::shouldExport(ObjectFile::Atom& atom)
{
	switch ( atom.getScope() ) {
		case ObjectFile::Atom::scopeGlobal:
			return true;
		case ObjectFile::Atom::scopeLinkageUnit:
			return ( fOptions.keepPrivateExterns() && (fOptions.outputKind() == Options::kObjectFile) );
		default:
			return false;
	}
}

void Writer::collectExportedAndImportedAndLocalAtoms()
{
	const int atomCount = fAllAtoms->size();
	for (int i=0; i < atomCount; ++i) {
		ObjectFile::Atom* atom = (*fAllAtoms)[i];
		// only named atoms go in symbol table
		if ( atom->getName() != NULL ) {
			// put atom into correct bucket: imports, exports, locals
			//printf("collectExportedAndImportedAndLocalAtoms() name=%s\n", atom->getDisplayName());
			if ( atom->isImportProxy() || ((fOptions.outputKind() == Options::kObjectFile) && (strcmp(atom->getSectionName(), "__common") == 0)) )
				fImportedAtoms.push_back(atom);
			else if ( this->shouldExport(*atom) )
				fExportedAtoms.push_back(atom);
			else if ( !fOptions.stripLocalSymbols() )
				fLocalSymbolAtoms.push_back(atom);
		}
	}
	
	// sort exported atoms by name
	std::sort(fExportedAtoms.begin(), fExportedAtoms.end(), ExportSorter());
}


bool Writer::stabChunkCompare(const struct StabChunks& lhs, const struct StabChunks& rhs)
{
	if ( lhs.fReader != rhs.fReader ) {
		return lhs.fReader < rhs.fReader;
	}
	return lhs.fOrderInReader < rhs.fOrderInReader;
}

unsigned int Writer::collectStabs()
{	
	unsigned int count = 0;
	
	// collect all stabs chunks
	std::set<ObjectFile::Reader*> seenReaders;
	const int atomCount = fAllAtoms->size();
	for (int i=0; i < atomCount; ++i) {
		ObjectFile::Atom* atom = (*fAllAtoms)[i];
		ObjectFile::Reader* atomsReader = atom->getFile();
		if ( (atomsReader != NULL) && (seenReaders.count(atomsReader) == 0) ) {
			seenReaders.insert(atomsReader);
			std::vector<ObjectFile::StabsInfo>* readerStabs = atomsReader->getStabsDebugInfo();
			if ( readerStabs != NULL ) {
				StabChunks chunk;
				chunk.fAtom				= NULL;
				chunk.fReader			= atomsReader;
				chunk.fOrderInReader	= 0;
				chunk.fStabs			= readerStabs;
				fStabChunks.push_back(chunk);
				count += readerStabs->size() + 1; // extra one is for trailing N_SO
			}
		}		
		std::vector<ObjectFile::StabsInfo>* atomStabs = atom->getStabsDebugInfo();
		if ( atomStabs != NULL ) {
			StabChunks chunk;
			chunk.fAtom				= atom;
			chunk.fReader			= atomsReader;
			chunk.fOrderInReader	= atom->getSortOrder();
			chunk.fStabs			= atomStabs;
			fStabChunks.push_back(chunk);
			count += atomStabs->size();
		}
	}
	
	// sort by order in original .o file
	std::sort(fStabChunks.begin(), fStabChunks.end(), stabChunkCompare);
	
	return count;
}

macho_uintptr_t Writer::valueForStab(const ObjectFile::StabsInfo& stab, const ObjectFile::Atom* atom)
{
	switch ( stab.type ) {
		case N_FUN:
			if ( stab.other == 0 )
				break;
			// end of function N_FUN has size (not address) so should not be adjusted
			// fall through
		case N_BNSYM:
		case N_ENSYM:
		case N_LBRAC:
		case N_RBRAC:
		case N_SLINE:
		case N_STSYM:
		case N_LCSYM:
			// all these stab types need their value changed from an offset in the atom to an address
			if ( atom != NULL )
				return getAtomLoadAddress(atom) + stab.atomOffset;
	}
	return stab.atomOffset;
}
 

void Writer::addStabs(uint32_t startIndex, uint32_t count)
{
	macho_nlist* entry = &fSymbolTable[startIndex];
	const int chunkCount = fStabChunks.size();
	for (int i=0; i < chunkCount; ++i ) {
		const StabChunks& chunk = fStabChunks[i];
		const int stabCount = chunk.fStabs->size();
		for (int j=0; j < stabCount; ++j ) {
			const ObjectFile::StabsInfo& stab = (*chunk.fStabs)[j];
			entry->set_n_type(stab.type);
			entry->set_n_sect(stab.other);
			entry->set_n_desc(stab.desc);
			entry->set_n_value(valueForStab(stab, chunk.fAtom));
			entry->set_n_strx(this->fStringsAtom->add(stab.string));
			++entry;
		}	
		if ( (i == chunkCount-1) || (fStabChunks[i+1].fReader != chunk.fReader) ) {
			// need to add empty SO at end of each file
			entry->set_n_type(N_SO);
			entry->set_n_sect(1);
			entry->set_n_desc(0);
			entry->set_n_value(0);
			entry->set_n_strx(this->fStringsAtom->emptyString());
			++entry;
		}
	}
}




uint32_t Writer::symbolIndex(ObjectFile::Atom& atom)
{
	// search imports
	const int importCount = fImportedAtoms.size();
	for (int i=0; i < importCount; ++i) {
		if ( &atom == fImportedAtoms[i] )
			return i + fSymbolTableImportStartIndex;
	}
	
	// search locals
	const int localCount = fLocalSymbolAtoms.size();
	for (int i=0; i < localCount; ++i) {
		if ( &atom == fLocalSymbolAtoms[i] )
			return i + fSymbolTableLocalStartIndex;
	}
	
	// search exports
	const int exportCount = fExportedAtoms.size();
	for (int i=0; i < exportCount; ++i) {
		if ( &atom == fExportedAtoms[i] )
			return i + fSymbolTableExportStartIndex;
	}
	
	fprintf(stderr, "symbolIndex(%s)\n", atom.getDisplayName());
	fprintf(stderr, "from %s\n", atom.getFile()->getPath());
	throw "atom not found";
}


void Writer::buildFixups()
{
	if ( fOptions.outputKind() == Options::kObjectFile )
		this->buildObjectFileFixups();
	else
		this->buildExecutableFixups();
}

uint32_t Writer::addRelocs(ObjectFile::Atom* atom, ObjectFile::Reference* ref)
{
	ObjectFile::Atom& target = ref->getTarget();
	bool isExtern = target.isImportProxy() || ( strcmp(target.getSectionName(), "__common") == 0 );
	uint32_t symbolIndex = 0;
	if ( isExtern ) 
		symbolIndex = this->symbolIndex(target);
	uint32_t sectionNum = target.getSection()->getIndex();
	uint32_t address = atom->getSectionOffset()+ref->getFixUpOffset();
	macho_relocation_info reloc1;
	macho_relocation_info reloc2;
	macho_scattered_relocation_info* sreloc1 = (macho_scattered_relocation_info*)&reloc1;
	macho_scattered_relocation_info* sreloc2 = (macho_scattered_relocation_info*)&reloc2;
	
	switch ( ref->getKind() ) {
		case ObjectFile::Reference::noFixUp:
			return 0;
			
		case ObjectFile::Reference::pointer:
			reloc1.set_r_address(address);
			if ( isExtern )
				reloc1.set_r_symbolnum(symbolIndex);
			else
				reloc1.set_r_symbolnum(sectionNum);
			reloc1.set_r_pcrel(false);
			reloc1.set_r_length(macho_relocation_info::pointer_length); 
			reloc1.set_r_extern(isExtern);
			reloc1.set_r_type(GENERIC_RELOC_VANILLA);
			fInternalRelocs.insert(fInternalRelocs.begin(), reloc1);
			return 1;
			
		case ObjectFile::Reference::ppcFixupBranch24:
			if ( (ref->getTargetOffset() == 0) || isExtern ) {
				reloc1.set_r_address(address);
				if ( isExtern )
					reloc1.set_r_symbolnum(symbolIndex);
				else
					reloc1.set_r_symbolnum(sectionNum);
				reloc1.set_r_pcrel(true);
				reloc1.set_r_length(2); 
				reloc1.set_r_type(PPC_RELOC_BR24);
				reloc1.set_r_extern(isExtern);
			} 
			else {
				sreloc1->set_r_scattered(true);
				sreloc1->set_r_pcrel(true);
				sreloc1->set_r_length(2); 
				sreloc1->set_r_type(PPC_RELOC_BR24);
				sreloc1->set_r_address(address);
				sreloc1->set_r_value(target.getAddress());
			}
			fInternalRelocs.insert(fInternalRelocs.begin(), reloc1);
			return 1;
		
		case ObjectFile::Reference::ppcFixupBranch14:
			reloc1.set_r_address(address);
			reloc1.set_r_symbolnum(sectionNum);
			reloc1.set_r_pcrel(true);
			reloc1.set_r_length(2); 
			reloc1.set_r_extern(false);
			reloc1.set_r_type(PPC_RELOC_BR14);
			fInternalRelocs.insert(fInternalRelocs.begin(), reloc1);
			return 1;

		case ObjectFile::Reference::ppcFixupPicBaseLow14:
		case ObjectFile::Reference::ppcFixupPicBaseLow16:
			{
				macho_uintptr_t fromAddr = atom->getAddress() + ref->getFromTargetOffset();
				macho_uintptr_t toAddr = target.getAddress() + ref->getTargetOffset();
				uint32_t overflow = 0;
				if ( ((toAddr-fromAddr) & 0x00008000) != 0 )
					overflow = 1;
				sreloc1->set_r_scattered(true);
				sreloc1->set_r_pcrel(false);
				sreloc1->set_r_length(2); 
				if ( ref->getKind() == ObjectFile::Reference::ppcFixupPicBaseLow16 )
					sreloc1->set_r_type(PPC_RELOC_LO16_SECTDIFF);
				else
					sreloc1->set_r_type(PPC_RELOC_LO14_SECTDIFF);
				sreloc1->set_r_address(address);
				sreloc1->set_r_value(target.getAddress());
				sreloc2->set_r_scattered(true);
				sreloc2->set_r_pcrel(false);
				sreloc2->set_r_length(2); 
				sreloc2->set_r_type(PPC_RELOC_PAIR);
				sreloc2->set_r_address(((toAddr-fromAddr) >> 16));
				sreloc2->set_r_value(fromAddr);
				fInternalRelocs.insert(fInternalRelocs.begin(), reloc2);
				fInternalRelocs.insert(fInternalRelocs.begin(), reloc1);
				return 2;
			}
		
		case ObjectFile::Reference::ppcFixupPicBaseHigh16:
			{
				macho_uintptr_t fromAddr = atom->getAddress() + ref->getFromTargetOffset();
				macho_uintptr_t toAddr = target.getAddress() + ref->getTargetOffset();
				sreloc1->set_r_scattered(true);
				sreloc1->set_r_pcrel(false);
				sreloc1->set_r_length(2); 
				sreloc1->set_r_type(PPC_RELOC_HA16_SECTDIFF);
				sreloc1->set_r_address(address);
				sreloc1->set_r_value(target.getAddress());
				sreloc2->set_r_scattered(true);
				sreloc2->set_r_pcrel(false);
				sreloc2->set_r_length(2); 
				sreloc2->set_r_type(PPC_RELOC_PAIR);
				sreloc2->set_r_address((toAddr-fromAddr) & 0xFFFF);
				sreloc2->set_r_value(fromAddr);
				fInternalRelocs.insert(fInternalRelocs.begin(), reloc2);
				fInternalRelocs.insert(fInternalRelocs.begin(), reloc1);
				return 2;
			}
			
		case ObjectFile::Reference::ppcFixupAbsLow14:
		case ObjectFile::Reference::ppcFixupAbsLow16:
			{
				macho_uintptr_t toAddr = target.getAddress() + ref->getTargetOffset();
				if ( (ref->getTargetOffset() == 0) || isExtern ) {
					reloc1.set_r_address(address);
					if ( isExtern )
						reloc1.set_r_symbolnum(symbolIndex);
					else
						reloc1.set_r_symbolnum(sectionNum);
					reloc1.set_r_pcrel(false);
					reloc1.set_r_length(2); 
					reloc1.set_r_extern(isExtern);
					if ( ref->getKind() == ObjectFile::Reference::ppcFixupAbsLow16 )
						reloc1.set_r_type(PPC_RELOC_LO16);
					else
						reloc1.set_r_type(PPC_RELOC_LO14);
				}
				else {
					sreloc1->set_r_scattered(true);
					sreloc1->set_r_pcrel(false);
					sreloc1->set_r_length(2); 
					if ( ref->getKind() == ObjectFile::Reference::ppcFixupAbsLow16 )
						sreloc1->set_r_type(PPC_RELOC_LO16);
					else
						sreloc1->set_r_type(PPC_RELOC_LO14);
					sreloc1->set_r_address(address);
					sreloc1->set_r_value(target.getAddress());
				}
				if ( isExtern )
					reloc2.set_r_address(ref->getTargetOffset() >> 16); 
				else
					reloc2.set_r_address(toAddr >> 16);  
				reloc2.set_r_symbolnum(0);
				reloc2.set_r_pcrel(false);
				reloc2.set_r_length(2); 
				reloc2.set_r_extern(false);
				reloc2.set_r_type(PPC_RELOC_PAIR);
				fInternalRelocs.insert(fInternalRelocs.begin(), reloc2);
				fInternalRelocs.insert(fInternalRelocs.begin(), reloc1);
				return 2;
			}
			
		case ObjectFile::Reference::ppcFixupAbsHigh16:
			{
				macho_uintptr_t toAddr = target.getAddress() + ref->getTargetOffset();
				if ( (ref->getTargetOffset() == 0) || isExtern ) {
					reloc1.set_r_address(address);
					if ( isExtern )
						reloc1.set_r_symbolnum(symbolIndex);
					else
						reloc1.set_r_symbolnum(sectionNum);
					reloc1.set_r_pcrel(false);
					reloc1.set_r_length(2); 
					reloc1.set_r_extern(isExtern);
					reloc1.set_r_type(PPC_RELOC_HI16);
				}
				else {
					sreloc1->set_r_scattered(true);
					sreloc1->set_r_pcrel(false);
					sreloc1->set_r_length(2); 
					sreloc1->set_r_type(PPC_RELOC_HI16);
					sreloc1->set_r_address(address);
					sreloc1->set_r_value(target.getAddress());
				}
				if ( isExtern )
					reloc2.set_r_address(ref->getTargetOffset() & 0xFFFF); 
				else
					reloc2.set_r_address(toAddr & 0xFFFF);  
				reloc2.set_r_symbolnum(0);
				reloc2.set_r_pcrel(false);
				reloc2.set_r_length(2); 
				reloc2.set_r_extern(false);
				reloc2.set_r_type(PPC_RELOC_PAIR);
				fInternalRelocs.insert(fInternalRelocs.begin(), reloc2);
				fInternalRelocs.insert(fInternalRelocs.begin(), reloc1);
				return 2;
			}
		
		case ObjectFile::Reference::ppcFixupAbsHigh16AddLow:
			{
				macho_uintptr_t toAddr = target.getAddress() + ref->getTargetOffset();
				uint32_t overflow = 0;
				if ( (toAddr & 0x00008000) != 0 )
					overflow = 0x10000;
				if ( (ref->getTargetOffset() == 0) || isExtern ) {
					reloc1.set_r_address(address);
					if ( isExtern )
						reloc1.set_r_symbolnum(symbolIndex);
					else
						reloc1.set_r_symbolnum(sectionNum);
					reloc1.set_r_pcrel(false);
					reloc1.set_r_length(2); 
					reloc1.set_r_extern(isExtern);
					reloc1.set_r_type(PPC_RELOC_HA16);
				}
				else {
					sreloc1->set_r_scattered(true);
					sreloc1->set_r_pcrel(false);
					sreloc1->set_r_length(2); 
					sreloc1->set_r_type(PPC_RELOC_HA16);
					sreloc1->set_r_address(address);
					sreloc1->set_r_value(target.getAddress());
				}
				if ( isExtern )
					reloc2.set_r_address(ref->getTargetOffset() & 0xFFFF); 
				else
					reloc2.set_r_address(toAddr & 0xFFFF);  
				reloc2.set_r_symbolnum(0);
				reloc2.set_r_pcrel(false);
				reloc2.set_r_length(2); 
				reloc2.set_r_extern(false);
				reloc2.set_r_type(PPC_RELOC_PAIR);
				fInternalRelocs.insert(fInternalRelocs.begin(), reloc2);
				fInternalRelocs.insert(fInternalRelocs.begin(), reloc1);
				return 2;
			}
		
		case ObjectFile::Reference::pointer32Difference:
		case ObjectFile::Reference::pointer64Difference:
			{
				macho_uintptr_t toAddr = target.getAddress() + ref->getTargetOffset();
				macho_uintptr_t fromAddr = ref->getFromTarget().getAddress() + ref->getFromTargetOffset();
				sreloc1->set_r_scattered(true);
				sreloc1->set_r_pcrel(false);
				if ( ref->getKind() == ObjectFile::Reference::pointer64Difference )
					sreloc1->set_r_length(3); 
				else
					sreloc1->set_r_length(2); 
				if ( ref->getTargetOffset() != 0 )
					sreloc1->set_r_type(PPC_RELOC_LOCAL_SECTDIFF); 
				else
					sreloc1->set_r_type(PPC_RELOC_SECTDIFF); 
				sreloc1->set_r_address(address);
				sreloc1->set_r_value(toAddr);
				sreloc2->set_r_scattered(true);
				sreloc2->set_r_pcrel(false);
				sreloc2->set_r_length(macho_relocation_info::pointer_length); 
				sreloc2->set_r_type(PPC_RELOC_PAIR);
				sreloc2->set_r_address(0);
				sreloc2->set_r_value(fromAddr);
				fInternalRelocs.insert(fInternalRelocs.begin(), reloc2);
				fInternalRelocs.insert(fInternalRelocs.begin(), reloc1);
				return 2;
			}
			
		case ObjectFile::Reference::x86FixupBranch32:
			reloc1.set_r_address(address);
			reloc1.set_r_symbolnum(sectionNum);
			reloc1.set_r_pcrel(true);
			reloc1.set_r_length(2); 
			reloc1.set_r_extern(false);
			reloc1.set_r_type(GENERIC_RELOC_VANILLA);
			fInternalRelocs.insert(fInternalRelocs.begin(), reloc1);
			return 1;
		
	}
	return 0;
}


void Writer::buildObjectFileFixups()
{
	uint32_t relocIndex = 0;
	std::vector<SegmentInfo*>& segmentInfos = fSegmentInfos;
	const int segCount = segmentInfos.size();
	for(int i=0; i < segCount; ++i) {
		SegmentInfo* curSegment = segmentInfos[i];
		std::vector<SectionInfo*>& sectionInfos = curSegment->fSections;
		const int sectionCount = sectionInfos.size();
		for(int j=0; j < sectionCount; ++j) {
			SectionInfo* curSection = sectionInfos[j];
			std::vector<ObjectFile::Atom*>& sectionAtoms = curSection->fAtoms;
			if ( ! curSection->fAllZeroFill ) {
				if ( curSection->fAllNonLazyPointers || curSection->fAllLazyPointers )
					curSection->fIndirectSymbolOffset = fIndirectSymbolTable.size();
				curSection->fRelocOffset = relocIndex;
				const int atomCount = sectionAtoms.size();
				for (int k=0; k < atomCount; ++k) {
					ObjectFile::Atom* atom = sectionAtoms[k];
					std::vector<ObjectFile::Reference*>& refs = atom->getReferences();
					const int refCount = refs.size();
					for (int l=0; l < refCount; ++l) {
						ObjectFile::Reference* ref = refs[l];
						relocIndex += this->addRelocs(atom, ref);
						if ( curSection->fAllNonLazyPointers || curSection->fAllLazyPointers ) {
							uint32_t offsetInSection = atom->getSectionOffset();
							uint32_t indexInSection = offsetInSection / sizeof(macho_uintptr_t);	
							uint32_t undefinedSymbolIndex = this->symbolIndex(ref->getTarget());
							uint32_t indirectTableIndex = indexInSection + curSection->fIndirectSymbolOffset;
							IndirectEntry entry = { indirectTableIndex, undefinedSymbolIndex };
							//printf("fIndirectSymbolTable.add(%d-%d => 0x%X-%s), size=%lld\n", indexInSection, indirectTableIndex, undefinedSymbolIndex, ref->getTarget().getName(), atom->getSize());
							fIndirectSymbolTable.push_back(entry);
						}
					}
				}
				curSection->fRelocCount = relocIndex - curSection->fRelocOffset;
			}
		}
	}
	
	// now reverse reloc entries
	for(int i=0; i < segCount; ++i) {
		SegmentInfo* curSegment = segmentInfos[i];
		std::vector<SectionInfo*>& sectionInfos = curSegment->fSections;
		const int sectionCount = sectionInfos.size();
		for(int j=0; j < sectionCount; ++j) {
			SectionInfo* curSection = sectionInfos[j];
			curSection->fRelocOffset = relocIndex - curSection->fRelocOffset - curSection->fRelocCount; 
		}
	}
	
}


void Writer::buildExecutableFixups()
{
	const bool slideable = (fOptions.outputKind() != Options::kDynamicExecutable) && (fOptions.outputKind() != Options::kStaticExecutable);
	std::vector<SegmentInfo*>& segmentInfos = fSegmentInfos;
	const int segCount = segmentInfos.size();
	for(int i=0; i < segCount; ++i) {
		SegmentInfo* curSegment = segmentInfos[i];
		std::vector<SectionInfo*>& sectionInfos = curSegment->fSections;
		const int sectionCount = sectionInfos.size();
		for(int j=0; j < sectionCount; ++j) {
			SectionInfo* curSection = sectionInfos[j];
			std::vector<ObjectFile::Atom*>& sectionAtoms = curSection->fAtoms;
			if ( ! curSection->fAllZeroFill ) {
				if ( curSection->fAllNonLazyPointers || curSection->fAllLazyPointers )
					curSection->fIndirectSymbolOffset = fIndirectSymbolTable.size();
				const int atomCount = sectionAtoms.size();
				for (int k=0; k < atomCount; ++k) {
					ObjectFile::Atom* atom = sectionAtoms[k];
					std::vector<ObjectFile::Reference*>& refs = atom->getReferences();
					const int refCount = refs.size();
					//printf("atom %s has %d references\n", atom->getDisplayName(), refCount);
									
					for (int l=0; l < refCount; ++l) {
						ObjectFile::Reference* ref = refs[l];
						// only care about references that need dyld fixups
						if ( ref->requiresRuntimeFixUp() ) {
							if ( ! atom->getSegment().isContentWritable() )
								throwf("relocations in read-only segments not supported. %s in %s reference to %s", atom->getDisplayName(), atom->getFile()->getPath(), ref->getTarget().getDisplayName());
							if ( curSection->fAllNonLazyPointers || curSection->fAllLazyPointers ) {
								// if atom is in (non)lazy_pointer section, this is encoded as an indirect symbol
								if ( atom->getSize() != sizeof(macho_uintptr_t) ) {
									printf("oversize pointer atom %s from file %s\n", atom->getDisplayName(), atom->getFile()->getPath());
								}
								uint32_t offsetInSection = atom->getSectionOffset();
								uint32_t indexInSection = offsetInSection / sizeof(macho_uintptr_t);	
								uint32_t undefinedSymbolIndex = INDIRECT_SYMBOL_LOCAL;
								//printf("indirect pointer atom %s section offset = %d\n", atom->getDisplayName(), offsetInSection);
								if ( ref->getTarget().isImportProxy() 
								  || ref->getTarget().isWeakDefinition() 
								  || (fOptions.interposable() && fOptions.shouldExport(ref->getTarget().getName()))
								  || (fOptions.nameSpace() == Options::kFlatNameSpace) 
								  || (fOptions.nameSpace() == Options::kForceFlatNameSpace) ) {
									undefinedSymbolIndex = this->symbolIndex(ref->getTarget());
								}
								uint32_t indirectTableIndex = indexInSection + curSection->fIndirectSymbolOffset;
								IndirectEntry entry = { indirectTableIndex, undefinedSymbolIndex };
								//printf("fIndirectSymbolTable.add(%d-%d => 0x%X-%s), size=%lld\n", indexInSection, indirectTableIndex, undefinedSymbolIndex, ref->getTarget().getName(), atom->getSize());
								fIndirectSymbolTable.push_back(entry);
								if ( slideable && curSection->fAllLazyPointers ) {
									// if this is a dylib/bundle, need PBLAPTR internal relocation to fix up binding handler if image slides
									macho_relocation_info pblaReloc;
									macho_scattered_relocation_info* pblaSReloc = (macho_scattered_relocation_info*)&pblaReloc;
									pblaSReloc->set_r_scattered(true);
									pblaSReloc->set_r_pcrel(false);
									pblaSReloc->set_r_length(macho_relocation_info::pointer_length); 
									pblaSReloc->set_r_type(PPC_RELOC_PB_LA_PTR);
									pblaSReloc->set_r_address(atom->getAddress()-fOptions.baseAddress());
									pblaSReloc->set_r_value(ref->getFromTarget().getAddress()); // helper is stored in "from" address
									fInternalRelocs.push_back(pblaReloc);
								}
							}
							else if ( ref->getTarget().isImportProxy() ) {
								// if import is to antoher dylib, this is encoded as an external relocation
								macho_relocation_info externalReloc;
								externalReloc.set_r_address(atom->getAddress()+ref->getFixUpOffset()-fOptions.baseAddress());
								externalReloc.set_r_symbolnum(this->symbolIndex(ref->getTarget()));
								externalReloc.set_r_pcrel(false);
								externalReloc.set_r_length(macho_relocation_info::pointer_length); 
								externalReloc.set_r_extern(true);
								externalReloc.set_r_type(GENERIC_RELOC_VANILLA);
								fExternalRelocs.push_back(externalReloc);
							}
							else if ( slideable ) {
								// if this is a dylib/bundle, need fix-up encoded as an internal relocation
								macho_relocation_info internalReloc;
								SectionInfo* sectInfo = (SectionInfo*)ref->getTarget().getSection();
								uint32_t sectionNum = sectInfo->getIndex();
								// special case _mh_dylib_header and friends which are not in any real section
								if ( (sectionNum ==0) && sectInfo->fVirtualSection && (strcmp(sectInfo->fSectionName, "._mach_header") == 0) ) 
									sectionNum = 1;
								internalReloc.set_r_address(atom->getAddress()+ref->getFixUpOffset()-fOptions.baseAddress());
								internalReloc.set_r_symbolnum(sectionNum);
								internalReloc.set_r_pcrel(false);
								internalReloc.set_r_length(macho_relocation_info::pointer_length); 
								internalReloc.set_r_extern(false);
								internalReloc.set_r_type(GENERIC_RELOC_VANILLA);
								fInternalRelocs.push_back(internalReloc);
							}
						}
					}
				}
			}
		}
	}
}

class ContentWriter : public ObjectFile::ContentWriter
{
public:
					ContentWriter(int fd, uint64_t fileOffset) : fFileDescriptor(fd), fFileOffset(fileOffset) {}
	virtual void	write(uint64_t atomOffset, const void* buffer, uint64_t size) {
		::pwrite(fFileDescriptor, buffer, size, fFileOffset+atomOffset);
	}
private:
	int			fFileDescriptor;
	uint64_t 	fFileOffset;
};


void Writer::writeAtoms()
{
	const bool requireAllFixUps = (fOptions.outputKind() != Options::kObjectFile);

	std::vector<SegmentInfo*>& segmentInfos = fSegmentInfos;
	const int segCount = segmentInfos.size();
	for(int i=0; i < segCount; ++i) {
		SegmentInfo* curSegment = segmentInfos[i];
		bool isText = ((curSegment->fInitProtection & VM_PROT_EXECUTE) != 0);
		std::vector<SectionInfo*>& sectionInfos = curSegment->fSections;
		const int sectionCount = sectionInfos.size();
		for(int j=0; j < sectionCount; ++j) {
			SectionInfo* curSection = sectionInfos[j];
			std::vector<ObjectFile::Atom*>& sectionAtoms = curSection->fAtoms;
			//printf("writing %d atoms for section %s\n", (int)sectionAtoms.size(), curSection->fSectionName);
			if ( ! curSection->fAllZeroFill ) {
				const int atomCount = sectionAtoms.size();
				uint32_t end = curSection->fFileOffset;
				for (int k=0; k < atomCount; ++k) {
					ObjectFile::Atom* atom = sectionAtoms[k];
					if ( !atom->isImportProxy() ) {
						uint32_t offset = curSection->fFileOffset + atom->getSectionOffset();
						if ( isText && (offset != end) ) {
							// fill gaps with no-ops
			#if defined(ARCH_PPC) || defined(ARCH_PPC64) 
							uint32_t ppcNop;
							OSWriteBigInt32(&ppcNop, 0, 0x60000000);
							for (uint32_t p=end; p < offset; p += 4)
								::pwrite(fFileDescriptor, &ppcNop, 4, p);
			#else  defined(ARCH_I386)
							uint8_t x86Nop = 0x90;
							for (uint32_t p=end; p < offset; ++p)
								::pwrite(fFileDescriptor, &x86Nop, 1, p);
			#endif
						}
						ContentWriter writer(fFileDescriptor, offset);
						atom->writeContent(requireAllFixUps, writer);
						end = offset+atom->getSize();
						//printf("wrote 0x%08X -> 0x%08X, atom %s\n", offset, end, atom->getDisplayName());
					}
				}
			}
		}
	}
}	


void Writer::partitionIntoSections()
{
	const bool oneSegmentCommand = (fOptions.outputKind() == Options::kObjectFile);
	
	// for every atom, set its sectionInfo object and section offset
	// build up fSegmentInfos along the way
	ObjectFile::Section* curSection = NULL;
	SectionInfo* currentSectionInfo = NULL;
	SegmentInfo* currentSegmentInfo = NULL;
	unsigned int sectionIndex = 1;
	for (unsigned int i=0; i < fAllAtoms->size(); ++i) {
		ObjectFile::Atom* atom = (*fAllAtoms)[i];
		if ( atom->getSection() != curSection ) {
			if ( oneSegmentCommand ) {
				if ( currentSegmentInfo == NULL ) {
					currentSegmentInfo = new SegmentInfo();
					currentSegmentInfo->fInitProtection = VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE;
					currentSegmentInfo->fMaxProtection = VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE;
					this->fSegmentInfos.push_back(currentSegmentInfo);
				}
				currentSectionInfo = new SectionInfo();
				strcpy(currentSectionInfo->fSectionName, atom->getSectionName());
				strcpy(currentSectionInfo->fSegmentName, atom->getSegment().getName());
				currentSectionInfo->fAlignment = atom->getAlignment();
				currentSectionInfo->fAllZeroFill = atom->isZeroFill();
				currentSectionInfo->fVirtualSection = ( currentSectionInfo->fSectionName[0] == '.');
				if ( !currentSectionInfo->fVirtualSection || fEmitVirtualSections )
					currentSectionInfo->setIndex(sectionIndex++);
				currentSegmentInfo->fSections.push_back(currentSectionInfo);
			}
			else {
				if ( (currentSegmentInfo == NULL) || (strcmp(currentSegmentInfo->fName, atom->getSegment().getName()) != 0) ) {
					currentSegmentInfo = new SegmentInfo();
					strcpy(currentSegmentInfo->fName, atom->getSegment().getName());
					uint32_t initprot  = 0;
					if ( atom->getSegment().isContentReadable() )
						initprot |= VM_PROT_READ;
					if ( atom->getSegment().isContentWritable() )
						initprot |= VM_PROT_WRITE;
					if ( atom->getSegment().isContentExecutable() )
						initprot |= VM_PROT_EXECUTE;
					currentSegmentInfo->fInitProtection = initprot;
					if ( initprot == 0 )
						currentSegmentInfo->fMaxProtection = 0;  // pagezero should have maxprot==initprot==0
					else
						currentSegmentInfo->fMaxProtection = VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE;
					currentSegmentInfo->fBaseAddress = atom->getSegment().getBaseAddress();
					this->fSegmentInfos.push_back(currentSegmentInfo);
				}
				currentSectionInfo = new SectionInfo();
				strcpy(currentSectionInfo->fSectionName, atom->getSectionName());
				strcpy(currentSectionInfo->fSegmentName, atom->getSegment().getName());
				currentSectionInfo->fAlignment = atom->getAlignment();
				// check for -sectalign override
				std::vector<Options::SectionAlignment>&	alignmentOverrides = fOptions.sectionAlignments();
				for(std::vector<Options::SectionAlignment>::iterator it=alignmentOverrides.begin(); it != alignmentOverrides.end(); ++it) {
					if ( (strcmp(it->segmentName, currentSectionInfo->fSegmentName) == 0) && (strcmp(it->sectionName, currentSectionInfo->fSectionName) == 0) )
						currentSectionInfo->fAlignment = it->alignment;
				}
				currentSectionInfo->fAllZeroFill = atom->isZeroFill();
				currentSectionInfo->fVirtualSection = ( currentSectionInfo->fSectionName[0] == '.');
				if ( !currentSectionInfo->fVirtualSection || fEmitVirtualSections )
					currentSectionInfo->setIndex(sectionIndex++);
				currentSegmentInfo->fSections.push_back(currentSectionInfo);
			}
			if ( (strcmp(currentSectionInfo->fSegmentName, "__TEXT") == 0) && (strcmp(currentSectionInfo->fSectionName, "._load_commands") == 0) ) {
				fLoadCommandsSection = currentSectionInfo;
				fLoadCommandsSegment = currentSegmentInfo;
			}
			if ( (strcmp(currentSectionInfo->fSegmentName, "__DATA") == 0) && (strcmp(currentSectionInfo->fSectionName, "__la_symbol_ptr") == 0) )
				currentSectionInfo->fAllLazyPointers = true;
			if ( (strcmp(currentSectionInfo->fSegmentName, "__DATA") == 0) && (strcmp(currentSectionInfo->fSectionName, "__nl_symbol_ptr") == 0) )
				currentSectionInfo->fAllNonLazyPointers = true;
			curSection = atom->getSection();
		}
		// any non-zero fill atoms make whole section marked not-zero-fill
		if ( currentSectionInfo->fAllZeroFill && ! atom->isZeroFill() )
			currentSectionInfo->fAllZeroFill = false;
		// change section object to be Writer's SectionInfo object
		atom->setSection(currentSectionInfo);
		// section alignment is that of a contained atom with the greatest alignment
		uint8_t atomAlign = atom->getAlignment();
		if ( currentSectionInfo->fAlignment < atomAlign )
			currentSectionInfo->fAlignment = atomAlign;
		// calculate section offset for this atom
		uint64_t offset = currentSectionInfo->fSize;
		uint64_t alignment = 1 << atomAlign;
		offset = ( (offset+alignment-1) & (-alignment) );
		atom->setSectionOffset(offset);
		currentSectionInfo->fSize = offset + atom->getSize();
		// add atom to section vector
		currentSectionInfo->fAtoms.push_back(atom);
	}
}


void Writer::adjustLoadCommandsAndPadding()
{
	fSegmentCommands->computeSize();
	
	// recompute load command section offsets
	uint64_t offset = 0;
	std::vector<class ObjectFile::Atom*>& loadCommandAtoms = fLoadCommandsSection->fAtoms;
	const unsigned int atomCount = loadCommandAtoms.size();
	for (unsigned int i=0; i < atomCount; ++i) {
		ObjectFile::Atom* atom = loadCommandAtoms[i];
		uint64_t alignment = 1 << atom->getAlignment();
		offset = ( (offset+alignment-1) & (-alignment) );
		atom->setSectionOffset(offset);
		offset += atom->getSize();
		fLoadCommandsSection->fSize = offset;
	}
	
	std::vector<SectionInfo*>& sectionInfos = fLoadCommandsSegment->fSections;
	const int sectionCount = sectionInfos.size();
	uint64_t paddingSize = 0;
	if ( fOptions.outputKind() == Options::kDyld ) {
		// dyld itself has special padding requirements.  We want the beginning __text section to start at a stable address
		uint32_t totalSizeOfHeaderAndLoadCommands = 0;
		for(int j=0; j < sectionCount; ++j) {
			SectionInfo* curSection = sectionInfos[j];
			totalSizeOfHeaderAndLoadCommands += curSection->fSize;
			if ( strcmp(curSection->fSectionName, fHeaderPadding->getSectionName()) == 0 )
				break;
		}
		paddingSize = 4096 - (totalSizeOfHeaderAndLoadCommands % 4096);
	}
	else {
		// calculate max padding to keep segment size same, but all free space at end of load commands
		uint64_t totalSize = 0;
		uint64_t worstCaseAlignmentPadding = 0;
		for(int j=0; j < sectionCount; ++j) {
			SectionInfo* curSection = sectionInfos[j];
			totalSize += curSection->fSize;
			if ( j != 0 ) // don't count aligment of mach_header which is page-aligned
				worstCaseAlignmentPadding += (1 << curSection->fAlignment) - 1;
		}
		uint64_t segmentSize = ((totalSize+worstCaseAlignmentPadding+4095) & (-4096));
		// don't know exactly how it will layout, but we can inflate padding atom this big and still keep aligment constraints
		paddingSize = segmentSize - totalSize;
		
		// if command line requires more padding than this
		if ( paddingSize < fOptions.minimumHeaderPad() ) {
			int extraPages = (fOptions.minimumHeaderPad() - paddingSize + 4095)/4096;
			paddingSize += extraPages * 4096;
		}
	}
	
	// adjust atom size and update section size
	fHeaderPadding->setSize(paddingSize);
	for(int j=0; j < sectionCount; ++j) {
		SectionInfo* curSection = sectionInfos[j];
		if ( strcmp(curSection->fSectionName, fHeaderPadding->getSectionName()) == 0 )
			curSection->fSize = paddingSize;
	}
}

// assign file offsets and logical address to all segments
void Writer::assignFileOffsets()
{
	bool haveFixedSegments = false;
	uint64_t fileOffset = 0;
	uint64_t nextContiguousAddress = fOptions.baseAddress();
	std::vector<SegmentInfo*>& segmentInfos = fSegmentInfos;
	const int segCount = segmentInfos.size();
	for(int i=0; i < segCount; ++i) {
		SegmentInfo* curSegment = segmentInfos[i];
		fileOffset = (fileOffset+4095) & (-4096);
		curSegment->fFileOffset = fileOffset;
		if ( curSegment->fBaseAddress == 0 ) {
			// segment has uses next address
			curSegment->fBaseAddress = nextContiguousAddress;
		}
		else {
			// segment has fixed address
			haveFixedSegments = true;
		}
		uint64_t address = curSegment->fBaseAddress;
		std::vector<SectionInfo*>& sectionInfos = curSegment->fSections;
		const int sectionCount = sectionInfos.size();
		for(int j=0; j < sectionCount; ++j) {
			SectionInfo* curSection = sectionInfos[j];
			uint64_t alignment = 1 << curSection->fAlignment;
			fileOffset = ( (fileOffset+alignment-1) & (-alignment) );
			address    = ( (address+alignment-1)    & (-alignment) );
			curSection->fFileOffset = fileOffset;
			curSection->setBaseAddress(address);
			//printf("assignFileOffsets(): setBaseAddress(%s, 0x%08llX)\n", curSection->fSectionName, address);
			curSegment->fSize = curSection->getBaseAddress() + curSection->fSize - curSegment->fBaseAddress;
			if ( (fOptions.outputKind() != Options::kObjectFile) || ! curSection->fVirtualSection )
				address += curSection->fSize;
			if ( !curSection->fAllZeroFill ) {
				curSegment->fFileSize = curSegment->fSize;
				fileOffset += curSection->fSize;
			}
		}
		if ( curSegment->fBaseAddress == nextContiguousAddress )
			nextContiguousAddress = (curSegment->fBaseAddress+curSegment->fSize+4095) & (-4096);
	}
	
	// check for segment overlaps
	if ( haveFixedSegments ) {
		for(int i=0; i < segCount; ++i) {
			SegmentInfo* segment1 = segmentInfos[i];
			for(int j=0; j < segCount; ++j) {
				if ( i != j ) {
					SegmentInfo* segment2 = segmentInfos[j];
					if ( segment1->fBaseAddress < segment2->fBaseAddress ) {
						if ( (segment1->fBaseAddress+segment1->fSize) > segment2->fBaseAddress )
							throwf("segments overlap: %s (0x%08llX + 0x%08llX) and %s (0x%08llX + 0x%08llX)", 
								segment1->fName, segment1->fBaseAddress, segment1->fSize, segment2->fName, segment2->fBaseAddress, segment2->fSize);
					}
					else if ( segment1->fBaseAddress > segment2->fBaseAddress ) {
						if ( (segment2->fBaseAddress+segment2->fSize) > segment1->fBaseAddress )
							throwf("segments overlap: %s (0x%08llX + 0x%08llX) and %s (0x%08llX + 0x%08llX)", 
								segment1->fName, segment1->fBaseAddress, segment1->fSize, segment2->fName, segment2->fBaseAddress, segment2->fSize);
					}
					else {
							throwf("segments overlap: %s (0x%08llX + 0x%08llX) and %s (0x%08llX + 0x%08llX)", 
								segment1->fName, segment1->fBaseAddress, segment1->fSize, segment2->fName, segment2->fBaseAddress, segment2->fSize);
					}
				}
			}
		}
	}
}

void Writer::adjustLinkEditSections()
{
	// link edit content is always in last segment
	SegmentInfo* lastSeg = fSegmentInfos[fSegmentInfos.size()-1];
	unsigned int firstLinkEditSectionIndex = 0;
	while ( strcmp(lastSeg->fSections[firstLinkEditSectionIndex]->fSegmentName, "__LINKEDIT") != 0 )
		++firstLinkEditSectionIndex;
	
	const unsigned int sectionCount = lastSeg->fSections.size();
	uint64_t fileOffset = lastSeg->fSections[firstLinkEditSectionIndex]->fFileOffset;
	uint64_t address = lastSeg->fSections[firstLinkEditSectionIndex]->getBaseAddress();
	for (unsigned int i=firstLinkEditSectionIndex; i < sectionCount; ++i) {
		std::vector<class ObjectFile::Atom*>& atoms = lastSeg->fSections[i]->fAtoms;
		const unsigned int atomCount = atoms.size();
		uint64_t sectionOffset = 0;
		lastSeg->fSections[i]->fFileOffset = fileOffset;
		lastSeg->fSections[i]->setBaseAddress(address);
		for (unsigned int j=0; j < atomCount; ++j) {
			ObjectFile::Atom* atom = atoms[j];
			uint64_t alignment = 1 << atom->getAlignment();
			sectionOffset = ( (sectionOffset+alignment-1) & (-alignment) );
			atom->setSectionOffset(sectionOffset);
			sectionOffset += atom->getSize();
		}
		lastSeg->fSections[i]->fSize = sectionOffset;
		fileOffset += sectionOffset;
		address += sectionOffset;
	}
	if ( fOptions.outputKind() == Options::kObjectFile ) {
		
		//lastSeg->fBaseAddress = 0;
		//lastSeg->fSize = lastSeg->fSections[firstLinkEditSectionIndex]->
		//lastSeg->fFileOffset = 0;
		//lastSeg->fFileSize =
	}
	else {
		lastSeg->fFileSize = fileOffset - lastSeg->fFileOffset;
		lastSeg->fSize = address - lastSeg->fBaseAddress;
	}
}


ObjectFile::Atom::Scope MachHeaderAtom::getScope() const
{ 
	switch ( fWriter.fOptions.outputKind() ) {
		case Options::kDynamicExecutable:
		case Options::kStaticExecutable:
			return ObjectFile::Atom::scopeGlobal; 
		case Options::kDynamicLibrary:
		case Options::kDynamicBundle:
		case Options::kDyld:
		case Options::kObjectFile:
			return ObjectFile::Atom::scopeLinkageUnit; 
	}
	throw "unknown header type";
}

bool MachHeaderAtom::dontStripName() const
{ 
	switch ( fWriter.fOptions.outputKind() ) {
		case Options::kDynamicExecutable:
		case Options::kStaticExecutable:
			return true; 
		case Options::kDynamicLibrary:
		case Options::kDynamicBundle:
		case Options::kDyld:
		case Options::kObjectFile:
			return false; 
	}
	throw "unknown header type";
}

const char* MachHeaderAtom::getName() const
{
	switch ( fWriter.fOptions.outputKind() ) {
		case Options::kDynamicExecutable:
		case Options::kStaticExecutable:
			return "__mh_execute_header";
		case Options::kDynamicLibrary:
			return "__mh_dylib_header";
		case Options::kDynamicBundle:
			return "__mh_bundle_header";
		case Options::kObjectFile:
			return NULL;
		case Options::kDyld:
			return "__mh_dylinker_header";
	}
	throw "unknown header type";
}

const char* MachHeaderAtom::getDisplayName() const
{
	switch ( fWriter.fOptions.outputKind() ) {
		case Options::kDynamicExecutable:
		case Options::kStaticExecutable:
		case Options::kDynamicLibrary:
		case Options::kDynamicBundle:
		case Options::kDyld:
			return this->getName();
		case Options::kObjectFile:
			return "mach header";
	}
	throw "unknown header type";
}

uint64_t MachHeaderAtom::getSize() const
{
	return macho_header::size;
}

void MachHeaderAtom::writeContent(bool finalLinkedImage, ObjectFile::ContentWriter& writer) const
{
	macho_header mh;

	// get file type
	uint32_t fileType = 0;
	switch ( fWriter.fOptions.outputKind() ) {
		case Options::kDynamicExecutable:
		case Options::kStaticExecutable:
			fileType = MH_EXECUTE;
			break;
		case Options::kDynamicLibrary:
			fileType = MH_DYLIB;
			break;
		case Options::kDynamicBundle:
			fileType = MH_BUNDLE;
			break;
		case Options::kObjectFile:
			fileType = MH_OBJECT;
			break;
		case Options::kDyld:
			fileType = MH_DYLINKER;
			break;
	}

	// get flags
	uint32_t flags = 0;
	if ( fWriter.fOptions.outputKind() == Options::kObjectFile ) {
		flags = MH_SUBSECTIONS_VIA_SYMBOLS;
	}
	else {
		flags = MH_DYLDLINK;
		if ( fWriter.fOptions.bindAtLoad() )
			flags |= MH_BINDATLOAD;
		switch ( fWriter.fOptions.nameSpace() ) {
			case Options::kTwoLevelNameSpace:
				flags |= MH_TWOLEVEL | MH_NOUNDEFS;
				break;
			case Options::kFlatNameSpace:
				break;
			case Options::kForceFlatNameSpace:
				flags |= MH_FORCE_FLAT;
				break;
		}
		if ( fWriter.fHasWeakExports ) 
			flags |= MH_WEAK_DEFINES;
		if ( fWriter.fReferencesWeakImports || fWriter.fHasWeakExports )
			flags |= MH_BINDS_TO_WEAK;
	}
	
	// get commands info
	uint32_t commandsSize = 0;
	uint32_t commandsCount = 0;
	
	std::vector<class ObjectFile::Atom*>& loadCommandAtoms = fWriter.fLoadCommandsSection->fAtoms;
	const unsigned int atomCount = loadCommandAtoms.size();
	for (unsigned int i=0; i < atomCount; ++i) {
		ObjectFile::Atom* atom = loadCommandAtoms[i];
		commandsSize += atom->getSize();
		// segment and symbol table atoms can contain more than one load command
		if ( atom == fWriter.fSegmentCommands )
			commandsCount += fWriter.fSegmentCommands->commandCount();
		else if ( atom == fWriter.fSymbolTableCommands )
			commandsCount += 2;
		else
			++commandsCount;
	}
	
	// fill out mach_header
	mh.set_magic(macho_header::magic_value);
	mh.set_cputype(fWriter.fOptions.architecture());
	mh.set_cpusubtype(0);
	mh.set_filetype(fileType);
	mh.set_ncmds(commandsCount);		
	mh.set_sizeofcmds(commandsSize);	
	mh.set_flags(flags);
	mh.set_reserved();
	
	// write it
	writer.write(0, &mh, macho_header::size);
}


CustomStackAtom::CustomStackAtom(Writer& writer) 
 : WriterAtom(writer, fgStackSegment)
{
#if defined(ARCH_PPC) || defined(ARCH_PPC64) || defined(ARCH_I386)
	// stack grows down for these architectures
	fgStackSegment.setBaseAddress(writer.fOptions.customStackAddr() - writer.fOptions.customStackSize());
#else
	#error unknown architecture
#endif
}


void SegmentLoadCommandsAtom::computeSize() 
{
	uint64_t size = 0;
	std::vector<Writer::SegmentInfo*>& segmentInfos = fWriter.fSegmentInfos;
	const int segCount = segmentInfos.size();
	for(int i=0; i < segCount; ++i) {
		size += macho_segment_command::size;
		std::vector<Writer::SectionInfo*>& sectionInfos = segmentInfos[i]->fSections;
		const int sectionCount = sectionInfos.size();		
		for(int j=0; j < sectionCount; ++j) {
			if ( fWriter.fEmitVirtualSections || ! sectionInfos[j]->fVirtualSection )
				size += macho_section::content_size;
		}
	}
	fSize = size;
	fCommandCount = segCount;
}



void SegmentLoadCommandsAtom::writeContent(bool finalLinkedImage, ObjectFile::ContentWriter& writer) const
{
	uint64_t size = this->getSize();
	uint8_t buffer[size];
	const bool oneSegment =( fWriter.fOptions.outputKind() == Options::kObjectFile );
	bzero(buffer, fSize);
	uint8_t* p = buffer;
	std::vector<Writer::SegmentInfo*>& segmentInfos = fWriter.fSegmentInfos;
	const int segCount = segmentInfos.size();
	for(int i=0; i < segCount; ++i) {
		Writer::SegmentInfo* segInfo = segmentInfos[i];
		const int sectionCount = segInfo->fSections.size();
		macho_segment_command* cmd = (macho_segment_command*)p;
		cmd->set_cmd(macho_segment_command::command);
		cmd->set_segname(segInfo->fName);
		cmd->set_vmaddr(segInfo->fBaseAddress);
		cmd->set_vmsize(segInfo->fSize);
		cmd->set_fileoff(segInfo->fFileOffset);
		cmd->set_filesize(segInfo->fFileSize);
		cmd->set_maxprot(segInfo->fMaxProtection);
		cmd->set_initprot(segInfo->fInitProtection);
		// add sections array
		macho_section* const sections = (macho_section*)&p[macho_segment_command::size];
		unsigned int sectionsEmitted = 0;
		for (int j=0; j < sectionCount; ++j) {
			Writer::SectionInfo* sectInfo = segInfo->fSections[j];
			if ( fWriter.fEmitVirtualSections || !sectInfo->fVirtualSection ) {
				macho_section* sect = &sections[sectionsEmitted++];
				if ( oneSegment ) {
					// .o files have weird segment range
					if ( sectionsEmitted == 1 ) {
						cmd->set_vmaddr(sectInfo->getBaseAddress());
						cmd->set_fileoff(sectInfo->fFileOffset);
						cmd->set_filesize(segInfo->fFileSize-sectInfo->fFileOffset);
					}
					cmd->set_vmsize(sectInfo->getBaseAddress() + sectInfo->fSize);
				}
				sect->set_sectname(sectInfo->fSectionName);
				sect->set_segname(sectInfo->fSegmentName);
				sect->set_addr(sectInfo->getBaseAddress());
				sect->set_size(sectInfo->fSize);
				sect->set_offset(sectInfo->fFileOffset);
				sect->set_align(sectInfo->fAlignment);
				if ( sectInfo->fRelocCount != 0 ) {
					sect->set_reloff(sectInfo->fRelocOffset * macho_relocation_info::size + fWriter.fLocalRelocationsAtom->getFileOffset());
					sect->set_nreloc(sectInfo->fRelocCount);
				}
				if ( sectInfo->fAllZeroFill ) {
					sect->set_flags(S_ZEROFILL);
				}
				else if ( sectInfo->fAllLazyPointers ) {
					sect->set_flags(S_LAZY_SYMBOL_POINTERS);
					sect->set_reserved1(sectInfo->fIndirectSymbolOffset);
				}
				else if ( sectInfo->fAllNonLazyPointers ) {
					sect->set_flags(S_NON_LAZY_SYMBOL_POINTERS);
					sect->set_reserved1(sectInfo->fIndirectSymbolOffset);
				}
				else if ( (strcmp(sectInfo->fSectionName, "__mod_init_func") == 0) && (strcmp(sectInfo->fSegmentName, "__DATA") == 0) ) {
					sect->set_flags(S_MOD_INIT_FUNC_POINTERS);
				}
				else if ( (strcmp(sectInfo->fSectionName, "__mod_term_func") == 0) && (strcmp(sectInfo->fSegmentName, "__DATA") == 0) ) {
					sect->set_flags(S_MOD_TERM_FUNC_POINTERS);
				}
			}
		}
		p = &p[macho_segment_command::size + sectionsEmitted*macho_section::content_size];
		cmd->set_cmdsize(macho_segment_command::size + sectionsEmitted*macho_section::content_size);
		cmd->set_nsects(sectionsEmitted);
	}
	writer.write(0, buffer, size);
}


SymbolTableLoadCommandsAtom::SymbolTableLoadCommandsAtom(Writer& writer)
 : WriterAtom(writer, fgTextSegment)
{
	bzero(&fSymbolTable, macho_symtab_command::size);
	bzero(&fDynamicSymbolTable, macho_dysymtab_command::size);
	writer.fSymbolTableCommands = this;
}

uint64_t SymbolTableLoadCommandsAtom::getSize() const
{
	return macho_symtab_command::size + macho_dysymtab_command::size;
}

void SymbolTableLoadCommandsAtom::writeContent(bool finalLinkedImage, ObjectFile::ContentWriter& writer) const
{
	// build LC_DYSYMTAB command
	macho_symtab_command   symbolTableCmd;
	bzero(&symbolTableCmd, macho_symtab_command::size);
	symbolTableCmd.set_cmd(LC_SYMTAB);
	symbolTableCmd.set_cmdsize(macho_symtab_command::size);
	symbolTableCmd.set_nsyms(fWriter.fSymbolTableCount);
	symbolTableCmd.set_symoff(fWriter.fSymbolTableAtom->getFileOffset());
	symbolTableCmd.set_stroff(fWriter.fStringsAtom->getFileOffset());
	symbolTableCmd.set_strsize(fWriter.fStringsAtom->getSize());
	writer.write(0, &symbolTableCmd, macho_symtab_command::size);
		
	// build LC_DYSYMTAB command
	macho_dysymtab_command dynamicSymbolTableCmd;
	bzero(&dynamicSymbolTableCmd, macho_dysymtab_command::size);
	dynamicSymbolTableCmd.set_cmd(LC_DYSYMTAB);
	dynamicSymbolTableCmd.set_cmdsize(macho_dysymtab_command::size);
	dynamicSymbolTableCmd.set_ilocalsym(fWriter.fSymbolTableStabsStartIndex);
	dynamicSymbolTableCmd.set_nlocalsym(fWriter.fSymbolTableStabsCount + fWriter.fSymbolTableLocalCount);
	dynamicSymbolTableCmd.set_iextdefsym(fWriter.fSymbolTableExportStartIndex);
	dynamicSymbolTableCmd.set_nextdefsym(fWriter.fSymbolTableExportCount);
	dynamicSymbolTableCmd.set_iundefsym(fWriter.fSymbolTableImportStartIndex);
	dynamicSymbolTableCmd.set_nundefsym(fWriter.fSymbolTableImportCount);
	dynamicSymbolTableCmd.set_indirectsymoff(fWriter.fIndirectTableAtom->getFileOffset());
	dynamicSymbolTableCmd.set_nindirectsyms(fWriter.fIndirectSymbolTable.size());
	if ( fWriter.fOptions.outputKind() != Options::kObjectFile ) {
		dynamicSymbolTableCmd.set_extreloff((fWriter.fExternalRelocs.size()==0) ? 0 : fWriter.fExternalRelocationsAtom->getFileOffset());
		dynamicSymbolTableCmd.set_nextrel(fWriter.fExternalRelocs.size());
		dynamicSymbolTableCmd.set_locreloff((fWriter.fInternalRelocs.size()==0) ? 0 : fWriter.fLocalRelocationsAtom->getFileOffset());
		dynamicSymbolTableCmd.set_nlocrel(fWriter.fInternalRelocs.size());
	}
	writer.write(macho_symtab_command::size, &dynamicSymbolTableCmd, macho_dysymtab_command::size);
}

uint64_t DyldLoadCommandsAtom::getSize() const
{
	uint32_t len = macho_dylinker_command::name_offset + strlen("/usr/lib/dyld");
	len = (len+7) & (-8);	// 8-byte align
	return len;
}

void DyldLoadCommandsAtom::writeContent(bool finalLinkedImage, ObjectFile::ContentWriter& writer) const
{
	uint64_t size = this->getSize();
	uint8_t buffer[size];
	macho_dylinker_command* cmd = (macho_dylinker_command*)buffer;
	if ( fWriter.fOptions.outputKind() == Options::kDyld )
		cmd->set_cmd(LC_ID_DYLINKER);
	else
		cmd->set_cmd(LC_LOAD_DYLINKER);
	cmd->set_cmdsize(this->getSize());
	cmd->set_name_offset();
	strcpy((char*)&buffer[macho_dylinker_command::name_offset], "/usr/lib/dyld");
	writer.write(0, buffer, size);
}



uint64_t DylibLoadCommandsAtom::getSize() const
{
	const char* path = fInfo.reader->getInstallPath();
	if ( fInfo.options.fInstallPathOverride != NULL ) 
		path = fInfo.options.fInstallPathOverride;
	uint32_t len = macho_dylib_command::name_offset + strlen(path);
	len = (len+7) & (-8);	// 8-byte align
	return len;
}

void DylibLoadCommandsAtom::writeContent(bool finalLinkedImage, ObjectFile::ContentWriter& writer) const
{
	uint64_t size = this->getSize();
	uint8_t buffer[size];
	bzero(buffer, size);
	const char* path = fInfo.reader->getInstallPath();
	if ( fInfo.options.fInstallPathOverride != NULL ) 
		path = fInfo.options.fInstallPathOverride;
	macho_dylib_command* cmd = (macho_dylib_command*)buffer;
	if ( fInfo.options.fWeakImport )
		cmd->set_cmd(LC_LOAD_WEAK_DYLIB);
	else
		cmd->set_cmd(LC_LOAD_DYLIB);
	cmd->set_cmdsize(this->getSize());
	cmd->set_timestamp(fInfo.reader->getTimestamp());
	cmd->set_current_version(fInfo.reader->getCurrentVersion());
	cmd->set_compatibility_version(fInfo.reader->getCompatibilityVersion());
	cmd->set_name_offset();
	strcpy((char*)&buffer[macho_dylib_command::name_offset], path);
	writer.write(0, buffer, size);
}



uint64_t DylibIDLoadCommandsAtom::getSize() const
{
	uint32_t len = macho_dylib_command::name_offset + strlen(fWriter.fOptions.installPath());
	len = (len+7) & (-8);	// 8-byte align
	return len;
}

void DylibIDLoadCommandsAtom::writeContent(bool finalLinkedImage, ObjectFile::ContentWriter& writer) const
{
	struct timeval currentTime = { 0 , 0 };
	gettimeofday(&currentTime, NULL);
	time_t timestamp = currentTime.tv_sec;
	uint64_t size = this->getSize();
	uint8_t buffer[size];
	bzero(buffer, size);
	macho_dylib_command* cmd = (macho_dylib_command*)buffer;
	cmd->set_cmd(LC_ID_DYLIB);
	cmd->set_cmdsize(this->getSize());
	cmd->set_name_offset();
	cmd->set_timestamp(timestamp);
	cmd->set_current_version(fWriter.fOptions.currentVersion());
	cmd->set_compatibility_version(fWriter.fOptions.compatibilityVersion());
	strcpy((char*)&buffer[macho_dylib_command::name_offset], fWriter.fOptions.installPath());
	writer.write(0, buffer, size);
}


uint64_t RoutinesLoadCommandsAtom::getSize() const
{
	return macho_routines_command::size;
}

void RoutinesLoadCommandsAtom::writeContent(bool finalLinkedImage, ObjectFile::ContentWriter& writer) const
{
	uint64_t initAddr = fWriter.getAtomLoadAddress(fWriter.fEntryPoint);
	uint8_t buffer[macho_routines_command::size];
	bzero(buffer, macho_routines_command::size);
	macho_routines_command* cmd = (macho_routines_command*)buffer;
	cmd->set_cmd(macho_routines_command::command);
	cmd->set_cmdsize(this->getSize());
	cmd->set_init_address(initAddr);
	writer.write(0, buffer, macho_routines_command::size);
}


uint64_t SubUmbrellaLoadCommandsAtom::getSize() const
{
	uint32_t len = macho_sub_umbrella_command::name_offset + strlen(fName);
	len = (len+7) & (-8);	// 8-byte align
	return len;
}

void SubUmbrellaLoadCommandsAtom::writeContent(bool finalLinkedImage, ObjectFile::ContentWriter& writer) const
{
	uint64_t size = this->getSize();
	uint8_t buffer[size];
	bzero(buffer, size);
	macho_sub_umbrella_command* cmd = (macho_sub_umbrella_command*)buffer;
	cmd->set_cmd(LC_SUB_UMBRELLA);
	cmd->set_cmdsize(this->getSize());
	cmd->set_name_offset();
	strcpy((char*)&buffer[macho_sub_umbrella_command::name_offset], fName);
	writer.write(0, buffer, size);
}


uint64_t SubLibraryLoadCommandsAtom::getSize() const
{
	uint32_t len = macho_sub_library_command::name_offset + fNameLength + 1;
	len = (len+7) & (-8);	// 8-byte align
	return len;
}

void SubLibraryLoadCommandsAtom::writeContent(bool finalLinkedImage, ObjectFile::ContentWriter& writer) const
{
	uint64_t size = this->getSize();
	uint8_t buffer[size];
	bzero(buffer, size);
	macho_sub_library_command* cmd = (macho_sub_library_command*)buffer;
	cmd->set_cmd(LC_SUB_LIBRARY);
	cmd->set_cmdsize(this->getSize());
	cmd->set_name_offset();
	strncpy((char*)&buffer[macho_sub_library_command::name_offset], fNameStart, fNameLength);
	buffer[macho_sub_library_command::name_offset+fNameLength] = '\0';
	writer.write(0, buffer, size);
}

uint64_t UmbrellaLoadCommandsAtom::getSize() const
{
	uint32_t len = macho_sub_framework_command::name_offset + strlen(fName);
	len = (len+7) & (-8);	// 8-byte align
	return len;
}

void UmbrellaLoadCommandsAtom::writeContent(bool finalLinkedImage, ObjectFile::ContentWriter& writer) const
{
	uint64_t size = this->getSize();
	uint8_t buffer[size];
	bzero(buffer, size);
	macho_sub_framework_command* cmd = (macho_sub_framework_command*)buffer;
	cmd->set_cmd(LC_SUB_FRAMEWORK);
	cmd->set_cmdsize(this->getSize());
	cmd->set_name_offset();
	strcpy((char*)&buffer[macho_sub_framework_command::name_offset], fName);
	writer.write(0, buffer, size);
}

uint64_t ThreadsLoadCommandsAtom::getSize() const
{
#if defined(ARCH_PPC)
		uint32_t stateSize = 40;		// PPC_THREAD_STATE_COUNT;
#elif defined(ARCH_PPC64)
		uint32_t stateSize = 76;		// PPC_THREAD_STATE64_COUNT;
#elif defined(ARCH_I386)
		uint32_t stateSize = 16;		// i386_THREAD_STATE_COUNT;
#else
	#error unknown architecture
#endif
	return macho_thread_command::size + stateSize*4;
}

void ThreadsLoadCommandsAtom::writeContent(bool finalLinkedImage, ObjectFile::ContentWriter& writer) const
{
	uint64_t size = this->getSize();
	uint8_t buffer[size];
	uint64_t start = fWriter.getAtomLoadAddress(fWriter.fEntryPoint);
	bzero(buffer, size);
	macho_thread_command* cmd = (macho_thread_command*)buffer;
	cmd->set_cmd(LC_UNIXTHREAD);
	cmd->set_cmdsize(size);
#if defined(ARCH_PPC)
	cmd->set_flavor(1);				// PPC_THREAD_STATE
	cmd->set_count(40);				// PPC_THREAD_STATE_COUNT;
	cmd->set_threadState32(0, start);
	if ( fWriter.fOptions.hasCustomStack() )
		cmd->set_threadState32(3, fWriter.fOptions.customStackAddr());	// r1
#elif defined(ARCH_PPC64)
	cmd->set_flavor(5);				// PPC_THREAD_STATE64
	cmd->set_count(76);				// PPC_THREAD_STATE64_COUNT;
	cmd->set_threadState64(0, start);
	if ( fWriter.fOptions.hasCustomStack() )
		cmd->set_threadState64(6, fWriter.fOptions.customStackAddr());	// r1
#elif defined(ARCH_I386)
	cmd->set_flavor(0xFFFFFFFF);	// i386_THREAD_STATE
	cmd->set_count(16);				// i386_THREAD_STATE_COUNT;
	cmd->set_threadState32(0, start);
	if ( fWriter.fOptions.hasCustomStack() )
		cmd->set_threadState32(15, fWriter.fOptions.customStackAddr());	// uesp
#else
	#error unknown architecture
#endif
	writer.write(0, buffer, size);
}



uint64_t LoadCommandsPaddingAtom::getSize() const
{
	return fSize;
}

void LoadCommandsPaddingAtom::writeContent(bool finalLinkedImage, ObjectFile::ContentWriter& writer) const
{
	uint8_t buffer[fSize];
	bzero(buffer, fSize);
	writer.write(0, buffer, fSize);
}


uint64_t LinkEditAtom::getFileOffset() const
{
	return ((Writer::SectionInfo*)this->getSection())->fFileOffset + this->getSectionOffset();
}


uint64_t LocalRelocationsLinkEditAtom::getSize() const
{
	return fWriter.fInternalRelocs.size() * macho_relocation_info::size;
}

void LocalRelocationsLinkEditAtom::writeContent(bool finalLinkedImage, ObjectFile::ContentWriter& writer) const
{
	writer.write(0, &fWriter.fInternalRelocs[0], this->getSize());
}



uint64_t SymbolTableLinkEditAtom::getSize() const
{
	return fWriter.fSymbolTableCount * macho_nlist::size;
}

void SymbolTableLinkEditAtom::writeContent(bool finalLinkedImage, ObjectFile::ContentWriter& writer) const
{
	writer.write(0, fWriter.fSymbolTable, this->getSize());
}

uint64_t ExternalRelocationsLinkEditAtom::getSize() const
{
	return fWriter.fExternalRelocs.size() * macho_relocation_info::size;
}

void ExternalRelocationsLinkEditAtom::writeContent(bool finalLinkedImage, ObjectFile::ContentWriter& writer) const
{
	writer.write(0, &fWriter.fExternalRelocs[0], this->getSize());
}



uint64_t IndirectTableLinkEditAtom::getSize() const
{
	return fWriter.fIndirectSymbolTable.size() * sizeof(uint32_t);
}

void IndirectTableLinkEditAtom::writeContent(bool finalLinkedImage, ObjectFile::ContentWriter& writer) const
{
	uint64_t size = this->getSize();
	uint8_t buffer[size];
	bzero(buffer, size);
	const uint32_t indirectTableSize = fWriter.fIndirectSymbolTable.size();
	uint32_t* indirectTable = (uint32_t*)buffer;
	for (uint32_t i=0; i < indirectTableSize; ++i) {
		Writer::IndirectEntry& entry = fWriter.fIndirectSymbolTable[i];
		if ( entry.indirectIndex < indirectTableSize ) {
			ENDIAN_WRITE32(indirectTable[entry.indirectIndex], entry.symbolIndex);
		}
		else {
			throw "malformed indirect table";
		}
	}
	writer.write(0, buffer, size);
}



StringsLinkEditAtom::StringsLinkEditAtom(Writer& writer) 
	: LinkEditAtom(writer), fCurrentBuffer(NULL), fCurrentBufferUsed(0)
{ 
	fCurrentBuffer = new char[kBufferSize];
	// burn first byte of string pool (so zero is never a valid string offset)
	fCurrentBuffer[fCurrentBufferUsed++] = ' ';
	// make offset 1 always point to an empty string
	fCurrentBuffer[fCurrentBufferUsed++] = '\0';
}

uint64_t StringsLinkEditAtom::getSize() const
{
	return kBufferSize * fFullBuffers.size() + fCurrentBufferUsed;
}

void StringsLinkEditAtom::writeContent(bool finalLinkedImage, ObjectFile::ContentWriter& writer) const
{
	uint64_t offset = 0;
	for (unsigned int i=0; i < fFullBuffers.size(); ++i) {
		writer.write(offset, fFullBuffers[i], kBufferSize);
		offset += kBufferSize;
	}
	writer.write(offset, fCurrentBuffer, fCurrentBufferUsed);
}

int32_t StringsLinkEditAtom::add(const char* name)
{
	int lenNeeded = strlen(name)+1;
	while ( lenNeeded + fCurrentBufferUsed >= kBufferSize ) {
		// first part of string fits in current buffer
		int firstLen = kBufferSize - fCurrentBufferUsed;
		memcpy(&fCurrentBuffer[fCurrentBufferUsed], name, firstLen);
		// alloc next buffer
		fFullBuffers.push_back(fCurrentBuffer);
		fCurrentBuffer = new char[kBufferSize];
		fCurrentBufferUsed = 0;
		// advance name to second part
		name += firstLen;
		lenNeeded -= firstLen;
	}
	//fprintf(stderr, "StringsLinkEditAtom::add(): lenNeeded=%d, fCurrentBuffer=%d, fCurrentBufferUsed=%d\n", lenNeeded, fCurrentBuffer, fCurrentBufferUsed);
	// string all fits in current buffer
	strcpy(&fCurrentBuffer[fCurrentBufferUsed], name);
	int32_t offset = kBufferSize * fFullBuffers.size() + fCurrentBufferUsed;
	fCurrentBufferUsed += lenNeeded;
	return offset;
}

// returns the index of an empty string
int32_t StringsLinkEditAtom::emptyString()
{
	 return 1;
}



};