ObjectDump.cpp   [plain text]


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

#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <mach-o/nlist.h>
#include <mach-o/stab.h>
#include <mach-o/fat.h>
#include <mach-o/loader.h>

#include "MachOFileAbstraction.hpp"
#include "parsers/macho_relocatable_file.h"
#include "parsers/lto_file.h"

static bool			sDumpContent= true;
static bool			sDumpStabs	= false;
static bool			sSort		= true;
static bool			sNMmode		= false;
static bool			sShowSection			= true;
static bool			sShowDefinitionKind		= true;
static bool			sShowCombineKind		= true;
static bool			sShowLineInfo			= true;

static cpu_type_t		sPreferredArch = 0xFFFFFFFF;
static cpu_subtype_t	sPreferredSubArch = 0xFFFFFFFF;
static const char* sMatchName = NULL;
static int sPrintRestrict;
static int sPrintAlign;
static int sPrintName;

 __attribute__((noreturn))
void throwf(const char* format, ...) 
{
	va_list	list;
	char*	p;
	va_start(list, format);
	vasprintf(&p, format, list);
	va_end(list);
	
	const char*	t = p;
	throw t;
}

void warning(const char* format, ...)
{
	va_list	list;
	fprintf(stderr, "warning: ");
	va_start(list, format);
	vfprintf(stderr, format, list);
	va_end(list);
	fprintf(stderr, "\n");
}

static void dumpStabs(const std::vector<ld::relocatable::File::Stab>* stabs)
{
	// debug info
	printf("stabs: (%lu)\n", stabs->size());
	for (std::vector<ld::relocatable::File::Stab>::const_iterator it = stabs->begin(); it != stabs->end(); ++it ) {
		const ld::relocatable::File::Stab& stab = *it;
		const char* code = "?????";
		switch (stab.type) {
			case N_GSYM:
				code = " GSYM";
				break;
			case N_FNAME:
				code = "FNAME";
				break;
			case N_FUN:
				code = "  FUN";
				break;
			case N_STSYM:
				code = "STSYM";
				break;
			case N_LCSYM:
				code = "LCSYM";
				break;
			case N_BNSYM:
				code = "BNSYM";
				break;
			case N_OPT:
				code = "  OPT";
				break;
			case N_RSYM:
				code = " RSYM";
				break;
			case N_SLINE:
				code = "SLINE";
				break;
			case N_ENSYM:
				code = "ENSYM";
				break;
			case N_SSYM:
				code = " SSYM";
				break;
			case N_SO:
				code = "   SO";
				break;
			case N_OSO:
				code = "  OSO";
				break;
			case N_LSYM:
				code = " LSYM";
				break;
			case N_BINCL:
				code = "BINCL";
				break;
			case N_SOL:
				code = "  SOL";
				break;
			case N_PARAMS:
				code = "PARMS";
				break;
			case N_VERSION:
				code = " VERS";
				break;
			case N_OLEVEL:
				code = "OLEVL";
				break;
			case N_PSYM:
				code = " PSYM";
				break;
			case N_EINCL:
				code = "EINCL";
				break;
			case N_ENTRY:
				code = "ENTRY";
				break;
			case N_LBRAC:
				code = "LBRAC";
				break;
			case N_EXCL:
				code = " EXCL";
				break;
			case N_RBRAC:
				code = "RBRAC";
				break;
			case N_BCOMM:
				code = "BCOMM";
				break;
			case N_ECOMM:
				code = "ECOMM";
				break;
			case N_LENG:
				code =  "LENG";
				break;
		}
		printf("  [atom=%20s] %02X %04X %s %s\n", ((stab.atom != NULL) ? stab.atom->name() : ""), stab.other, stab.desc, code, stab.string);
	}
}

#if 0
static void dumpAtomLikeNM(ld::Atom* atom)
{
	uint32_t size = atom->size();
	
	const char* visibility;
	switch ( atom->scope() ) {
		case ld::Atom::scopeTranslationUnit:
			visibility = "internal";
			break;
		case ld::Atom::scopeLinkageUnit:
			visibility = "hidden  ";
			break;
		case ld::Atom::scopeGlobal:
			visibility = "global  ";
			break;
		default:
			visibility = "        ";
			break;
	}

	const char* kind;
	switch ( atom->definitionKind() ) {
		case ld::Atom::kRegularDefinition:
			kind = "regular  ";
			break;
		case ld::Atom::kTentativeDefinition:
			kind = "tentative";
			break;
		case ld::Atom::kWeakDefinition:
			kind = "weak     ";
			break;
		case ld::Atom::kAbsoluteSymbol:
			kind = "absolute ";
			break;
		default:
			kind = "         ";
			break;
	}

	printf("0x%08X %s %s %s\n", size, visibility, kind, atom->name());
}


