/*-*- 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; }