cosmopolitan/tool/build/lib/disarg.c

576 lines
16 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/conv/itoa.h"
#include "libc/limits.h"
#include "libc/log/check.h"
#include "libc/macros.h"
#include "libc/str/str.h"
#include "tool/build/lib/dis.h"
#include "tool/build/lib/endian.h"
#include "tool/build/lib/modrm.h"
#define SSTRCMP8(S1, S2) \
({ \
uint64_t x, y; \
x = READ64BE(S1); \
y = READ64BE(S2); \
x > y ? 1 : x < y ? -1 : 0; \
})
static const char kScale[4][4] = {"", ",2", ",4", ",8"};
static const char kSegName[8][3] = {"es", "cs", "ss", "ds", "fs", "gs"};
static const char kSegOverride[8][3] = {"", "cs", "ds", "es", "fs", "gs", "ss"};
static const char kRegisterName8[2][2][8][5] = {
{{"al", "cl", "dl", "bl", "ah", "ch", "dh", "bh"},
{"al", "cl", "dl", "bl", "spl", "bpl", "sil", "dil"}},
{{"???", "???", "???", "???", "???", "???", "???", "???"},
{"r8b", "r9b", "r10b", "r11b", "r12b", "r13b", "r14b", "r15b"}},
};
static const char kRegisterName[2][2][2][8][5] = {
{{{"eax", "ecx", "edx", "ebx", "esp", "ebp", "esi", "edi"},
{"r8d", "r9d", "r10d", "r11d", "r12d", "r13d", "r14d", "r15d"}},
{{"rax", "rcx", "rdx", "rbx", "rsp", "rbp", "rsi", "rdi"},
{"r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15"}}},
{{{"ax", "cx", "dx", "bx", "sp", "bp", "si", "di"},
{"r8w", "re9w", "r10w", "r11w", "r12w", "r13w", "r14w", "r15w"}},
{{"rax", "rcx", "rdx", "rbx", "rsp", "rbp", "rsi", "rdi"},
{"r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15"}}},
};
static int64_t RipRelative(struct DisBuilder b, int64_t d) {
return b.addr + b.xedd->length + d;
}
static const char *GetAddrReg(struct DisBuilder b, uint8_t x, uint8_t r) {
return kRegisterName[0][!Asz(b.xedd)][x & 1][r & 7];
}
static char *DisRegister(char *p, const char *s) {
if (g_dis_high) p = DisHigh(p, g_dis_high->reg);
*p++ = '%';
p = stpcpy(p, s);
if (g_dis_high) p = DisHigh(p, -1);
return p;
}
static char *DisLiteral(char *p, const char *s) {
if (g_dis_high) p = DisHigh(p, g_dis_high->literal);
p = stpcpy(p, s);
if (g_dis_high) p = DisHigh(p, -1);
return p;
}
static char *DisComment(char *p, const char *s) {
if (g_dis_high) p = DisHigh(p, g_dis_high->comment);
p = stpcpy(p, s);
if (g_dis_high) p = DisHigh(p, -1);
return p;
}
static char *DisRegisterByte(struct DisBuilder b, char *p, bool g, int r) {
return DisRegister(p, kRegisterName8[g][Rex(b.xedd)][r]);
}
static char *DisRegisterWord(struct DisBuilder b, char *p, bool g, int r) {
return DisRegister(p, kRegisterName[Osz(b.xedd)][Rexw(b.xedd)][g][r]);
}
static char *DisGvqp(struct DisBuilder b, char *p) {
return DisRegisterWord(b, p, Rexr(b.xedd), ModrmReg(b.xedd));
}
static char *DisGdqp(struct DisBuilder b, char *p) {
return DisRegister(
p, kRegisterName[0][Rexw(b.xedd)][Rexr(b.xedd)][ModrmReg(b.xedd)]);
}
static char *DisGb(struct DisBuilder b, char *p) {
return DisRegisterByte(b, p, Rexr(b.xedd), ModrmReg(b.xedd));
}
static uint8_t DisSeg(struct DisBuilder b) {
return b.xedd->op.seg_ovd ? b.xedd->op.seg_ovd : b.xedd->op.hint;
}
static char *DisInt(char *p, int64_t x) {
if (-15 <= x && x <= 15) {
p += int64toarray_radix10(x, p);
} else if (x == INT64_MIN) {
p = stpcpy(p, "-0x");
p += uint64toarray_radix16(INT64_MIN, p);
} else if (x < 0 && -x < 0xFFFFFFFF) {
p = stpcpy(p, "-0x");
p += uint64toarray_radix16(-x, p);
} else {
p = stpcpy(p, "0x");
p += uint64toarray_radix16(x, p);
}
return p;
}
static char *DisSym(struct DisBuilder b, char *p, int64_t addr) {
long sym;
int64_t addend;
const char *name;
if ((sym = DisFindSym(b.dis, addr)) != -1 && b.dis->syms.p[sym].name) {
addend = addr - b.dis->syms.p[sym].addr;
name = b.dis->syms.stab + b.dis->syms.p[sym].name;
p = stpcpy(p, name);
if (addend) {
*p++ = '+';
p = DisInt(p, addend);
}
return p;
} else {
return DisInt(p, addr);
}
}
static char *DisM(struct DisBuilder b, char *p) {
int64_t disp;
const char *seg, *base, *index, *scale;
base = index = scale = NULL;
seg = kSegOverride[b.xedd->op.seg_ovd ? b.xedd->op.seg_ovd : b.xedd->op.hint];
if (*seg) {
p = DisRegister(p, seg);
*p++ = ':';
}
if (ModrmMod(b.xedd) == 0b01 || ModrmMod(b.xedd) == 0b10 ||
IsRipRelative(b.xedd) ||
(ModrmMod(b.xedd) == 0b00 && ModrmRm(b.xedd) == 0b100 &&
SibBase(b.xedd) == 0b101)) {
disp = b.xedd->op.disp;
if (IsRipRelative(b.xedd)) disp = RipRelative(b, disp);
p = DisSym(b, p, disp);
}
if (!SibExists(b.xedd)) {
DCHECK(!b.xedd->op.has_sib);
if (IsRipRelative(b.xedd)) {
base = "rip";
} else {
base = GetAddrReg(b, Rexb(b.xedd), ModrmRm(b.xedd));
}
} else if (!SibIsAbsolute(b.xedd)) {
DCHECK(b.xedd->op.has_sib);
if (SibHasBase(b.xedd)) {
base = GetAddrReg(b, Rexb(b.xedd), SibBase(b.xedd));
}
if (SibHasIndex(b.xedd)) {
index = GetAddrReg(b, Rexx(b.xedd), SibIndex(b.xedd));
} else if (b.xedd->op.scale) {
index = Asz(b.xedd) ? "eiz" : "riz";
}
scale = kScale[b.xedd->op.scale];
}
if (base || index) {
*p++ = '(';
if (base) {
p = DisRegister(p, base);
}
if (index) {
*p++ = ',';
p = DisRegister(p, index);
p = stpcpy(p, scale);
}
*p++ = ')';
}
*p = '\0';
return p;
}
static char *DisEb(struct DisBuilder b, char *p) {
if (IsModrmRegister(b.xedd)) {
return DisRegisterByte(b, p, Rexb(b.xedd), ModrmRm(b.xedd));
} else {
return DisM(b, p);
}
}
static char *DisEvqp(struct DisBuilder b, char *p) {
if (IsModrmRegister(b.xedd)) {
return DisRegisterWord(b, p, Rexb(b.xedd), ModrmRm(b.xedd));
} else {
return DisM(b, p);
}
}
static char *DisEdqp(struct DisBuilder b, char *p) {
if (IsModrmRegister(b.xedd)) {
return DisRegister(
p, kRegisterName[0][Rexw(b.xedd)][Rexb(b.xedd)][ModrmRm(b.xedd)]);
} else {
return DisM(b, p);
}
}
static char *DisEvq(struct DisBuilder b, char *p) {
const char *s;
if (IsModrmRegister(b.xedd)) {
if (Osz(b.xedd)) {
s = kRegisterName[1][0][Rexb(b.xedd)][ModrmRm(b.xedd)];
} else {
s = kRegisterName[0][1][Rexb(b.xedd)][ModrmRm(b.xedd)];
}
return DisRegister(p, s);
} else {
return DisM(b, p);
}
}
static char *DisEd(struct DisBuilder b, char *p) {
if (IsModrmRegister(b.xedd)) {
return DisRegister(p, kRegisterName[0][0][Rexb(b.xedd)][ModrmRm(b.xedd)]);
} else {
return DisM(b, p);
}
}
static char *DisEq(struct DisBuilder b, char *p) {
if (IsModrmRegister(b.xedd)) {
return DisRegister(p, kRegisterName[0][1][Rexb(b.xedd)][ModrmRm(b.xedd)]);
} else {
return DisM(b, p);
}
}
static char *DisZvq(struct DisBuilder b, char *p) {
if (Osz(b.xedd)) {
return DisRegister(p, kRegisterName[1][0][Rexb(b.xedd)][ModrmSrm(b.xedd)]);
} else {
return DisRegister(p, kRegisterName[0][1][Rexb(b.xedd)][ModrmSrm(b.xedd)]);
}
}
static char *DisZvqp(struct DisBuilder b, char *p) {
return DisRegisterWord(b, p, Rexb(b.xedd), ModrmSrm(b.xedd));
}
static char *DisZb(struct DisBuilder b, char *p) {
return DisRegisterByte(b, p, Rexb(b.xedd), ModrmSrm(b.xedd));
}
static char *DisEax(struct DisBuilder b, char *p) {
return DisRegister(p, kRegisterName[Osz(b.xedd)][0][0][0]);
}
static char *DisRax(struct DisBuilder b, char *p) {
return DisRegister(p, kRegisterName[Osz(b.xedd)][Rexw(b.xedd)][0][0]);
}
static char *DisRdx(struct DisBuilder b, char *p) {
return DisRegister(p, kRegisterName[Osz(b.xedd)][Rexw(b.xedd)][0][2]);
}
static char *DisImm(struct DisBuilder b, char *p) {
*p++ = '$';
if (g_dis_high) p = DisHigh(p, g_dis_high->literal);
p = DisSym(b, p, b.xedd->op.uimm0);
if (g_dis_high) p = DisHigh(p, -1);
return p;
}
static char *DisJbs(struct DisBuilder b, char *p) {
if (b.xedd->op.disp > 0) *p++ = '+';
p += int64toarray_radix10(b.xedd->op.disp, p);
return p;
}
static char *DisJb(struct DisBuilder b, char *p) {
if (b.xedd->op.disp > 0) *p++ = '+';
p += uint64toarray_radix10(b.xedd->op.disp & 0xff, p);
return p;
}
static char *DisJvds(struct DisBuilder b, char *p) {
return DisSym(b, p, RipRelative(b, b.xedd->op.disp));
}
static char *DisAbs(struct DisBuilder b, char *p) {
return DisSym(b, p, b.xedd->op.disp);
}
static char *DisSw(struct DisBuilder b, char *p) {
if (kSegName[ModrmReg(b.xedd)][0]) {
p = DisRegister(p, kSegName[ModrmReg(b.xedd)]);
}
*p = '\0';
return p;
}
static char *DisY(struct DisBuilder b, char *p) {
*p++ = '(';
p = DisRegister(p, Asz(b.xedd) ? "edi" : "rdi");
*p++ = ')';
*p = '\0';
return p;
}
static char *DisX(struct DisBuilder b, char *p) {
if (kSegOverride[b.xedd->op.seg_ovd][0]) {
p = DisRegister(p, kSegOverride[b.xedd->op.seg_ovd]);
}
*p++ = '(';
p = DisRegister(p, Asz(b.xedd) ? "esi" : "rsi");
*p++ = ')';
*p = '\0';
return p;
}
static char *DisBBb(struct DisBuilder b, char *p) {
if (kSegOverride[b.xedd->op.seg_ovd][0]) {
p = DisRegister(p, kSegOverride[b.xedd->op.seg_ovd]);
}
*p++ = '(';
p = DisRegister(p, Asz(b.xedd) ? "ebx" : "rbx");
*p++ = ')';
*p = '\0';
return p;
}
static char *DisXmm(struct DisBuilder b, char *p, const char *s, int reg) {
if (g_dis_high) p = DisHigh(p, g_dis_high->reg);
*p++ = '%';
p = stpcpy(p, s);
p += uint64toarray_radix10(Rexr(b.xedd) << 3 | reg, p);
if (g_dis_high) p = DisHigh(p, -1);
return p;
}
static char *DisNq(struct DisBuilder b, char *p) {
return DisXmm(b, p, "mm", ModrmRm(b.xedd));
}
static char *DisUq(struct DisBuilder b, char *p) {
return DisXmm(b, p, "mm", ModrmRm(b.xedd));
}
static char *DisPq(struct DisBuilder b, char *p) {
return DisXmm(b, p, "mm", ModrmReg(b.xedd));
}
static char *DisUdq(struct DisBuilder b, char *p) {
return DisXmm(b, p, "xmm", ModrmRm(b.xedd));
}
static char *DisVdq(struct DisBuilder b, char *p) {
return DisXmm(b, p, "xmm", ModrmReg(b.xedd));
}
static char *DisQq(struct DisBuilder b, char *p) {
if (IsModrmRegister(b.xedd)) {
return DisNq(b, p);
} else {
return DisM(b, p);
}
}
static char *DisEst(struct DisBuilder b, char *p) {
p = DisRegister(p, "st");
if (ModrmRm(b.xedd) != 0) {
*p++ = '(';
*p++ = '0' + ModrmRm(b.xedd);
*p++ = ')';
*p = '\0';
}
return p;
}
static char *DisEst1(struct DisBuilder b, char *p) {
if (ModrmRm(b.xedd) != 1) {
p = DisEst(b, p);
} else {
*p = '\0';
}
return p;
}
static char *DisEssr(struct DisBuilder b, char *p) {
if (IsModrmRegister(b.xedd)) {
return DisEst(b, p);
} else {
return DisM(b, p);
}
}
static char *DisWps(struct DisBuilder b, char *p) {
if (IsModrmRegister(b.xedd)) {
return DisUdq(b, p);
} else {
return DisM(b, p);
}
}
#define DisEdr DisM
#define DisEqp DisEq
#define DisEsr DisM
#define DisIb DisImm
#define DisIbs DisImm
#define DisIbss DisImm
#define DisIvds DisImm
#define DisIvqp DisImm
#define DisIvs DisImm
#define DisIw DisImm
#define DisMdi DisM
#define DisMdq DisM
#define DisMdqp DisM
#define DisMdr DisM
#define DisMe DisM
#define DisMer DisM
#define DisMps DisM
#define DisMq DisM
#define DisMqi DisM
#define DisMsr DisEssr
#define DisMw DisM
#define DisMwi DisM
#define DisOb DisAbs
#define DisOvqp DisAbs
#define DisPpi DisPq
#define DisQpi DisQq
#define DisRdqp DisGdqp
#define DisRvqp DisGvqp
#define DisVpd DisVdq
#define DisVps DisVdq
#define DisVq DisVdq
#define DisVsd DisVdq
#define DisVss DisVdq
#define DisWdq DisWps
#define DisWpd DisWps
#define DisWpsq DisWps
#define DisWq DisWps
#define DisWsd DisWps
#define DisWss DisWps
#define DisXb DisX
#define DisXv DisX
#define DisXvqp DisX
#define DisYb DisY
#define DisYv DisY
#define DisYvqp DisY
static const struct DisArg {
char s[8];
char *(*f)(struct DisBuilder, char *);
} kDisArgs[] = /* <sorted> */ {
{"%Gb", DisGb}, //
{"%Gdqp", DisGdqp}, //
{"%Gvqp", DisGvqp}, //
{"%Nq", DisNq}, //
{"%Ppi", DisPpi}, //
{"%Pq", DisPq}, //
{"%Rdqp", DisRdqp}, //
{"%Rvqp", DisRvqp}, //
{"%Sw", DisSw}, //
{"%Udq", DisUdq}, //
{"%Uq", DisUq}, //
{"%Vdq", DisVdq}, //
{"%Vpd", DisVpd}, //
{"%Vps", DisVps}, //
{"%Vq", DisVq}, //
{"%Vsd", DisVsd}, //
{"%Vss", DisVss}, //
{"%Zb", DisZb}, //
{"%Zvq", DisZvq}, //
{"%Zvqp", DisZvqp}, //
{"%eAX", DisEax}, //
{"%rAX", DisRax}, //
{"%rDX", DisRdx}, //
{"BBb", DisBBb}, //
{"EST", DisEst}, //
{"EST1", DisEst1}, //
{"ESsr", DisEssr}, //
{"Eb", DisEb}, //
{"Ed", DisEd}, //
{"Edqp", DisEdqp}, //
{"Edr", DisEdr}, //
{"Eq", DisEq}, //
{"Eqp", DisEqp}, //
{"Esr", DisEsr}, //
{"Ev", DisEvqp}, //
{"Evq", DisEvq}, //
{"Evqp", DisEvqp}, //
{"Ew", DisEvqp}, //
{"Ib", DisIb}, //
{"Ibs", DisIbs}, //
{"Ibss", DisIbss}, //
{"Ivds", DisIvds}, //
{"Ivqp", DisIvqp}, //
{"Ivs", DisIvs}, //
{"Iw", DisIw}, //
{"Jb", DisJb}, //
{"Jbs", DisJbs}, //
{"Jvds", DisJvds}, //
{"M", DisM}, //
{"Mdi", DisMdi}, //
{"Mdq", DisMdq}, //
{"Mdqp", DisMdqp}, //
{"Mdr", DisMdr}, //
{"Me", DisMe}, //
{"Mer", DisMer}, //
{"Mps", DisMps}, //
{"Mq", DisMq}, //
{"Mqi", DisMqi}, //
{"Msr", DisMsr}, //
{"Mw", DisMw}, //
{"Mwi", DisMwi}, //
{"Ob", DisOb}, //
{"Ovqp", DisOvqp}, //
{"Qpi", DisQpi}, //
{"Qq", DisQq}, //
{"Wdq", DisWdq}, //
{"Wpd", DisWpd}, //
{"Wps", DisWps}, //
{"Wpsq", DisWpsq}, //
{"Wq", DisWq}, //
{"Wsd", DisWsd}, //
{"Wss", DisWss}, //
{"Xb", DisXb}, //
{"Xv", DisXv}, //
{"Xvqp", DisXvqp}, //
{"Yb", DisYb}, //
{"Yv", DisYv}, //
{"Yvqp", DisYvqp}, //
};
char *DisArg(struct DisBuilder b, char *p, const char *s) {
char key[8];
int c, m, l, r;
l = 0;
r = ARRAYLEN(kDisArgs) - 1;
strncpy(key, s, 8);
while (l <= r) {
m = (l + r) >> 1;
c = SSTRCMP8(kDisArgs[m].s, key);
if (c < 0) {
l = m + 1;
} else if (c > 0) {
r = m - 1;
} else {
return kDisArgs[m].f(b, p);
}
}
if (*s == '%') {
p = DisRegister(p, s + 1);
} else {
p = stpcpy(p, s);
}
return p;
}