static void dumpAtom(ld::Atom* atom)
{
	if(sMatchName && strcmp(sMatchName, atom->name()))
		return;

	//printf("atom:    %p\n", atom);
	
	// name
	if(!sPrintRestrict || sPrintName)
		printf("name:    %s\n",  atom->name());
	
	// scope
	if(!sPrintRestrict)
		switch ( atom->scope() ) {
		case ld::Atom::scopeTranslationUnit:
			printf("scope:   translation unit\n");
			break;
		case ld::Atom::scopeLinkageUnit:
			printf("scope:   linkage unit\n");
			break;
		case ld::Atom::scopeGlobal:
			printf("scope:   global\n");
			break;
		default:
			printf("scope:   unknown\n");
		}
	
	// kind
	if(!sPrintRestrict)
		switch ( atom->definitionKind() ) {
		case ld::Atom::kRegularDefinition:
			printf("kind:     regular\n");
			break;
		case ld::Atom::kWeakDefinition:
			printf("kind:     weak\n");
			break;
		case ld::Atom::kTentativeDefinition:
			printf("kind:     tentative\n");
			break;
		case ld::Atom::kExternalDefinition:
			printf("kind:     import\n");
			break;
		case ld::Atom::kExternalWeakDefinition:
			printf("kind:     weak import\n");
			break;
		case ld::Atom::kAbsoluteSymbol:
			printf("kind:     absolute symbol\n");
			break;
		default:
			printf("kind:   unknown\n");
		}

	// segment and section
	if(!sPrintRestrict && (atom->section().sectionName() != NULL) )
		printf("section: %s,%s\n", atom->section().segmentName(), atom->section().sectionName());

	// attributes
	if(!sPrintRestrict) {
		printf("attrs:   ");
		if ( atom->dontDeadStrip() )
			printf("dont-dead-strip ");
		if ( atom->isThumb() )
			printf("thumb ");
		printf("\n");
	}
	
	// size
	if(!sPrintRestrict)
		printf("size:    0x%012llX\n", atom->size());
	
	// alignment
	if(!sPrintRestrict || sPrintAlign)
		printf("align:    %u mod %u\n", atom->alignment().modulus, (1 << atom->alignment().powerOf2) );

	// content
	if (!sPrintRestrict && sDumpContent ) { 
		uint64_t size = atom->size();
		if ( size < 4096 ) {
			uint8_t content[size];
			atom->copyRawContent(content);
			printf("content: ");
			if ( atom->contentType() == ld::Atom::typeCString ) {
				printf("\"");
				for (unsigned int i=0; i < size; ++i) {
					if(content[i]<'!' || content[i]>=127)
						printf("\\%o", content[i]);
					else
						printf("%c", content[i]);
				}
				printf("\"");
			}
			else {
				for (unsigned int i=0; i < size; ++i)
					printf("%02X ", content[i]);
			}
		}
		printf("\n");
	}
	
	// unwind info
	if(!sPrintRestrict) {
		if ( atom->beginUnwind() != atom->endUnwind() ) {
			printf("unwind encodings:\n");
			for (ld::Atom::UnwindInfo::iterator it = atom->beginUnwind(); it != atom->endUnwind(); ++it) {
				printf("\t 0x%04X  0x%08X\n", it->startOffset, it->unwindInfo);
			}
		}
	}
#if 0	
	// references
	if(!sPrintRestrict) {
		std::vector<ObjectFile::Reference*>&  references = atom->getReferences();
		const int refCount = references.size();
		printf("references: (%u)\n", refCount);
		for (int i=0; i < refCount; ++i) {
			ObjectFile::Reference* ref = references[i];
			printf("   %s\n", ref->getDescription());
		}
	}
#endif	
	// line info
	if(!sPrintRestrict) {
		if ( atom->beginLineInfo() != atom->endLineInfo() ) {
			printf("line info:\n");
			for (ld::Atom::LineInfo::iterator it = atom->beginLineInfo(); it != atom->endLineInfo(); ++it) {
				printf("   offset 0x%04X, line %d, file %s\n", it->atomOffset, it->lineNumber, it->fileName);
			}
		}
	}

	if(!sPrintRestrict)
		printf("\n");
}
#endif
struct AtomSorter
{
     bool operator()(const ld::Atom* left, const ld::Atom* right)
     {
		if ( left == right )
			return false;
		// first sort by segment name
		int diff = strcmp(left->section().segmentName(), right->section().segmentName());
		if ( diff != 0 )
			return (diff > 0);
		
		// then sort by section name
		diff = strcmp(left->section().sectionName(), right->section().sectionName());
		if ( diff != 0 )
			return (diff < 0);
		
		// then sort by atom name
		diff = strcmp(left->name(), right->name());
		if ( diff != 0 )
			return (diff < 0);
		
		// if cstring, sort by content
		if ( left->contentType() == ld::Atom::typeCString ) {
			diff = strcmp((char*)left->rawContentPointer(), (char*)right->rawContentPointer());
			if ( diff != 0 )
				return (diff < 0);
		}
		else if ( left->section().type() == ld::Section::typeCStringPointer ) {
			// if pointer to c-string sort by name
			const char* leftString = NULL;
			assert(left->fixupsBegin() != left->fixupsEnd());
			for (ld::Fixup::iterator fit = left->fixupsBegin(); fit != left->fixupsEnd(); ++fit) {
				if ( fit->binding == ld::Fixup::bindingByContentBound ) {
					const ld::Atom* cstringAtom = fit->u.target;
					assert(cstringAtom->contentType() == ld::Atom::typeCString);
					leftString = (char*)cstringAtom->rawContentPointer();
				}
			}
			const char* rightString = NULL;
			assert(right->fixupsBegin() != right->fixupsEnd());
			for (ld::Fixup::iterator fit = right->fixupsBegin(); fit != right->fixupsEnd(); ++fit) {
				if ( fit->binding == ld::Fixup::bindingByContentBound ) {
					const ld::Atom* cstringAtom = fit->u.target;
					assert(cstringAtom->contentType() == ld::Atom::typeCString);
					rightString = (char*)cstringAtom->rawContentPointer();
				}
			}
			assert(leftString != NULL);
			assert(rightString != NULL);
			diff = strcmp(leftString, rightString);
			if ( diff != 0 )
				return (diff < 0);
		}
		else if ( left->section().type() == ld::Section::typeLiteral4 ) {
			// if literal sort by content
			uint32_t leftValue  = *(uint32_t*)left->rawContentPointer();
			uint32_t rightValue = *(uint32_t*)right->rawContentPointer();
			diff = (leftValue - rightValue);
			if ( diff != 0 )
				return (diff < 0);
		}
		else if ( left->section().type() == ld::Section::typeCFI ) {
			// if __he_frame sort by address
			diff = (left->objectAddress() - right->objectAddress());
			if ( diff != 0 )
				return (diff < 0);
		}
		else if ( left->section().type() == ld::Section::typeNonLazyPointer ) {
			// if non-lazy-pointer sort by name
			const char* leftString = NULL;
			assert(left->fixupsBegin() != left->fixupsEnd());
			for (ld::Fixup::iterator fit = left->fixupsBegin(); fit != left->fixupsEnd(); ++fit) {
				if ( fit->binding == ld::Fixup::bindingByNameUnbound ) {
					leftString = fit->u.name;
				}
				else if ( fit->binding == ld::Fixup::bindingDirectlyBound ) {
					leftString = fit->u.target->name();
				}
			}
			const char* rightString = NULL;
			assert(right->fixupsBegin() != right->fixupsEnd());
			for (ld::Fixup::iterator fit = right->fixupsBegin(); fit != right->fixupsEnd(); ++fit) {
				if ( fit->binding == ld::Fixup::bindingByNameUnbound ) {
					rightString = fit->u.name;
				}
				else if ( fit->binding == ld::Fixup::bindingDirectlyBound ) {
					rightString = fit->u.target->name();
				}
			}
			assert(leftString != NULL);
			assert(rightString != NULL);
			diff = strcmp(leftString, rightString);
			if ( diff != 0 )
				return (diff < 0);
		}
		
		// else sort by size
		return (left->size() < right->size());
     }
};


