#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <fcntl.h>
#include <mach-o/loader.h>
#include <mach-o/fat.h>
#include <mach-o/stab.h>
#include "MachOReaderRelocatable.hpp"
static bool sDumpContent= true;
static bool sDumpStabs = false;
static bool sSort = true;
static cpu_type_t sPreferredArch = CPU_TYPE_POWERPC64;
__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;
}
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 dumpAtom(ObjectFile::Atom* atom)
{
printf("name: %s\n", atom->getDisplayName());
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");
}
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;
default:
printf("scope: unknown\n");
}
printf("section: %s,%s\n", atom->getSegment().getName(), atom->getSectionName());
printf("attrs: ");
if ( atom->dontDeadStrip() )
printf("dont-dead-strip ");
if ( atom->isZeroFill() )
printf("zero-fill ");
printf("\n");
printf("size: 0x%012llX\n", atom->getSize());
printf("align: %d\n", atom->getAlignment());
if ( sDumpContent ) {
uint64_t size = atom->getSize();
if ( size < 4096 ) {
uint8_t content[size];
atom->copyRawContent(content);
printf("content: ");
if ( strcmp(atom->getSectionName(), "__cstring") == 0 ) {
printf("\"%s\"", content);
}
else {
for (unsigned int i=0; i < size; ++i)
printf("%02X ", content[i]);
}
}
printf("\n");
}
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());
}
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);
}
}
}
struct AtomSorter
{
bool operator()(ObjectFile::Atom* left, ObjectFile::Atom* right)
{
return (strcmp(left->getDisplayName(), right->getDisplayName()) < 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) {
dumpAtom(*it);
printf("\n");
}
}
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, 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 < fh->nfat_arch; ++i) {
if ( archs[i].cputype == sPreferredArch ) {
p = p + archs[i].offset;
mh = (struct mach_header*)p;
}
}
}
if ( mach_o::relocatable::Reader<x86>::validFile(p) )
return mach_o::relocatable::Reader<x86>::make(p, path, 0, options);
else if ( mach_o::relocatable::Reader<ppc>::validFile(p) )
return mach_o::relocatable::Reader<ppc>::make(p, path, 0, options);
else if ( mach_o::relocatable::Reader<ppc64>::validFile(p) )
return mach_o::relocatable::Reader<ppc64>::make(p, path, 0, options);
throwf("not a mach-o object file: %s", path);
}
int main(int argc, const char* argv[])
{
ObjectFile::ReaderOptions options;
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, "-stabs") == 0 ) {
sDumpStabs = true;
}
else if ( strcmp(arg, "-no_sort") == 0 ) {
sSort = false;
}
else if ( strcmp(arg, "-arch") == 0 ) {
const char* arch = 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
throwf("unknown architecture %s", arch);
}
else {
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;
}