| /* Helper routines for disassembler for x86/x86-64. |
| Copyright (C) 2007, 2008 Red Hat, Inc. |
| This file is part of Red Hat elfutils. |
| Written by Ulrich Drepper <drepper@redhat.com>, 2007. |
| |
| Red Hat elfutils 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. |
| |
| Red Hat elfutils 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 Red Hat elfutils; if not, write to the Free Software Foundation, |
| Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA. |
| |
| Red Hat elfutils is an included package of the Open Invention Network. |
| An included package of the Open Invention Network is a package for which |
| Open Invention Network licensees cross-license their patents. No patent |
| license is granted, either expressly or impliedly, by designation as an |
| included package. Should you wish to participate in the Open Invention |
| Network licensing program, please visit www.openinventionnetwork.com |
| <http://www.openinventionnetwork.com>. */ |
| |
| #include <inttypes.h> |
| #include <stddef.h> |
| #include <stdio.h> |
| #include <stdint.h> |
| #include <libasm.h> |
| |
| struct instr_enc |
| { |
| /* The mnemonic. Especially encoded for the optimized table. */ |
| unsigned int mnemonic : MNEMONIC_BITS; |
| |
| /* The rep/repe prefixes. */ |
| unsigned int rep : 1; |
| unsigned int repe : 1; |
| |
| /* Mnemonic suffix. */ |
| unsigned int suffix : SUFFIX_BITS; |
| |
| /* Nonzero if the instruction uses modr/m. */ |
| unsigned int modrm : 1; |
| |
| /* 1st parameter. */ |
| unsigned int fct1 : FCT1_BITS; |
| #ifdef STR1_BITS |
| unsigned int str1 : STR1_BITS; |
| #endif |
| unsigned int off1_1 : OFF1_1_BITS; |
| unsigned int off1_2 : OFF1_2_BITS; |
| unsigned int off1_3 : OFF1_3_BITS; |
| |
| /* 2nd parameter. */ |
| unsigned int fct2 : FCT2_BITS; |
| #ifdef STR2_BITS |
| unsigned int str2 : STR2_BITS; |
| #endif |
| unsigned int off2_1 : OFF2_1_BITS; |
| unsigned int off2_2 : OFF2_2_BITS; |
| unsigned int off2_3 : OFF2_3_BITS; |
| |
| /* 3rd parameter. */ |
| unsigned int fct3 : FCT3_BITS; |
| #ifdef STR3_BITS |
| unsigned int str3 : STR3_BITS; |
| #endif |
| unsigned int off3_1 : OFF3_1_BITS; |
| #ifdef OFF3_2_BITS |
| unsigned int off3_2 : OFF3_2_BITS; |
| #endif |
| #ifdef OFF3_3_BITS |
| unsigned int off3_3 : OFF3_3_BITS; |
| #endif |
| }; |
| |
| |
| typedef int (*opfct_t) (struct output_data *); |
| |
| |
| static int |
| data_prefix (struct output_data *d) |
| { |
| char ch = '\0'; |
| if (*d->prefixes & has_cs) |
| { |
| ch = 'c'; |
| *d->prefixes &= ~has_cs; |
| } |
| else if (*d->prefixes & has_ds) |
| { |
| ch = 'd'; |
| *d->prefixes &= ~has_ds; |
| } |
| else if (*d->prefixes & has_es) |
| { |
| ch = 'e'; |
| *d->prefixes &= ~has_es; |
| } |
| else if (*d->prefixes & has_fs) |
| { |
| ch = 'f'; |
| *d->prefixes &= ~has_fs; |
| } |
| else if (*d->prefixes & has_gs) |
| { |
| ch = 'g'; |
| *d->prefixes &= ~has_gs; |
| } |
| else if (*d->prefixes & has_ss) |
| { |
| ch = 's'; |
| *d->prefixes &= ~has_ss; |
| } |
| else |
| return 0; |
| |
| if (*d->bufcntp + 4 > d->bufsize) |
| return *d->bufcntp + 4 - d->bufsize; |
| |
| d->bufp[(*d->bufcntp)++] = '%'; |
| d->bufp[(*d->bufcntp)++] = ch; |
| d->bufp[(*d->bufcntp)++] = 's'; |
| d->bufp[(*d->bufcntp)++] = ':'; |
| |
| return 0; |
| } |
| |
| #ifdef X86_64 |
| static const char hiregs[8][4] = |
| { |
| "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15" |
| }; |
| static const char aregs[8][4] = |
| { |
| "rax", "rcx", "rdx", "rbx", "rsp", "rbp", "rsi", "rdi" |
| }; |
| static const char dregs[8][4] = |
| { |
| "eax", "ecx", "edx", "ebx", "esp", "ebp", "esi", "edi" |
| }; |
| #else |
| static const char aregs[8][4] = |
| { |
| "eax", "ecx", "edx", "ebx", "esp", "ebp", "esi", "edi" |
| }; |
| # define dregs aregs |
| #endif |
| |
| static int |
| general_mod$r_m (struct output_data *d) |
| { |
| int r = data_prefix (d); |
| if (r != 0) |
| return r; |
| |
| int prefixes = *d->prefixes; |
| const uint8_t *data = &d->data[d->opoff1 / 8]; |
| char *bufp = d->bufp; |
| size_t *bufcntp = d->bufcntp; |
| size_t bufsize = d->bufsize; |
| |
| uint_fast8_t modrm = data[0]; |
| #ifndef X86_64 |
| if (unlikely ((prefixes & has_addr16) != 0)) |
| { |
| int16_t disp = 0; |
| bool nodisp = false; |
| |
| if ((modrm & 0xc7) == 6 || (modrm & 0xc0) == 0x80) |
| /* 16 bit displacement. */ |
| disp = read_2sbyte_unaligned (&data[1]); |
| else if ((modrm & 0xc0) == 0x40) |
| /* 8 bit displacement. */ |
| disp = *(const int8_t *) &data[1]; |
| else if ((modrm & 0xc0) == 0) |
| nodisp = true; |
| |
| char tmpbuf[sizeof ("-0x1234(%rr,%rr)")]; |
| int n; |
| if ((modrm & 0xc7) == 6) |
| n = snprintf (tmpbuf, sizeof (tmpbuf), "0x%" PRIx16, disp); |
| else |
| { |
| n = 0; |
| if (!nodisp) |
| n = snprintf (tmpbuf, sizeof (tmpbuf), "%s0x%" PRIx16, |
| disp < 0 ? "-" : "", disp < 0 ? -disp : disp); |
| |
| if ((modrm & 0x4) == 0) |
| n += snprintf (tmpbuf + n, sizeof (tmpbuf) - n, "(%%b%c,%%%ci)", |
| "xp"[(modrm >> 1) & 1], "sd"[modrm & 1]); |
| else |
| n += snprintf (tmpbuf + n, sizeof (tmpbuf) - n, "(%%%s)", |
| ((const char [4][3]) { "si", "di", "bp", "bx" })[modrm & 3]); |
| } |
| |
| if (*bufcntp + n + 1 > bufsize) |
| return *bufcntp + n + 1 - bufsize; |
| |
| memcpy (&bufp[*bufcntp], tmpbuf, n + 1); |
| *bufcntp += n; |
| } |
| else |
| #endif |
| { |
| if ((modrm & 7) != 4) |
| { |
| int32_t disp = 0; |
| bool nodisp = false; |
| |
| if ((modrm & 0xc7) == 5 || (modrm & 0xc0) == 0x80) |
| /* 32 bit displacement. */ |
| disp = read_4sbyte_unaligned (&data[1]); |
| else if ((modrm & 0xc0) == 0x40) |
| /* 8 bit displacement. */ |
| disp = *(const int8_t *) &data[1]; |
| else if ((modrm & 0xc0) == 0) |
| nodisp = true; |
| |
| char tmpbuf[sizeof ("-0x12345678(%rrrr)")]; |
| int n; |
| if (nodisp) |
| { |
| n = snprintf (tmpbuf, sizeof (tmpbuf), "(%%%s)", |
| #ifdef X86_64 |
| (prefixes & has_rex_b) ? hiregs[modrm & 7] : |
| #endif |
| aregs[modrm & 7]); |
| #ifdef X86_64 |
| if (prefixes & has_addr16) |
| { |
| if (prefixes & has_rex_b) |
| tmpbuf[n++] = 'd'; |
| else |
| tmpbuf[2] = 'e'; |
| } |
| #endif |
| } |
| else if ((modrm & 0xc7) != 5) |
| { |
| int p; |
| n = snprintf (tmpbuf, sizeof (tmpbuf), "%s0x%" PRIx32 "(%%%n%s)", |
| disp < 0 ? "-" : "", disp < 0 ? -disp : disp, &p, |
| #ifdef X86_64 |
| (prefixes & has_rex_b) ? hiregs[modrm & 7] : |
| #endif |
| aregs[modrm & 7]); |
| #ifdef X86_64 |
| if (prefixes & has_addr16) |
| { |
| if (prefixes & has_rex_b) |
| tmpbuf[n++] = 'd'; |
| else |
| tmpbuf[p] = 'e'; |
| } |
| #endif |
| } |
| else |
| { |
| #ifdef X86_64 |
| n = snprintf (tmpbuf, sizeof (tmpbuf), "%s0x%" PRIx32 "(%%rip)", |
| disp < 0 ? "-" : "", disp < 0 ? -disp : disp); |
| |
| d->symaddr_use = addr_rel_always; |
| d->symaddr = disp; |
| #else |
| n = snprintf (tmpbuf, sizeof (tmpbuf), "0x%" PRIx32, disp); |
| #endif |
| } |
| |
| if (*bufcntp + n + 1 > bufsize) |
| return *bufcntp + n + 1 - bufsize; |
| |
| memcpy (&bufp[*bufcntp], tmpbuf, n + 1); |
| *bufcntp += n; |
| } |
| else |
| { |
| /* SIB */ |
| uint_fast8_t sib = data[1]; |
| int32_t disp = 0; |
| bool nodisp = false; |
| |
| if ((modrm & 0xc7) == 5 || (modrm & 0xc0) == 0x80 |
| || ((modrm & 0xc7) == 0x4 && (sib & 0x7) == 0x5)) |
| /* 32 bit displacement. */ |
| disp = read_4sbyte_unaligned (&data[2]); |
| else if ((modrm & 0xc0) == 0x40) |
| /* 8 bit displacement. */ |
| disp = *(const int8_t *) &data[2]; |
| else |
| nodisp = true; |
| |
| char tmpbuf[sizeof ("-0x12345678(%rrrr,%rrrr,N)")]; |
| char *cp = tmpbuf; |
| int n; |
| if ((modrm & 0xc0) != 0 || (sib & 0x3f) != 0x25 |
| #ifdef X86_64 |
| || (prefixes & has_rex_x) != 0 |
| #endif |
| ) |
| { |
| if (!nodisp) |
| { |
| n = snprintf (cp, sizeof (tmpbuf), "%s0x%" PRIx32, |
| disp < 0 ? "-" : "", disp < 0 ? -disp : disp); |
| cp += n; |
| } |
| |
| *cp++ = '('; |
| |
| if ((modrm & 0xc7) != 0x4 || (sib & 0x7) != 0x5) |
| { |
| *cp++ = '%'; |
| cp = stpcpy (cp, |
| #ifdef X86_64 |
| (prefixes & has_rex_b) ? hiregs[sib & 7] : |
| (prefixes & has_addr16) ? dregs[sib & 7] : |
| #endif |
| aregs[sib & 7]); |
| #ifdef X86_64 |
| if ((prefixes & (has_rex_b | has_addr16)) |
| == (has_rex_b | has_addr16)) |
| *cp++ = 'd'; |
| #endif |
| } |
| |
| if ((sib & 0x38) != 0x20 |
| #ifdef X86_64 |
| || (prefixes & has_rex_x) != 0 |
| #endif |
| ) |
| { |
| *cp++ = ','; |
| *cp++ = '%'; |
| cp = stpcpy (cp, |
| #ifdef X86_64 |
| (prefixes & has_rex_x) |
| ? hiregs[(sib >> 3) & 7] : |
| (prefixes & has_addr16) |
| ? dregs[(sib >> 3) & 7] : |
| #endif |
| aregs[(sib >> 3) & 7]); |
| #ifdef X86_64 |
| if ((prefixes & (has_rex_b | has_addr16)) |
| == (has_rex_b | has_addr16)) |
| *cp++ = 'd'; |
| #endif |
| |
| *cp++ = ','; |
| *cp++ = '0' + (1 << (sib >> 6)); |
| } |
| |
| *cp++ = ')'; |
| } |
| else |
| { |
| assert (! nodisp); |
| #ifdef X86_64 |
| if ((prefixes & has_addr16) == 0) |
| n = snprintf (cp, sizeof (tmpbuf), "0x%" PRIx64, |
| (int64_t) disp); |
| else |
| #endif |
| n = snprintf (cp, sizeof (tmpbuf), "0x%" PRIx32, disp); |
| cp += n; |
| } |
| |
| if (*bufcntp + (cp - tmpbuf) > bufsize) |
| return *bufcntp + (cp - tmpbuf) - bufsize; |
| |
| memcpy (&bufp[*bufcntp], tmpbuf, cp - tmpbuf); |
| *bufcntp += cp - tmpbuf; |
| } |
| } |
| return 0; |
| } |
| |
| |
| static int |
| FCT_MOD$R_M (struct output_data *d) |
| { |
| assert (d->opoff1 % 8 == 0); |
| uint_fast8_t modrm = d->data[d->opoff1 / 8]; |
| if ((modrm & 0xc0) == 0xc0) |
| { |
| assert (d->opoff1 / 8 == d->opoff2 / 8); |
| assert (d->opoff2 % 8 == 5); |
| //uint_fast8_t byte = d->data[d->opoff2 / 8] & 7; |
| uint_fast8_t byte = modrm & 7; |
| |
| size_t *bufcntp = d->bufcntp; |
| char *buf = d->bufp + *bufcntp; |
| size_t avail = d->bufsize - *bufcntp; |
| int needed; |
| if (*d->prefixes & (has_rep | has_repne)) |
| needed = snprintf (buf, avail, "%%%s", dregs[byte]); |
| else |
| needed = snprintf (buf, avail, "%%mm%" PRIxFAST8, byte); |
| if ((size_t) needed > avail) |
| return needed - avail; |
| *bufcntp += needed; |
| return 0; |
| } |
| |
| return general_mod$r_m (d); |
| } |
| |
| |
| static int |
| FCT_Mod$R_m (struct output_data *d) |
| { |
| assert (d->opoff1 % 8 == 0); |
| uint_fast8_t modrm = d->data[d->opoff1 / 8]; |
| if ((modrm & 0xc0) == 0xc0) |
| { |
| assert (d->opoff1 / 8 == d->opoff2 / 8); |
| assert (d->opoff2 % 8 == 5); |
| //uint_fast8_t byte = data[opoff2 / 8] & 7; |
| uint_fast8_t byte = modrm & 7; |
| |
| size_t *bufcntp = d->bufcntp; |
| size_t avail = d->bufsize - *bufcntp; |
| int needed = snprintf (&d->bufp[*bufcntp], avail, "%%xmm%" PRIxFAST8, |
| byte); |
| if ((size_t) needed > avail) |
| return needed - avail; |
| *d->bufcntp += needed; |
| return 0; |
| } |
| |
| return general_mod$r_m (d); |
| } |
| |
| static int |
| generic_abs (struct output_data *d, const char *absstring |
| #ifdef X86_64 |
| , int abslen |
| #else |
| # define abslen 4 |
| #endif |
| ) |
| { |
| int r = data_prefix (d); |
| if (r != 0) |
| return r; |
| |
| assert (d->opoff1 % 8 == 0); |
| assert (d->opoff1 / 8 == 1); |
| if (*d->param_start + abslen > d->end) |
| return -1; |
| *d->param_start += abslen; |
| #ifndef X86_64 |
| uint32_t absval; |
| # define ABSPRIFMT PRIx32 |
| #else |
| uint64_t absval; |
| # define ABSPRIFMT PRIx64 |
| if (abslen == 8) |
| absval = read_8ubyte_unaligned (&d->data[1]); |
| else |
| #endif |
| absval = read_4ubyte_unaligned (&d->data[1]); |
| size_t *bufcntp = d->bufcntp; |
| size_t avail = d->bufsize - *bufcntp; |
| int needed = snprintf (&d->bufp[*bufcntp], avail, "%s0x%" ABSPRIFMT, |
| absstring, absval); |
| if ((size_t) needed > avail) |
| return needed - avail; |
| *bufcntp += needed; |
| return 0; |
| } |
| |
| |
| static int |
| FCT_absval (struct output_data *d) |
| { |
| return generic_abs (d, "$" |
| #ifdef X86_64 |
| , 4 |
| #endif |
| ); |
| } |
| |
| static int |
| FCT_abs (struct output_data *d) |
| { |
| return generic_abs (d, "" |
| #ifdef X86_64 |
| , 8 |
| #endif |
| ); |
| } |
| |
| static int |
| FCT_ax (struct output_data *d) |
| { |
| int is_16bit = (*d->prefixes & has_data16) != 0; |
| |
| size_t *bufcntp = d->bufcntp; |
| char *bufp = d->bufp; |
| size_t bufsize = d->bufsize; |
| |
| if (*bufcntp + 4 - is_16bit > bufsize) |
| return *bufcntp + 4 - is_16bit - bufsize; |
| |
| bufp[(*bufcntp)++] = '%'; |
| if (! is_16bit) |
| bufp[(*bufcntp)++] = ( |
| #ifdef X86_64 |
| (*d->prefixes & has_rex_w) ? 'r' : |
| #endif |
| 'e'); |
| bufp[(*bufcntp)++] = 'a'; |
| bufp[(*bufcntp)++] = 'x'; |
| |
| return 0; |
| } |
| |
| |
| static int |
| FCT_ax$w (struct output_data *d) |
| { |
| if ((d->data[d->opoff2 / 8] & (1 << (7 - (d->opoff2 & 7)))) != 0) |
| return FCT_ax (d); |
| |
| size_t *bufcntp = d->bufcntp; |
| char *bufp = d->bufp; |
| size_t bufsize = d->bufsize; |
| |
| if (*bufcntp + 3 > bufsize) |
| return *bufcntp + 3 - bufsize; |
| |
| bufp[(*bufcntp)++] = '%'; |
| bufp[(*bufcntp)++] = 'a'; |
| bufp[(*bufcntp)++] = 'l'; |
| |
| return 0; |
| } |
| |
| |
| static int |
| __attribute__ ((noinline)) |
| FCT_crdb (struct output_data *d, const char *regstr) |
| { |
| if (*d->prefixes & has_data16) |
| return -1; |
| |
| size_t *bufcntp = d->bufcntp; |
| |
| // XXX If this assert is true, use absolute offset below |
| assert (d->opoff1 / 8 == 2); |
| assert (d->opoff1 % 8 == 2); |
| size_t avail = d->bufsize - *bufcntp; |
| int needed = snprintf (&d->bufp[*bufcntp], avail, "%%%s%" PRIx32, |
| regstr, (uint32_t) (d->data[d->opoff1 / 8] >> 3) & 7); |
| if ((size_t) needed > avail) |
| return needed - avail; |
| *bufcntp += needed; |
| return 0; |
| } |
| |
| |
| static int |
| FCT_ccc (struct output_data *d) |
| { |
| return FCT_crdb (d, "cr"); |
| } |
| |
| |
| static int |
| FCT_ddd (struct output_data *d) |
| { |
| return FCT_crdb (d, "db"); |
| } |
| |
| |
| static int |
| FCT_disp8 (struct output_data *d) |
| { |
| assert (d->opoff1 % 8 == 0); |
| if (*d->param_start >= d->end) |
| return -1; |
| int32_t offset = *(const int8_t *) (*d->param_start)++; |
| |
| size_t *bufcntp = d->bufcntp; |
| size_t avail = d->bufsize - *bufcntp; |
| int needed = snprintf (&d->bufp[*bufcntp], avail, "0x%" PRIx32, |
| (uint32_t) (d->addr + (*d->param_start - d->data) |
| + offset)); |
| if ((size_t) needed > avail) |
| return needed - avail; |
| *bufcntp += needed; |
| return 0; |
| } |
| |
| |
| static int |
| __attribute__ ((noinline)) |
| FCT_ds_xx (struct output_data *d, const char *reg) |
| { |
| int prefix = *d->prefixes & SEGMENT_PREFIXES; |
| |
| if (prefix == 0) |
| *d->prefixes |= prefix = has_ds; |
| /* Make sure only one bit is set. */ |
| else if ((prefix - 1) & prefix) |
| return -1; |
| |
| int r = data_prefix (d); |
| |
| assert ((*d->prefixes & prefix) == 0); |
| |
| if (r != 0) |
| return r; |
| |
| size_t *bufcntp = d->bufcntp; |
| size_t avail = d->bufsize - *bufcntp; |
| int needed = snprintf (&d->bufp[*bufcntp], avail, "(%%%s%s)", |
| #ifdef X86_64 |
| *d->prefixes & idx_addr16 ? "e" : "r", |
| #else |
| *d->prefixes & idx_addr16 ? "" : "e", |
| #endif |
| reg); |
| if ((size_t) needed > avail) |
| return (size_t) needed - avail; |
| *bufcntp += needed; |
| |
| return 0; |
| } |
| |
| |
| static int |
| FCT_ds_bx (struct output_data *d) |
| { |
| return FCT_ds_xx (d, "bx"); |
| } |
| |
| |
| static int |
| FCT_ds_si (struct output_data *d) |
| { |
| return FCT_ds_xx (d, "si"); |
| } |
| |
| |
| static int |
| FCT_dx (struct output_data *d) |
| { |
| size_t *bufcntp = d->bufcntp; |
| |
| if (*bufcntp + 7 > d->bufsize) |
| return *bufcntp + 7 - d->bufsize; |
| |
| memcpy (&d->bufp[*bufcntp], "(%dx)", 5); |
| *bufcntp += 5; |
| |
| return 0; |
| } |
| |
| |
| static int |
| FCT_es_di (struct output_data *d) |
| { |
| size_t *bufcntp = d->bufcntp; |
| size_t avail = d->bufsize - *bufcntp; |
| int needed = snprintf (&d->bufp[*bufcntp], avail, "%%es:(%%%sdi)", |
| #ifdef X86_64 |
| *d->prefixes & idx_addr16 ? "e" : "r" |
| #else |
| *d->prefixes & idx_addr16 ? "" : "e" |
| #endif |
| ); |
| if ((size_t) needed > avail) |
| return (size_t) needed - avail; |
| *bufcntp += needed; |
| |
| return 0; |
| } |
| |
| |
| static int |
| FCT_imm (struct output_data *d) |
| { |
| size_t *bufcntp = d->bufcntp; |
| size_t avail = d->bufsize - *bufcntp; |
| int needed; |
| if (*d->prefixes & has_data16) |
| { |
| if (*d->param_start + 2 > d->end) |
| return -1; |
| uint16_t word = read_2ubyte_unaligned_inc (*d->param_start); |
| needed = snprintf (&d->bufp[*bufcntp], avail, "$0x%" PRIx16, word); |
| } |
| else |
| { |
| if (*d->param_start + 4 > d->end) |
| return -1; |
| int32_t word = read_4sbyte_unaligned_inc (*d->param_start); |
| #ifdef X86_64 |
| if (*d->prefixes & has_rex_w) |
| needed = snprintf (&d->bufp[*bufcntp], avail, "$0x%" PRIx64, |
| (int64_t) word); |
| else |
| #endif |
| needed = snprintf (&d->bufp[*bufcntp], avail, "$0x%" PRIx32, word); |
| } |
| if ((size_t) needed > avail) |
| return (size_t) needed - avail; |
| *bufcntp += needed; |
| return 0; |
| } |
| |
| |
| static int |
| FCT_imm$w (struct output_data *d) |
| { |
| if ((d->data[d->opoff2 / 8] & (1 << (7 - (d->opoff2 & 7)))) != 0) |
| return FCT_imm (d); |
| |
| size_t *bufcntp = d->bufcntp; |
| size_t avail = d->bufsize - *bufcntp; |
| if (*d->param_start>= d->end) |
| return -1; |
| uint_fast8_t word = *(*d->param_start)++; |
| int needed = snprintf (&d->bufp[*bufcntp], avail, "$0x%" PRIxFAST8, word); |
| if ((size_t) needed > avail) |
| return (size_t) needed - avail; |
| *bufcntp += needed; |
| return 0; |
| } |
| |
| |
| #ifdef X86_64 |
| static int |
| FCT_imm64$w (struct output_data *d) |
| { |
| if ((d->data[d->opoff2 / 8] & (1 << (7 - (d->opoff2 & 7)))) == 0 |
| || (*d->prefixes & has_data16) != 0) |
| return FCT_imm$w (d); |
| |
| size_t *bufcntp = d->bufcntp; |
| size_t avail = d->bufsize - *bufcntp; |
| int needed; |
| if (*d->prefixes & has_rex_w) |
| { |
| if (*d->param_start + 8 > d->end) |
| return -1; |
| uint64_t word = read_8ubyte_unaligned_inc (*d->param_start); |
| needed = snprintf (&d->bufp[*bufcntp], avail, "$0x%" PRIx64, word); |
| } |
| else |
| { |
| if (*d->param_start + 4 > d->end) |
| return -1; |
| int32_t word = read_4sbyte_unaligned_inc (*d->param_start); |
| needed = snprintf (&d->bufp[*bufcntp], avail, "$0x%" PRIx32, word); |
| } |
| if ((size_t) needed > avail) |
| return (size_t) needed - avail; |
| *bufcntp += needed; |
| return 0; |
| } |
| #endif |
| |
| |
| static int |
| FCT_imms (struct output_data *d) |
| { |
| size_t *bufcntp = d->bufcntp; |
| size_t avail = d->bufsize - *bufcntp; |
| if (*d->param_start>= d->end) |
| return -1; |
| int8_t byte = *(*d->param_start)++; |
| #ifdef X86_64 |
| int needed = snprintf (&d->bufp[*bufcntp], avail, "$0x%" PRIx64, |
| (int64_t) byte); |
| #else |
| int needed = snprintf (&d->bufp[*bufcntp], avail, "$0x%" PRIx32, |
| (int32_t) byte); |
| #endif |
| if ((size_t) needed > avail) |
| return (size_t) needed - avail; |
| *bufcntp += needed; |
| return 0; |
| } |
| |
| |
| static int |
| FCT_imm$s (struct output_data *d) |
| { |
| uint_fast8_t opcode = d->data[d->opoff2 / 8]; |
| size_t *bufcntp = d->bufcntp; |
| size_t avail = d->bufsize - *bufcntp; |
| if ((opcode & 2) != 0) |
| return FCT_imms (d); |
| |
| if ((*d->prefixes & has_data16) == 0) |
| { |
| if (*d->param_start + 4 > d->end) |
| return -1; |
| int32_t word = read_4sbyte_unaligned_inc (*d->param_start); |
| #ifdef X86_64 |
| int needed = snprintf (&d->bufp[*bufcntp], avail, "$0x%" PRIx64, |
| (int64_t) word); |
| #else |
| int needed = snprintf (&d->bufp[*bufcntp], avail, "$0x%" PRIx32, word); |
| #endif |
| if ((size_t) needed > avail) |
| return (size_t) needed - avail; |
| *bufcntp += needed; |
| } |
| else |
| { |
| if (*d->param_start + 2 > d->end) |
| return -1; |
| uint16_t word = read_2ubyte_unaligned_inc (*d->param_start); |
| int needed = snprintf (&d->bufp[*bufcntp], avail, "$0x%" PRIx16, word); |
| if ((size_t) needed > avail) |
| return (size_t) needed - avail; |
| *bufcntp += needed; |
| } |
| return 0; |
| } |
| |
| |
| static int |
| FCT_imm16 (struct output_data *d) |
| { |
| if (*d->param_start + 2 > d->end) |
| return -1; |
| uint16_t word = read_2ubyte_unaligned_inc (*d->param_start); |
| size_t *bufcntp = d->bufcntp; |
| size_t avail = d->bufsize - *bufcntp; |
| int needed = snprintf (&d->bufp[*bufcntp], avail, "$0x%" PRIx16, word); |
| if ((size_t) needed > avail) |
| return (size_t) needed - avail; |
| *bufcntp += needed; |
| return 0; |
| } |
| |
| |
| static int |
| FCT_imms8 (struct output_data *d) |
| { |
| size_t *bufcntp = d->bufcntp; |
| size_t avail = d->bufsize - *bufcntp; |
| if (*d->param_start >= d->end) |
| return -1; |
| int_fast8_t byte = *(*d->param_start)++; |
| int needed; |
| #ifdef X86_64 |
| if (*d->prefixes & has_rex_w) |
| needed = snprintf (&d->bufp[*bufcntp], avail, "$0x%" PRIx64, |
| (int64_t) byte); |
| else |
| #endif |
| needed = snprintf (&d->bufp[*bufcntp], avail, "$0x%" PRIx32, |
| (int32_t) byte); |
| if ((size_t) needed > avail) |
| return (size_t) needed - avail; |
| *bufcntp += needed; |
| return 0; |
| } |
| |
| |
| static int |
| FCT_imm8 (struct output_data *d) |
| { |
| size_t *bufcntp = d->bufcntp; |
| size_t avail = d->bufsize - *bufcntp; |
| if (*d->param_start >= d->end) |
| return -1; |
| uint_fast8_t byte = *(*d->param_start)++; |
| int needed = snprintf (&d->bufp[*bufcntp], avail, "$0x%" PRIx32, |
| (uint32_t) byte); |
| if ((size_t) needed > avail) |
| return (size_t) needed - avail; |
| *bufcntp += needed; |
| return 0; |
| } |
| |
| |
| static int |
| FCT_rel (struct output_data *d) |
| { |
| size_t *bufcntp = d->bufcntp; |
| size_t avail = d->bufsize - *bufcntp; |
| if (*d->param_start + 4 > d->end) |
| return -1; |
| int32_t rel = read_4sbyte_unaligned_inc (*d->param_start); |
| #ifdef X86_64 |
| int needed = snprintf (&d->bufp[*bufcntp], avail, "0x%" PRIx64, |
| (uint64_t) (d->addr + rel |
| + (*d->param_start - d->data))); |
| #else |
| int needed = snprintf (&d->bufp[*bufcntp], avail, "0x%" PRIx32, |
| (uint32_t) (d->addr + rel |
| + (*d->param_start - d->data))); |
| #endif |
| if ((size_t) needed > avail) |
| return (size_t) needed - avail; |
| *bufcntp += needed; |
| return 0; |
| } |
| |
| |
| static int |
| FCT_mmxreg (struct output_data *d) |
| { |
| uint_fast8_t byte = d->data[d->opoff1 / 8]; |
| assert (d->opoff1 % 8 == 2 || d->opoff1 % 8 == 5); |
| byte = (byte >> (5 - d->opoff1 % 8)) & 7; |
| size_t *bufcntp = d->bufcntp; |
| size_t avail = d->bufsize - *bufcntp; |
| int needed = snprintf (&d->bufp[*bufcntp], avail, "%%mm%" PRIxFAST8, byte); |
| if ((size_t) needed > avail) |
| return needed - avail; |
| *bufcntp += needed; |
| return 0; |
| } |
| |
| |
| static int |
| FCT_mod$r_m (struct output_data *d) |
| { |
| assert (d->opoff1 % 8 == 0); |
| uint_fast8_t modrm = d->data[d->opoff1 / 8]; |
| if ((modrm & 0xc0) == 0xc0) |
| { |
| int prefixes = *d->prefixes; |
| if (prefixes & has_addr16) |
| return -1; |
| |
| int is_16bit = (prefixes & has_data16) != 0; |
| |
| size_t *bufcntp = d->bufcntp; |
| char *bufp = d->bufp; |
| if (*bufcntp + 5 - is_16bit > d->bufsize) |
| return *bufcntp + 5 - is_16bit - d->bufsize; |
| bufp[(*bufcntp)++] = '%'; |
| |
| char *cp; |
| #ifdef X86_64 |
| if ((prefixes & has_rex_b) != 0 && !is_16bit) |
| { |
| cp = stpcpy (&bufp[*bufcntp], hiregs[modrm & 7]); |
| if ((prefixes & has_rex_w) == 0) |
| *cp++ = 'd'; |
| } |
| else |
| #endif |
| { |
| cp = stpcpy (&bufp[*bufcntp], dregs[modrm & 7] + is_16bit); |
| #ifdef X86_64 |
| if ((prefixes & has_rex_w) != 0) |
| bufp[*bufcntp] = 'r'; |
| #endif |
| } |
| *bufcntp = cp - bufp; |
| return 0; |
| } |
| |
| return general_mod$r_m (d); |
| } |
| |
| |
| #ifndef X86_64 |
| static int |
| FCT_moda$r_m (struct output_data *d) |
| { |
| assert (d->opoff1 % 8 == 0); |
| uint_fast8_t modrm = d->data[d->opoff1 / 8]; |
| if ((modrm & 0xc0) == 0xc0) |
| { |
| if (*d->prefixes & has_addr16) |
| return -1; |
| |
| size_t *bufcntp = d->bufcntp; |
| if (*bufcntp + 3 > d->bufsize) |
| return *bufcntp + 3 - d->bufsize; |
| |
| memcpy (&d->bufp[*bufcntp], "???", 3); |
| *bufcntp += 3; |
| |
| return 0; |
| } |
| |
| return general_mod$r_m (d); |
| } |
| #endif |
| |
| |
| #ifdef X86_64 |
| static const char rex_8bit[8][3] = |
| { |
| [0] = "a", [1] = "c", [2] = "d", [3] = "b", |
| [4] = "sp", [5] = "bp", [6] = "si", [7] = "di" |
| }; |
| #endif |
| |
| |
| static int |
| FCT_mod$r_m$w (struct output_data *d) |
| { |
| assert (d->opoff1 % 8 == 0); |
| const uint8_t *data = d->data; |
| uint_fast8_t modrm = data[d->opoff1 / 8]; |
| if ((modrm & 0xc0) == 0xc0) |
| { |
| int prefixes = *d->prefixes; |
| |
| if (prefixes & has_addr16) |
| return -1; |
| |
| size_t *bufcntp = d->bufcntp; |
| char *bufp = d->bufp; |
| if (*bufcntp + 5 > d->bufsize) |
| return *bufcntp + 5 - d->bufsize; |
| |
| if ((data[d->opoff3 / 8] & (1 << (7 - (d->opoff3 & 7)))) == 0) |
| { |
| bufp[(*bufcntp)++] = '%'; |
| |
| #ifdef X86_64 |
| if (prefixes & has_rex) |
| { |
| if (prefixes & has_rex_r) |
| *bufcntp += snprintf (bufp + *bufcntp, d->bufsize - *bufcntp, |
| "r%db", 8 + (modrm & 7)); |
| else |
| { |
| char *cp = stpcpy (bufp + *bufcntp, hiregs[modrm & 7]); |
| *cp++ = 'l'; |
| *bufcntp = cp - bufp; |
| } |
| } |
| else |
| #endif |
| { |
| bufp[(*bufcntp)++] = "acdb"[modrm & 3]; |
| bufp[(*bufcntp)++] = "lh"[(modrm & 4) >> 2]; |
| } |
| } |
| else |
| { |
| int is_16bit = (prefixes & has_data16) != 0; |
| |
| bufp[(*bufcntp)++] = '%'; |
| |
| char *cp; |
| #ifdef X86_64 |
| if ((prefixes & has_rex_b) != 0 && !is_16bit) |
| { |
| cp = stpcpy (&bufp[*bufcntp], hiregs[modrm & 7]); |
| if ((prefixes & has_rex_w) == 0) |
| *cp++ = 'd'; |
| } |
| else |
| #endif |
| { |
| cp = stpcpy (&bufp[*bufcntp], dregs[modrm & 7] + is_16bit); |
| #ifdef X86_64 |
| if ((prefixes & has_rex_w) != 0) |
| bufp[*bufcntp] = 'r'; |
| #endif |
| } |
| *bufcntp = cp - bufp; |
| } |
| return 0; |
| } |
| |
| return general_mod$r_m (d); |
| } |
| |
| |
| static int |
| FCT_mod$8r_m (struct output_data *d) |
| { |
| assert (d->opoff1 % 8 == 0); |
| uint_fast8_t modrm = d->data[d->opoff1 / 8]; |
| if ((modrm & 0xc0) == 0xc0) |
| { |
| size_t *bufcntp = d->bufcntp; |
| char *bufp = d->bufp; |
| if (*bufcntp + 3 > d->bufsize) |
| return *bufcntp + 3 - d->bufsize; |
| bufp[(*bufcntp)++] = '%'; |
| bufp[(*bufcntp)++] = "acdb"[modrm & 3]; |
| bufp[(*bufcntp)++] = "lh"[(modrm & 4) >> 2]; |
| return 0; |
| } |
| |
| return general_mod$r_m (d); |
| } |
| |
| |
| static int |
| FCT_mod$16r_m (struct output_data *d) |
| { |
| assert (d->opoff1 % 8 == 0); |
| uint_fast8_t modrm = d->data[d->opoff1 / 8]; |
| if ((modrm & 0xc0) == 0xc0) |
| { |
| assert (d->opoff1 / 8 == d->opoff2 / 8); |
| //uint_fast8_t byte = data[opoff2 / 8] & 7; |
| uint_fast8_t byte = modrm & 7; |
| |
| size_t *bufcntp = d->bufcntp; |
| if (*bufcntp + 3 > d->bufsize) |
| return *bufcntp + 3 - d->bufsize; |
| d->bufp[(*bufcntp)++] = '%'; |
| memcpy (&d->bufp[*bufcntp], dregs[byte] + 1, sizeof (dregs[0]) - 1); |
| *bufcntp += 2; |
| return 0; |
| } |
| |
| return general_mod$r_m (d); |
| } |
| |
| |
| #ifdef X86_64 |
| static int |
| FCT_mod$64r_m (struct output_data *d) |
| { |
| assert (d->opoff1 % 8 == 0); |
| uint_fast8_t modrm = d->data[d->opoff1 / 8]; |
| if ((modrm & 0xc0) == 0xc0) |
| { |
| assert (d->opoff1 / 8 == d->opoff2 / 8); |
| //uint_fast8_t byte = data[opoff2 / 8] & 7; |
| uint_fast8_t byte = modrm & 7; |
| |
| size_t *bufcntp = d->bufcntp; |
| if (*bufcntp + 4 > d->bufsize) |
| return *bufcntp + 4 - d->bufsize; |
| char *cp = &d->bufp[*bufcntp]; |
| *cp++ = '%'; |
| cp = stpcpy (cp, |
| (*d->prefixes & has_rex_b) ? hiregs[byte] : aregs[byte]); |
| *bufcntp = cp - d->bufp; |
| return 0; |
| } |
| |
| return general_mod$r_m (d); |
| } |
| #else |
| static typeof (FCT_mod$r_m) FCT_mod$64r_m __attribute__ ((alias ("FCT_mod$r_m"))); |
| #endif |
| |
| |
| static int |
| FCT_reg (struct output_data *d) |
| { |
| uint_fast8_t byte = d->data[d->opoff1 / 8]; |
| assert (d->opoff1 % 8 + 3 <= 8); |
| byte >>= 8 - (d->opoff1 % 8 + 3); |
| byte &= 7; |
| int is_16bit = (*d->prefixes & has_data16) != 0; |
| size_t *bufcntp = d->bufcntp; |
| if (*bufcntp + 5 > d->bufsize) |
| return *bufcntp + 5 - d->bufsize; |
| d->bufp[(*bufcntp)++] = '%'; |
| #ifdef X86_64 |
| if ((*d->prefixes & has_rex_r) != 0 && !is_16bit) |
| { |
| *bufcntp += snprintf (&d->bufp[*bufcntp], d->bufsize - *bufcntp, "r%d", |
| 8 + byte); |
| if ((*d->prefixes & has_rex_w) == 0) |
| d->bufp[(*bufcntp)++] = 'd'; |
| } |
| else |
| #endif |
| { |
| memcpy (&d->bufp[*bufcntp], dregs[byte] + is_16bit, 3 - is_16bit); |
| #ifdef X86_64 |
| if ((*d->prefixes & has_rex_w) != 0 && !is_16bit) |
| d->bufp[*bufcntp] = 'r'; |
| #endif |
| *bufcntp += 3 - is_16bit; |
| } |
| return 0; |
| } |
| |
| |
| #ifdef X86_64 |
| static int |
| FCT_oreg (struct output_data *d) |
| { |
| /* Special form where register comes from opcode. The rex.B bit is used, |
| rex.R and rex.X are ignored. */ |
| int save_prefixes = *d->prefixes; |
| |
| *d->prefixes = ((save_prefixes & ~has_rex_r) |
| | ((save_prefixes & has_rex_b) << (idx_rex_r - idx_rex_b))); |
| |
| int r = FCT_reg (d); |
| |
| *d->prefixes = save_prefixes; |
| |
| return r; |
| } |
| #endif |
| |
| |
| static int |
| FCT_reg64 (struct output_data *d) |
| { |
| uint_fast8_t byte = d->data[d->opoff1 / 8]; |
| assert (d->opoff1 % 8 + 3 <= 8); |
| byte >>= 8 - (d->opoff1 % 8 + 3); |
| byte &= 7; |
| if ((*d->prefixes & has_data16) != 0) |
| return -1; |
| size_t *bufcntp = d->bufcntp; |
| if (*bufcntp + 5 > d->bufsize) |
| return *bufcntp + 5 - d->bufsize; |
| d->bufp[(*bufcntp)++] = '%'; |
| #ifdef X86_64 |
| if ((*d->prefixes & has_rex_r) != 0) |
| { |
| *bufcntp += snprintf (&d->bufp[*bufcntp], d->bufsize - *bufcntp, "r%d", |
| 8 + byte); |
| if ((*d->prefixes & has_rex_w) == 0) |
| d->bufp[(*bufcntp)++] = 'd'; |
| } |
| else |
| #endif |
| { |
| memcpy (&d->bufp[*bufcntp], aregs[byte], 3); |
| *bufcntp += 3; |
| } |
| return 0; |
| } |
| |
| |
| static int |
| FCT_reg$w (struct output_data *d) |
| { |
| if (d->data[d->opoff2 / 8] & (1 << (7 - (d->opoff2 & 7)))) |
| return FCT_reg (d); |
| |
| uint_fast8_t byte = d->data[d->opoff1 / 8]; |
| assert (d->opoff1 % 8 + 3 <= 8); |
| byte >>= 8 - (d->opoff1 % 8 + 3); |
| byte &= 7; |
| |
| size_t *bufcntp = d->bufcntp; |
| if (*bufcntp + 4 > d->bufsize) |
| return *bufcntp + 4 - d->bufsize; |
| |
| d->bufp[(*bufcntp)++] = '%'; |
| |
| #ifdef X86_64 |
| if (*d->prefixes & has_rex) |
| { |
| if (*d->prefixes & has_rex_r) |
| *bufcntp += snprintf (d->bufp + *bufcntp, d->bufsize - *bufcntp, |
| "r%db", 8 + byte); |
| else |
| { |
| char* cp = stpcpy (d->bufp + *bufcntp, rex_8bit[byte]); |
| *cp++ = 'l'; |
| *bufcntp = cp - d->bufp; |
| } |
| } |
| else |
| #endif |
| { |
| d->bufp[(*bufcntp)++] = "acdb"[byte & 3]; |
| d->bufp[(*bufcntp)++] = "lh"[byte >> 2]; |
| } |
| return 0; |
| } |
| |
| |
| #ifdef X86_64 |
| static int |
| FCT_oreg$w (struct output_data *d) |
| { |
| /* Special form where register comes from opcode. The rex.B bit is used, |
| rex.R and rex.X are ignored. */ |
| int save_prefixes = *d->prefixes; |
| |
| *d->prefixes = ((save_prefixes & ~has_rex_r) |
| | ((save_prefixes & has_rex_b) << (idx_rex_r - idx_rex_b))); |
| |
| int r = FCT_reg$w (d); |
| |
| *d->prefixes = save_prefixes; |
| |
| return r; |
| } |
| #endif |
| |
| |
| static int |
| FCT_freg (struct output_data *d) |
| { |
| assert (d->opoff1 / 8 == 1); |
| assert (d->opoff1 % 8 == 5); |
| size_t *bufcntp = d->bufcntp; |
| size_t avail = d->bufsize - *bufcntp; |
| int needed = snprintf (&d->bufp[*bufcntp], avail, "%%st(%" PRIx32 ")", |
| (uint32_t) (d->data[1] & 7)); |
| if ((size_t) needed > avail) |
| return (size_t) needed - avail; |
| *bufcntp += needed; |
| return 0; |
| } |
| |
| |
| #ifndef X86_64 |
| static int |
| FCT_reg16 (struct output_data *d) |
| { |
| if (*d->prefixes & has_data16) |
| return -1; |
| |
| *d->prefixes |= has_data16; |
| return FCT_reg (d); |
| } |
| #endif |
| |
| |
| static int |
| FCT_sel (struct output_data *d) |
| { |
| assert (d->opoff1 % 8 == 0); |
| assert (d->opoff1 / 8 == 5); |
| if (*d->param_start + 2 > d->end) |
| return -1; |
| *d->param_start += 2; |
| uint16_t absval = read_2ubyte_unaligned (&d->data[5]); |
| |
| size_t *bufcntp = d->bufcntp; |
| size_t avail = d->bufsize - *bufcntp; |
| int needed = snprintf (&d->bufp[*bufcntp], avail, "$0x%" PRIx16, absval); |
| if ((size_t) needed > avail) |
| return needed - avail; |
| *bufcntp += needed; |
| return 0; |
| } |
| |
| |
| static int |
| FCT_sreg2 (struct output_data *d) |
| { |
| uint_fast8_t byte = d->data[d->opoff1 / 8]; |
| assert (d->opoff1 % 8 + 3 <= 8); |
| byte >>= 8 - (d->opoff1 % 8 + 2); |
| |
| size_t *bufcntp = d->bufcntp; |
| char *bufp = d->bufp; |
| if (*bufcntp + 3 > d->bufsize) |
| return *bufcntp + 3 - d->bufsize; |
| |
| bufp[(*bufcntp)++] = '%'; |
| bufp[(*bufcntp)++] = "ecsd"[byte & 3]; |
| bufp[(*bufcntp)++] = 's'; |
| |
| return 0; |
| } |
| |
| |
| static int |
| FCT_sreg3 (struct output_data *d) |
| { |
| uint_fast8_t byte = d->data[d->opoff1 / 8]; |
| assert (d->opoff1 % 8 + 4 <= 8); |
| byte >>= 8 - (d->opoff1 % 8 + 3); |
| |
| if ((byte & 7) >= 6) |
| return -1; |
| |
| size_t *bufcntp = d->bufcntp; |
| char *bufp = d->bufp; |
| if (*bufcntp + 3 > d->bufsize) |
| return *bufcntp + 3 - d->bufsize; |
| |
| bufp[(*bufcntp)++] = '%'; |
| bufp[(*bufcntp)++] = "ecsdfg"[byte & 7]; |
| bufp[(*bufcntp)++] = 's'; |
| |
| return 0; |
| } |
| |
| |
| static int |
| FCT_string (struct output_data *d __attribute__ ((unused))) |
| { |
| return 0; |
| } |
| |
| |
| static int |
| FCT_xmmreg (struct output_data *d) |
| { |
| uint_fast8_t byte = d->data[d->opoff1 / 8]; |
| assert (d->opoff1 % 8 == 2 || d->opoff1 % 8 == 5); |
| byte = (byte >> (5 - d->opoff1 % 8)) & 7; |
| |
| size_t *bufcntp = d->bufcntp; |
| size_t avail = d->bufsize - *bufcntp; |
| int needed = snprintf (&d->bufp[*bufcntp], avail, "%%xmm%" PRIxFAST8, byte); |
| if ((size_t) needed > avail) |
| return needed - avail; |
| *bufcntp += needed; |
| return 0; |
| } |