class dumper : public ld::File::AtomHandler
{
public:
			void dump();
	virtual void doAtom(const ld::Atom&);
	virtual void doFile(const ld::File&) {} 
private:
	void			dumpAtom(const ld::Atom& atom);
	const char*		scopeString(const ld::Atom&);
	const char*		definitionString(const ld::Atom&);
	const char*		combineString(const ld::Atom&);
	const char*		inclusionString(const ld::Atom&);
	const char*		attributeString(const ld::Atom&);
	const char*		makeName(const ld::Atom& atom);
	const char*		referenceTargetAtomName(const ld::Fixup* ref);
	void			dumpFixup(const ld::Fixup* ref);
	
	uint64_t		addressOfFirstAtomInSection(const ld::Section&);
	
	std::vector<const ld::Atom*> _atoms;
};

const char*	dumper::scopeString(const ld::Atom& atom)
{
	switch ( (ld::Atom::Scope)atom.scope() ) {
		case ld::Atom::scopeTranslationUnit:
			return "translation-unit";
		case ld::Atom::scopeLinkageUnit:
			return "hidden";
		case ld::Atom::scopeGlobal:
			if ( atom.autoHide() )
				return "global but automatically hidden";
			else
				return "global";
	}
	return "UNKNOWN";	
}

const char*	dumper::definitionString(const ld::Atom& atom)
{
	switch ( (ld::Atom::Definition)atom.definition() ) {
		case ld::Atom::definitionRegular:
			return "regular";
		case ld::Atom::definitionTentative:
			return "tentative";
		case ld::Atom::definitionAbsolute:
			return "absolute";
		case ld::Atom::definitionProxy:
			return "proxy";
		}
	return "UNKNOWN";	
}

const char*	dumper::combineString(const ld::Atom& atom)
{
	switch ( (ld::Atom::Combine)atom.combine() ) {
		case ld::Atom::combineNever:
			return "never";
		case ld::Atom::combineByName:
			return "by-name";
		case ld::Atom::combineByNameAndContent:
			return "by-name-and-content";
		case ld::Atom::combineByNameAndReferences:
			return "by-name-and-references";
		}
	return "UNKNOWN";	
}

const char*	dumper::inclusionString(const ld::Atom& atom)
{
	switch ( (ld::Atom::SymbolTableInclusion)atom.symbolTableInclusion() ) {
		case ld::Atom::symbolTableNotIn:
			return "not in";
		case ld::Atom::symbolTableNotInFinalLinkedImages:
			return "not in final linked images";
		case ld::Atom::symbolTableIn:
			return "in";
		case ld::Atom::symbolTableInAndNeverStrip:
			return "in and never strip";
		case ld::Atom::symbolTableInAsAbsolute:
			return "in as absolute";
		case ld::Atom::symbolTableInWithRandomAutoStripLabel:
			return "in as random auto-strip label";
		}
	return "UNKNOWN";	
}



const char*	dumper::attributeString(const ld::Atom& atom)
{
	static char buffer[256];
	buffer[0] = '\0';
	
	if ( atom.dontDeadStrip() )
		strcat(buffer, "dont-dead-strip ");

	if ( atom.isThumb() )
		strcat(buffer, "thumb ");
		
	if ( atom.isAlias() )
		strcat(buffer, "alias ");
		
	if ( atom.contentType() == ld::Atom::typeResolver )
		strcat(buffer, "resolver ");
		
	return buffer;
}

