#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <fcntl.h>
#include "MachOReaderRelocatable.hpp"
#define LTO_SUPPORT 1
#if LTO_SUPPORT
#include "LTOReader.hpp"
#endif
static bool sDumpContent= true;
static bool sDumpStabs = false;
static bool sSort = true;
static bool sNMmode = false;
static cpu_type_t sPreferredArch = CPU_TYPE_I386;
static cpu_subtype_t sPreferredSubArch = 0xFFFFFFFF;
static const char* sMatchName;
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(std::vector<ObjectFile::Reader::Stab>* stabs)
{
printf("stabs: (%lu)\n", stabs->size());
for (std::vector<ObjectFile::Reader::Stab>::iterator it = stabs->begin(); it != stabs->end(); ++it ) {
ObjectFile::Reader::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->getDisplayName() : ""), stab.other, stab.desc, code, stab.string);
}
}
static void dumpAtomLikeNM(ObjectFile::Atom* atom)
{
uint32_t size = atom->getSize();
const char* visibility;
switch ( atom->getScope() ) {
case ObjectFile::Atom::scopeTranslationUnit:
visibility = "internal";
break;
case ObjectFile::Atom::scopeLinkageUnit:
visibility = "hidden ";
break;
case ObjectFile::Atom::scopeGlobal:
visibility = "global ";
break;
default:
visibility = " ";
break;
}
const char* kind;
switch ( atom->getDefinitionKind() ) {
case ObjectFile::Atom::kRegularDefinition:
kind = "regular ";
break;
case ObjectFile::Atom::kTentativeDefinition:
kind = "tentative";
break;
case ObjectFile::Atom::kWeakDefinition:
kind = "weak ";
break;
case ObjectFile::Atom::kAbsoluteSymbol:
kind = "absolute ";
break;
default:
kind = " ";
break;
}
printf("0x%08X %s %s %s\n", size, visibility, kind, atom->getDisplayName());
}
static void dumpAtom(ObjectFile::Atom* atom)
{
if(sMatchName && strcmp(sMatchName, atom->getDisplayName()))
return;
if(!sPrintRestrict || sPrintName)
printf("name: %s\n", atom->getDisplayName());
if(!sPrintRestrict)
switch ( atom->getScope() ) {
case ObjectFile::Atom::scopeTranslationUnit:
printf("scope: translation unit\n");
break;
case ObjectFile::Atom::scopeLinkageUnit:
printf("scope: linkage unit\n");
break;
case ObjectFile::Atom::scopeGlobal:
printf("scope: global\n");
break;
default:
printf("scope: unknown\n");
}
if(!sPrintRestrict)
switch ( atom->getDefinitionKind() ) {
case ObjectFile::Atom::kRegularDefinition:
printf("kind: regular\n");
break;
case ObjectFile::Atom::kWeakDefinition:
printf("kind: weak\n");
break;
case ObjectFile::Atom::kTentativeDefinition:
printf("kind: tentative\n");
break;
case ObjectFile::Atom::kExternalDefinition:
printf("kind: import\n");
break;
case ObjectFile::Atom::kExternalWeakDefinition:
printf("kind: weak import\n");
break;
case ObjectFile::Atom::kAbsoluteSymbol:
printf("kind: absolute symbol\n");
break;
default:
printf("kind: unknown\n");
}
if(!sPrintRestrict && (atom->getSectionName() != NULL) )
printf("section: %s,%s\n", atom->getSegment().getName(), atom->getSectionName());
if(!sPrintRestrict) {
printf("attrs: ");
if ( atom->dontDeadStrip() )
printf("dont-dead-strip ");
if ( atom->isZeroFill() )
printf("zero-fill ");
if ( atom->isThumb() )
printf("thumb ");
printf("\n");
}
if(!sPrintRestrict)
printf("size: 0x%012llX\n", atom->getSize());
if(!sPrintRestrict || sPrintAlign)
printf("align: %u mod %u\n", atom->getAlignment().modulus, (1 << atom->getAlignment().powerOf2) );
if (!sPrintRestrict && sDumpContent ) {
uint64_t size = atom->getSize();
if ( size < 4096 ) {
uint8_t content[size];
atom->copyRawContent(content);
printf("content: ");
if ( atom->getContentType() == ObjectFile::Atom::kCStringType ) {
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");
}
if(!sPrintRestrict) {
if ( atom->beginUnwind() != atom->endUnwind() ) {
printf("unwind encodings:\n");
for (ObjectFile::UnwindInfo::iterator it = atom->beginUnwind(); it != atom->endUnwind(); ++it) {
printf("\t 0x%04X 0x%08X\n", it->startOffset, it->unwindInfo);
}
}
}
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());
}
}
if(!sPrintRestrict) {
std::vector<ObjectFile::LineInfo>* lineInfo = atom->getLineInfo();
if ( (lineInfo != NULL) && (lineInfo->size() > 0) ) {
printf("line info: (%lu)\n", lineInfo->size());
for (std::vector<ObjectFile::LineInfo>::iterator it = lineInfo->begin(); it != lineInfo->end(); ++it) {
printf(" offset 0x%04X, line %d, file %s\n", it->atomOffset, it->lineNumber, it->fileName);
}
}
}
if(!sPrintRestrict)
printf("\n");
}
struct AtomSorter
{
bool operator()(const ObjectFile::Atom* left, const ObjectFile::Atom* right)
{
if ( left == right )
return false;
int name = strcmp(left->getDisplayName(), right->getDisplayName());
if ( name == 0 )
return (left->getSize() < right->getSize());
else
return ( name < 0);
}
};
static void dumpFile(ObjectFile::Reader* reader)
{
if ( sDumpStabs && (reader->getDebugInfoKind() == ObjectFile::Reader::kDebugInfoStabs) ) {
std::vector<ObjectFile::Reader::Stab>* stabs = reader->getStabs();
if ( stabs != NULL )
dumpStabs(stabs);
}
std::vector<ObjectFile::Atom*> atoms = reader->getAtoms();
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);
}
}
static ObjectFile::Reader* createReader(const char* path, const ObjectFile::ReaderOptions& options)
{
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);
const mach_header* mh = (mach_header*)p;
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));
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;
break;
}
}
}
}
if ( mach_o::relocatable::Reader<x86>::validFile(p) )
return new mach_o::relocatable::Reader<x86>::Reader(p, path, 0, options, 0);
else if ( mach_o::relocatable::Reader<ppc>::validFile(p) )
return new mach_o::relocatable::Reader<ppc>::Reader(p, path, 0, options, 0);
else if ( mach_o::relocatable::Reader<ppc64>::validFile(p) )
return new mach_o::relocatable::Reader<ppc64>::Reader(p, path, 0, options, 0);
else if ( mach_o::relocatable::Reader<x86_64>::validFile(p) )
return new mach_o::relocatable::Reader<x86_64>::Reader(p, path, 0, options, 0);
else if ( mach_o::relocatable::Reader<arm>::validFile(p) )
return new mach_o::relocatable::Reader<arm>::Reader(p, path, 0, options, 0);
#if LTO_SUPPORT
if ( lto::Reader::validFile(p, stat_buf.st_size, 0) ) {
return new lto::Reader(p, stat_buf.st_size, path, 0, options, 0);
}
#endif
throwf("not a mach-o object file: %s", path);
}
static
void
usage()
{
fprintf(stderr, "ObjectDump options:\n"
"\t-no_content\tdon't dump contents\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;
}
ObjectFile::ReaderOptions options;
options.fAddCompactUnwindEncoding = true;
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, "-arch") == 0 ) {
const char* arch = ++i<argc? argv[i]: "";
if ( strcmp(arch, "ppc64") == 0 )
sPreferredArch = CPU_TYPE_POWERPC64;
else if ( strcmp(arch, "ppc") == 0 )
sPreferredArch = CPU_TYPE_POWERPC;
else if ( strcmp(arch, "i386") == 0 )
sPreferredArch = CPU_TYPE_I386;
else if ( strcmp(arch, "x86_64") == 0 )
sPreferredArch = CPU_TYPE_X86_64;
else if ( strcmp(arch, "arm") == 0 )
sPreferredArch = CPU_TYPE_ARM;
else if ( strcmp(arch, "armv4t") == 0 ) {
sPreferredArch = CPU_TYPE_ARM;
sPreferredSubArch = CPU_SUBTYPE_ARM_V4T;
}
else if ( strcmp(arch, "armv5") == 0 ) {
sPreferredArch = CPU_TYPE_ARM;
sPreferredSubArch = CPU_SUBTYPE_ARM_V5TEJ;
}
else if ( strcmp(arch, "armv6") == 0 ) {
sPreferredArch = CPU_TYPE_ARM;
sPreferredSubArch = CPU_SUBTYPE_ARM_V6;
}
else if ( strcmp(arch, "armv7") == 0 ) {
sPreferredArch = CPU_TYPE_ARM;
sPreferredSubArch = CPU_SUBTYPE_ARM_V7;
}
else
throwf("unknown architecture %s", arch);
}
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 {
ObjectFile::Reader* reader = createReader(arg, options);
dumpFile(reader);
}
}
}
catch (const char* msg) {
fprintf(stderr, "ObjDump failed: %s\n", msg);
return 1;
}
return 0;
}