264 lines
9.5 KiB
C
264 lines
9.5 KiB
C
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||
│ Copyright 2020 Justine Alexandra Roberts Tunney │
|
||
│ │
|
||
│ This program is free software; you can redistribute it and/or modify │
|
||
│ it under the terms of the GNU General Public License as published by │
|
||
│ the Free Software Foundation; version 2 of the License. │
|
||
│ │
|
||
│ This program is distributed in the hope that it will be useful, but │
|
||
│ WITHOUT ANY WARRANTY; without even the implied warranty of │
|
||
│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │
|
||
│ General Public License for more details. │
|
||
│ │
|
||
│ You should have received a copy of the GNU General Public License │
|
||
│ along with this program; if not, write to the Free Software │
|
||
│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │
|
||
│ 02110-1301 USA │
|
||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||
#include "libc/assert.h"
|
||
#include "libc/bits/safemacros.internal.h"
|
||
#include "libc/calls/calls.h"
|
||
#include "libc/calls/struct/stat.h"
|
||
#include "libc/fmt/conv.h"
|
||
#include "libc/macho.internal.h"
|
||
#include "libc/runtime/runtime.h"
|
||
#include "libc/stdio/stdio.h"
|
||
#include "libc/sysv/consts/map.h"
|
||
#include "libc/sysv/consts/o.h"
|
||
#include "libc/sysv/consts/prot.h"
|
||
#include "tool/decode/lib/asmcodegen.h"
|
||
#include "tool/decode/lib/flagger.h"
|
||
#include "tool/decode/lib/machoidnames.h"
|
||
#include "tool/decode/lib/titlegen.h"
|
||
|
||
/**
|
||
* @fileoverview Apple Mach-O metadata disassembler.
|
||
*/
|
||
|
||
static const char *path;
|
||
static struct MachoHeader *macho;
|
||
static size_t machosize;
|
||
|
||
static void startfile(void) {
|
||
showtitle("αcτµαlly pδrταblε εxεcµταblε", "tool/decode/macho", NULL, NULL,
|
||
&kModelineAsm);
|
||
printf("#include \"libc/macho.internal.h\"\n\n", path);
|
||
}
|
||
|
||
static void showmachoheader(void) {
|
||
#if !defined(TRUSTWORTHY) && !defined(MACHO_TRUSTWORTHY)
|
||
if (sizeof(struct MachoHeader) > machosize) {
|
||
fprintf(stderr, "error: %'s: MachoHeader overruns eof\n", path);
|
||
exit(1);
|
||
}
|
||
#endif
|
||
showtitle(basename(path), "macho", "header", NULL, NULL);
|
||
printf("\n");
|
||
showinthex(macho->magic);
|
||
showinthex(macho->arch);
|
||
showinthex(macho->arch2);
|
||
show(".long",
|
||
firstnonnull(findnamebyid(kMachoFileTypeNames, macho->filetype),
|
||
format(b1, "%#x", macho->filetype)),
|
||
"macho->filetype");
|
||
showinthex(macho->loadcount);
|
||
showinthex(macho->loadsize);
|
||
show(".long",
|
||
firstnonnull(RecreateFlags(kMachoFlagNames, macho->flags),
|
||
format(b1, "%#x", macho->flags)),
|
||
"macho->flags");
|
||
showinthex(macho->__reserved);
|
||
printf("\n");
|
||
}
|
||
|
||
static void showmachosection(struct MachoSection *section) {
|
||
show(".ascin", format(b1, "%`'s,16", section->name), "section->name");
|
||
show(".ascin", format(b1, "%`'s,16", section->commandname),
|
||
"section->commandname");
|
||
showint64hex(section->vaddr);
|
||
showint64hex(section->memsz);
|
||
showinthex(section->offset);
|
||
showinthex(section->alignlog2);
|
||
showinthex(section->relotaboff);
|
||
showinthex(section->relocount);
|
||
showinthex(section->attr);
|
||
show(".long",
|
||
format(b1, "%d,%d,%d", section->__reserved[0], section->__reserved[1],
|
||
section->__reserved[2]),
|
||
"section->__reserved");
|
||
}
|
||
|
||
static void showmacholoadsegment(unsigned i, struct MachoLoadSegment *loadseg) {
|
||
assert(loadseg->size ==
|
||
sizeof(struct MachoLoadSegment) +
|
||
loadseg->sectioncount * sizeof(struct MachoSection));
|
||
#if !defined(TRUSTWORTHY) && !defined(MACHO_TRUSTWORTHY)
|
||
if ((intptr_t)loadseg + sizeof(struct MachoLoadSegment) +
|
||
loadseg->sectioncount * sizeof(struct MachoSection) >
|
||
(intptr_t)macho + machosize) {
|
||
abort();
|
||
}
|
||
#endif
|
||
show(".ascin", format(b1, "%`'s,16", loadseg->name), "loadseg->name");
|
||
showint64hex(loadseg->vaddr);
|
||
showint64hex(loadseg->memsz);
|
||
showint64hex(loadseg->offset);
|
||
showint64hex(loadseg->filesz);
|
||
show(".long",
|
||
firstnonnull(RecreateFlags(kMachoVmProtNames, loadseg->maxprot),
|
||
format(b1, "%#x", loadseg->maxprot)),
|
||
"loadseg->maxprot");
|
||
show(".long",
|
||
firstnonnull(RecreateFlags(kMachoVmProtNames, loadseg->initprot),
|
||
format(b1, "%#x", loadseg->initprot)),
|
||
"loadseg->initprot");
|
||
showinthex(loadseg->sectioncount);
|
||
show(".long",
|
||
firstnonnull(RecreateFlags(kMachoSegmentFlagNames, loadseg->flags),
|
||
format(b1, "%#x", loadseg->flags)),
|
||
"loadseg->flags");
|
||
for (unsigned j = 0; j < loadseg->sectioncount; ++j) {
|
||
printf("%d:", (i + 1) * 100 + (j + 1) * 10);
|
||
showmachosection((struct MachoSection *)((intptr_t)loadseg +
|
||
sizeof(struct MachoLoadSegment) +
|
||
j * sizeof(struct MachoSection)));
|
||
}
|
||
}
|
||
|
||
static void showmacholoadsymtabshowall(struct MachoLoadSymtab *ls) {
|
||
assert(ls->size == sizeof(struct MachoLoadSymtab));
|
||
#if !defined(TRUSTWORTHY) && !defined(MACHO_TRUSTWORTHY)
|
||
if ((intptr_t)ls + sizeof(struct MachoLoadSymtab) >
|
||
(intptr_t)macho + machosize) {
|
||
abort();
|
||
}
|
||
#endif
|
||
showinthex(ls->offset);
|
||
showinthex(ls->count);
|
||
showinthex(ls->stroff);
|
||
showinthex(ls->strsize);
|
||
}
|
||
|
||
static void showmacholoaduuid(struct MachoLoadUuid *lu) {
|
||
assert(lu->size == sizeof(struct MachoLoadUuid));
|
||
#if !defined(TRUSTWORTHY) && !defined(MACHO_TRUSTWORTHY)
|
||
if ((intptr_t)lu + sizeof(struct MachoLoadUuid) >
|
||
(intptr_t)macho + machosize) {
|
||
abort();
|
||
}
|
||
#endif
|
||
printf("\t.byte\t");
|
||
for (unsigned i = 0; i < 16; ++i) {
|
||
if (i) {
|
||
if (i == 8) {
|
||
printf("\n\t.byte\t");
|
||
} else {
|
||
printf(",");
|
||
}
|
||
}
|
||
printf("%#hhx", lu->uuid[i]);
|
||
}
|
||
printf("\n");
|
||
}
|
||
|
||
static void showmacholoadsourceversion(
|
||
struct MachoLoadSourceVersionCommand *sv) {
|
||
assert(sv->size == sizeof(struct MachoLoadSourceVersionCommand));
|
||
#if !defined(TRUSTWORTHY) && !defined(MACHO_TRUSTWORTHY)
|
||
if ((intptr_t)sv + sizeof(struct MachoLoadSourceVersionCommand) >
|
||
(intptr_t)macho + machosize) {
|
||
abort();
|
||
}
|
||
#endif
|
||
showint64hex(sv->version);
|
||
}
|
||
|
||
static void showmacholoadunixthread(struct MachoLoadThreadCommand *lc) {
|
||
assert(lc->size == 4 + 4 + 4 + 4 + lc->count * 4);
|
||
showinthex(lc->flavor);
|
||
showint(lc->count);
|
||
for (unsigned i = 0; i < lc->count; ++i) {
|
||
showinthex(lc->wut[i]);
|
||
}
|
||
}
|
||
|
||
static void showmacholoadcommand(struct MachoLoadCommand *lc, unsigned i) {
|
||
#if !defined(TRUSTWORTHY) && !defined(MACHO_TRUSTWORTHY)
|
||
if ((intptr_t)lc > (intptr_t)macho + machosize ||
|
||
(intptr_t)lc + lc->size > (intptr_t)macho + machosize) {
|
||
abort();
|
||
}
|
||
#endif
|
||
showorg((intptr_t)lc - (intptr_t)macho);
|
||
printf("%d:", (i + 1) * 10);
|
||
show(".long",
|
||
firstnonnull(findnamebyid(kMachoLoadCommandNames, lc->command),
|
||
format(b1, "%#x", lc->command)),
|
||
"lc->command");
|
||
showinthex(lc->size);
|
||
switch (lc->command) {
|
||
case MAC_LC_SEGMENT_64:
|
||
showmacholoadsegment(i, (struct MachoLoadSegment *)lc);
|
||
break;
|
||
case MAC_LC_SYMTAB:
|
||
showmacholoadsymtabshowall((struct MachoLoadSymtab *)lc);
|
||
break;
|
||
case MAC_LC_UUID:
|
||
showmacholoaduuid((struct MachoLoadUuid *)lc);
|
||
break;
|
||
case MAC_LC_SOURCE_VERSION:
|
||
showmacholoadsourceversion((struct MachoLoadSourceVersionCommand *)lc);
|
||
break;
|
||
case MAC_LC_UNIXTHREAD:
|
||
showmacholoadunixthread((struct MachoLoadThreadCommand *)lc);
|
||
break;
|
||
default:
|
||
break;
|
||
}
|
||
printf("\n");
|
||
}
|
||
|
||
static void showmacholoadcommands(void) {
|
||
#if !defined(TRUSTWORTHY) && !defined(MACHO_TRUSTWORTHY)
|
||
if (sizeof(struct MachoHeader) + macho->loadsize > machosize) {
|
||
fprintf(stderr, "error: %'s: macho->loadsize overruns eof\n", path);
|
||
exit(1);
|
||
}
|
||
#endif
|
||
unsigned i = 0;
|
||
const unsigned count = macho->loadcount;
|
||
for (struct MachoLoadCommand *lc =
|
||
(void *)((intptr_t)macho + sizeof(struct MachoHeader));
|
||
i < count; ++i, lc = (void *)((intptr_t)lc + lc->size)) {
|
||
showmacholoadcommand(lc, i);
|
||
}
|
||
}
|
||
|
||
void showall(void) {
|
||
startfile();
|
||
showmachoheader();
|
||
showmacholoadcommands();
|
||
}
|
||
|
||
int main(int argc, char *argv[]) {
|
||
int64_t fd;
|
||
struct stat st[1];
|
||
if (argc != 2) fprintf(stderr, "usage: %s FILE\n", argv[0]), exit(1);
|
||
if ((fd = open((path = argv[1]), O_RDONLY)) == -1 || fstat(fd, st) == -1 ||
|
||
(macho = mmap(NULL, (machosize = st->st_size), PROT_READ, MAP_SHARED, fd,
|
||
0)) == MAP_FAILED) {
|
||
fprintf(stderr, "error: %'s %m\n", path);
|
||
exit(1);
|
||
}
|
||
if (macho->magic != 0xFEEDFACF) {
|
||
fprintf(stderr, "error: %'s not a macho x64 executable\n", path);
|
||
exit(1);
|
||
}
|
||
showall();
|
||
munmap(macho, machosize);
|
||
close(fd);
|
||
return 0;
|
||
}
|