const char* dumper::makeName(const ld::Atom& atom)
{
	static char buffer[4096];
	strcpy(buffer, "???");
	switch ( atom.symbolTableInclusion() ) {
		case ld::Atom::symbolTableNotIn:
			if ( atom.contentType() == ld::Atom::typeCString ) {
				strcpy(buffer, "cstring=");
				strlcat(buffer, (char*)atom.rawContentPointer(), 4096);
			}
			else if ( atom.section().type() == ld::Section::typeLiteral4 ) {
				char temp[16];
				strcpy(buffer, "literal4=");
				uint32_t value = *(uint32_t*)atom.rawContentPointer();
				sprintf(temp, "0x%08X", value);
				strcat(buffer, temp);
			}
			else if ( atom.section().type() == ld::Section::typeLiteral8 ) {
				char temp[32];
				strcpy(buffer, "literal8=");
				uint32_t value1 = *(uint32_t*)atom.rawContentPointer();
				uint32_t value2 = ((uint32_t*)atom.rawContentPointer())[1];
				sprintf(temp, "0x%08X%08X", value1, value2);
				strcat(buffer, temp);
			}
			else if ( atom.section().type() == ld::Section::typeLiteral16 ) {
				char temp[64];
				strcpy(buffer, "literal16=");
				uint32_t value1 = *(uint32_t*)atom.rawContentPointer();
				uint32_t value2 = ((uint32_t*)atom.rawContentPointer())[1];
				uint32_t value3 = ((uint32_t*)atom.rawContentPointer())[2];
				uint32_t value4 = ((uint32_t*)atom.rawContentPointer())[3];
				sprintf(temp, "0x%08X%08X%08X%08X", value1, value2, value3, value4);
				strcat(buffer, temp);
			}
			else if ( atom.section().type() == ld::Section::typeCStringPointer ) {
				assert(atom.fixupsBegin() != atom.fixupsEnd());
				for (ld::Fixup::iterator fit = atom.fixupsBegin(); fit != atom.fixupsEnd(); ++fit) {
					if ( fit->binding == ld::Fixup::bindingByContentBound ) {
						const ld::Atom* cstringAtom = fit->u.target;
						if ( (cstringAtom != NULL) && (cstringAtom->contentType() == ld::Atom::typeCString) ) {
							strlcpy(buffer, atom.name(), 4096);
							strlcat(buffer, "=", 4096);
							strlcat(buffer, (char*)cstringAtom->rawContentPointer(), 4096);
						}
					}
				}
			}
			else if ( atom.section().type() == ld::Section::typeNonLazyPointer ) {
				assert(atom.fixupsBegin() != atom.fixupsEnd());
				for (ld::Fixup::iterator fit = atom.fixupsBegin(); fit != atom.fixupsEnd(); ++fit) {
					if ( fit->binding == ld::Fixup::bindingByNameUnbound ) {
						strcpy(buffer, "non-lazy-pointer-to:");
						strlcat(buffer, fit->u.name, 4096);
						return buffer;
					}
					else if ( fit->binding == ld::Fixup::bindingDirectlyBound ) {
						strcpy(buffer, "non-lazy-pointer-to-local:");
						strlcat(buffer, fit->u.target->name(), 4096);
						return buffer;
					}
				}
				strlcpy(buffer, atom.name(), 4096);
			}
			else {
				uint64_t sectAddr = addressOfFirstAtomInSection(atom.section());
				sprintf(buffer, "%s@%s+0x%08llX", atom.name(), atom.section().sectionName(), atom.objectAddress()-sectAddr);
			}
			break;
		case ld::Atom::symbolTableNotInFinalLinkedImages:
		case ld::Atom::symbolTableIn:
		case ld::Atom::symbolTableInAndNeverStrip:
		case ld::Atom::symbolTableInAsAbsolute:
		case ld::Atom::symbolTableInWithRandomAutoStripLabel:
			strlcpy(buffer, atom.name(), 4096);
			break;
	}
	return buffer;
}

const char* dumper::referenceTargetAtomName(const ld::Fixup* ref)
{
	static char buffer[4096];
	switch ( ref->binding ) {
		case ld::Fixup::bindingNone:
			return "NO BINDING";
		case ld::Fixup::bindingByNameUnbound:
			strcpy(buffer, "by-name(");
			strlcat(buffer, ref->u.name, 4096);
			strlcat(buffer, ")", 4096);
			return buffer;
			//return ref->u.name;
		case ld::Fixup::bindingByContentBound:
			strcpy(buffer, "by-content(");
			strlcat(buffer, makeName(*ref->u.target), 4096);
			strlcat(buffer, ")", 4096);
			return buffer;
		case ld::Fixup::bindingDirectlyBound:
			strcpy(buffer, "direct(");
			strlcat(buffer, makeName(*ref->u.target), 4096);
			strlcat(buffer, ")", 4096);
			return buffer;
		case ld::Fixup::bindingsIndirectlyBound:
			return "BOUND INDIRECTLY";
	}
	return "BAD BINDING";
}


