diff options
Diffstat (limited to 'elfutils/libcpu/i386_disasm.c')
-rw-r--r-- | elfutils/libcpu/i386_disasm.c | 226 |
1 files changed, 175 insertions, 51 deletions
diff --git a/elfutils/libcpu/i386_disasm.c b/elfutils/libcpu/i386_disasm.c index 843fd749..c6bb0a58 100644 --- a/elfutils/libcpu/i386_disasm.c +++ b/elfutils/libcpu/i386_disasm.c @@ -1,5 +1,5 @@ /* Disassembler for x86. - Copyright (C) 2007, 2008 Red Hat, Inc. + Copyright (C) 2007, 2008, 2009 Red Hat, Inc. This file is part of Red Hat elfutils. Written by Ulrich Drepper <drepper@redhat.com>, 2007. @@ -184,6 +184,73 @@ static const char *prefix_str[] = #endif +static const char amd3dnowstr[] = +#define MNE_3DNOW_PAVGUSB 1 + "pavgusb\0" +#define MNE_3DNOW_PFADD (MNE_3DNOW_PAVGUSB + 8) + "pfadd\0" +#define MNE_3DNOW_PFSUB (MNE_3DNOW_PFADD + 6) + "pfsub\0" +#define MNE_3DNOW_PFSUBR (MNE_3DNOW_PFSUB + 6) + "pfsubr\0" +#define MNE_3DNOW_PFACC (MNE_3DNOW_PFSUBR + 7) + "pfacc\0" +#define MNE_3DNOW_PFCMPGE (MNE_3DNOW_PFACC + 6) + "pfcmpge\0" +#define MNE_3DNOW_PFCMPGT (MNE_3DNOW_PFCMPGE + 8) + "pfcmpgt\0" +#define MNE_3DNOW_PFCMPEQ (MNE_3DNOW_PFCMPGT + 8) + "pfcmpeq\0" +#define MNE_3DNOW_PFMIN (MNE_3DNOW_PFCMPEQ + 8) + "pfmin\0" +#define MNE_3DNOW_PFMAX (MNE_3DNOW_PFMIN + 6) + "pfmax\0" +#define MNE_3DNOW_PI2FD (MNE_3DNOW_PFMAX + 6) + "pi2fd\0" +#define MNE_3DNOW_PF2ID (MNE_3DNOW_PI2FD + 6) + "pf2id\0" +#define MNE_3DNOW_PFRCP (MNE_3DNOW_PF2ID + 6) + "pfrcp\0" +#define MNE_3DNOW_PFRSQRT (MNE_3DNOW_PFRCP + 6) + "pfrsqrt\0" +#define MNE_3DNOW_PFMUL (MNE_3DNOW_PFRSQRT + 8) + "pfmul\0" +#define MNE_3DNOW_PFRCPIT1 (MNE_3DNOW_PFMUL + 6) + "pfrcpit1\0" +#define MNE_3DNOW_PFRSQIT1 (MNE_3DNOW_PFRCPIT1 + 9) + "pfrsqit1\0" +#define MNE_3DNOW_PFRCPIT2 (MNE_3DNOW_PFRSQIT1 + 9) + "pfrcpit2\0" +#define MNE_3DNOW_PMULHRW (MNE_3DNOW_PFRCPIT2 + 9) + "pmulhrw"; + +#define AMD3DNOW_LOW_IDX 0x0d +#define AMD3DNOW_HIGH_IDX (sizeof (amd3dnow) + AMD3DNOW_LOW_IDX - 1) +#define AMD3DNOW_IDX(val) ((val) - AMD3DNOW_LOW_IDX) +static const unsigned char amd3dnow[] = + { + [AMD3DNOW_IDX (0xbf)] = MNE_3DNOW_PAVGUSB, + [AMD3DNOW_IDX (0x9e)] = MNE_3DNOW_PFADD, + [AMD3DNOW_IDX (0x9a)] = MNE_3DNOW_PFSUB, + [AMD3DNOW_IDX (0xaa)] = MNE_3DNOW_PFSUBR, + [AMD3DNOW_IDX (0xae)] = MNE_3DNOW_PFACC, + [AMD3DNOW_IDX (0x90)] = MNE_3DNOW_PFCMPGE, + [AMD3DNOW_IDX (0xa0)] = MNE_3DNOW_PFCMPGT, + [AMD3DNOW_IDX (0xb0)] = MNE_3DNOW_PFCMPEQ, + [AMD3DNOW_IDX (0x94)] = MNE_3DNOW_PFMIN, + [AMD3DNOW_IDX (0xa4)] = MNE_3DNOW_PFMAX, + [AMD3DNOW_IDX (0x0d)] = MNE_3DNOW_PI2FD, + [AMD3DNOW_IDX (0x1d)] = MNE_3DNOW_PF2ID, + [AMD3DNOW_IDX (0x96)] = MNE_3DNOW_PFRCP, + [AMD3DNOW_IDX (0x97)] = MNE_3DNOW_PFRSQRT, + [AMD3DNOW_IDX (0xb4)] = MNE_3DNOW_PFMUL, + [AMD3DNOW_IDX (0xa6)] = MNE_3DNOW_PFRCPIT1, + [AMD3DNOW_IDX (0xa7)] = MNE_3DNOW_PFRSQIT1, + [AMD3DNOW_IDX (0xb6)] = MNE_3DNOW_PFRCPIT2, + [AMD3DNOW_IDX (0xb7)] = MNE_3DNOW_PMULHRW + }; + + struct output_data { GElf_Addr addr; @@ -289,6 +356,9 @@ i386_disasm (const uint8_t **startp, const uint8_t *end, GElf_Addr addr, prefixes |= ((*data++) & 0xf) | has_rex; #endif + bufcnt = 0; + size_t cnt = 0; + const uint8_t *curr = match_data; const uint8_t *const match_end = match_data + sizeof (match_data); @@ -302,79 +372,61 @@ i386_disasm (const uint8_t **startp, const uint8_t *end, GElf_Addr addr, goto do_ret; } - if (0) - { - /* Resize the buffer. */ - char *oldbuf; - enomem: - oldbuf = buf; - if (buf == initbuf) - buf = malloc (2 * bufsize); - else - buf = realloc (buf, 2 * bufsize); - if (buf == NULL) - { - buf = oldbuf; - retval = ENOMEM; - goto do_ret; - } - bufsize *= 2; - - output_data.bufp = buf; - output_data.bufsize = bufsize; - } - bufcnt = 0; - - size_t cnt = 0; + next_match: while (curr < match_end) { - const uint8_t *start = curr; - uint_fast8_t len = *curr++; + uint_fast8_t clen = len >> 4; + len &= 0xf; + const uint8_t *next_curr = curr + clen + (len - clen) * 2; assert (len > 0); - assert (curr + 2 * len + 2 <= match_end); + assert (curr + clen + 2 * (len - clen) <= match_end); const uint8_t *codep = data; - size_t avail = len; int correct_prefix = 0; int opoff = 0; - if (data > begin && codep[-1] == curr[1] && curr[0] == 0xff) + if (data > begin && codep[-1] == *curr && clen > 0) { /* We match a prefix byte. This is exactly one byte and is matched exactly, without a mask. */ - --avail; - --len; - start += 2; + --clen; opoff = 8; - curr += 2; - assert (avail > 0); + ++curr; assert (last_prefix_bit != 0); correct_prefix = last_prefix_bit; } - do + size_t avail = len; + while (clen > 0) { - uint_fast8_t masked = *codep++ & *curr++; - if (masked != *curr++) - break; - + if (*codep++ != *curr++) + goto not; --avail; + --clen; if (codep == end && avail > 0) goto do_ret; } - while (avail > 0); - if (avail != 0) + while (avail > 0) { - not: - curr = start + 1 + 2 * len + 2; - ++cnt; - continue; + uint_fast8_t masked = *codep++ & *curr++; + if (masked != *curr++) + { + not: + curr = next_curr; + ++cnt; + bufcnt = 0; + goto next_match; + } + + --avail; + if (codep == end && avail > 0) + goto do_ret; } if (len > end - data) @@ -387,6 +439,41 @@ i386_disasm (const uint8_t **startp, const uint8_t *end, GElf_Addr addr, || (prefixes & correct_prefix) != 0); prefixes ^= correct_prefix; + if (0) + { + /* Resize the buffer. */ + char *oldbuf; + enomem: + oldbuf = buf; + if (buf == initbuf) + buf = malloc (2 * bufsize); + else + buf = realloc (buf, 2 * bufsize); + if (buf == NULL) + { + buf = oldbuf; + retval = ENOMEM; + goto do_ret; + } + bufsize *= 2; + + output_data.bufp = buf; + output_data.bufsize = bufsize; + bufcnt = 0; + + if (data == end) + { + assert (prefixes != 0); + goto print_prefix; + } + + /* gcc is not clever enough to see the following variables + are not used uninitialized. */ + asm ("" + : "=mr" (opoff), "=mr" (correct_prefix), "=mr" (codep), + "=mr" (next_curr), "=mr" (len)); + } + size_t prefix_size = 0; // XXXonly print as prefix if valid? @@ -460,6 +547,23 @@ i386_disasm (const uint8_t **startp, const uint8_t *end, GElf_Addr addr, case prefix_lock: ADD_STRING ("lock"); break; +#ifdef X86_64 + case 0x40 ... 0x4f: + ADD_STRING ("rex"); + if (byte != 0x40) + { + ADD_CHAR ('.'); + if (byte & 0x8) + ADD_CHAR ('w'); + if (byte & 0x4) + ADD_CHAR ('r'); + if (byte & 0x3) + ADD_CHAR ('x'); + if (byte & 0x1) + ADD_CHAR ('b'); + } + break; +#endif default: /* Cannot happen. */ puts ("unknown prefix"); @@ -625,6 +729,24 @@ i386_disasm (const uint8_t **startp, const uint8_t *end, GElf_Addr addr, break; case 0x0f: + if (data[1] == 0x0f) + { + /* AMD 3DNOW. We need one more byte. */ + if (param_start >= end) + goto not; + if (*param_start < AMD3DNOW_LOW_IDX + || *param_start > AMD3DNOW_HIGH_IDX) + goto not; + unsigned int idx + = amd3dnow[AMD3DNOW_IDX (*param_start)]; + if (idx == 0) + goto not; + str = amd3dnowstr + idx - 1; + /* Eat the immediate byte indicating the + operation. */ + ++param_start; + break; + } #ifdef X86_64 if (data[1] == 0xc7) { @@ -765,7 +887,8 @@ i386_disasm (const uint8_t **startp, const uint8_t *end, GElf_Addr addr, { /* First parameter. */ if (instrtab[cnt].str1 != 0) - ADD_STRING (op1_str[instrtab[cnt].str1]); + ADD_STRING (op1_str + + op1_str_idx[instrtab[cnt].str1 - 1]); output_data.opoff1 = (instrtab[cnt].off1_1 + OFF1_1_BIAS - opoff); @@ -785,7 +908,8 @@ i386_disasm (const uint8_t **startp, const uint8_t *end, GElf_Addr addr, { /* Second parameter. */ if (instrtab[cnt].str2 != 0) - ADD_STRING (op2_str[instrtab[cnt].str2]); + ADD_STRING (op2_str + + op2_str_idx[instrtab[cnt].str2 - 1]); output_data.opoff1 = (instrtab[cnt].off2_1 + OFF2_1_BIAS - opoff); @@ -805,7 +929,8 @@ i386_disasm (const uint8_t **startp, const uint8_t *end, GElf_Addr addr, { /* Third parameter. */ if (instrtab[cnt].str3 != 0) - ADD_STRING (op3_str[instrtab[cnt].str3]); + ADD_STRING (op3_str + + op3_str_idx[instrtab[cnt].str3 - 1]); output_data.opoff1 = (instrtab[cnt].off3_1 + OFF3_1_BIAS - opoff); @@ -872,8 +997,7 @@ i386_disasm (const uint8_t **startp, const uint8_t *end, GElf_Addr addr, r = snprintf (&buf[bufcnt], bufavail, "# %#" PRIx64, (uint64_t) symaddr); - if (r < 0) - goto not; + assert (r >= 0); if ((size_t) r >= bufavail) goto enomem; bufcnt += r; |