You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
263 lines
9.5 KiB
263 lines
9.5 KiB
/*-*- 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/conv/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; |
|
}
|
|
|