void dumper::dumpFixup(const ld::Fixup* ref)
{
	if ( ref->weakImport ) {
		printf("weak_import ");
	}
	switch ( (ld::Fixup::Kind)(ref->kind) ) {
		case ld::Fixup::kindNone:
			printf("none");
			break;
		case ld::Fixup::kindNoneFollowOn:
			printf("followed by %s", referenceTargetAtomName(ref));
			break;
		case ld::Fixup::kindNoneGroupSubordinate:
			printf("group subordinate %s", referenceTargetAtomName(ref));
			break;
		case ld::Fixup::kindNoneGroupSubordinateFDE:
			printf("group subordinate FDE %s", referenceTargetAtomName(ref));
			break;
		case ld::Fixup::kindNoneGroupSubordinateLSDA:
			printf("group subordinate LSDA %s", referenceTargetAtomName(ref));
			break;
		case ld::Fixup::kindNoneGroupSubordinatePersonality:
			printf("group subordinate personality %s", referenceTargetAtomName(ref));
			break;
		case ld::Fixup::kindSetTargetAddress:
			printf("%s", referenceTargetAtomName(ref));
			break;
		case ld::Fixup::kindSubtractTargetAddress:
			printf(" - %s", referenceTargetAtomName(ref));
			break;
		case ld::Fixup::kindAddAddend:
			printf(" + 0x%llX", ref->u.addend);
			break;
		case ld::Fixup::kindSubtractAddend:
			printf(" - 0x%llX", ref->u.addend);
			break;
		case ld::Fixup::kindSetTargetImageOffset:
			printf("imageOffset(%s)", referenceTargetAtomName(ref));
			break;
		case ld::Fixup::kindSetTargetSectionOffset:
			printf("sectionOffset(%s)", referenceTargetAtomName(ref));
			break;
		case ld::Fixup::kindStore8:
			printf(", then store byte");
			break;
		case ld::Fixup::kindStoreLittleEndian16:
			printf(", then store 16-bit little endian");
			break;
		case ld::Fixup::kindStoreLittleEndianLow24of32:
			printf(", then store low 24-bit little endian");
			break;
		case ld::Fixup::kindStoreLittleEndian32:
			printf(", then store 32-bit little endian");
			break;
		case ld::Fixup::kindStoreLittleEndian64:
			printf(", then store 64-bit little endian");
			break;
		case ld::Fixup::kindStoreBigEndian16:
			printf(", then store 16-bit big endian");
			break;
		case ld::Fixup::kindStoreBigEndianLow24of32:
			printf(", then store low 24-bit big endian");
			break;
		case ld::Fixup::kindStoreBigEndian32:
			printf(", then store 32-bit big endian");
			break;
		case ld::Fixup::kindStoreBigEndian64:
			printf(", then store 64-bit big endian");
			break;
		case ld::Fixup::kindStoreX86BranchPCRel8:
			printf(", then store as x86 8-bit pcrel branch");
			break;
		case ld::Fixup::kindStoreX86BranchPCRel32:
			printf(", then store as x86 32-bit pcrel branch");
			break;
		case ld::Fixup::kindStoreX86PCRel8:
			printf(", then store as x86 8-bit pcrel");
			break;
		case ld::Fixup::kindStoreX86PCRel16:
			printf(", then store as x86 16-bit pcrel");
			break;
		case ld::Fixup::kindStoreX86PCRel32:
			printf(", then store as x86 32-bit pcrel");
			break;
		case ld::Fixup::kindStoreX86PCRel32_1:
			printf(", then store as x86 32-bit pcrel from +1");
			break;
		case ld::Fixup::kindStoreX86PCRel32_2:
			printf(", then store as x86 32-bit pcrel from +2");
			break;
		case ld::Fixup::kindStoreX86PCRel32_4:
			printf(", then store as x86 32-bit pcrel from +4");
			break;
		case ld::Fixup::kindStoreX86PCRel32GOTLoad:
			printf(", then store as x86 32-bit pcrel GOT load");
			break;
		case ld::Fixup::kindStoreX86PCRel32GOTLoadNowLEA:
			printf(", then store as x86 32-bit pcrel GOT load -> LEA");
			break;
		case ld::Fixup::kindStoreX86PCRel32GOT:
			printf(", then store as x86 32-bit pcrel GOT access");
			break;
		case ld::Fixup::kindStoreX86PCRel32TLVLoad:
			printf(", then store as x86 32-bit pcrel TLV load");
			break;
		case ld::Fixup::kindStoreX86PCRel32TLVLoadNowLEA:
			printf(", then store as x86 32-bit pcrel TLV load");
			break;
		case ld::Fixup::kindStoreX86Abs32TLVLoad:
			printf(", then store as x86 32-bit absolute TLV load");
			break;
		case ld::Fixup::kindStoreX86Abs32TLVLoadNowLEA:
			printf(", then store as x86 32-bit absolute TLV load -> LEA");
			break;
		case ld::Fixup::kindStoreARMBranch24:
			printf(", then store as ARM 24-bit pcrel branch");
			break;
		case ld::Fixup::kindStoreThumbBranch22:
			printf(", then store as Thumb 22-bit pcrel branch");
			break;
		case ld::Fixup::kindStoreARMLoad12:
			printf(", then store as ARM 12-bit pcrel load");
			break;
		case ld::Fixup::kindStoreARMLow16:
			printf(", then store low-16 in ARM movw");
			break;
		case ld::Fixup::kindStoreARMHigh16:
			printf(", then store high-16 in ARM movt");
			break;
		case ld::Fixup::kindStoreThumbLow16:
			printf(", then store low-16 in Thumb movw");
			break;
		case ld::Fixup::kindStoreThumbHigh16:
			printf(", then store high-16 in Thumb movt");
			break;
		case ld::Fixup::kindDtraceExtra:
			printf("dtrace static probe extra info");
			break;
		case ld::Fixup::kindStoreX86DtraceCallSiteNop:
			printf("x86 dtrace static probe site");
			break;
		case ld::Fixup::kindStoreX86DtraceIsEnableSiteClear:
			printf("x86 dtrace static is-enabled site");
			break;
		case ld::Fixup::kindStoreARMDtraceCallSiteNop:
			printf("ARM dtrace static probe site");
			break;
		case ld::Fixup::kindStoreARMDtraceIsEnableSiteClear:
			printf("ARM dtrace static is-enabled site");
			break;
		case ld::Fixup::kindStoreThumbDtraceCallSiteNop:
			printf("Thumb dtrace static probe site");
			break;
		case ld::Fixup::kindStoreThumbDtraceIsEnableSiteClear:
			printf("Thumb dtrace static is-enabled site");
			break;
		case ld::Fixup::kindLazyTarget:
			printf("lazy reference to external symbol %s", referenceTargetAtomName(ref));
			break;
		case ld::Fixup::kindSetLazyOffset:
			printf("offset of lazy binding info for %s", referenceTargetAtomName(ref));
			break;
		case ld::Fixup::kindDataInCodeStartData:
			printf("start of data in code");
			break;
		case ld::Fixup::kindDataInCodeStartJT8:
			printf("start of jump table 8 data in code");
			break;
		case ld::Fixup::kindDataInCodeStartJT16:
			printf("start of jump table 16 data in code");
			break;
		case ld::Fixup::kindDataInCodeStartJT32:
			printf("start of jump table 32 data in code");
			break;
		case ld::Fixup::kindDataInCodeStartJTA32:
			printf("start of jump table absolute 32 data in code");
			break;
		case ld::Fixup::kindDataInCodeEnd:
			printf("end of data in code");
			break;
		case ld::Fixup::kindStoreTargetAddressLittleEndian32:
			printf("store 32-bit little endian address of %s", referenceTargetAtomName(ref));
			break;
		case ld::Fixup::kindStoreTargetAddressLittleEndian64:
			printf("store 64-bit little endian address of %s", referenceTargetAtomName(ref));
			break;
		case ld::Fixup::kindStoreTargetAddressBigEndian32:
			printf("store 32-bit big endian address of %s", referenceTargetAtomName(ref));
			break;
		case ld::Fixup::kindStoreTargetAddressBigEndian64:
			printf("store 64-bit big endian address of %s", referenceTargetAtomName(ref));
			break;
		case ld::Fixup::kindStoreTargetAddressX86PCRel32:
			printf("x86 store 32-bit pc-rel address of %s", referenceTargetAtomName(ref));
			break;
		case ld::Fixup::kindStoreTargetAddressX86BranchPCRel32:
			printf("x86 store 32-bit pc-rel branch to %s", referenceTargetAtomName(ref));
			break;
		case ld::Fixup::kindStoreTargetAddressX86PCRel32GOTLoad:
			printf("x86 store 32-bit pc-rel GOT load of %s", referenceTargetAtomName(ref));
			break;
		case ld::Fixup::kindStoreTargetAddressX86PCRel32GOTLoadNowLEA:
			printf("x86 store 32-bit pc-rel lea of %s", referenceTargetAtomName(ref));
			break;
		case ld::Fixup::kindStoreTargetAddressX86PCRel32TLVLoad:
			printf("x86 store 32-bit pc-rel TLV load of %s", referenceTargetAtomName(ref));
			break;
		case ld::Fixup::kindStoreTargetAddressX86PCRel32TLVLoadNowLEA:
			printf("x86 store 32-bit pc-rel TLV lea of %s", referenceTargetAtomName(ref));
			break;
		case ld::Fixup::kindStoreTargetAddressX86Abs32TLVLoad:
			printf("x86 store 32-bit absolute TLV load of %s", referenceTargetAtomName(ref));
			break;
		case ld::Fixup::kindStoreTargetAddressX86Abs32TLVLoadNowLEA:
			printf("x86 store 32-bit absolute TLV lea of %s", referenceTargetAtomName(ref));
			break;
		case ld::Fixup::kindStoreTargetAddressARMBranch24:
			printf("ARM store 24-bit pc-rel branch to %s", referenceTargetAtomName(ref));
			break;
		case ld::Fixup::kindStoreTargetAddressThumbBranch22:
			printf("Thumb store 22-bit pc-rel branch to %s", referenceTargetAtomName(ref));
			break;
		case ld::Fixup::kindStoreTargetAddressARMLoad12:
			printf("ARM store 12-bit pc-rel branch to %s", referenceTargetAtomName(ref));
			break;
		case ld::Fixup::kindSetTargetTLVTemplateOffset:
		case ld::Fixup::kindSetTargetTLVTemplateOffsetLittleEndian32:
		case ld::Fixup::kindSetTargetTLVTemplateOffsetLittleEndian64:
			printf("tlv template offset of %s", referenceTargetAtomName(ref));
		//default:
		//	printf("unknown fixup");
		//	break;
	}
}

