/*-*- 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 2021 Justine Alexandra Roberts Tunney │ │ │ │ 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. │ │ │ │ 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. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" #include "libc/calls/struct/stat.h" #include "libc/elf/def.h" #include "libc/elf/elf.h" #include "libc/elf/struct/ehdr.h" #include "libc/elf/struct/shdr.h" #include "libc/elf/struct/sym.h" #include "libc/log/check.h" #include "libc/log/log.h" #include "libc/macros.internal.h" #include "libc/nt/struct/importobjectheader.internal.h" #include "libc/runtime/runtime.h" #include "libc/stdio/stdio.h" #include "libc/str/str.h" #include "libc/sysv/consts/map.h" #include "libc/sysv/consts/o.h" #include "libc/sysv/consts/prot.h" #include "third_party/xed/x86.h" #include "tool/build/lib/dis.h" #include "tool/build/lib/high.h" #include "tool/build/lib/loader.h" #define HEXWIDTH 8 const char *const kRealSymbols[] = { "a20", "ape.mbrpad", "ape.str", "ape_disk", "ape_grub", "ape_mz", "apesh", "dsknfo", "e820", "gdt", "golong", "hiload", "lcheck", "longmodeloader", "pc", "pcread", "pinit", "realmodeloader", "rldie", "rvputs", "sconf", "sinit", "sinit4", "str.cpuid", "str.crlf", "str.dsknfo", "str.e820", "str.error", "str.long", "str.memory", "str.oldskool", "stub", "unreal", }; const char *const kLegacySymbols[] = { "ape_grub_entry", }; bool boop; long cursym; Elf64_Ehdr *elf; Elf64_Shdr *sec; struct Dis dis[1]; struct Machine m[1]; struct Elf diself[1]; char codebuf[256]; char optspecbuf[128]; const Elf64_Sym *syms; char *symstrs, *secstrs; size_t i, j, k, l, size, skip, symcount; bool IsRealSymbol(const char *s) { int m, l, r, x; l = 0; r = ARRAYLEN(kRealSymbols) - 1; while (l <= r) { m = (l + r) >> 1; x = strcmp(s, kRealSymbols[m]); if (x < 0) { r = m - 1; } else if (x > 0) { l = m + 1; } else { return true; } } return false; } bool IsLegacySymbol(const char *s) { int m, l, r, x; l = 0; r = ARRAYLEN(kLegacySymbols) - 1; while (l <= r) { m = (l + r) >> 1; x = strcmp(s, kLegacySymbols[m]); if (x < 0) { r = m - 1; } else if (x > 0) { l = m + 1; } else { return true; } } return false; } void SetSymbol(bool printit) { if (!boop) { printf("\n"); boop = true; } if (printit) { printf("\e[38;5;%dm%s\e[0m:\n", g_high.label, symstrs + syms[k].st_name); } if (IsRealSymbol(symstrs + syms[k].st_name)) { if (m->mode != XED_MACHINE_MODE_REAL) { printf("\t\e[38;5;%dm.code16\e[0m\n", g_high.keyword); } m->mode = XED_MACHINE_MODE_REAL; } else if (IsLegacySymbol(symstrs + syms[k].st_name)) { if (m->mode != XED_MACHINE_MODE_LEGACY_32) { printf("\t\e[38;5;%dm.code32\e[0m\n", g_high.keyword); } m->mode = XED_MACHINE_MODE_LEGACY_32; } else { if (m->mode != XED_MACHINE_MODE_LONG_64) { printf("\t\e[38;5;%dm.code64\e[0m\n", g_high.keyword); } m->mode = XED_MACHINE_MODE_LONG_64; } cursym = k; if (syms[k].st_size) { skip = syms[k].st_size; } else { skip = -1; for (l = 0; l < symcount; ++l) { if (syms[l].st_shndx == i && syms[l].st_name && syms[l].st_value > syms[cursym].st_value) { skip = MIN(skip, syms[l].st_value - syms[cursym].st_value); } } if (skip == -1) { skip = sec->sh_addr + sec->sh_size - syms[cursym].st_value; } } } void PrintSymbolName(void) { bool done; done = false; boop = false; for (k = 0; k < symcount; ++k) { if (syms[k].st_value == sec->sh_addr + j && syms[k].st_name) { if (!done && syms[k].st_size) { SetSymbol(true); done = true; } else { if (!boop) { printf("\n"); boop = true; } printf("\e[38;5;%dm%s\e[0m:\n", g_high.label, symstrs + syms[k].st_name); } } } if (done) { return; } for (k = 0; k < symcount; ++k) { if (ELF64_ST_TYPE(syms[k].st_info) && syms[k].st_name && syms[k].st_value == sec->sh_addr + j) { SetSymbol(false); return; } } for (k = 0; k < symcount; ++k) { if (syms[k].st_name && syms[k].st_value == sec->sh_addr + j) { SetSymbol(false); return; } } if (cursym != -1 && syms[cursym].st_size && sec->sh_addr + j >= syms[cursym].st_value + syms[cursym].st_size) { cursym = -1; skip = 1; } } bool Ild(void) { int remain; remain = 15; if (cursym != -1 && syms[cursym].st_size) { remain = (syms[cursym].st_value + syms[cursym].st_size) - (sec->sh_addr + j); } xed_decoded_inst_zero_set_mode(dis->xedd, m->mode); xed_instruction_length_decode(dis->xedd, (char *)elf + sec->sh_offset + j, remain); skip = dis->xedd->op.error ? 1 : MAX(1, dis->xedd->length); return !dis->xedd->op.error; } bool IsCode(void) { if (!(sec->sh_flags & SHF_EXECINSTR)) return false; if (cursym != -1 && ELF64_ST_TYPE(syms[cursym].st_info) == STT_OBJECT) { return false; } return true; } bool IsBss(void) { return sec->sh_type == SHT_NOBITS; } void Disassemble(void) { int c; bool istext; cursym = -1; secstrs = GetElfSectionNameStringTable(elf, size); symstrs = GetElfStringTable(elf, size); syms = GetElfSymbolTable(elf, size, &symcount); for (i = 0; i < elf->e_shnum; ++i) { sec = GetElfSectionHeaderAddress(elf, size, i); if (!sec->sh_size) continue; if (!(sec->sh_flags & SHF_ALLOC)) continue; printf("\n\t\e[38;5;%dm.section\e[0m %s\n", g_high.keyword, secstrs + sec->sh_name); for (j = 0; j < sec->sh_size; j += MAX(1, skip)) { PrintSymbolName(); if (cursym == -1) continue; if (sec->sh_type == SHT_NOBITS) { printf("\t\e[38;5;%dm.zero\e[0m\t%ld\n", g_high.keyword, skip); } else if (IsCode()) { if (Ild()) { dis->addr = sec->sh_addr + j; DisInst(dis, codebuf, DisSpec(dis->xedd, optspecbuf)); printf("\t%s\n", codebuf); } else { printf("\t.wut\t%ld\n", dis->xedd->op.error); } } else { for (k = 0; k < skip; ++k) { if (!(k & (HEXWIDTH - 1))) { if (k) { printf(" \e[38;5;%dm# %#.*s\e[0m\n", g_high.comment, HEXWIDTH, (unsigned char *)elf + sec->sh_offset + j + k - HEXWIDTH); } printf("\t\e[38;5;%dm.byte\e[0m\t", g_high.keyword); } else if (k) { printf(","); } printf("0x%02x", ((unsigned char *)elf)[sec->sh_offset + j + k]); } if (k) { if (!(k & (HEXWIDTH - 1))) { printf(" \e[38;5;%dm# %#.*s\e[0m\n", g_high.comment, HEXWIDTH, (unsigned char *)elf + sec->sh_offset + j + skip - HEXWIDTH); } else { for (l = 0; l < HEXWIDTH - (k & (HEXWIDTH - 1)); ++l) { printf(" "); } printf(" \e[38;5;%dm# %#.*s\e[0m\n", g_high.comment, skip - ROUNDDOWN(skip, HEXWIDTH), (unsigned char *)elf + sec->sh_offset + j + ROUNDDOWN(skip, HEXWIDTH)); } } } } } } int main(int argc, char *argv[]) { showcrashreports(); int fd; void *map; struct stat st; const char *path; if (argc == 1) { fprintf(stderr, "USAGE: %s ELF\n", program_invocation_name); exit(1); } else { path = argv[1]; } if ((fd = open(path, O_RDONLY)) == -1) { fprintf(stderr, "ERROR: NOT FOUND: %`'s\n", path); exit(1); } CHECK_NE(-1, fstat(fd, &st)); CHECK_NE(0, st.st_size); CHECK_NE(MAP_FAILED, (map = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0))); if (memcmp(map, ELFMAG, 4)) { fprintf(stderr, "ERROR: NOT AN ELF: %`'s\n", path); exit(1); } m->mode = XED_MACHINE_MODE_LONG_64; g_high.keyword = 155; g_high.reg = 215; g_high.literal = 182; g_high.label = 221; g_high.comment = 112; g_high.quote = 180; dis->m = m; diself->prog = path; LoadDebugSymbols(diself); DisLoadElf(dis, diself); LOGIFNEG1(close(fd)); elf = map; size = st.st_size; Disassemble(); LOGIFNEG1(munmap(map, st.st_size)); return 0; }