cosmopolitan/tool/decode/macho.c

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

355 lines
12 KiB
C
Raw Normal View History

2020-06-15 14:18:57 +00:00
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2020 Justine Alexandra Roberts Tunney
2020-12-28 01:18:44 +00:00
Permission to use, copy, modify, and/or distribute this software for
any purpose with or without fee is hereby granted, provided that the
above copyright notice and this permission notice appear in all copies.
2020-06-15 14:18:57 +00:00
2020-12-28 01:18:44 +00:00
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
2020-06-15 14:18:57 +00:00
*/
#include "libc/assert.h"
#include "libc/calls/calls.h"
#include "libc/calls/struct/stat.h"
#include "libc/fmt/conv.h"
2023-06-18 12:39:31 +00:00
#include "libc/fmt/libgen.h"
2022-08-11 19:13:18 +00:00
#include "libc/intrin/safemacros.h"
#include "libc/macho.h"
#include "libc/mem/gc.h"
#include "libc/mem/mem.h"
2020-06-15 14:18:57 +00:00
#include "libc/runtime/runtime.h"
#include "libc/stdio/stdio.h"
2023-05-17 09:29:30 +00:00
#include "libc/str/str.h"
#include "libc/str/tab.h"
2020-06-15 14:18:57 +00:00
#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.h\"\n\n", path);
2020-06-15 14:18:57 +00:00
}
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(gc(strdup(path))), "macho", "header", NULL, NULL);
2020-06-15 14:18:57 +00:00
printf("\n");
showinthex(macho->magic);
2023-05-17 09:29:30 +00:00
show(".long",
firstnonnull(findnamebyid(kMachoArchitectures, macho->arch),
format(b1, "%#x", macho->arch)),
"macho->arch");
2020-06-15 14:18:57 +00:00
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),
2020-06-15 14:18:57 +00:00
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),
2020-06-15 14:18:57 +00:00
format(b1, "%#x", loadseg->maxprot)),
"loadseg->maxprot");
show(".long",
firstnonnull(RecreateFlags(kMachoVmProtNames, loadseg->initprot),
2020-06-15 14:18:57 +00:00
format(b1, "%#x", loadseg->initprot)),
"loadseg->initprot");
showinthex(loadseg->sectioncount);
show(".long",
firstnonnull(RecreateFlags(kMachoSegmentFlagNames, loadseg->flags),
2020-06-15 14:18:57 +00:00
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(",");
}
}
2023-05-17 09:29:30 +00:00
printf("%#02hhx", lu->uuid[i]);
2020-06-15 14:18:57 +00:00
}
printf("\n");
}
2023-05-17 09:29:30 +00:00
#define COLS 8
static void showmacholoadgeneric(struct MachoLoadCommand *lc) {
int c, col = 0;
2023-05-17 09:29:30 +00:00
int need_newline = 0;
char16_t glyphs[COLS + 1];
const unsigned char *p, *pe;
p = (const unsigned char *)(lc + 1);
pe = p + (lc->size - sizeof(*lc));
while (p < pe) {
c = *p++;
if (!col) {
need_newline = 1;
printf("\t.byte\t");
bzero(glyphs, sizeof(glyphs));
}
glyphs[col] = kCp437[c];
if (col)
putchar(',');
printf("0x%02x", c);
if (++col == COLS) {
col = 0;
printf("\t//%hs\n", glyphs);
need_newline = 0;
}
}
if (need_newline) {
printf("\n");
}
}
2020-06-15 14:18:57 +00:00
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);
2023-05-17 09:29:30 +00:00
char buf[256];
buf[0] = 0;
const char *name;
uint32_t command = lc->command;
if (!(name = findnamebyid(kMachoLoadCommandNames, command)) &&
(command & MAC_LC_REQ_DYLD)) {
command &= ~MAC_LC_REQ_DYLD;
strcpy(buf, "MAC_LC_REQ_DYLD|");
name = findnamebyid(kMachoLoadCommandNames, command);
}
if (name) {
strlcat(buf, name, sizeof(buf));
} else {
strlcat(buf, format(b1, "%#x", command), sizeof(buf));
}
show(".long", buf, "lc->command");
2020-06-15 14:18:57 +00:00
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;
2023-05-17 09:29:30 +00:00
#if 0
2020-06-15 14:18:57 +00:00
case MAC_LC_SOURCE_VERSION:
showmacholoadsourceversion((struct MachoLoadSourceVersionCommand *)lc);
break;
2023-05-17 09:29:30 +00:00
#endif
2020-06-15 14:18:57 +00:00
case MAC_LC_UNIXTHREAD:
showmacholoadunixthread((struct MachoLoadThreadCommand *)lc);
break;
2023-05-17 09:29:30 +00:00
case MAC_LC_DYLD_INFO:
case MAC_LC_DYLD_INFO_ONLY: {
const struct MachoDyldInfoCommand *di =
(const struct MachoDyldInfoCommand *)lc;
showinthex(di->rebase_off);
showinthex(di->rebase_size);
showinthex(di->bind_off);
showinthex(di->bind_size);
showinthex(di->weak_bind_off);
showinthex(di->weak_bind_size);
showinthex(di->lazy_bind_off);
showinthex(di->lazy_bind_size);
showinthex(di->export_off);
showinthex(di->export_size);
break;
}
case MAC_LC_CODE_SIGNATURE:
case MAC_LC_SEGMENT_SPLIT_INFO:
case MAC_LC_FUNCTION_STARTS:
case MAC_LC_DATA_IN_CODE:
case MAC_LC_DYLIB_CODE_SIGN_DRS:
case MAC_LC_LINKER_OPTIMIZATION_HINT:
case MAC_LC_DYLD_EXPORTS_TRIE:
case MAC_LC_DYLD_CHAINED_FIXUPS: {
const struct MachoLinkeditDataCommand *ld =
(const struct MachoLinkeditDataCommand *)lc;
showint64hex(ld->dataoff);
showint64hex(ld->datasize);
break;
}
case MAC_LC_MAIN: {
const struct MachoEntryPointCommand *main =
(const struct MachoEntryPointCommand *)lc;
showint64hex(main->entryoff);
showint64hex(main->stacksize);
break;
}
2020-06-15 14:18:57 +00:00
default:
2023-05-17 09:29:30 +00:00
showmacholoadgeneric(lc);
2020-06-15 14:18:57 +00:00
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;
}