uint64_t dumper::addressOfFirstAtomInSection(const ld::Section& sect)
{
	uint64_t lowestAddr = (uint64_t)(-1);
	for (std::vector<const ld::Atom*>::iterator it=_atoms.begin(); it != _atoms.end(); ++it) {
		const ld::Atom* atom = *it;
		if ( &atom->section() == &sect ) {
			if ( atom->objectAddress() < lowestAddr )
				lowestAddr = atom->objectAddress();
		}
	}
	return lowestAddr;
}

void dumper::doAtom(const ld::Atom& atom)
{
	if ( (sMatchName != NULL) && (strcmp(sMatchName, atom.name()) != 0) )
		return;
	_atoms.push_back(&atom);
}

void dumper::dump()
{	
	if ( sSort ) 
		std::sort(_atoms.begin(), _atoms.end(), AtomSorter());

	for (std::vector<const ld::Atom*>::iterator it=_atoms.begin(); it != _atoms.end(); ++it) {
		this->dumpAtom(**it);
	}
}	

void dumper::dumpAtom(const ld::Atom& atom)
{		
 	printf("name:     %s\n", makeName(atom)); 
	printf("size:     0x%0llX\n", atom.size());
	printf("align:    %u mod %u\n", atom.alignment().modulus, (1 << atom.alignment().powerOf2) );
	printf("scope:    %s\n", scopeString(atom));
	if ( sShowDefinitionKind ) 
		printf("def:      %s\n", definitionString(atom));
	if ( sShowCombineKind )
		printf("combine:  %s\n", combineString(atom));
	printf("symbol:   %s\n", inclusionString(atom));
	printf("attrs:    %s\n", attributeString(atom));
	if ( sShowSection )
		printf("section:  %s,%s\n", atom.section().segmentName(), atom.section().sectionName());
	if ( atom.beginUnwind() != atom.endUnwind() ) {
		uint32_t lastOffset = 0;
		uint32_t lastCUE = 0;
		bool first = true;
		const char* label = "unwind:";
		for (ld::Atom::UnwindInfo::iterator it=atom.beginUnwind(); it != atom.endUnwind(); ++it) {
			if ( !first ) {
				printf("%s   0x%08X -> 0x%08X: 0x%08X\n", label, lastOffset, it->startOffset, lastCUE);
				label = "       ";
			}
			lastOffset = it->startOffset;
			lastCUE = it->unwindInfo;
			first = false;
		}
		printf("%s   0x%08X -> 0x%08X: 0x%08X\n", label, lastOffset, (uint32_t)atom.size(), lastCUE);
	}
	if ( atom.contentType() == ld::Atom::typeCString ) {
		uint8_t buffer[atom.size()+2];
		atom.copyRawContent(buffer);
		buffer[atom.size()] = '\0';
		printf("content:  \"%s\"\n", buffer);
	}
	if ( atom.fixupsBegin() != atom.fixupsEnd() ) {
		printf("fixups:\n");
		for (unsigned int off=0; off < atom.size()+1; ++off) {
			for (ld::Fixup::iterator it = atom.fixupsBegin(); it != atom.fixupsEnd(); ++it) {
				if ( it->offsetInAtom == off ) {
					switch ( it->clusterSize ) {
						case ld::Fixup::k1of1:
							printf("    0x%04X ", it->offsetInAtom);
							dumpFixup(it);
							break;
						case ld::Fixup::k1of2:
							printf("    0x%04X ", it->offsetInAtom);
							dumpFixup(it);
							++it;
							dumpFixup(it);
							break;
						case ld::Fixup::k1of3:
							printf("    0x%04X ", it->offsetInAtom);
							dumpFixup(it);
							++it;
							dumpFixup(it);
							++it;
							dumpFixup(it);
							break;
						case ld::Fixup::k1of4:
							printf("    0x%04X ", it->offsetInAtom);
							dumpFixup(it);
							++it;
							dumpFixup(it);
							++it;
							dumpFixup(it);
							++it;
							dumpFixup(it);
							break;
						case ld::Fixup::k1of5:
							printf("    0x%04X ", it->offsetInAtom);
							dumpFixup(it);
							++it;
							dumpFixup(it);
							++it;
							dumpFixup(it);
							++it;
							dumpFixup(it);
							++it;
							dumpFixup(it);
							break;
						default:
							printf("   BAD CLUSTER SIZE: cluster=%d\n", it->clusterSize);
					}
					printf("\n");
				}
			}
		}
	}
	if ( sShowLineInfo ) {
		if ( atom.beginLineInfo() != atom.endLineInfo() ) {
			printf("line info:\n");
			for (ld::Atom::LineInfo::iterator it = atom.beginLineInfo(); it != atom.endLineInfo(); ++it) {
				printf("   offset 0x%04X, line %d, file %s\n", it->atomOffset, it->lineNumber, it->fileName);
			}
		}
	}
	
	printf("\n");
}

static void dumpFile(ld::relocatable::File* file)
{
	// stabs debug info
	if ( sDumpStabs && (file->debugInfo() == ld::relocatable::File::kDebugInfoStabs) ) {
		const std::vector<ld::relocatable::File::Stab>* stabs = file->stabs();
		if ( stabs != NULL )
			dumpStabs(stabs);
	}
	// dump atoms
	dumper d;
	file->forEachAtom(d);
	d.dump();

#if 0	
	// get all atoms
	std::vector<ObjectFile::Atom*> atoms = reader->getAtoms();
	
	// make copy of vector and sort (so output is canonical)
	std::vector<ObjectFile::Atom*> sortedAtoms(atoms);
	if ( sSort )
		std::sort(sortedAtoms.begin(), sortedAtoms.end(), AtomSorter());
	
	for(std::vector<ObjectFile::Atom*>::iterator it=sortedAtoms.begin(); it != sortedAtoms.end(); ++it) {
		if ( sNMmode )
			dumpAtomLikeNM(*it);
		else
			dumpAtom(*it);
	}
#endif
}


static ld::relocatable::File* createReader(const char* path)
{
	struct stat stat_buf;
	
	int fd = ::open(path, O_RDONLY, 0);
	if ( fd == -1 )
		throwf("cannot open file: %s", path);
	::fstat(fd, &stat_buf);
	uint8_t* p = (uint8_t*)::mmap(NULL, stat_buf.st_size, PROT_READ, MAP_FILE | MAP_PRIVATE, fd, 0);
	::close(fd);
	if ( p == (uint8_t*)(-1) )
		throwf("cannot mmap file: %s", path);
	const mach_header* mh = (mach_header*)p;
	uint64_t fileLen = stat_buf.st_size;
	bool foundFatSlice = false;
	if ( mh->magic == OSSwapBigToHostInt32(FAT_MAGIC) ) {
		const struct fat_header* fh = (struct fat_header*)p;
		const struct fat_arch* archs = (struct fat_arch*)(p + sizeof(struct fat_header));
		if ( (uint32_t)sPreferredArch ==  0xFFFFFFFF ) {
			// just dump first slice of fat .o file
			if ( OSSwapBigToHostInt32(fh->nfat_arch) > 0 )  {
				p = p + OSSwapBigToHostInt32(archs[0].offset);
				mh = (struct mach_header*)p;
				fileLen = OSSwapBigToHostInt32(archs[0].size);
				sPreferredArch = OSSwapBigToHostInt32(archs[0].cputype);
				sPreferredSubArch = OSSwapBigToHostInt32(archs[0].cpusubtype);
				foundFatSlice = true;
			}
		}
		else {
			for (unsigned long i=0; i < OSSwapBigToHostInt32(fh->nfat_arch); ++i) {
				if ( OSSwapBigToHostInt32(archs[i].cputype) == (uint32_t)sPreferredArch ) {
					if ( ((uint32_t)sPreferredSubArch == 0xFFFFFFFF) || ((uint32_t)sPreferredSubArch == OSSwapBigToHostInt32(archs[i].cpusubtype)) ) {
						p = p + OSSwapBigToHostInt32(archs[i].offset);
						mh = (struct mach_header*)p;
						fileLen = OSSwapBigToHostInt32(archs[i].size);
						foundFatSlice = true;
						break;
					}
				}
			}
		}
	}

	mach_o::relocatable::ParserOptions objOpts;
	objOpts.architecture		= sPreferredArch;
	objOpts.objSubtypeMustMatch = false;
	objOpts.logAllFiles			= false;
	objOpts.convertUnwindInfo	= true;
	objOpts.subType				= sPreferredSubArch;
#if 1
	if ( ! foundFatSlice ) {
		cpu_type_t archOfObj;
		cpu_subtype_t subArchOfObj;
		if ( mach_o::relocatable::isObjectFile(p, &archOfObj, &subArchOfObj) ) {
			objOpts.architecture = archOfObj;
			objOpts.subType = subArchOfObj;
		}
	}

	ld::relocatable::File* objResult = mach_o::relocatable::parse(p, fileLen, path, stat_buf.st_mtime, ld::File::Ordinal::NullOrdinal(), objOpts);
	if ( objResult != NULL )
		return objResult;

	// see if it is an llvm object file
	objResult = lto::parse(p, fileLen, path, stat_buf.st_mtime, ld::File::Ordinal::NullOrdinal(), sPreferredArch, sPreferredSubArch, false);
	if ( objResult != NULL ) 
		return objResult;

	throwf("not a mach-o object file: %s", path);
#else
	// for peformance testing
	for (int i=0; i < 500; ++i ) {
		ld::relocatable::File* objResult = mach_o::relocatable::parse(p, fileLen, path, stat_buf.st_mtime, 0, objOpts);
		delete objResult;
	}
	exit(0);
#endif
}

static
void
usage()
{
	fprintf(stderr, "ObjectDump options:\n"
			"\t-no_content\tdon't dump contents\n"
			"\t-no_section\tdon't dump section name\n"
			"\t-no_defintion\tdon't dump definition kind\n"
			"\t-no_combine\tdon't dump combine mode\n"
			"\t-stabs\t\tdump stabs\n"
			"\t-arch aaa\tonly dump info about arch aaa\n"
			"\t-only sym\tonly dump info about sym\n"
			"\t-align\t\tonly print alignment info\n"
			"\t-name\t\tonly print symbol names\n"
		);
}

int main(int argc, const char* argv[])
{
	if(argc<2) {
		usage();
		return 0;
	}

	try {
		for(int i=1; i < argc; ++i) {
			const char* arg = argv[i];
			if ( arg[0] == '-' ) {
				if ( strcmp(arg, "-no_content") == 0 ) {
					sDumpContent = false;
				}
				else if ( strcmp(arg, "-nm") == 0 ) {
					sNMmode = true;
				}
				else if ( strcmp(arg, "-stabs") == 0 ) {
					sDumpStabs = true;
				}
				else if ( strcmp(arg, "-no_sort") == 0 ) {
					sSort = false;
				}
				else if ( strcmp(arg, "-no_section") == 0 ) {
					sShowSection = false;
				}
				else if ( strcmp(arg, "-no_definition") == 0 ) {
					sShowDefinitionKind = false;
				}
				else if ( strcmp(arg, "-no_combine") == 0 ) {
					sShowCombineKind = false;
				}
				else if ( strcmp(arg, "-no_line_info") == 0 ) {
					sShowLineInfo = false;
				}
				else if ( strcmp(arg, "-arch") == 0 ) {
					const char* archName = argv[++i];
					if ( archName == NULL )
						throw "-arch missing architecture name";
					bool found = false;
					for (const ArchInfo* t=archInfoArray; t->archName != NULL; ++t) {
						if ( strcmp(t->archName,archName) == 0 ) {
							sPreferredArch = t->cpuType;
							if ( t->isSubType )
								sPreferredSubArch = t->cpuSubType;
							found = true;
						}
					}
					if ( !found )
						throwf("unknown architecture %s", archName);
				}
				else if ( strcmp(arg, "-only") == 0 ) {
					sMatchName = ++i<argc? argv[i]: NULL;
				}
				else if ( strcmp(arg, "-align") == 0 ) {
					sPrintRestrict = true;
					sPrintAlign = true;
				}
				else if ( strcmp(arg, "-name") == 0 ) {
					sPrintRestrict = true;
					sPrintName = true;
				}
				else {
					usage();
					throwf("unknown option: %s\n", arg);
				}
			}
			else {
				ld::relocatable::File* reader = createReader(arg);
				dumpFile(reader);
			}
		}
	}
	catch (const char* msg) {
		fprintf(stderr, "ObjDump failed: %s\n", msg);
		return 1;
	}
	
	return 0;
}