From 3cbdd387c752999255aea91600b5cfdefbeac7d0 Mon Sep 17 00:00:00 2001 From: Ulrich Drepper Date: Wed, 2 Jan 2008 17:44:39 +0000 Subject: [PATCH] propagate from branch 'com.redhat.elfutils.disasm' (head d15b4eb794e81e477f9896fe82a74cb5ecf4514c) to branch 'com.redhat.elfutils' (head eaacbf01f8cc89d043ec6eca9b5e35cb5c4cde06) --- ChangeLog | 5 + backends/ChangeLog | 4 + backends/Makefile.am | 30 +- backends/i386_init.c | 1 + backends/x86_64_init.c | 1 + configure.ac | 53 +- libasm/ChangeLog | 11 + libasm/Makefile.am | 1 + libasm/asm_error.c | 1 + libasm/disasm_begin.c | 49 ++ libasm/disasm_cb.c | 176 ++++ libasm/disasm_end.c | 30 + libasm/disasm_str.c | 56 ++ libasm/libasm.h | 33 + libasm/libasm.map | 5 + libasm/libasmP.h | 24 + libcpu/ChangeLog | 181 ++++ libcpu/Makefile.am | 57 +- libcpu/defs/i386 | 634 ++++++++++++++ libcpu/defs/i386.doc | 74 ++ libcpu/defs/x86_64 | 341 ++++++++ libcpu/i386_data.h | 1406 ++++++++++++++++++++++++++++++ libcpu/i386_disasm.c | 914 +++++++++++++++++++ libcpu/i386_gendis.c | 39 + libcpu/i386_lex.l | 115 +++ libcpu/i386_parse.y | 1641 +++++++++++++++++++++++++++++++++++ libcpu/memory-access.h | 168 ++++ libcpu/x86_64_disasm.c | 5 + libebl/Makefile.am | 2 +- libebl/ebl-hooks.h | 5 + libebl/eblopenbackend.c | 1 + libebl/libeblP.h | 1 + libelf/ChangeLog | 11 + libelf/Makefile.am | 3 +- libelf/elf32_getshdr.c | 14 + libelf/elf_begin.c | 26 + libelf/elf_scnshndx.c | 71 ++ libelf/libelf.h | 4 + libelf/libelf.map | 7 +- libelf/libelfP.h | 6 +- src/ChangeLog | 34 + src/Makefile.am | 11 +- src/addr2line.c | 3 + src/ar.c | 3 + src/debugpred.h | 53 ++ src/elfcmp.c | 3 + src/elflint.c | 12 +- src/findtextrel.c | 3 + src/ld.c | 3 + src/nm.c | 3 + src/objdump.c | 86 +- src/ranlib.c | 3 + src/readelf.c | 256 +++--- src/size.c | 3 + src/strings.c | 7 +- src/strip.c | 9 +- src/unstrip.c | 3 + tests/ChangeLog | 49 ++ tests/Makefile.am | 6 +- tests/run-disasm.sh | 36 + tests/testfile44.S.bz2 | Bin 0 -> 10255 bytes tests/testfile44.expect.bz2 | Bin 0 -> 32107 bytes 62 files changed, 6614 insertions(+), 178 deletions(-) create mode 100644 libasm/disasm_begin.c create mode 100644 libasm/disasm_cb.c create mode 100644 libasm/disasm_end.c create mode 100644 libasm/disasm_str.c create mode 100644 libcpu/defs/i386 create mode 100644 libcpu/defs/i386.doc create mode 100644 libcpu/defs/x86_64 create mode 100644 libcpu/i386_data.h create mode 100644 libcpu/i386_disasm.c create mode 100644 libcpu/i386_gendis.c create mode 100644 libcpu/i386_lex.l create mode 100644 libcpu/i386_parse.y create mode 100644 libcpu/memory-access.h create mode 100644 libcpu/x86_64_disasm.c create mode 100644 libelf/elf_scnshndx.c create mode 100644 src/debugpred.h create mode 100755 tests/run-disasm.sh create mode 100644 tests/testfile44.S.bz2 create mode 100644 tests/testfile44.expect.bz2 diff --git a/ChangeLog b/ChangeLog index 9a55c0050..fee15a494 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2007-12-20 Ulrich Drepper + + * configure.ac: Add support for --enable-debugpred. + Update likely/unlikely macros for it. + 2007-06-05 Ulrich Drepper * Makefile.am: Remove traces of mini builds. diff --git a/backends/ChangeLog b/backends/ChangeLog index 97cddd237..3e35ef269 100644 --- a/backends/ChangeLog +++ b/backends/ChangeLog @@ -42,6 +42,10 @@ * sparc_retval.c: Likewise. * x86_64_retval.c: Likewise. +2007-10-31 Ulrich Drepper + + * Makefile.am: More dependencies for the libebl_* libraries. + 2007-08-23 Roland McGrath * x86_64_regs.c (x86_64_register_info): Put %rflags in "integer" set. diff --git a/backends/Makefile.am b/backends/Makefile.am index 4174f8e37..28e55f6ed 100644 --- a/backends/Makefile.am +++ b/backends/Makefile.am @@ -1,6 +1,6 @@ ## Process this file with automake to create Makefile.in ## -## Copyright (C) 2000,2001,2002,2003,2004,2005,2006,2007 Red Hat, Inc. +## Copyright (C) 2000-2006, 2007 Red Hat, Inc. ## This file is part of Red Hat elfutils. ## ## Red Hat elfutils is free software; you can redistribute it and/or modify @@ -32,7 +32,7 @@ AM_CFLAGS = endif AM_CFLAGS += -fpic -Wall -Wshadow -Werror -Wunused -Wextra -Wformat=2 \ -std=gnu99 -INCLUDES = -I$(srcdir) -I$(top_srcdir)/libebl \ +INCLUDES = -I$(srcdir) -I$(top_srcdir)/libebl -I$(top_srcdir)/libasm \ -I$(top_srcdir)/libelf -I$(top_srcdir)/libdw \ -I$(top_srcdir)/lib -I.. @@ -59,18 +59,10 @@ endif textrel_check = if readelf -d $@ | fgrep -q TEXTREL; then exit 1; fi -libebl_%.so: libebl_%_pic.a libebl_%.map $(libelf) $(libdw) - $(LINK) -shared -o $@ -Wl,--whole-archive,$<,--no-whole-archive \ - -Wl,--version-script,$(word 2,$^) \ - -Wl,-z,defs -Wl,--as-needed $(libelf) $(libdw) $(libmudflap) - $(textrel_check) - -libebl_%.map: Makefile - echo 'ELFUTILS_$(PACKAGE_VERSION) { global: $*_init; local: *; };' > $@ - i386_SRCS = i386_init.c i386_symbol.c i386_corenote.c \ i386_retval.c i386_regs.c i386_auxv.c +cpu_i386 = ../libcpu/libcpu_i386.a libebl_i386_pic_a_SOURCES = $(i386_SRCS) am_libebl_i386_pic_a_OBJECTS = $(i386_SRCS:.c=.os) @@ -80,6 +72,7 @@ am_libebl_sh_pic_a_OBJECTS = $(sh_SRCS:.c=.os) x86_64_SRCS = x86_64_init.c x86_64_symbol.c x86_64_corenote.c \ x86_64_retval.c x86_64_regs.c i386_auxv.c +cpu_x86_64 = ../libcpu/libcpu_x86_64.a libebl_x86_64_pic_a_SOURCES = $(x86_64_SRCS) am_libebl_x86_64_pic_a_OBJECTS = $(x86_64_SRCS:.c=.os) @@ -116,6 +109,21 @@ libebl_s390_pic_a_SOURCES = $(s390_SRCS) am_libebl_s390_pic_a_OBJECTS = $(s390_SRCS:.c=.os) +libebl_%.so: libebl_%_pic.a libebl_%.map $(libelf) $(libdw) \ + $(cpu_$(@:libebl_%.so=%) + $(LINK) -shared -o $@ -Wl,--whole-archive,$<\ + $(cpu_$(@:libebl_%.so=%)) -Wl,--no-whole-archive \ + -Wl,--version-script,$(word 2,$^) \ + -Wl,-z,defs -Wl,--as-needed $(libelf) $(libdw) $(libmudflap) + $(textrel_check) + +# XXX Should not be needed... +libebl_i386.so: $(cpu_i386) +libebl_x86_64.so: $(cpu_x86_64) + +libebl_%.map: Makefile + echo 'ELFUTILS_$(PACKAGE_VERSION) { global: $*_init; local: *; };' > $@ + %.os: %.c if $(COMPILE) -c -o $@ -fpic -DPIC -DSHARED -MT $@ -MD -MP \ -MF "$(DEPDIR)/$*.Tpo" `test -f '$<' || echo '$(srcdir)/'`$<; \ diff --git a/backends/i386_init.c b/backends/i386_init.c index f25e1eb99..bf6c130c3 100644 --- a/backends/i386_init.c +++ b/backends/i386_init.c @@ -57,6 +57,7 @@ i386_init (elf, machine, eh, ehlen) HOOK (eh, return_value_location); HOOK (eh, register_info); HOOK (eh, auxv_info); + HOOK (eh, disasm); return MODVERSION; } diff --git a/backends/x86_64_init.c b/backends/x86_64_init.c index b1764832e..c2d3de129 100644 --- a/backends/x86_64_init.c +++ b/backends/x86_64_init.c @@ -54,6 +54,7 @@ x86_64_init (elf, machine, eh, ehlen) HOOK (eh, return_value_location); HOOK (eh, register_info); HOOK (eh, auxv_info); + HOOK (eh, disasm); return MODVERSION; } diff --git a/configure.ac b/configure.ac index b89f9a426..0b943ccb2 100644 --- a/configure.ac +++ b/configure.ac @@ -134,7 +134,15 @@ if test "$use_mudflap" = fail; then fi AM_CONDITIONAL(MUDFLAP, test "$use_mudflap" = yes) -# Enable gprof suport. +dnl enable debugging of branch prediction. +use_debugpred=0 +AC_ARG_ENABLE([debugpred], +AC_HELP_STRING([--enable-debugpred], +[build binaries with support to debug branch prediction]), +[use_debugpred=1], [use_debugpred=0]) +AC_SUBST([DEBUGPRED], $use_debugpred) + +dnl Enable gprof suport. AC_ARG_ENABLE([gprof], AC_HELP_STRING([--enable-gprof], [build binaries with gprof support]), [use_gprof=yes], [use_gprof=no]) @@ -267,8 +275,47 @@ AH_BOTTOM([ # define ALLOW_UNALIGNED 0 #endif -#define unlikely(expr) __builtin_expect (expr, 0) -#define likely(expr) __builtin_expect (expr, 1) +#if DEBUGPRED +# ifdef __x86_64__ +asm (".section predict_data, \"aw\"; .previous\n" + ".section predict_line, \"a\"; .previous\n" + ".section predict_file, \"a\"; .previous"); +# ifndef PIC +# define debugpred__(e, E) \ + ({ long int _e = !!(e); \ + asm volatile (".pushsection predict_data; ..predictcnt%=: .quad 0; .quad 0\n" \ + ".section predict_line; .quad %c1\n" \ + ".section predict_file; .quad %c2; .popsection\n" \ + "addq $1,..predictcnt%=(,%0,8)" \ + : : "r" (_e == E), "i" (__LINE__), "i" (__FILE__)); \ + __builtin_expect (_e, E); \ + }) +# endif +# elif defined __i386__ +asm (".section predict_data, \"aw\"; .previous\n" + ".section predict_line, \"a\"; .previous\n" + ".section predict_file, \"a\"; .previous"); +# ifndef PIC +# define debugpred__(e, E) \ + ({ long int _e = !!(e); \ + asm volatile (".pushsection predict_data; ..predictcnt%=: .long 0; .long 0\n" \ + ".section predict_line; .long %c1\n" \ + ".section predict_file; .long %c2; .popsection\n" \ + "incl ..predictcnt%=(,%0,8)" \ + : : "r" (_e == E), "i" (__LINE__), "i" (__FILE__)); \ + __builtin_expect (_e, E); \ + }) +# endif +# endif +# ifdef debugpred__ +# define unlikely(e) debugpred__ (e,0) +# define likely(e) debugpred__ (e,1) +# endif +#endif +#ifndef likely +# define unlikely(expr) __builtin_expect (!!(expr), 0) +# define likely(expr) __builtin_expect (!!(expr), 1) +#endif #define obstack_calloc(ob, size) \ ({ size_t _s = (size); memset (obstack_alloc (ob, _s), '\0', _s); }) diff --git a/libasm/ChangeLog b/libasm/ChangeLog index 99fb5e4f8..78a895c25 100644 --- a/libasm/ChangeLog +++ b/libasm/ChangeLog @@ -1,3 +1,14 @@ +2007-12-20 Ulrich Drepper + + * disasm_cb.c: Add initial support to resolve addresses to symbols. + +2007-02-05 Ulrich Drepper + + * disasm_begin.c: New file. + * disasm_cb.c: New file. + * disasm_end.c: New file. + * disasm_str.c: New file. + 2006-08-29 Roland McGrath * Makefile.am (CLEANFILES): Add libasm.so.$(VERSION). diff --git a/libasm/Makefile.am b/libasm/Makefile.am index 3bea3747b..95939617e 100644 --- a/libasm/Makefile.am +++ b/libasm/Makefile.am @@ -59,6 +59,7 @@ libasm_a_SOURCES = asm_begin.c asm_abort.c asm_end.c asm_error.c \ asm_addint32.c asm_adduint32.c \ asm_addint64.c asm_adduint64.c \ asm_adduleb128.c asm_addsleb128.c \ + disasm_begin.c disasm_cb.c disasm_end.c disasm_str.c \ symbolhash.c if !MUDFLAP diff --git a/libasm/asm_error.c b/libasm/asm_error.c index f91bb78e6..29c54cb05 100644 --- a/libasm/asm_error.c +++ b/libasm/asm_error.c @@ -126,6 +126,7 @@ static const char *msgs[ASM_E_NUM] = [ASM_E_DUPLSYM] = N_("duplicate symbol"), [ASM_E_TYPE] = N_("invalid section type for operation"), [ASM_E_IOERROR] = N_("error during output of data"), + [ASM_E_ENOSUP] = N_("no backend support available"), }; const char * diff --git a/libasm/disasm_begin.c b/libasm/disasm_begin.c new file mode 100644 index 000000000..5cdb5424e --- /dev/null +++ b/libasm/disasm_begin.c @@ -0,0 +1,49 @@ +/* Create context descriptor for disassembler. + Copyright (C) 2005 Red Hat, Inc. + Written by Ulrich Drepper , 2005. + + This program is Open Source software; you can redistribute it and/or + modify it under the terms of the Open Software License version 1.0 as + published by the Open Source Initiative. + + You should have received a copy of the Open Software License along + with this program; if not, you may obtain a copy of the Open Software + License version 1.0 from http://www.opensource.org/licenses/osl.php or + by writing the Open Source Initiative c/o Lawrence Rosen, Esq., + 3001 King Ranch Road, Ukiah, CA 95482. */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include + +#include "libasmP.h" +#include "../libebl/libeblP.h" + + +DisasmCtx_t * +disasm_begin (Ebl *ebl, Elf *elf, DisasmGetSymCB_t symcb) +{ + if (ebl == NULL) + return NULL; + + if (ebl->disasm == NULL) + { + __libasm_seterrno (ASM_E_ENOSUP); + return NULL; + } + + DisasmCtx_t *ctx = (DisasmCtx_t *) malloc (sizeof (DisasmCtx_t)); + if (ctx == NULL) + { + __libasm_seterrno (ASM_E_NOMEM); + return NULL; + } + + ctx->ebl = ebl; + ctx->elf = elf; + ctx->symcb = symcb; + + return ctx; +} diff --git a/libasm/disasm_cb.c b/libasm/disasm_cb.c new file mode 100644 index 000000000..a04416213 --- /dev/null +++ b/libasm/disasm_cb.c @@ -0,0 +1,176 @@ +/* Copyright (C) 2005, 2007 Red Hat, Inc. + This file is part of Red Hat elfutils. + Written by Ulrich Drepper , 2005. + + 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 + . */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include + +#include "libasmP.h" +#include "../libebl/libeblP.h" + + +struct symtoken +{ + DisasmCtx_t *ctx; + void *symcbarg; +}; + + +static int +default_elf_getsym (GElf_Addr addr, Elf32_Word scnndx, GElf_Addr value, + char *buf, size_t buflen, void *arg) +{ + struct symtoken *symtoken = (struct symtoken *) arg; + + /* First try the user provided function. */ + if (symtoken->ctx->symcb != NULL) + { + int res = symtoken->ctx->symcb (addr, scnndx, value, buf, buflen, + symtoken->symcbarg); + if (res >= 0) + return res; + } + + // XXX Look up in ELF file. + + return -1; +} + + +struct symaddrpair +{ + GElf_Addr addr; + const char *name; +}; + + +static void +read_symtab_exec (DisasmCtx_t *ctx) +{ + /* We simply use all we can get our hands on. This will produce + some duplicate information but this is no problem, we simply + ignore the latter definitions. */ + Elf_Scn *scn= NULL; + while ((scn = elf_nextscn (ctx->elf, scn)) != NULL) + { + GElf_Shdr shdr_mem; + GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem); + Elf_Data *data; + if (shdr == NULL || shdr->sh_type != SHT_SYMTAB + || (data = elf_getdata (scn, NULL)) == NULL) + continue; + + int xndxscnidx = elf_scnshndx (scn); + Elf_Data *xndxdata = NULL; + if (xndxscnidx > 0) + xndxdata = elf_getdata (elf_getscn (ctx->elf, xndxscnidx), NULL); + + /* Iterate over all symbols. Add all defined symbols. */ + int nsyms = shdr->sh_size / shdr->sh_entsize; + for (int cnt = 1; cnt < nsyms; ++cnt) + { + Elf32_Word xshndx; + GElf_Sym sym_mem; + GElf_Sym *sym = gelf_getsymshndx (data, xndxdata, cnt, &sym_mem, + &xshndx); + if (sym == NULL) + continue; + + /* Undefined symbols are useless here. */ + if (sym->st_shndx == SHN_UNDEF) + continue; + + + } + } +} + + +static void +read_symtab (DisasmCtx_t *ctx) +{ + /* Find the symbol table(s). */ + GElf_Ehdr ehdr_mem; + GElf_Ehdr *ehdr = gelf_getehdr (ctx->elf, &ehdr_mem); + if (ehdr == NULL) + return; + + switch (ehdr->e_type) + { + case ET_EXEC: + case ET_DYN: + read_symtab_exec (ctx); + break; + + case ET_REL: + // XXX Handle + break; + + default: + break; + } +} + + +static int +null_elf_getsym (GElf_Addr addr __attribute__ ((unused)), + Elf32_Word scnndx __attribute__ ((unused)), + GElf_Addr value __attribute__ ((unused)), + char *buf __attribute__ ((unused)), + size_t buflen __attribute__ ((unused)), + void *arg __attribute__ ((unused))) +{ + return -1; +} + + +int +disasm_cb (DisasmCtx_t *ctx, const uint8_t **startp, const uint8_t *end, + GElf_Addr addr, const char *fmt, DisasmOutputCB_t outcb, + void *outcbarg, void *symcbarg) +{ + struct symtoken symtoken; + DisasmGetSymCB_t getsym = ctx->symcb ?: null_elf_getsym; + + if (ctx->elf != NULL) + { + /* Read all symbols of the ELF file and stuff them into a hash + table. The key is the address and the section index. */ + read_symtab (ctx); + + symtoken.ctx = ctx; + symtoken.symcbarg = symcbarg; + + symcbarg = &symtoken; + + getsym = default_elf_getsym; + } + + return ctx->ebl->disasm (startp, end, addr, fmt, outcb, getsym, outcbarg, + symcbarg); +} +INTDEF (disasm_cb) diff --git a/libasm/disasm_end.c b/libasm/disasm_end.c new file mode 100644 index 000000000..3165c2dc3 --- /dev/null +++ b/libasm/disasm_end.c @@ -0,0 +1,30 @@ +/* Release descriptor for disassembler. + Copyright (C) 2005 Red Hat, Inc. + Written by Ulrich Drepper , 2005. + + This program is Open Source software; you can redistribute it and/or + modify it under the terms of the Open Software License version 1.0 as + published by the Open Source Initiative. + + You should have received a copy of the Open Software License along + with this program; if not, you may obtain a copy of the Open Software + License version 1.0 from http://www.opensource.org/licenses/osl.php or + by writing the Open Source Initiative c/o Lawrence Rosen, Esq., + 3001 King Ranch Road, Ukiah, CA 95482. */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include + +#include "libasmP.h" + + +int +disasm_end (DisasmCtx_t *ctx) +{ + free (ctx); + + return 0; +} diff --git a/libasm/disasm_str.c b/libasm/disasm_str.c new file mode 100644 index 000000000..9f4073719 --- /dev/null +++ b/libasm/disasm_str.c @@ -0,0 +1,56 @@ +/* Copyright (C) 2005 Red Hat, Inc. + + This program is Open Source software; you can redistribute it and/or + modify it under the terms of the Open Software License version 1.0 as + published by the Open Source Initiative. + + You should have received a copy of the Open Software License along + with this program; if not, you may obtain a copy of the Open Software + License version 1.0 from http://www.opensource.org/licenses/osl.php or + by writing the Open Source Initiative c/o Lawrence Rosen, Esq., + 3001 King Ranch Road, Ukiah, CA 95482. */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include + +#include "libasmP.h" + + +struct buffer +{ + char *buf; + size_t len; +}; + + +static int +buffer_cb (char *str, size_t len, void *arg) +{ + struct buffer *buffer = (struct buffer *) arg; + + if (len > buffer->len) + /* Return additional needed space. */ + return len - buffer->len; + + buffer->buf = mempcpy (buffer->buf, str, len); + buffer->len = len; + + return 0; +} + + +int +disasm_str (DisasmCtx_t *ctx, const uint8_t **startp, const uint8_t *end, + GElf_Addr addr, const char *fmt, char **bufp, size_t len, + void *symcbarg) +{ + struct buffer buffer = { .buf = *bufp, .len = len }; + + int res = INTUSE(disasm_cb) (ctx, startp, end, addr, fmt, buffer_cb, &buffer, + symcbarg); + *bufp = buffer.buf; + return res; +} diff --git a/libasm/libasm.h b/libasm/libasm.h index 6daf221ea..8a005f1cd 100644 --- a/libasm/libasm.h +++ b/libasm/libasm.h @@ -45,6 +45,20 @@ typedef struct AsmScnGrp AsmScnGrp_t; typedef struct AsmSym AsmSym_t; +/* Opaque type for the disassembler context descriptor. */ +typedef struct DisasmCtx DisasmCtx_t; + +/* Type used for callback functions to retrieve symbol name. The + symbol reference is in the section designated by the second parameter + at an offset described by the first parameter. The value is the + third parameter. */ +typedef int (*DisasmGetSymCB_t) (GElf_Addr, Elf32_Word, GElf_Addr, char *, + size_t, void *); + +/* Output function callback. */ +typedef int (*DisasmOutputCB_t) (char *, size_t, void *); + + #ifdef __cplusplus extern "C" { #endif @@ -159,6 +173,25 @@ extern int asm_errno (void); string is returned. */ extern const char *asm_errmsg (int __error); + +/* Create context descriptor for disassembler. */ +extern DisasmCtx_t *disasm_begin (Ebl *ebl, Elf *elf, DisasmGetSymCB_t symcb); + +/* Release descriptor for disassembler. */ +extern int disasm_end (DisasmCtx_t *ctx); + +/* Produce of disassembly output for given memory, store text in + provided buffer. */ +extern int disasm_str (DisasmCtx_t *ctx, const uint8_t **startp, + const uint8_t *end, GElf_Addr addr, const char *fmt, + char **bufp, size_t len, void *symcbarg); + +/* Produce disassembly output for given memory and output it using the + given callback functions. */ +extern int disasm_cb (DisasmCtx_t *ctx, const uint8_t **startp, + const uint8_t *end, GElf_Addr addr, const char *fmt, + DisasmOutputCB_t outcb, void *outcbarg, void *symcbarg); + #ifdef __cplusplus } #endif diff --git a/libasm/libasm.map b/libasm/libasm.map index b0b5b5b7a..a36cdbfec 100644 --- a/libasm/libasm.map +++ b/libasm/libasm.map @@ -28,6 +28,11 @@ ELFUTILS_1.0 { asm_newsym; asm_scngrp_newsignature; + disasm_begin; + disasm_cb; + disasm_end; + disasm_str; + local: *; }; diff --git a/libasm/libasmP.h b/libasm/libasmP.h index e64015750..ee7005f5b 100644 --- a/libasm/libasmP.h +++ b/libasm/libasmP.h @@ -47,6 +47,7 @@ enum ASM_E_LIBELF, /* Refer to error in libelf. */ ASM_E_TYPE, /* Invalid section type for operation. */ ASM_E_IOERROR, /* Error during output of data. */ + ASM_E_ENOSUP, /* No backend support. */ ASM_E_NUM /* Keep this entry as the last. */ }; @@ -235,6 +236,21 @@ struct AsmScnGrp }; +/* Descriptor for disassembler. */ +struct DisasmCtx +{ + /* Handle for the backend library with the disassembler routine. */ + Ebl *ebl; + + /* ELF file containing all the data passed to the function. This + allows to look up symbols. */ + Elf *elf; + + /* Callback function to determine symbol names. */ + DisasmGetSymCB_t symcb; +}; + + /* The default fill pattern: one zero byte. */ extern const struct FillPattern *__libasm_default_pattern attribute_hidden; @@ -269,6 +285,14 @@ extern int __asm_addint64_internal (AsmScn_t *asmscn, int64_t num) attribute_hidden; +/* Produce disassembly output for given memory and output it using the + given callback functions. */ +extern int __disasm_cb_internal (DisasmCtx_t *ctx, const uint8_t **startp, + const uint8_t *end, GElf_Addr addr, + const char *fmt, DisasmOutputCB_t outcb, + void *outcbarp, void *symcbarg) + attribute_hidden; + /* Test whether given symbol is an internal symbol and if yes, whether we should nevertheless emit it in the symbol table. */ diff --git a/libcpu/ChangeLog b/libcpu/ChangeLog index 5c2a4ac76..c5be1d7b3 100644 --- a/libcpu/ChangeLog +++ b/libcpu/ChangeLog @@ -1,3 +1,184 @@ +2008-01-01 Ulrich Drepper + + * defs/i386: More 0f prefix support. + * i386_data.h (FCT_mmxreg): Implement. + (FCT_mmxreg2): Implement. + (FCT_mmreg): Remove. + * i386_disasm.c (i386_disasm): More special instructions. + Fix tttn suffix for cmov. + * i386_parse.y: Simplify test for mod/r_m mode. + +2007-12-31 Ulrich Drepper + + * defs/i386: Fix order or arguments for mov of control/debug registers. + * i386_data.h (FCT_ccc): Implement + (FCT_ddd): Implement + +2007-12-30 Ulrich Drepper + + * defs/i386: Fix 0f groups 6 and 7. + * i386_data.c (FCT_mod$16r_m): Implement. + * i386_disasm.c (i386_disasm): Third parameter can also have string. + +2007-12-29 Ulrich Drepper + + * defs/i386: Add lots of floating point ops. + * i386_data.h (FCT_fmod$fr_m): Removed. + (FCT_freg): Implement. + * i386_disasm.c (i386_disasm): Implement suffix_D. + * i386_parse.y: Emit suffix_D. + + * defs/i386: Use rel instead of dispA. + Fix lcall, dec, div, idiv, imul, inc, jmp, ljmp, mul, neg, not, push, + test. + + * i386_data.h (FCT_dispA): Removed. + (FCT_ds_xx): Add test for end of input buffer. + * i386_disasm.c (ABORT_ENTRY): Removed. + (i386_disasm): Fix handling of SIB. Pass correct address value to + operand callbacks. + + * Makefile.am (*.mnemonics): Filter out INVALID entry. + * defs/i386: Define imms8 and use in appropriate places. + Add INVALID entries for special opcodes with special mnemonics. + Fix int3. Fix typo in shl. Correct xlat. + * i386_data.h (FCT_ds_xx): New function. + (FCT_ds_si): Use it. + (FCT_ds_bx): New function. + (FCT_imms8): New function. + * i386_disasm.c (MNE_INVALID): Define. + (i386_disasm): Handle invalid opcodes in mnemonics printing, not + separately. Fix address value passed to operand handlers. + * i386_parse.y (bx_reg): Define. + (instrtable_out): Handle INVALID entries differently, just use + MNE_INVALID value for .mnemonic. + +2007-12-28 Ulrich Drepper + + * defs/i386: Fix shift and mov immediate instructions. + * i386_data.h (FCT_imm16): Implement. + + * defs/i386: Use absval instead of abs of lcall and ljmp. + Add parameters for cmps. Fix test and mov immediate. + * i386_data.h: Implement FCT_absval. + * i386_disasm.c: Handle data16 for suffix_w and FCT_imm. + + * defs/i386: Move entries with 0x9b prefix together. + * i386_disasm.c (i386_disasm): Fix recognizing insufficient bytes in + input. Handle data16 with suffix_W. + + * i386_data.h (FCT_*): Add end parameter to all functions. Check + before using more bytes. + (FCT_sel): Implement. + * i386_disasm.c (i386_disasm): Better handle end of input buffer. + Specal opcode 0x99. + + * Makefile.am: Use m4 to preprocess defs/* files. + * defs/i386: Adjust appropriately. + * i386_data.c (FCT_ax): Implement. + (FCT_ax$w): Use FCT_ax. + * i386_disasm.c (ADD_STRING): Use _len instead of len. + (i386_disasm): If no instruction can be matched because of lack of + input and prefixes have been matched, print prefixes. + Recognize abort entries. + Handle special cases. + * i386_gendis.c: Recognize - input file name. + * i386_lex.c: Recognize INVALID token. + * i386_parse.y: Handle INVALID token input. + + * defs/i386: Fix mov, pop. + * i386_data.h (FCT_sreg3): Implement. + +2007-12-27 Ulrich Drepper + + * defs/i386: Fix adc, add, cmp, or, sbb, sub, xchg, xor. + * i386_data.h (FCT_imms): New function. + (FCT_imm$s): Use FCT_imms for handling of signed values. + (FCT_imm8): Sign extend values. + * i386_disasm.c (i386_disasm): Implement suffix_w0. + * i386_parse.y: Emit suffix w0. + + * i386_data.h (FCT_disp8): Add 0x prefix. + (FCT_ds_si): Implement. + * i386_disasm.c (i386_disasm): Increment addr for invalid prefixes. + Implement tttn suffix. + * i386_parse.y: Emit tttn suffix definition. + +2007-12-26 Ulrich Drepper + + * i386_data.h (struct instr_enc): Use suffix field. + (FCT_dx): Fill in body. + (FCT_es_di): Likewise. + (FCT_imm$s): Sign-extended byte values. + * i386_disasm.c: Protect ADD_CHAR and ADD_STRING macros. Adjust uses. + (i386_disasm): Handle suffix. + * i386_parse.y: Emit suffix information. + * defs/i386: Remove unnecessary suffixes. + + * Makefile.am: Disable building x86-64 version for now. + + * defs/i386: Fix and, bound, cmp, or, pop, sbb, sub, xor. + * i386_data.h: Pass pointer to prefix to functions. If not prefixes + are consumed this means invalid input. + * i386_disasm.c: Fix prefix printing. Adjust function calls for + parameter change. + * i386_parse.y: Recognize moda prefix. + +2007-12-21 Ulrich Drepper + + * i386_data.h: Fix SIB handling. + * i386_disasm.c: Likewise. + +2007-12-19 Ulrich Drepper + + * defs/i386: Fix up 'and' opcode. + +2007-10-31 Ulrich Drepper + + * Makefile.am: Add dependencies of the generated files on the source + files. + (i386_lex_CFLAGS): Add -Wno-sign-compare. + + * defs/i386: A lot more data. + * defs/x86_64: Likewise. + * i386_data.h (struct instr_enc): Add off1_3, off2_3, and off3_3 + fields. + (opfct_t): Add parameter for third operand. + (FCT_*): Likewise. + (data_prefix): New function. + (FCT_abs): Implement. + (FCT_ax): Renamed to FCT_ax$w amd implement. + (FCT_disp8): Implement. + (FCT_dispA): Implement. + (FCT_imm): Implement. + (FCT_imm$w): Implement. + (FCT_imm$s): Don't zero-pad numbers. + (FCT_imm8): Likewise. + (FCT_rel): Likewise. + (general_mod$r_m): New function. + (FCT_mod$r_m): Use it. + (FCT_mod$r_m$w): New function. + (FCT_mod$8r_m): New function. + (FCT_reg): Correctly handle 16-bit registers. + (FCT_reg$w): New function. + * i386_disasm.c (i386_disasm): Handle prefixes better. + Pass third parameter to operand functions. + * i386_parse.y (struct instruction): Add off3 field. + Handle third operand throughout. + +2007-02-05 Ulrich Drepper + + * i386_disasm.c: New file. + * i386_data.h: New file. + * i386_gendis.c: New file. + * i386_lex.l: New file. + * i386_parse.y: New file. + * memory-access.h: New file. + * x86_64_disasm.c: New file. + * defs/i386: New file. + * defs/i386.doc: New file. + * defs/x86_64: New file. + 2005-02-15 Ulrich Drepper * Makefile (AM_CFLAGS): Add -Wunused -Wextra -Wformat=2. diff --git a/libcpu/Makefile.am b/libcpu/Makefile.am index 23222bec4..c511408d5 100644 --- a/libcpu/Makefile.am +++ b/libcpu/Makefile.am @@ -1,6 +1,6 @@ ## Process this file with automake to create Makefile.in ## -## Copyright (C) 2002, 2005 Red Hat, Inc. +## Copyright (C) 2002, 2004, 2005, 2007 Red Hat, Inc. ## This file is part of Red Hat elfutils. ## ## Red Hat elfutils is free software; you can redistribute it and/or modify @@ -25,9 +25,56 @@ ## . ## DEFS = -D_GNU_SOURCE -DHAVE_CONFIG_H -AM_CFLAGS = -Wall -Wshadow -Werror -Wextra -Wformat=2 -Wunused -INCLUDES = -I$(srcdir) +if MUDFLAP +AM_CFLAGS = -fmudflap +else +AM_CFLAGS = +endif +AM_CFLAGS += -Wall -Wshadow -Werror -Wunused -Wextra -std=gnu99 -fpic \ + $($(*F)_CFLAGS) +INCLUDES = -I$(srcdir) -I$(srcdir)/../lib -I$(srcdir)/../libelf \ + -I$(srcdir)/../libebl -I$(srcdir)/../libdw -I$(srcdir)/../libasm +LEXCOMPILE = $(LEX) $(LFLAGS) $(AM_LFLAGS) -P$( $@ +x86_64_dis.h: i386_gendis $(srcdir)/defs/x86_64 + m4 -Dx86_64 -DDISASSEMBLER $(srcdir)/defs/x86_64 | ./i386_gendis - > $@ + +i386.mnemonics x86_64.mnemonics: %.mnemonics: $(srcdir)/defs/% + m4 -D$( $@ + +libeu = ../lib/libeu.a + +i386_lex_CFLAGS = -Wno-unused-label -Wno-unused-function -Wno-sign-compare +i386_gendis.o: i386_parse.c i386.mnemonics +i386_parse_CFLAGS = -DNMNES=$$(wc -l < i386.mnemonics) +i386_lex.o: i386_parse.c +i386_gendis_LDADD = $(libeu) -lm $(libmudflap) + +noinst_HEADERS = memory-access.h i386_parse.h i386_data.h + +EXTRA_DIST = defs/i386 defs/x86_64 + +CLEANFILES = i386.mnemonics i386_dis.h x86_64.mnemonics x86_64_dis.h diff --git a/libcpu/defs/i386 b/libcpu/defs/i386 new file mode 100644 index 000000000..b0cdfb4b9 --- /dev/null +++ b/libcpu/defs/i386 @@ -0,0 +1,634 @@ +%mask {s} 1 +%mask {w} 1 +%mask {w1} 1 +dnl floating point reg suffix +%mask {D} 1 +%mask {imm8} 8 +%mask {imms8} 8 +%mask {imm16} 16 +%mask {reg} 3 +%mask {reg16} 3 +%mask {tttn} 4 +%mask {gg} 2 +%mask {mod} 2 +%mask {moda} 2 +%mask {MOD} 2 +%mask {r_m} 3 +dnl like {r_m} but referencing byte register +%mask {8r_m} 3 +dnl like {r_m} but referencing 16-bit register +%mask {16r_m} 3 +%mask {disp8} 8 +dnl imm really is 8/16/32 bit depending on the situation. +%mask {imm} 8 +%mask {imms} 8 +%mask {rel} 32 +%mask {abs} 32 +%mask {absval} 32 +%mask {sel} 16 +%mask {imm32} 32 +%mask {ccc} 3 +%mask {ddd} 3 +%mask {sreg3} 3 +%mask {sreg2} 2 +%mask {mmxreg} 3 +%mask {mmxreg2} 3 +%mask {R_M} 3 +%mask {0g} 2 +%mask {GG} 2 +%mask {gG} 2 +%mask {Mod} 2 +%mask {xmmreg} 3 +%mask {R_m} 3 +%mask {mmreg} 3 +%mask {xmmreg1} 3 +%mask {xmmreg2} 3 +%mask {predpd} 8 +%mask {predps} 8 +%mask {predsd} 8 +%mask {predss} 8 +%mask {freg} 3 +%mask {fmod} 2 +%mask {fr_m} 3 +%prefix {R} +%prefix {RE} +%suffix {W} +%suffix {w0} +%synonym {xmmreg1} {xmmreg} +%synonym {xmmreg2} {xmmreg} + +%% +ifdef(`i386', +`00110111:aaa +11010101,00001010:aad +11010100,00001010:aam +00111111:aas +')dnl +0001010{w},{imm}:adc {imm}{w},{ax}{w} +1000000{w},{mod}010{r_m},{imm}:adc{w} {imm}{w},{mod}{r_m}{w} +1000001{w},{mod}010{r_m},{imms8}:adc{w} {imms8},{mod}{r_m} +0001000{w},{mod}{reg}{r_m}:adc {reg}{w},{mod}{r_m} +0001001{w},{mod}{reg}{r_m}:adc {mod}{r_m},{reg}{w} +0000010{w},{imm}:add {imm}{w},{ax}{w} +1000000{w},{mod}000{r_m},{imm}:add{w} {imm}{w},{mod}{r_m}{w} +10000011,{mod}000{r_m},{imms8}:add{w0} {imms8},{mod}{r_m} +0000000{w},{mod}{reg}{r_m}:add {reg}{w},{mod}{r_m} +0000001{w},{mod}{reg}{r_m}:add {mod}{r_m},{reg}{w} +11110010,00001111,01011000,{Mod}{xmmreg}{R_m}:addsd {Mod}{R_m},{xmmreg} +11110011,00001111,01011000,{Mod}{xmmreg}{R_m}:addss {Mod}{R_m},{xmmreg} +01100110,00001111,11010000,{Mod}{xmmreg}{R_m}:addsubpd {Mod}{R_m},{xmmreg} +11110010,00001111,11010000,{Mod}{xmmreg}{R_m}:addsubps {Mod}{R_m},{xmmreg} +0010010{w},{imm}:and {imm}{w},{ax}{w} +1000000{w},{mod}100{r_m},{imm}:and{w} {imm}{w},{mod}{r_m}{w} +1000001{w},{mod}100{r_m},{imms}:and{w} {imms},{mod}{r_m} +0010000{w},{mod}{reg}{r_m}:and {reg}{w},{mod}{r_m}{w} +0010001{w},{mod}{reg}{r_m}:and {mod}{r_m}{w},{reg}{w} +01100110,00001111,01010100,{Mod}{xmmreg}{R_m}:andpd {Mod}{R_m},{xmmreg} +00001111,01010100,{Mod}{xmmreg}{R_m}:andps {Mod}{R_m},{xmmreg} +01100110,00001111,01010101,{Mod}{xmmreg}{R_m}:andnpd {Mod}{R_m},{xmmreg} +00001111,01010101,{Mod}{xmmreg}{R_m}:andnps {Mod}{R_m},{xmmreg} +ifdef(`i386', +`01100011,{mod}{reg16}{r_m}:arpl {reg16},{mod}{r_m} +01100010,{moda}{reg}{r_m}:bound {reg},{moda}{r_m} +')dnl +00001111,10111100,{mod}{reg}{r_m}:bsf {reg},{mod}{r_m} +00001111,10111101,{mod}{reg}{r_m}:bsr {reg},{mod}{r_m} +00001111,11001{reg}:bswap {reg} +00001111,10100011,{mod}{reg}{r_m}:bt {reg},{mod}{r_m} +00001111,10111010,{mod}100{r_m},{imm8}:bt {imm8},{mod}{r_m} +00001111,10111011,{mod}{reg}{r_m}:btc {reg},{mod}{r_m} +00001111,10111010,{mod}111{r_m},{imm8}:btc {imm8},{mod}{r_m} +00001111,10110011,{mod}{reg}{r_m}:btr {reg},{mod}{r_m} +00001111,10111010,{mod}110{r_m},{imm8}:btr {imm8},{mod}{r_m} +00001111,10101011,{mod}{reg}{r_m}:bts {reg},{mod}{r_m} +00001111,10111010,{mod}101{r_m},{imm8}:bts {imm8},{mod}{r_m} +11101000,{rel}:call {rel} +11111111,{mod}010{r_m}:call *{mod}{r_m} +ifdef(`i386', +`10011010,{absval},{sel}:lcall {sel},{absval} +')dnl +11111111,{mod}011{r_m}:lcall *{mod}{r_m} +# SPECIAL 10011000:[{rex.w}?cltq:{dpfx}?cbtw:cwtl] +10011000:INVALID +# SPECIAL 10011001:[{rew.w}?cqto:{dpfx}?cltd:cwtd] +10011001:INVALID +11111000:clc +11111100:cld +00001111,10101110,{mod}111{r_m}:clflush {mod}{r_m} +11111010:cli +00001111,00000101:syscall +00001111,00000110:clts +00001111,00000111:sysret +00001111,00110100:sysenter +00001111,00110101:sysexit +11110101:cmc +00001111,0100{tttn},{mod}{reg}{r_m}:cmov{tttn} {mod}{r_m},{reg} +0011110{w},{imm}:cmp {imm}{w},{ax}{w} +1000000{w},{mod}111{r_m},{imm}:cmp{w} {imm}{w},{mod}{r_m}{w} +10000011,{mod}111{r_m},{imms8}:cmp{w0} {imms8},{mod}{r_m} +0011100{w},{mod}{reg}{r_m}:cmp {reg}{w},{mod}{r_m}{w} +0011101{w},{mod}{reg}{r_m}:cmp {mod}{r_m}{w},{reg}{w} +01100110,00001111,11000010,{Mod}{xmmreg}{R_m},{predpd}:cmpl{predpd} {Mod}{R_m},{xmmreg} +ifdef(`ASSEMBLER', +`01100110,00001111,11000010,{Mod}{xmmreg}{R_m},{imm8}:cmppd {imm8},{Mod}{R_m},{xmmreg +}')dnl +00001111,11000010,{Mod}{xmmreg}{R_m},{predps}:cmpl{predps} {Mod}{R_m},{xmmreg} +ifdef(`ASSEMBLER', +`00001111,11000010,{Mod}{xmmreg}{R_m},{imm8}:cmpps {imm8},{Mod}{R_m},{xmmreg} +')dnl +1010011{w}:{RE}cmps{w} {es_di},{ds_si} +11110010,00001111,11000010,{Mod}{xmmreg}{R_m},{predsd}:cmpl{predsd} {Mod}{R_m},{xmmreg} +ifdef(`ASSEMBLER', +`11110010,00001111,11000010,{Mod}{xmmreg}{R_m},{imm8}:cmpsd {imm8},{Mod}{R_m},{xmmreg} +')dnl +11110011,00001111,11000010,{Mod}{xmmreg}{R_m},{predss}:cmpl{predss} {Mod}{R_m},{xmmreg} +ifdef(`ASSEMBLER', +`11110011,00001111,11000010,{Mod}{xmmreg}{R_m},{imm8}:cmpss {imm8},{Mod}{R_m},{xmmreg} +')dnl +00001111,1011000{w},{mod}{reg}{r_m}:cmpxchg{w} {reg},{mod}{r_m} +# SPECIAL 00001111,11000111,{mod}001{r_m}:[{rex.w}?cmpxchg16b:cmpxchg8b] {reg},{mod}{r_m} +00001111,10100010:cpuid +11110011,00001111,11100110,{Mod}{xmmreg}{R_m}:cvtdq2pd {Mod}{R_m},{xmmreg} +00001111,01011011,{Mod}{xmmreg}{R_m}:cvtdq2ps {Mod}{R_m},{xmmreg} +11110010,00001111,11100110,{Mod}{xmmreg}{R_m}:cvtpd2dq {Mod}{R_m},{xmmreg} +01100110,00001111,01011010,{Mod}{xmmreg}{R_m}:cvtpd2ps {Mod}{R_m},{xmmreg} +01100110,00001111,00101010,{MOD}{xmmreg}{R_M}:cvtpi2pd {MOD}{R_M},{xmmreg} +00001111,00101010,{MOD}{xmmreg}{R_M}:{R}INVALID {MOD}{R_M},{xmmreg} +01100110,00001111,01011011,{Mod}{xmmreg}{R_m}:cvtps2dq {Mod}{R_m},{xmmreg} +00001111,01011010,{Mod}{xmmreg}{R_m}:cvtps2pd {Mod}{R_m},{xmmreg} +11110010,00001111,01011010,{Mod}{xmmreg}{R_m}:cvtsd2ss {Mod}{R_m},{xmmreg} +11110010,00001111,00101010,{mod}{xmmreg}{r_m}:cvtsi2sd {mod}{r_m},{xmmreg} +11110011,00001111,00101010,{mod}{xmmreg}{r_m}:cvtsi2ss {mod}{r_m},{xmmreg} +11110011,00001111,01011010,{Mod}{xmmreg}{R_m}:cvtss2sd {Mod}{R_m},{xmmreg} +01100110,00001111,11100110,{Mod}{xmmreg}{R_m}:cvttpd2dq {Mod}{R_m},{xmmreg} +11110011,00001111,01011011,{Mod}{mmxreg}{R_m}:cvttps2dq {Mod}{R_m},{mmxreg} +ifdef(`i386', +`00100111:daa +00101111:das +')dnl +1111111{w},{mod}001{r_m}:dec{w} {mod}{r_m}{w} +ifdef(`i386', +`01001{reg}:dec {reg} +')dnl +1111011{w},{mod}110{r_m}:div{w} {mod}{r_m}{w} +01100110,00001111,01011110,{Mod}{xmmreg}{R_m}:divpd {Mod}{R_m},{xmmreg} +00001111,01011110,{Mod}{xmmreg}{R_m}:divps {Mod}{R_m},{xmmreg} +11110010,00001111,01011110,{Mod}{xmmreg}{R_m}:divsd {Mod}{R_m},{xmmreg} +11110011,00001111,01011110,{Mod}{xmmreg}{R_m}:divss {Mod}{R_m},{xmmreg} +00001111,01110111:emms +11001000,{imm16},{imm8}:enter {imm16},{imm8} +11011001,11010000:fnop +11011001,11100000:fchs +11011001,11100001:fabs +11011001,11100100:ftst +11011001,11100101:fxam +11011001,11101000:fld1 +11011001,11101001:fldl2t +11011001,11101010:fldl2e +11011001,11101011:fldpi +11011001,11101100:fldlg2 +11011001,11101101:fldln2 +11011001,11101110:fldz +11011001,11110000:f2xm1 +11011001,11110001:fyl2x +11011001,11110010:fptan +11011001,11110011:fpatan +11011001,11110100:fxtract +11011001,11110101:fprem1 +11011001,11110110:fdecstp +11011001,11110111:fincstp +11011001,11111000:fprem +11011001,11111001:fyl2xp1 +11011001,11111010:fsqrt +11011001,11111011:fsincos +11011001,11111100:frndint +11011001,11111101:fscale +11011001,11111110:fsin +11011001,11111111:fcos +# ORDER +11011000,11000{freg}:fadd {freg},%st +11011100,11000{freg}:fadd %st,{freg} +11011{D}00,{mod}000{r_m}:fadd{D} {mod}{r_m} +# ORDER END +# ORDER +11011000,11001{freg}:fmul {freg},%st +11011100,11001{freg}:fmul %st,{freg} +11011{D}00,{mod}001{r_m}:fmul{D} {mod}{r_m} +# ORDER END +# ORDER +11011000,11100{freg}:fsub {freg},%st +11011100,11100{freg}:fsub %st,{freg} +11011{D}00,{mod}100{r_m}:fsub{D} {mod}{r_m} +# ORDER END +# ORDER +11011000,11101{freg}:fsubr {freg},%st +11011100,11101{freg}:fsubr %st,{freg} +11011{D}00,{mod}101{r_m}:fsubr{D} {mod}{r_m} +# ORDER END +# ORDER +11011101,11010{freg}:fst {freg} +11011{D}01,{mod}010{r_m}:fst{D} {mod}{r_m} +# ORDER END +# ORDER +11011101,11011{freg}:fstp {freg} +11011{D}01,{mod}011{r_m}:fstp{D} {mod}{r_m} +# ORDER END +11011001,{mod}100{r_m}:fldenv {mod}{r_m} +11011001,{mod}101{r_m}:fldcw {mod}{r_m} +11011001,{mod}110{r_m}:fnstenv {mod}{r_m} +11011001,{mod}111{r_m}:fnstcw {mod}{r_m} +11011001,11001{freg}:fxch {freg} +# ORDER +11011110,11000{freg}:faddp %st,{freg} +ifdef(`ASSEMBLER', +`11011110,11000001:faddp +')dnl +# ORDER +11011010,11000{freg}:fcmovb {freg},%st +11011{w1}10,{mod}000{r_m}:fiadd{w1} {mod}{r_m} +# ORDER END +# ORDER +11011010,11001{freg}:fcmove {freg},%st +11011110,11001{freg}:fmulp %st,{freg} +11011{w1}10,{mod}001{r_m}:fimul{w1} {mod}{r_m} +# ORDER END +# ORDER +11011110,11100{freg}:fsubp %st,{freg} +11011{w1}10,{mod}100{r_m}:fisub{w1} {mod}{r_m} +# ORDER END +# ORDER +11011110,11101{freg}:fsubrp %st,{freg} +11011{w1}10,{mod}101{r_m}:fisubr{w1} {mod}{r_m} +# ORDER END +# ORDER +11011111,11100000:fnstsw %ax +11011111,{mod}100{r_m}:fbld {mod}{r_m} +# ORDER END +# ORDER +11011111,11110{freg}:fcomip {freg},%st +11011111,{mod}110{r_m}:fbstp {mod}{r_m} +# ORDER END +11011001,11100000:fchs +# ORDER +10011011,11011011,11100010:fclex +10011011,11011011,11100011:finit +10011011:fwait +# END ORDER +11011011,11100010:fnclex +11011010,11000{freg}:fcmovb {freg},%st +11011010,11001{freg}:fcmove {freg},%st +11011010,11010{freg}:fcmovbe {freg},%st +11011010,11011{freg}:fcmovu {freg},%st +11011011,11000{freg}:fcmovnb {freg},%st +11011011,11001{freg}:fcmovne {freg},%st +11011011,11010{freg}:fcmovnbe {freg},%st +11011011,11011{freg}:fcmovnu {freg},%st +# ORDER +11011000,11010{freg}:fcom {freg} +ifdef(`ASSEMBLER', +`11011000,11010001:fcom +')dnl +11011{D}00,{mod}010{r_m}:fcom{D} {mod}{r_m} +# END ORDER +# ORDER +11011000,11011{freg}:fcomp {freg} +ifdef(`ASSEMBLER', +`11011000,11011001:fcomp +')dnl +11011{D}00,{mod}011{r_m}:fcomp{D} {mod}{r_m} +# END ORDER +11011110,11011001:fcompp +11011011,11110{freg}:fcomi {freg},%st +11011111,11110{freg}:fcomip {freg},%st +11011011,11101{freg}:fucomi {freg},%st +11011111,11101{freg}:fucomip {freg},%st +11011001,11111111:fcos +11011001,11110110:fdecstp +# ORDER +11011000,11110{freg}:fdiv {freg},%st +11011100,11110{freg}:fdiv %st,{freg} +11011{D}00,{mod}110{r_m}:fdiv{D} {mod}{r_m} +# END ORDER +11011010,{mod}110{r_m}:fidivl {mod}{r_m} +# ORDER +11011110,11110{freg}:fdivp %st,{freg} +11011110,{mod}110{r_m}:fidiv {mod}{r_m} +# END ORDER +11011110,11111{freg}:fdivrp %st,{freg} +ifdef(`ASSEMBLER', +`11011110,11111001:fdivp +')dnl +# ORDER +11011000,11111{freg}:fdivr {freg},%st +11011100,11111{freg}:fdivr %st,{freg} +11011{D}00,{mod}111{r_m}:fdivr{D} {mod}{r_m} +# END ORDER +11011010,{mod}111{r_m}:fidivrl {mod}{r_m} +11011110,{mod}111{r_m}:fidivr {mod}{r_m} +11011110,11110{freg}:fdivrp %st,{freg} +ifdef(`ASSEMBLER', +`11011110,11110001:fdivrp +')dnl +11011101,11000{freg}:ffree {freg} +11011010,11010{freg}:fcmovbe {freg} +11011{w1}10,{mod}010{r_m}:ficom{w1} {mod}{r_m} +11011010,11011{freg}:fcmovu {freg} +11011{w1}10,{mod}011{r_m}:ficomp{w1} {mod}{r_m} +11011111,{mod}000{r_m}:fild {mod}{r_m} +11011011,{mod}000{r_m}:fildl {mod}{r_m} +11011111,{mod}101{r_m}:fildll {mod}{r_m} +11011001,11110111:fincstp +11011011,11100011:fninit +11011{w1}11,{mod}010{r_m}:fist{w1} {mod}{r_m} +11011{w1}11,{mod}011{r_m}:fistp{w1} {mod}{r_m} +11011111,{mod}111{r_m}:fistpll {mod}{r_m} +11011{w1}11,{mod}001{r_m}:fisttp{w1} {mod}{r_m} +11011101,{mod}001{r_m}:fisttpll {mod}{r_m} +11011011,{mod}101{r_m}:fldt {mod}{r_m} +11011011,{mod}111{r_m}:fstpt {mod}{r_m} +# ORDER +11011001,11000{freg}:fld {freg} +11011{D}01,{mod}000{r_m}:fld{D} {mod}{r_m} +# ORDER END +# ORDER +11011101,11100{freg}:fucom {freg} +11011101,{mod}100{r_m}:frstor {mod}{r_m} +# ORDER END +11011101,11101{freg}:fucomp {freg} +11011101,{mod}110{r_m}:fnsave {mod}{r_m} +11011101,{mod}111{r_m}:fnstsw {mod}{r_m} +# +# +# +11110100:hlt +1111011{w},{mod}111{r_m}:idiv{w} {mod}{r_m}{w} +1111011{w},{mod}101{r_m}:imul{w} {mod}{r_m}{w} +00001111,10101111,{mod}{reg}{r_m}:imul {reg},{mod}{r_m} +011010{s}1,{mod}{reg}{r_m},{imm}:imul {imm}{s},{mod}{r_m},{reg} +1110010{w},{imm8}:in {imm8},{ax}{w} +1110110{w}:in {dx},{ax}{w} +1111111{w},{mod}000{r_m}:inc{w} {mod}{r_m}{w} +01000{reg}:inc {reg} +0110110{w}:{R}ins{w} {dx},{es_di} +11001101,{imm8}:int {imm8} +11001100:int3 +11001110:into +00001111,00001000:invd +# ORDER +00001111,00000001,11111000:swapgs +00001111,00000001,{mod}111{r_m}:invlpg {mod}{r_m} +# ORDER END +11001111:iret{W} +0111{tttn},{disp8}:j{tttn} {disp8} +00001111,1000{tttn},{rel}:j{tttn} {rel} +00001111,1001{tttn},{mod}000{8r_m}:set{tttn} {mod}{8r_m} +# SPECIAL 11100011,{disp8}:[{dpfx}?jcxz:jecxz] {disp8} +11100011,{disp8}:INVALID {disp8} +11101011,{disp8}:jmp {disp8} +11101001,{rel}:jmp {rel} +11111111,{mod}100{r_m}:jmp *{mod}{r_m} +11101010,{absval},{sel}:ljmp {sel},{absval} +11111111,{mod}101{r_m}:ljmp *{mod}{r_m} +10011111:lahf +00001111,00000010,{mod}{reg}{16r_m}:lar {mod}{16r_m},{reg} +11000101,{mod}{reg}{r_m}:lds {mod}{r_m},{reg} +10001101,{mod}{reg}{r_m}:lea {mod}{r_m},{reg} +11001001:leave +11000100,{mod}{reg}{r_m}:les {mod}{r_m},{reg} +00001111,10110100,{mod}{reg}{r_m}:lfs {mod}{r_m},{reg} +00001111,00000001,{mod}010{r_m}:lgdt{w0} {mod}{r_m} +00001111,10110101,{mod}{reg}{r_m}:lgs {mod}{r_m},{reg} +00001111,00000001,{mod}011{r_m}:lidt{w0} {mod}{r_m} +00001111,00000000,{mod}010{16r_m}:lldt {mod}{16r_m} +00001111,00000001,{mod}110{16r_m}:lmsw {mod}{16r_m} +11110000:lock +1010110{w}:{R}lods {ds_si},{ax}{w} +11100010,{disp8}:loop {disp8} +11100001,{disp8}:loope {disp8} +11100000,{disp8}:loopne {disp8} +00001111,00000011,{mod}{reg}{16r_m}:lsl {mod}{16r_m},{reg} +00001111,10110010,{mod}{reg}{r_m}:lss {mod}{r_m},{reg} +00001111,00000000,{mod}011{16r_m}:ltr {mod}{16r_m} +1000100{w},{mod}{reg}{r_m}:mov {reg}{w},{mod}{r_m}{w} +1000101{w},{mod}{reg}{r_m}:mov {mod}{r_m}{w},{reg}{w} +1100011{w},{mod}000{r_m},{imm}:mov{w} {imm}{w},{mod}{r_m}{w} +1011{w}{reg},{imm}:mov {imm}{w},{reg}{w} +1010000{w},{abs}:mov {abs},{ax}{w} +1010001{w},{abs}:mov {ax}{w},{abs} +00001111,00100000,11{ccc}{reg}:mov {ccc},{reg} +00001111,00100010,11{ccc}{reg}:mov {reg},{ccc} +00001111,00100001,11{ddd}{reg}:mov {ddd},{reg} +00001111,00100011,11{ddd}{reg}:mov {reg},{ddd} +10001100,{mod}{sreg3}{r_m}:mov {sreg3},{mod}{r_m} +10001110,{mod}{sreg3}{r_m}:mov {mod}{r_m},{sreg3} +1010010{w}:{R}movs{w} {ds_si},{es_di} +00001111,1011111{w},{mod}{reg}{r_m}:movsx{w} {mod}{r_m},{reg} +00001111,1011011{w},{mod}{reg}{r_m}:movzx{w} {mod}{r_m},{reg} +1111011{w},{mod}100{r_m}:mul{w} {mod}{r_m}{w} +1111011{w},{mod}011{r_m}:neg{w} {mod}{r_m}{w} +ifdef(`ASSEMBLER', +`10010000:nop +11110011,10010000:pause +', +`10010000:{R}INVALID +')dnl +1111011{w},{mod}010{r_m}:not{w} {mod}{r_m}{w} +0000100{w},{mod}{reg}{r_m}:or {reg}{w},{mod}{r_m}{w} +0000101{w},{mod}{reg}{r_m}:or {mod}{r_m}{w},{reg}{w} +1000000{w},{mod}001{r_m},{imm}:or{w} {imm}{w},{mod}{r_m}{w} +100000{s}{w},{mod}001{r_m},{imm}:or{w} {imm}{s},{mod}{r_m}{w} +0000110{w},{imm}:or {imm}{w},{ax}{w} +1110011{w},{imm8}:out {ax}{w},{imm8} +1110111{w}:out {ax}{w},{dx} +0110111{w}:{R}outs{w} {ds_si},{dx} +10001111,{mod}000{r_m}:pop{w} {mod}{r_m} +01011{reg}:pop {reg} +00001111,10{sreg3}001:pop {sreg3} +01100001:popa{W} +10011101:popf{W} +11111111,{mod}110{r_m}:push{w} {mod}{r_m} +01010{reg}:push {reg} +011010{s}0,{imm}:push {imm}{s} +000{sreg2}110:push {sreg2} +00001111,10{sreg3}000:push {sreg3} +01100000:pusha{W} +10011100:pushf{W} +1101000{w},{mod}010{r_m}:rcl{w} {mod}{r_m}{w} +1101001{w},{mod}010{r_m}:rcl{w} %cl,{mod}{r_m}{w} +1100000{w},{mod}010{r_m},{imm8}:rcl{w} {imm8},{mod}{r_m}{w} +1101000{w},{mod}011{r_m}:rcr{w} {mod}{r_m}{w} +1101001{w},{mod}011{r_m}:rcr{w} %cl,{mod}{r_m}{w} +1100000{w},{mod}011{r_m},{imm8}:rcr{w} {imm8},{mod}{r_m}{w} +00001111,00110010:rdmsr +00001111,00110011:rdpmc +00001111,00110001:rdtsc +11000011:ret +11000010,{imm16}:ret {imm16} +11001011:lret +11001010,{imm16}:lret {imm16} +1101000{w},{mod}000{r_m}:rol{w} {mod}{r_m}{w} +1101001{w},{mod}000{r_m}:rol{w} %cl,{mod}{r_m}{w} +1100000{w},{mod}000{r_m},{imm8}:rol{w} {imm8},{mod}{r_m}{w} +1101000{w},{mod}001{r_m}:ror{w} {mod}{r_m}{w} +1101001{w},{mod}001{r_m}:ror{w} %cl,{mod}{r_m}{w} +1100000{w},{mod}001{r_m},{imm8}:ror{w} {imm8},{mod}{r_m}{w} +00001111,10101010:rsm +10011110:sahf +1101000{w},{mod}111{r_m}:sar{w} {mod}{r_m}{w} +1101001{w},{mod}111{r_m}:sar{w} %cl,{mod}{r_m}{w} +1100000{w},{mod}111{r_m},{imm8}:sar{w} {imm8},{mod}{r_m}{w} +0001100{w},{mod}{reg}{r_m}:sbb {reg}{w},{mod}{r_m}{w} +0001101{w},{mod}{reg}{r_m}:sbb {mod}{r_m}{w},{reg}{w} +0001110{w},{imm}:sbb {imm}{w},{ax}{w} +1000000{w},{mod}011{r_m},{imm}:sbb{w} {imm}{w},{mod}{r_m}{w} +1000001{w},{mod}011{r_m},{imms}:sbb{w} {imms},{mod}{r_m} +1010111{w}:{RE}scas {es_di},{ax}{w} +00001111,1001{tttn},{mod}000{r_m}:set{tttn} {mod}{r_m} +1101000{w},{mod}100{r_m}:shl{w} {mod}{r_m}{w} +1101001{w},{mod}100{r_m}:shl{w} %cl,{mod}{r_m}{w} +1100000{w},{mod}100{r_m},{imm8}:shl{w} {imm8},{mod}{r_m}{w} +1101000{w},{mod}101{r_m}:shr{w} {mod}{r_m}{w} +00001111,10100100,{mod}{reg}{r_m},{imm8}:shld {imm8},{reg},{mod}{r_m} +00001111,10100101,{mod}{reg}{r_m}:shld %cl,{reg},{mod}{r_m} +1101001{w},{mod}101{r_m}:shr{w} %cl,{mod}{r_m}{w} +1100000{w},{mod}101{r_m},{imm8}:shr{w} {imm8},{mod}{r_m}{w} +00001111,10101100,{mod}{reg}{r_m},{imm8}:shrd {imm8},{reg},{mod}{r_m} +00001111,10101101,{mod}{reg}{r_m}:shrd %cl,{reg},{mod}{r_m} +# ORDER +00001111,00000001,11000001:vmcall +00001111,00000001,11000010:vmlaunch +00001111,00000001,11000011:vmresume +00001111,00000001,11000100:vmxoff +00001111,00000001,{mod}000{r_m}:sgdtl {mod}{r_m} +# ORDER END +# ORDER +00001111,00000001,11001000:monitor %eax,%ecx,%edx +00001111,00000001,11001001:mwait %eax,%ecx +00001111,00000001,{mod}001{r_m}:sidtl {mod}{r_m} +# ORDER END +00001111,00000000,{mod}000{r_m}:sldt {mod}{r_m} +00001111,00000001,{mod}100{r_m}:smsw {mod}{r_m} +11111001:stc +11111101:std +11111011:sti +1010101{w}:{R}stos {ax}{w},{es_di} +00001111,00000000,{mod}001{r_m}:str {mod}{r_m} +0010100{w},{mod}{reg}{r_m}:sub {reg}{w},{mod}{r_m}{w} +0010101{w},{mod}{reg}{r_m}:sub {mod}{r_m}{w},{reg}{w} +0010110{w},{imm}:sub {imm}{w},{ax}{w} +1000000{w},{mod}101{r_m},{imm}:sub{w} {imm}{w},{mod}{r_m}{w} +1000001{w},{mod}101{r_m},{imms}:sub{w} {imms},{mod}{r_m} +1000010{w},{mod}{reg}{r_m}:test {reg}{w},{mod}{r_m}{w} +1010100{w},{imm}:test {imm}{w},{ax}{w} +1111011{w},{mod}000{r_m},{imm}:test{w} {imm}{w},{mod}{r_m}{w} +00001111,00001011:ud2a +00001111,00000000,{mod}100{16r_m}:verr {mod}{16r_m} +00001111,00000000,{mod}101{16r_m}:verw {mod}{16r_m} +00001111,00001001:wbinvd +00001111,00001101,{mod}000{8r_m}:prefetch {mod}{8r_m} +00001111,00001101,{mod}001{8r_m}:prefetchw {mod}{8r_m} +00001111,00011000,{mod}000{r_m}:prefetchnta {mod}{r_m} +00001111,00011000,{mod}001{r_m}:prefetcht0 {mod}{r_m} +00001111,00011000,{mod}010{r_m}:prefetcht1 {mod}{r_m} +00001111,00011000,{mod}011{r_m}:prefetcht2 {mod}{r_m} +00001111,00011111,{mod}{reg}{r_m}:nop{w} {mod}{r_m} +dnl without prefix: movups +dnl with 0xf3: movss +dnl with 0x66: movupd +dnl with 0xf2: movsd +00001111,00010000,{Mod}{xmmreg}{R_m}:{R}INVALID {Mod}{R_m},{xmmreg} +00001111,00010001,{Mod}{xmmreg}{R_m}:{R}INVALID {xmmreg},{Mod}{R_m} +00001111,00110000:wrmsr +00001111,1100000{w},{mod}{reg}{r_m}:xadd{w} {reg},{mod}{r_m} +1000011{w},{mod}{reg}{r_m}:xchg {reg}{w},{mod}{r_m}{w} +10010{reg}:xchg {ax},{reg} +11010111:xlat {ds_bx} +0011000{w},{mod}{reg}{r_m}:xor {reg}{w},{mod}{r_m}{w} +0011001{w},{mod}{reg}{r_m}:xor {mod}{r_m}{w},{reg}{w} +0011010{w},{imm}:xor {imm}{w},{ax}{w} +1000000{w},{mod}110{r_m},{imm}:xor{w} {imm}{w},{mod}{r_m}{w} +1000001{w},{mod}110{r_m},{imms}:xor{w} {imms},{mod}{r_m} +00001111,01110111:emms +00001111,01101110,{mod}{mmxreg}{r_m}:movd {mod}{r_m},{mmxreg} +00001111,01111110,{mod}{mmxreg}{r_m}:movd {mmxreg},{mod}{r_m} +00001111,01101111,{MOD}{mmxreg}{R_M}:movq {MOD}{R_M},{mmxreg} +00001111,01111111,{MOD}{mmxreg}{R_M}:movq {mmxreg},{MOD}{R_M} +00001111,01101011,{MOD}{mmxreg}{R_M}:packssdw {MOD}{R_M},{mmxreg} +00001111,01100011,{MOD}{mmxreg}{R_M}:packsswb {MOD}{R_M},{mmxreg} +00001111,01100111,{MOD}{mmxreg}{R_M}:packuswb {MOD}{R_M},{mmxreg} +00001111,111111{gg},{MOD}{mmxreg}{R_M}:padd{gg} {MOD}{R_M},{mmxreg} +00001111,111111{0g},{MOD}{mmxreg}{R_M}:padds{0g} {MOD}{R_M},{mmxreg} +00001111,110111{0g},{MOD}{mmxreg}{R_M}:paddus{0g} {MOD}{R_M},{mmxreg} +00001111,11011011,{MOD}{mmxreg}{R_M}:pand {MOD}{R_M},{mmxreg} +00001111,11011111,{MOD}{mmxreg}{R_M}:pandn {MOD}{R_M},{mmxreg} +00001111,011101{gg},{MOD}{mmxreg}{R_M}:pcmpeq{gg} {MOD}{R_M},{mmxreg} +00001111,011001{gg},{MOD}{mmxreg}{R_M}:pcmpgt{gg} {MOD}{R_M},{mmxreg} +00001111,11110101,{MOD}{mmxreg}{R_M}:pmaddwd {MOD}{R_M},{mmxreg} +00001111,11100101,{MOD}{mmxreg}{R_M}:pmulhw {MOD}{R_M},{mmxreg} +00001111,11010101,{MOD}{mmxreg}{R_M}:pmullw {MOD}{R_M},{mmxreg} +00001111,11101011,{MOD}{mmxreg}{R_M}:por {MOD}{R_M},{mmxreg} +00001111,111100{GG},{MOD}{mmxreg}{R_M}:psll{GG} {MOD}{R_M},{mmxreg} +00001111,011100{GG},11110{mmxreg},{imm8}:psll{GG} {imm8},{mmxreg} +00001111,111000{gG},{MOD}{mmxreg}{R_M}:psra{gG} {MOD}{R_M},{mmxreg} +00001111,011100{gG},11100{mmxreg},{imm8}:psra{gG} {imm8},{mmxreg} +00001111,110100{GG},{MOD}{mmxreg}{R_M}:psrl{GG} {MOD}{R_M},{mmxreg} +00001111,011100{GG},11010{mmxreg},{imm8}:psrl{GG} {imm8},{mmxreg} +00001111,111110{gg},{MOD}{mmxreg}{R_M}:psub{gg} {MOD}{R_M},{mmxreg} +00001111,111010{0g},{MOD}{mmxreg}{R_M}:psubs{0g} {MOD}{R_M},{mmxreg} +00001111,110110{0g},{MOD}{mmxreg}{R_M}:psubus{0g} {MOD}{R_M},{mmxreg} +00001111,011010{gg},{MOD}{mmxreg}{R_M}:punpckh{gg} {MOD}{R_M},{mmxreg} +00001111,011000{gg},{MOD}{mmxreg}{R_M}:punpckl{gg} {MOD}{R_M},{mmxreg} +00001111,11101111,{MOD}{mmxreg}{R_M}:pxor {MOD}{R_M},{mmxreg} +00001111,01011000,{Mod}{xmmreg}{R_m}:addps {Mod}{R_m},{xmmreg} +11110011,00001111,01011000,{Mod}{xmmreg}{R_m}:addss {Mod}{R_m},{xmmreg} +00001111,01010101,{Mod}{xmmreg}{R_m}:andnps {Mod}{R_m},{xmmreg} +00001111,01010100,{Mod}{xmmreg}{R_m}:andps {Mod}{R_m},{xmmreg} +00001111,11000010,{Mod}{xmmreg}{R_m},00000000:cmpeqps {Mod}{R_m},{xmmreg} +00001111,11000010,{Mod}{xmmreg}{R_m},00000001:cmpltps {Mod}{R_m},{xmmreg} +00001111,11000010,{Mod}{xmmreg}{R_m},00000010:cmpleps {Mod}{R_m},{xmmreg} +00001111,11000010,{Mod}{xmmreg}{R_m},00000011:cmpunordps {Mod}{R_m},{xmmreg} +00001111,11000010,{Mod}{xmmreg}{R_m},00000100:cmpneqps {Mod}{R_m},{xmmreg} +00001111,11000010,{Mod}{xmmreg}{R_m},00000101:cmpnltps {Mod}{R_m},{xmmreg} +00001111,11000010,{Mod}{xmmreg}{R_m},00000110:cmpnleps {Mod}{R_m},{xmmreg} +00001111,11000010,{Mod}{xmmreg}{R_m},00000111:cmpordps {Mod}{R_m},{xmmreg} +11110011,00001111,11000010,{Mod}{xmmreg}{R_m},00000000:cmpeqss {Mod}{R_m},{xmmreg} +11110011,00001111,11000010,{Mod}{xmmreg}{R_m},00000001:cmpltss {Mod}{R_m},{xmmreg} +11110011,00001111,11000010,{Mod}{xmmreg}{R_m},00000010:cmpless {Mod}{R_m},{xmmreg} +11110011,00001111,11000010,{Mod}{xmmreg}{R_m},00000011:cmpunordss {Mod}{R_m},{xmmreg} +11110011,00001111,11000010,{Mod}{xmmreg}{R_m},00000100:cmpneqss {Mod}{R_m},{xmmreg} +11110011,00001111,11000010,{Mod}{xmmreg}{R_m},00000101:cmpnltss {Mod}{R_m},{xmmreg} +11110011,00001111,11000010,{Mod}{xmmreg}{R_m},00000110:cmpnless {Mod}{R_m},{xmmreg} +11110011,00001111,11000010,{Mod}{xmmreg}{R_m},00000111:cmpordss {Mod}{R_m},{xmmreg} +00001111,00101010,{MOD}{xmmreg}{R_M}:cvtpi2ps {MOD}{R_M},{xmmreg} +11110011,00001111,00101010,{mod}{xmmreg}{r_m}:cvtsi2ss {mod}{r_m},{xmmreg} +00001111,01011110,{Mod}{xmmreg}{R_m}:divps {Mod}{R_m},{xmmreg} +11110011,00001111,01011110,{Mod}{xmmreg}{R_m}:divss {Mod}{R_m},{xmmreg} +00001111,10101110,{mod}001{r_m}:fxrstor {mod}{r_m} +00001111,10101110,{mod}000{r_m}:fxsave {mod}{r_m} +00001111,10101110,{mod}010{r_m}:ldmxcsr {mod}{r_m} +00001111,01011111,{Mod}{xmmreg}{R_m}:maxps {Mod}{R_m},{xmmreg} +11110011,00001111,01011111,{Mod}{xmmreg}{R_m}:maxss {Mod}{R_m},{xmmreg} +00001111,01011101,{Mod}{xmmreg}{R_m}:minps {Mod}{R_m},{xmmreg} +11110011,00001111,01011101,{Mod}{xmmreg}{R_m}:minss {Mod}{R_m},{xmmreg} +00001111,00101000,{Mod}{xmmreg}{R_m}:INVALID {Mod}{R_m},{xmmreg} +00001111,00101001,{Mod}{xmmreg}{R_m}:INVALID {xmmreg},{Mod}{R_m} +00001111,00010010,{Mod}{xmmreg}{R_m}:{R}INVALID {Mod}{R_m},{xmmreg} +00001111,00010011,{Mod}{xmmreg}{R_m}:INVALID {xmmreg},{Mod}{R_m} +00001111,00010100,{Mod}{xmmreg}{R_m}:INVALID {Mod}{R_m},{xmmreg} +00001111,00010101,{Mod}{xmmreg}{R_m}:INVALID {Mod}{R_m},{xmmreg} +00001111,00010110,{Mod}{xmmreg}{R_m}:{R}INVALID {Mod}{R_m},{xmmreg} +00001111,00010111,{Mod}{xmmreg}{R_m}:INVALID {xmmreg},{Mod}{R_m} +00001111,00101011,{mod}{xmmreg}{r_m}:INVALID {xmmreg},{mod}{r_m} +00001111,00101100,{Mod}{mmxreg2}{R_m}:{R}INVALID {Mod}{R_m},{mmxreg2} +00001111,00101101,{Mod}{mmxreg2}{R_m}:{R}INVALID {Mod}{R_m},{mmxreg2} +00001111,00101110,{Mod}{xmmreg}{R_m}:INVALID {Mod}{R_m},{xmmreg} +00001111,00101111,{Mod}{xmmreg}{R_m}:INVALID {Mod}{R_m},{xmmreg} +00001111,00110111:getsec +00001111,01010000,11{reg}{xmmreg}:INVALID {xmmreg},{reg} +00001111,01010001,{Mod}{xmmreg}{R_m}:{R}INVALID {Mod}{R_m},{xmmreg} +00001111,01010010,{Mod}{xmmreg}{R_m}:{R}INVALID {Mod}{R_m},{xmmreg} +00001111,01010011,{Mod}{xmmreg}{R_m}:{R}INVALID {Mod}{R_m},{xmmreg} +# ORDER: +dnl Many previous entries depend on this being last. +000{sreg2}111:pop {sreg2} +# ORDER END: diff --git a/libcpu/defs/i386.doc b/libcpu/defs/i386.doc new file mode 100644 index 000000000..732cd2385 --- /dev/null +++ b/libcpu/defs/i386.doc @@ -0,0 +1,74 @@ +{imm} only parameter: + - is {s} in opcode: {s} == 0, unsigned (8/)16/32 bit immediate + {s} == 1, signed 8 bit immediate + +{es:di}: segment register normally %es, can be overwritten + edi/di depending on apfx + +{ds:si}: segment register normally %ds, can be overwritten + esi/si depending on apfx + +{ax} al/ax/eax depending of dpfx and w + +{dx} (%edx) or (%dx) depending on apfx + + +{w} 0 = b, 1 = { no dpfx = l, dpfx = w } + +{W} no dpfx = , dpfx = w +{WW} no dpfx = l, dpfx = w + +{R} rep prefix possible +{RE} repe or repne prefix possible + +{ccc} CRx registers +{ddd} DRx registers + +{gg} 00 = b, 01 = w, 10 = d, 11 = +{0g} 00 = b, 01 = w, 10 = , 11 = +{GG} 00 = , 01 = w, 10 = d, 11 = q +{gG} 00 = , 01 = w, 10 = d, 11 = + +{modr/m} normal registers +{MODR/M} MMX registers +{ModR/m} XMM registers + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Special opcodes (prefixes): + + +01100111:{apfx} +01100110:{dpfx} + +00101110:{cs} +00111110:{ds} +00100110:{es} +01100100:{fs} +01100101:{gs} + + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +table format + +1bit RE flag +1bit R flag +16bit mnemonic +3bit suffix + +5bit fct +2bit string +6bit offset1 +5bit offset2 + +4bit fct +1bit string +6bit offset1 +4bit offset2 + +2bit fct +1bit string +3bit offset1 +1bit offset2 + +61bit diff --git a/libcpu/defs/x86_64 b/libcpu/defs/x86_64 new file mode 100644 index 000000000..090c47518 --- /dev/null +++ b/libcpu/defs/x86_64 @@ -0,0 +1,341 @@ +%mask {s} 1 +%mask {w} 1 +%mask {D} 1 +%mask {imm8} 8 +%mask {imm16} 16 +%mask {reg} 3 +%mask {tttn} 4 +%mask {gg} 2 +%mask {mod} 2 +%mask {MOD} 2 +%mask {r_m} 3 +%mask {disp8} 8 +# imm really is 8/16/32 bit depending on the situation. +%mask {imm} 8 +%mask {abs} 32 +%mask {sel} 16 +%mask {imm32} 32 +%mask {dispA} 32 +%mask {ccc} 3 +%mask {ddd} 3 +%mask {sreg3} 3 +%mask {sreg2} 2 +%mask {mmxreg} 3 +%mask {R_M} 3 +%mask {0g} 2 +%mask {GG} 2 +%mask {gG} 2 +%mask {Mod} 2 +%mask {xmmreg} 3 +%mask {R_m} 3 +%mask {xmmreg1} 3 +%mask {xmmreg2} 3 +%mask {mmreg} 3 +%prefix {R} +%prefix {RE} +%suffix {W} +%suffix {WW} +%synonym {xmmreg1} {xmmreg} +%synonym {xmmreg2} {xmmreg} + +%% +0001010{w},{imm}:adc {imm}{w},{ax}{w} +1000000{w},{mod}010{r_m},{imm}:adc{w} {imm},{mod}{r_m} +1000001{w},{mod}010{r_m},{imm8}:adc{w} {imm8},{mod}{r_m} +0001000{w},{mod}{reg}{r_m}:adc{w} {reg},{mod}{r_m} +0001001{w},{mod}{reg}{r_m}:adc{w} {mod}{r_m},{reg} +0000010{w},{imm}:add {imm}{w},{ax}{w} +1000000{w},{mod}000{r_m},{imm}:add{w} {imm},{mod}{r_m} +1000001{w},{mod}000{r_m},{imm8}:add{w} {imm8},{mod}{r_m} +0000000{w},{mod}{reg}{r_m}:add {reg}{w},{mod}{r_m} +0000001{w},{mod}{reg}{r_m}:add {mod}{r_m},{reg}{w} +01100110,00001111,01011000,{Mod}{xmmreg}{R_m}:addpd {Mod}{R_m},{xmmreg} +00001111,01011000,{Mod}{xmmreg}{R_m}:addps {Mod}{R_m},{xmmreg} +11110010,00001111,01011000,{Mod}{xmmreg}{R_m}:addsd {Mod}{R_m},{xmmreg} +11110011,00001111,01011000,{Mod}{xmmreg}{R_m}:addss {Mod}{R_m},{xmmreg} +01100110,00001111,11010000,{Mod}{xmmreg}{R_m}:addsubpd {Mod}{R_m},{xmmreg} +11110010,00001111,11010000,{Mod}{xmmreg}{R_m}:addsubps {Mod}{R_m},{xmmreg} +# +# +# +0010000{w},{mod}{reg}{r_m}:and{w} {reg},{mod}{r_m} +0010001{w},{mod}{reg}{r_m}:and{w} {mod}{r_m},{reg} +0010010{w},{imm}:and {imm}{w},{ax}{w} +100000{s}{w},{mod}100{r_m},{imm}:and{w} {imm}{s},{mod}{r_m} +01100011,{mod}{reg}{r_m}:arpl {reg},{mod}{r_m} +01100010,{mod}{reg}{r_m}:bound {reg},{mod}{r_m} +00001111,10111100,{mod}{reg}{r_m}:bsf {reg},{mod}{r_m} +00001111,10111101,{mod}{reg}{r_m}:bsr {reg},{mod}{r_m} +00001111,11001{reg}:bswap {reg} +00001111,10111010,{mod}100{r_m},{imm8}:bt {imm8},{mod}{r_m} +00001111,10100011,{mod}{reg}{r_m}:bt {reg},{mod}{r_m} +00001111,10111010,{mod}111{r_m},{imm8}:btc {imm8},{mod}{r_m} +00001111,10111011,{mod}{reg}{r_m}:btc {reg},{mod}{r_m} +00001111,10111010,{mod}110{r_m},{imm8}:btr {imm8},{mod}{r_m} +00001111,10110011,{mod}{reg}{r_m}:btr {reg},{mod}{r_m} +00001111,10111010,{mod}101{r_m},{imm8}:bts {imm8},{mod}{r_m} +00001111,10101011,{mod}{reg}{r_m}:bts {reg},{mod}{r_m} +11101000,{abs}:call {abs} +11111111,{mod}010{r_m}:call *{mod}{r_m} +10011010,{abs},{sel}:lcall {sel},{abs} +11111111,{mod}011{r_m}:lcall {mod}{r_m} +10011000:cbt{WW} +#SPECIAL 10011001:[{dpfx}?cltd:cwtd] +11111000:clc +11111100:cld +11111010:cli +00001111,00000110:clts +11110101:cmc +00001111,0100{tttn},{mod}{reg}{r_m}:cmov{tttn} {mod}{r_m},{reg} +0011100{w},{mod}{reg}{r_m}:cmp{w} {reg},{mod}{r_m} +0011101{w},{mod}{reg}{r_m}:cmp{w} {mod}{r_m},{reg} +0011110{w},{imm}:cmp {imm}{w},{ax}{w} +100000{s}{w},{mod}111{r_m},{imm}:cmp{w} {imm}{s},{mod}{r_m} +1010011{w}:{RE}cmps{w} +00001111,1011000{w},{mod}{reg}{r_m}:cmpxchg{w} {reg},{mod}{r_m} +00001111,11000111,{mod}{reg}{r_m}:cmpxchg8b {reg},{mod}{r_m} +00001111,10100010:cpuid +00100111:daa +00101111:das +1111111{w},{mod}001{r_m}:dec{w} {mod}{r_m} +01001{reg}:dec {reg} +1111011{w},{mod}110{r_m}:div{w} {mod}{r_m} +11001000,{imm16},{imm8}:enter {imm16},{imm8} +11110100:hlt +1111011{w},{mod}111{r_m}:idiv{w} {mod}{r_m} +1111011{w},{mod}101{r_m}:imul{w} {mod}{r_m} +00001111,10101111,{mod}{reg}{r_m}:imul {reg},{mod}{r_m} +011010{s}1,{mod}{reg}{r_m},{imm}:imul {imm}{s},{mod}{r_m},{reg} +1110010{w},{imm8}:in {imm8},{ax}{w} +1110110{w}:in {dx},{ax}{w} +1111111{w},{mod}000{r_m}:inc{w} {mod}{r_m} +01000{reg}:inc {reg} +0110110{w}:{R}ins{w} {dx},{es_di} +11001101,{imm8}:int {imm8} +11001100:int 3 +11001110:into +00001111,00001000:invd +00001111,00000001,{mod}111{r_m}:invlpg {mod}{r_m} +11001111:iret{W} +0111{tttn},{disp8}:j{tttn} {disp8} +00001111,1000{tttn},{dispA}:j{tttn} {dispA} +#SPECIAL 11100011,{disp8}:[{dpfx}?jcxz:jecxz] {disp8} +11101011,{disp8}:jmp {disp8} +11101001,{dispA}:jmp {dispA} +11111111,{mod}100{r_m}:jmp *{mod}{r_m} +11101010,{abs},{sel}:ljmp {sel},{abs} +11111111,{mod}101{r_m}:ljmp {mod}{r_m} +10011111:lahf +00001111,00000010,{mod}{reg}{r_m}:lar {mod}{r_m},{reg} +11000101,{mod}{reg}{r_m}:lds {mod}{r_m},{reg} +10001101,{mod}{reg}{r_m}:lea {mod}{r_m},{reg} +11001001:leave +11000100,{mod}{reg}{r_m}:les {mod}{r_m},{reg} +00001111,10110100,{mod}{reg}{r_m}:lfs {mod}{r_m},{reg} +00001111,00000001,{mod}010{r_m}:lgdt{WW} {mod}{r_m} +00001111,10110101,{mod}{reg}{r_m}:lgs {mod}{r_m},{reg} +00001111,00000001,{mod}011{r_m}:lidt{WW} {mod}{r_m} +00001111,00000000,{mod}010{r_m}:lldt{WW} {mod}{r_m} +00001111,00000001,{mod}110{r_m}:lmsw {mod}{r_m} +11110000:lock +1010110{w}:{R}lods {ds_si},{ax}{w} +11100010,{disp8}:loop {disp8} +11100001,{disp8}:loope {disp8} +11100000,{disp8}:loopne {disp8} +00001111,00000011,{mod}{reg}{r_m}:lsl {mod}{r_m},{reg} +00001111,10110010,{mod}{reg}{r_m}:lss {mod}{r_m},{reg} +00001111,00000000,{mod}011{r_m}:ltr {mod}{r_m} +1000100{w},{mod}{reg}{r_m}:mov{w} {reg},{mod}{r_m} +1000101{w},{mod}{reg}{r_m}:mov{w} {mod}{r_m},{reg} +1100011{w},{mod}000{r_m},{imm}:mov{w} {imm},{mod}{r_m} +1011{w}{reg},{imm}:mov{w} {imm},{reg} +1010000{w},{abs}:mov {ax}{w},{abs} +1010001{w},{abs}:mov {abs},{ax}{w} +00001111,00100000,11{ccc}{reg}:mov {reg},{ccc} +00001111,00100010,11{ccc}{reg}:mov {ccc},{reg} +00001111,00100001,11{ddd}{reg}:mov {reg},{ddd} +00001111,00100011,11{ddd}{reg}:mov {ddd},{reg} +10001100,{mod}{sreg3}{r_m}:mov {sreg3},{mod}{r_m} +10001110,{mod}{sreg3}{r_m}:mov {mod}{r_m},{sreg3} +1010010{w}:{R}movs{w} {ds_si},{es_di} +00001111,1011111{w},{mod}{reg}{r_m}:movsx{w} {mod}{r_m},{reg} +00001111,1011011{w},{mod}{reg}{r_m}:movzx{w} {mod}{r_m},{reg} +1111011{w},{mod}100{r_m}:mul{w} {mod}{r_m} +1111011{w},{mod}011{r_m}:neg{w} {mod}{r_m} +10010000:nop +11110011,10010000:pause +1111011{w},{mod}010{r_m}:not{w} {mod}{r_m} +0000100{w},{mod}{reg}{r_m}:or{w} {reg},{mod}{r_m} +0000101{w},{mod}{reg}{r_m}:or{w} {mod}{r_m},{reg} +100000{s}{w},{mod}001{r_m},{imm}:or{w} {imm}{s},{mod}{r_m} +0000110{w},{imm}:mov {imm}{w},{ax}{w} +1110011{w},{imm8}:out {ax}{w},{imm8} +1110111{w}:out {ax}{w},{dx} +0110111{w}:{R}outs{w} {ds_si},{dx} +10001111,{mod}000{r_m}:pop {mod}{r_m} +01011{reg}:pop {reg} +000{sreg2}111:pop {sreg2} +00001111,10{sreg3}001:pop {sreg3} +01100001:popa{W} +10011101:popf{W} +11111111,{mod}110{r_m}:push {mod}{r_m} +01010{reg}:push {reg} +011010{s}0,{imm}:push {imm}{s} +000{sreg2}110:push {sreg2} +00001111,10{sreg3}000:push {sreg3} +01100000:pusha{W} +10011100:pushf{W} +1101000{w},{mod}010{r_m}:rcl{w} {mod}{r_m} +1101001{w},{mod}010{r_m}:rcl{w} %cl,{mod}{r_m} +1100000{w},{mod}010{r_m},{imm8}:rcl{w} {imm8},{mod}{r_m} +1101000{w},{mod}011{r_m}:rcr{w} {mod}{r_m} +1101001{w},{mod}011{r_m}:rcr{w} %cl,{mod}{r_m} +1100000{w},{mod}011{r_m},{imm8}:rcr{w} {imm8},{mod}{r_m} +00001111,00110010:rdmsr +00001111,00110011:rdpmc +00001111,00110001:rdtsc +11000011:ret +11000010,{imm16}:ret {imm16} +11001011:lret +11001010,{imm16}:lret {imm16} +1101000{w},{mod}000{r_m}:rol{w} {mod}{r_m} +1101001{w},{mod}000{r_m}:rol{w} %cl,{mod}{r_m} +1100000{w},{mod}000{r_m},{imm8}:rol{w} {imm8},{mod}{r_m} +1101000{w},{mod}001{r_m}:ror{w} {mod}{r_m} +1101001{w},{mod}001{r_m}:ror{w} %cl,{mod}{r_m} +1100000{w},{mod}001{r_m},{imm8}:ror{w} {imm8},{mod}{r_m} +00001111,10101010:rsm +10011110:sahf +1101000{w},{mod}111{r_m}:sar{w} {mod}{r_m} +1101001{w},{mod}111{r_m}:sar{w} %cl,{mod}{r_m} +1100000{w},{mod}111{r_m},{imm8}:sar{w} {imm8},{mod}{r_m} +0001100{w},{mod}{reg}{r_m}:sbb{w} {reg},{mod}{r_m} +0001101{w},{mod}{reg}{r_m}:sbb{w} {mod}{r_m},{reg} +0001110{w},{imm}:sbb {imm}{w},{ax}{w} +100000{s}{w},{mod}011{r_m},{imm}:sbb{w} {imm}{s},{mod}{r_m} +1010111{w}:{RE}scas {es_di},{ax}{w} +00001111,1001{tttn},{mod}000{r_m}:set{tttn} {mod}{r_m} +00001111,00000001,{mod}000{r_m}:sgdt {mod}{r_m} +1101000{w},{mod}100{r_m}:shl{w} {mod}{r_m} +1101001{w},{mod}100{r_m}:shl{w} %cl,{mod}{r_m} +1100000{w},{mod}100{r_m},{imm8}:shl{w} {imm8},{mod}{r_m} +1101000{w},{mod}101{r_m}:shr{w} {mod}{r_m} +00001111,10100100,{mod}{reg}{r_m},{imm8}:shld {imm8},{reg},{mod}{r_m} +00001111,10100101,{mod}{reg}{r_m}:shld %cl,{reg},{mod}{r_m} +1101001{w},{mod}101{r_m}:shr{w} %cl,{mod}{r_m} +1100000{w},{mod}101{r_m},{imm8}:shr{w} {imm8},{mod}{r_m} +00001111,10101100,{mod}{reg}{r_m},{imm8}:shrd {imm8},{reg},{mod}{r_m} +00001111,10101101,{mod}{reg}{r_m}:shrd %cl,{reg},{mod}{r_m} +00001111,00000001,{mod}001{r_m}:sidt {mod}{r_m} +00001111,00000000,{mod}000{r_m}:sldt {mod}{r_m} +00001111,00000001,{mod}100{r_m}:smsw {mod}{r_m} +11111001:stc +11111101:std +11111011:sti +1010101{w}:{R}stos {ax}{w},{es_di} +00001111,00000000,{mod}001{r_m}:str {mod}{r_m} +0010100{w},{mod}{reg}{r_m}:sub{w} {reg},{mod}{r_m} +0010101{w},{mod}{reg}{r_m}:sub{w} {mod}{r_m},{reg} +0010110{w},{imm}:sub {imm}{w},{ax}{w} +100000{s}{w},{mod}101{r_m},{imm}:sub{w} {imm}{s},{mod}{r_m} +1000010{w},{mod}{reg}{r_m}:test{w} {reg},{mod}{r_m}{w} +1000011{w},{mod}{reg}{r_m}:test{w} {mod}{r_m}{w},{reg} +0010100{w},{imm}:test {imm}{w},{ax}{w} +1111011{w},{mod}000{r_m},{imm}:test{w} {imm},{mod}{r_m} +00001111,00001011:ud2a +00001111,00000000,{mod}100{r_m}:verr {mod}{r_m} +00001111,00000000,{mod}101{r_m}:verw {mod}{r_m} +10011011:wait +00001111,00001001:wbinvd +00001111,00110000:wrmsr +00001111,1100000{w},{mod}{reg}{r_m}:xadd{w} {reg},{mod}{r_m} +1000011{w},{mod}{reg}{r_m}:xchg{w} {reg},{mod}{r_m} +11010111:xlat +0011000{w},{mod}{reg}{r_m}:xor{w} {reg},{mod}{r_m} +0011001{w},{mod}{reg}{r_m}:xor{w} {mod}{r_m},{reg} +0011010{w},{imm}:xor {imm}{w},{ax}{w} +100000{s}{w},{mod}110{r_m},{imm}:xor{w} {imm}{s},{mod}{r_m} +00001111,01110111:emms +00001111,01101110,{mod}{mmxreg}{r_m}:movd {mod}{r_m},{mmxreg} +00001111,01111110,{mod}{mmxreg}{r_m}:movd {mmxreg},{mod}{r_m} +00001111,01101111,{MOD}{mmxreg}{R_M}:movq {MOD}{R_M},{mmxreg} +00001111,01111111,{MOD}{mmxreg}{R_M}:movq {mmxreg},{MOD}{R_M} +00001111,01101011,{MOD}{mmxreg}{R_M}:packssdw {MOD}{R_M},{mmxreg} +00001111,01100011,{MOD}{mmxreg}{R_M}:packsswb {MOD}{R_M},{mmxreg} +00001111,01100111,{MOD}{mmxreg}{R_M}:packuswb {MOD}{R_M},{mmxreg} +00001111,111111{gg},{MOD}{mmxreg}{R_M}:padd{gg} {MOD}{R_M},{mmxreg} +00001111,111111{0g},{MOD}{mmxreg}{R_M}:padds{0g} {MOD}{R_M},{mmxreg} +00001111,110111{0g},{MOD}{mmxreg}{R_M}:paddus{0g} {MOD}{R_M},{mmxreg} +00001111,11011011,{MOD}{mmxreg}{R_M}:pand {MOD}{R_M},{mmxreg} +00001111,11011111,{MOD}{mmxreg}{R_M}:pandn {MOD}{R_M},{mmxreg} +00001111,011101{gg},{MOD}{mmxreg}{R_M}:pcmpeq{gg} {MOD}{R_M},{mmxreg} +00001111,011001{gg},{MOD}{mmxreg}{R_M}:pcmpgt{gg} {MOD}{R_M},{mmxreg} +00001111,11110101,{MOD}{mmxreg}{R_M}:pmaddwd {MOD}{R_M},{mmxreg} +00001111,11100101,{MOD}{mmxreg}{R_M}:pmulhw {MOD}{R_M},{mmxreg} +00001111,11010101,{MOD}{mmxreg}{R_M}:pmullw {MOD}{R_M},{mmxreg} +00001111,11101011,{MOD}{mmxreg}{R_M}:por {MOD}{R_M},{mmxreg} +00001111,111100{GG},{MOD}{mmxreg}{R_M}:psll{GG} {MOD}{R_M},{mmxreg} +00001111,011100{GG},11110{mmxreg},{imm8}:psll{GG} {imm8},{mmxreg} +00001111,111000{gG},{MOD}{mmxreg}{R_M}:psra{gG} {MOD}{R_M},{mmxreg} +00001111,011100{gG},11100{mmxreg},{imm8}:psra{gG} {imm8},{mmxreg} +00001111,110100{GG},{MOD}{mmxreg}{R_M}:psrl{GG} {MOD}{R_M},{mmxreg} +00001111,011100{GG},11010{mmxreg},{imm8}:psrl{GG} {imm8},{mmxreg} +00001111,111110{gg},{MOD}{mmxreg}{R_M}:psub{gg} {MOD}{R_M},{mmxreg} +00001111,111010{0g},{MOD}{mmxreg}{R_M}:psubs{0g} {MOD}{R_M},{mmxreg} +00001111,110110{0g},{MOD}{mmxreg}{R_M}:psubus{0g} {MOD}{R_M},{mmxreg} +00001111,011010{gg},{MOD}{mmxreg}{R_M}:punpckh{gg} {MOD}{R_M},{mmxreg} +00001111,011000{gg},{MOD}{mmxreg}{R_M}:punpckl{gg} {MOD}{R_M},{mmxreg} +00001111,11101111,{MOD}{mmxreg}{R_M}:pxor {MOD}{R_M},{mmxreg} +00001111,01011000,{Mod}{xmmreg}{R_m}:addps {Mod}{R_m},{xmmreg} +11110011,00001111,01011000,{Mod}{xmmreg}{R_m}:addss {Mod}{R_m},{xmmreg} +00001111,01010101,{Mod}{xmmreg}{R_m}:andnps {Mod}{R_m},{xmmreg} +00001111,01010100,{Mod}{xmmreg}{R_m}:andps {Mod}{R_m},{xmmreg} +00001111,11000010,{Mod}{xmmreg}{R_m},00000000:cmpeqps {Mod}{R_m},{xmmreg} +00001111,11000010,{Mod}{xmmreg}{R_m},00000001:cmpltps {Mod}{R_m},{xmmreg} +00001111,11000010,{Mod}{xmmreg}{R_m},00000010:cmpleps {Mod}{R_m},{xmmreg} +00001111,11000010,{Mod}{xmmreg}{R_m},00000011:cmpunordps {Mod}{R_m},{xmmreg} +00001111,11000010,{Mod}{xmmreg}{R_m},00000100:cmpneqps {Mod}{R_m},{xmmreg} +00001111,11000010,{Mod}{xmmreg}{R_m},00000101:cmpnltps {Mod}{R_m},{xmmreg} +00001111,11000010,{Mod}{xmmreg}{R_m},00000110:cmpnleps {Mod}{R_m},{xmmreg} +00001111,11000010,{Mod}{xmmreg}{R_m},00000111:cmpordps {Mod}{R_m},{xmmreg} +11110011,00001111,11000010,{Mod}{xmmreg}{R_m},00000000:cmpeqss {Mod}{R_m},{xmmreg} +11110011,00001111,11000010,{Mod}{xmmreg}{R_m},00000001:cmpltss {Mod}{R_m},{xmmreg} +11110011,00001111,11000010,{Mod}{xmmreg}{R_m},00000010:cmpless {Mod}{R_m},{xmmreg} +11110011,00001111,11000010,{Mod}{xmmreg}{R_m},00000011:cmpunordss {Mod}{R_m},{xmmreg} +11110011,00001111,11000010,{Mod}{xmmreg}{R_m},00000100:cmpneqss {Mod}{R_m},{xmmreg} +11110011,00001111,11000010,{Mod}{xmmreg}{R_m},00000101:cmpnltss {Mod}{R_m},{xmmreg} +11110011,00001111,11000010,{Mod}{xmmreg}{R_m},00000110:cmpnless {Mod}{R_m},{xmmreg} +11110011,00001111,11000010,{Mod}{xmmreg}{R_m},00000111:cmpordss {Mod}{R_m},{xmmreg} +00001111,00101111,{Mod}{xmmreg}{R_m}:comiss {Mod}{R_m},{xmmreg} +00001111,00101010,{MOD}{xmmreg}{R_M}:cvtpi2ps {MOD}{R_M},{xmmreg} +00001111,00101101,{MOD}{mmreg}{R_M}:cvtps2pi {MOD}{R_M},{mmreg} +11110011,00001111,00101010,{mod}{xmmreg}{r_m}:cvtsi2ss {mod}{r_m},{xmmreg} +11110011,00001111,00101101,{Mod}{reg}{R_m}:cvtss2si {Mod}{R_m},{reg} +00001111,00101100,{Mod}{mmreg}{R_m}:cvttps2pi {Mod}{R_m},{mmreg} +11110011,00001111,00101100,{Mod}{reg}{R_m}:cvttss2si {Mod}{R_m},{reg} +00001111,01011110,{Mod}{xmmreg}{R_m}:divps {Mod}{R_m},{xmmreg} +11110011,00001111,01011110,{Mod}{xmmreg}{R_m}:divss {Mod}{R_m},{xmmreg} +00001111,10101110,{mod}001{r_m}:fxrstor {mod}{r_m} +00001111,10101110,{mod}000{r_m}:fxsave {mod}{r_m} +00001111,10101110,{mod}010{r_m}:ldmxcsr {mod}{r_m} +00001111,01011111,{Mod}{xmmreg}{R_m}:maxps {Mod}{R_m},{xmmreg} +11110011,00001111,01011111,{Mod}{xmmreg}{R_m}:maxss {Mod}{R_m},{xmmreg} +00001111,01011101,{Mod}{xmmreg}{R_m}:minps {Mod}{R_m},{xmmreg} +11110011,00001111,01011101,{Mod}{xmmreg}{R_m}:minss {Mod}{R_m},{xmmreg} +00001111,00101000,{Mod}{xmmreg}{R_m}:movaps {Mod}{R_m},{xmmreg} +00001111,00101001,{Mod}{xmmreg}{R_m}:movaps {xmmreg},{Mod}{R_m} +# ORDER: +00001111,00010010,11{xmmreg1}{xmmreg2}:movhlps {xmmreg1},{xmmreg2} +00001111,00010011,11{xmmreg1}{xmmreg2}:movhlps {xmmreg2},{xmmreg1} +00001111,00010010,{Mod}{xmmreg}{R_m}:movlps {Mod}{R_m},{xmmreg} +00001111,00010011,{Mod}{xmmreg}{R_m}:movlps {xmmreg},{Mod}{R_m} +# ORDER END: +# ORDER: +00001111,00010110,11{xmmreg1}{xmmreg2}:movlhps {xmmreg1},{xmmreg2} +00001111,00010111,11{xmmreg1}{xmmreg2}:movlhps {xmmreg2},{xmmreg1} +00001111,00010110,{Mod}{xmmreg}{R_m}:movhps {Mod}{R_m},{xmmreg} +00001111,00010111,{Mod}{xmmreg}{R_m}:movhps {xmmreg},{Mod}{R_m} +# ORDER END: +# BOGUS +00000000,11{reg}111:fadd {reg} +00001111,00000001,11000001:vmcall diff --git a/libcpu/i386_data.h b/libcpu/i386_data.h new file mode 100644 index 000000000..38e99354e --- /dev/null +++ b/libcpu/i386_data.h @@ -0,0 +1,1406 @@ +#include +#include +#include +#include +#include + +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) (GElf_Addr, int *, const char *, size_t, size_t, size_t, + char *, size_t *, size_t, const uint8_t *data, + const uint8_t **param_start, const uint8_t *end, + DisasmGetSymCB_t, void *); + + +static int +data_prefix (int *prefixes, char *bufp, size_t *bufcntp, size_t bufsize) +{ + char ch = '\0'; + if (*prefixes & has_cs) + { + ch = 'c'; + *prefixes &= ~has_cs; + } + else if (*prefixes & has_ds) + { + ch = 'd'; + *prefixes &= ~has_ds; + } + else if (*prefixes & has_es) + { + ch = 'e'; + *prefixes &= has_es; + } + else if (*prefixes & has_fs) + { + ch = 'f'; + *prefixes &= ~has_fs; + } + else if (*prefixes & has_gs) + { + ch = 'g'; + *prefixes &= ~has_gs; + } + else if (*prefixes & has_ss) + { + ch = 's'; + *prefixes &= ~has_ss; + } + else + return 0; + + if (*bufcntp + 4 > bufsize) + return *bufcntp + 4 - bufsize; + + bufp[(*bufcntp)++] = '%'; + bufp[(*bufcntp)++] = ch; + bufp[(*bufcntp)++] = 's'; + bufp[(*bufcntp)++] = ':'; + + return 0; +} + +static const char regs[8][4] = + { + "eax", "ecx", "edx", "ebx", "esp", "ebp", "esi", "edi" + }; + +static int +general_mod$r_m (GElf_Addr addr __attribute__ ((unused)), + int *prefixes __attribute__ ((unused)), + const char *op1str __attribute__ ((unused)), + size_t opoff1 __attribute__ ((unused)), + size_t opoff2 __attribute__ ((unused)), + size_t opoff3 __attribute__ ((unused)), + char *bufp __attribute__ ((unused)), + size_t *bufcntp __attribute__ ((unused)), + size_t bufsize __attribute__ ((unused)), + const uint8_t *data __attribute__ ((unused)), + const uint8_t **param_start __attribute__ ((unused)), + const uint8_t *end __attribute__ ((unused)), + DisasmGetSymCB_t symcb __attribute__ ((unused)), + void *symcbarg __attribute__ ((unused))) +{ + int r = data_prefix (prefixes, bufp, bufcntp, bufsize); + if (r != 0) + return r; + + uint_fast8_t modrm = data[opoff1 / 8]; + if ((*prefixes & has_addr16) == 0) + { + 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[opoff1 / 8 + 1]); + else if ((modrm & 0xc0) == 0x40) + /* 8 bit displacement. */ + disp = *(const int8_t *) &data[opoff1 / 8 + 1]; + else if ((modrm & 0xc0) == 0) + nodisp = true; + + char tmpbuf[sizeof ("-0x12345678(%rrr)")]; + int n; + if (nodisp) + n = snprintf (tmpbuf, sizeof (tmpbuf), "(%%%s)", regs[modrm & 7]); + else if ((modrm & 0xc7) != 5) + n = snprintf (tmpbuf, sizeof (tmpbuf), "%s0x%" PRIx32 "(%%%s)", + disp < 0 ? "-" : "", disp < 0 ? -disp : disp, + regs[modrm & 7]); + else + n = snprintf (tmpbuf, sizeof (tmpbuf), "0x%" PRIx32, disp); + + 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[opoff1 / 8 + 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[opoff1 / 8 + 2]); + else if ((modrm & 0xc0) == 0x40) + /* 8 bit displacement. */ + disp = *(const int8_t *) &data[opoff1 / 8 + 2]; + else + nodisp = true; + + char tmpbuf[sizeof ("-0x12345678(%rrr,%rrr,N)")]; + char *cp = tmpbuf; + int n; + if ((modrm & 0xc0) != 0 || (sib & 0x3f) != 0x25) + { + 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 = mempcpy (cp, regs[sib & 7], 3); + } + + if ((sib & 0x38) != 0x20) + { + *cp++ = ','; + *cp++ = '%'; + cp = mempcpy (cp, regs[(sib >> 3) & 7], 3); + + *cp++ = ','; + *cp++ = '0' + (1 << (sib >> 6)); + } + + *cp++ = ')'; + } + else + { + assert (! nodisp); + 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 (GElf_Addr addr __attribute__ ((unused)), + int *prefixes __attribute__ ((unused)), + const char *op1str __attribute__ ((unused)), + size_t opoff1 __attribute__ ((unused)), + size_t opoff2 __attribute__ ((unused)), + size_t opoff3 __attribute__ ((unused)), + char *bufp __attribute__ ((unused)), + size_t *bufcntp __attribute__ ((unused)), + size_t bufsize __attribute__ ((unused)), + const uint8_t *data __attribute__ ((unused)), + const uint8_t **param_start __attribute__ ((unused)), + const uint8_t *end __attribute__ ((unused)), + DisasmGetSymCB_t symcb __attribute__ ((unused)), + void *symcbarg __attribute__ ((unused))) +{ + assert (opoff1 % 8 == 0); + uint_fast8_t modrm = data[opoff1 / 8]; + if ((modrm & 0xc0) == 0xc0) + { + uint_fast8_t byte = data[opoff2 / 8] & 7; + assert (opoff2 % 8 == 5); + size_t avail = bufsize - *bufcntp; + int needed; + if (*prefixes & (has_rep | has_repne)) + needed = snprintf (&bufp[*bufcntp], avail, "%%%s", regs[byte]); + else + needed = snprintf (&bufp[*bufcntp], avail, "%%mm%" PRIxFAST8, byte); + if ((size_t) needed > avail) + return needed - avail; + *bufcntp += needed; + return 0; + } + + return general_mod$r_m (addr, prefixes, op1str, opoff1, opoff2, opoff3, bufp, + bufcntp, bufsize, data, param_start, end, + symcb, symcbarg); +} + + +static int +FCT_Mod$R_m (GElf_Addr addr __attribute__ ((unused)), + int *prefixes __attribute__ ((unused)), + const char *op1str __attribute__ ((unused)), + size_t opoff1 __attribute__ ((unused)), + size_t opoff2 __attribute__ ((unused)), + size_t opoff3 __attribute__ ((unused)), + char *bufp __attribute__ ((unused)), + size_t *bufcntp __attribute__ ((unused)), + size_t bufsize __attribute__ ((unused)), + const uint8_t *data __attribute__ ((unused)), + const uint8_t **param_start __attribute__ ((unused)), + const uint8_t *end __attribute__ ((unused)), + DisasmGetSymCB_t symcb __attribute__ ((unused)), + void *symcbarg __attribute__ ((unused))) +{ + assert (opoff1 % 8 == 0); + uint_fast8_t modrm = data[opoff1 / 8]; + if ((modrm & 0xc0) == 0xc0) + { + uint_fast8_t byte = data[opoff2 / 8] & 7; + assert (opoff2 % 8 == 5); + size_t avail = bufsize - *bufcntp; + int needed = snprintf (&bufp[*bufcntp], avail, "%%xmm%" PRIxFAST8, byte); + if ((size_t) needed > avail) + return needed - avail; + *bufcntp += needed; + return 0; + } + + return general_mod$r_m (addr, prefixes, op1str, opoff1, opoff2, opoff3, bufp, + bufcntp, bufsize, data, param_start, end, + symcb, symcbarg); +} + +static int +generic_abs (int *prefixes __attribute__ ((unused)), + size_t opoff1 __attribute__ ((unused)), + char *bufp __attribute__ ((unused)), + size_t *bufcntp __attribute__ ((unused)), + size_t bufsize __attribute__ ((unused)), + const uint8_t *data __attribute__ ((unused)), + const uint8_t **param_start __attribute__ ((unused)), + const uint8_t *end __attribute__ ((unused)), + DisasmGetSymCB_t symcb __attribute__ ((unused)), + void *symcbarg __attribute__ ((unused)), + const char *absstring) +{ + int r = data_prefix (prefixes, bufp, bufcntp, bufsize); + if (r != 0) + return r; + + assert (opoff1 % 8 == 0); + assert (opoff1 / 8 == 1); + if (*param_start + 4 > end) + return -1; + *param_start += 4; + uint32_t absval = read_4ubyte_unaligned (&data[1]); + size_t avail = bufsize - *bufcntp; + int needed = snprintf (&bufp[*bufcntp], avail, "%s0x%" PRIx32, + absstring, absval); + if ((size_t) needed > avail) + return needed - avail; + *bufcntp += needed; + return 0; +} + +static int +FCT_absval (GElf_Addr addr __attribute__ ((unused)), + int *prefixes __attribute__ ((unused)), + const char *op1str __attribute__ ((unused)), + size_t opoff1 __attribute__ ((unused)), + size_t opoff2 __attribute__ ((unused)), + size_t opoff3 __attribute__ ((unused)), + char *bufp __attribute__ ((unused)), + size_t *bufcntp __attribute__ ((unused)), + size_t bufsize __attribute__ ((unused)), + const uint8_t *data __attribute__ ((unused)), + const uint8_t **param_start __attribute__ ((unused)), + const uint8_t *end __attribute__ ((unused)), + DisasmGetSymCB_t symcb __attribute__ ((unused)), + void *symcbarg __attribute__ ((unused))) +{ + return generic_abs (prefixes, opoff1, bufp, + bufcntp, bufsize, data, param_start, end, symcb, + symcbarg, "$"); +} + +static int +FCT_abs (GElf_Addr addr __attribute__ ((unused)), + int *prefixes __attribute__ ((unused)), + const char *op1str __attribute__ ((unused)), + size_t opoff1 __attribute__ ((unused)), + size_t opoff2 __attribute__ ((unused)), + size_t opoff3 __attribute__ ((unused)), + char *bufp __attribute__ ((unused)), + size_t *bufcntp __attribute__ ((unused)), + size_t bufsize __attribute__ ((unused)), + const uint8_t *data __attribute__ ((unused)), + const uint8_t **param_start __attribute__ ((unused)), + const uint8_t *end __attribute__ ((unused)), + DisasmGetSymCB_t symcb __attribute__ ((unused)), + void *symcbarg __attribute__ ((unused))) +{ + return generic_abs (prefixes, opoff1, bufp, + bufcntp, bufsize, data, param_start, end, symcb, + symcbarg, ""); +} + +static int +FCT_ax (GElf_Addr addr __attribute__ ((unused)), + int *prefixes __attribute__ ((unused)), + const char *op1str __attribute__ ((unused)), + size_t opoff1 __attribute__ ((unused)), + size_t opoff2 __attribute__ ((unused)), + size_t opoff3 __attribute__ ((unused)), + char *bufp __attribute__ ((unused)), + size_t *bufcntp __attribute__ ((unused)), + size_t bufsize __attribute__ ((unused)), + const uint8_t *data __attribute__ ((unused)), + const uint8_t **param_start __attribute__ ((unused)), + const uint8_t *end __attribute__ ((unused)), + DisasmGetSymCB_t symcb __attribute__ ((unused)), + void *symcbarg __attribute__ ((unused))) +{ + int is_16bit = (*prefixes & has_data16) != 0; + + if (*bufcntp + 4 - is_16bit > bufsize) + return *bufcntp + 4 - is_16bit - bufsize; + + bufp[(*bufcntp)++] = '%'; + if (! is_16bit) + bufp[(*bufcntp)++] = 'e'; + bufp[(*bufcntp)++] = 'a'; + bufp[(*bufcntp)++] = 'x'; + + return 0; +} + +static int +FCT_ax$w (GElf_Addr addr __attribute__ ((unused)), + int *prefixes __attribute__ ((unused)), + const char *op1str __attribute__ ((unused)), + size_t opoff1 __attribute__ ((unused)), + size_t opoff2 __attribute__ ((unused)), + size_t opoff3 __attribute__ ((unused)), + char *bufp __attribute__ ((unused)), + size_t *bufcntp __attribute__ ((unused)), + size_t bufsize __attribute__ ((unused)), + const uint8_t *data __attribute__ ((unused)), + const uint8_t **param_start __attribute__ ((unused)), + const uint8_t *end __attribute__ ((unused)), + DisasmGetSymCB_t symcb __attribute__ ((unused)), + void *symcbarg __attribute__ ((unused))) +{ + if ((data[opoff2 / 8] & (1 << (7 - (opoff2 & 7)))) != 0) + return FCT_ax (addr, prefixes, op1str, opoff1, opoff2, opoff3, bufp, + bufcntp, bufsize, data, param_start, end, symcb, symcbarg); + + if (*bufcntp + 3 > bufsize) + return *bufcntp + 3 - bufsize; + + bufp[(*bufcntp)++] = '%'; + bufp[(*bufcntp)++] = 'a'; + bufp[(*bufcntp)++] = 'l'; + + return 0; +} + +static int +FCT_ccc (GElf_Addr addr __attribute__ ((unused)), + int *prefixes __attribute__ ((unused)), + const char *op1str __attribute__ ((unused)), + size_t opoff1 __attribute__ ((unused)), + size_t opoff2 __attribute__ ((unused)), + size_t opoff3 __attribute__ ((unused)), + char *bufp __attribute__ ((unused)), + size_t *bufcntp __attribute__ ((unused)), + size_t bufsize __attribute__ ((unused)), + const uint8_t *data __attribute__ ((unused)), + const uint8_t **param_start __attribute__ ((unused)), + const uint8_t *end __attribute__ ((unused)), + DisasmGetSymCB_t symcb __attribute__ ((unused)), + void *symcbarg __attribute__ ((unused))) +{ + if (*prefixes & has_data16) + return -1; + + assert (opoff1 % 8 == 2); + size_t avail = bufsize - *bufcntp; + int needed = snprintf (&bufp[*bufcntp], avail, "%%cr%" PRIx32, + (uint32_t) (data[opoff1 / 8] >> 3) & 7); + if ((size_t) needed > avail) + return needed - avail; + *bufcntp += needed; + return 0; +} + +static int +FCT_ddd (GElf_Addr addr __attribute__ ((unused)), + int *prefixes __attribute__ ((unused)), + const char *op1str __attribute__ ((unused)), + size_t opoff1 __attribute__ ((unused)), + size_t opoff2 __attribute__ ((unused)), + size_t opoff3 __attribute__ ((unused)), + char *bufp __attribute__ ((unused)), + size_t *bufcntp __attribute__ ((unused)), + size_t bufsize __attribute__ ((unused)), + const uint8_t *data __attribute__ ((unused)), + const uint8_t **param_start __attribute__ ((unused)), + const uint8_t *end __attribute__ ((unused)), + DisasmGetSymCB_t symcb __attribute__ ((unused)), + void *symcbarg __attribute__ ((unused))) +{ + if (*prefixes & has_data16) + return -1; + + assert (opoff1 % 8 == 2); + size_t avail = bufsize - *bufcntp; + int needed = snprintf (&bufp[*bufcntp], avail, "%%db%" PRIx32, + (uint32_t) (data[opoff1 / 8] >> 3) & 7); + if ((size_t) needed > avail) + return needed - avail; + *bufcntp += needed; + return 0; +} + +static int +FCT_disp8 (GElf_Addr addr __attribute__ ((unused)), + int *prefixes __attribute__ ((unused)), + const char *op1str __attribute__ ((unused)), + size_t opoff1 __attribute__ ((unused)), + size_t opoff2 __attribute__ ((unused)), + size_t opoff3 __attribute__ ((unused)), + char *bufp __attribute__ ((unused)), + size_t *bufcntp __attribute__ ((unused)), + size_t bufsize __attribute__ ((unused)), + const uint8_t *data __attribute__ ((unused)), + const uint8_t **param_start __attribute__ ((unused)), + const uint8_t *end __attribute__ ((unused)), + DisasmGetSymCB_t symcb __attribute__ ((unused)), + void *symcbarg __attribute__ ((unused))) +{ + assert (opoff1 % 8 == 0); + int32_t offset = *(const int8_t *) (*param_start)++; + size_t avail = bufsize - *bufcntp; + int needed = snprintf (&bufp[*bufcntp], avail, "0x%" PRIx32, + (uint32_t) (addr + (*param_start - data) + offset)); + if ((size_t) needed > avail) + return needed - avail; + *bufcntp += needed; + return 0; +} + +static int +__attribute__ ((noinline)) +FCT_ds_xx (const char *reg, + int *prefixes __attribute__ ((unused)), + char *bufp __attribute__ ((unused)), + size_t *bufcntp __attribute__ ((unused)), + size_t bufsize __attribute__ ((unused))) +{ + int prefix = *prefixes & SEGMENT_PREFIXES; + + if (prefix == 0) + prefix = has_ds; + /* Make sure only one bit is set. */ + else if ((prefix - 1) & prefix) + return -1; + else + *prefixes ^= prefix; + + int r = data_prefix (&prefix, bufp, bufcntp, bufsize); + if (r != 0) + return r; + + size_t avail = bufsize - *bufcntp; + int needed = snprintf (&bufp[*bufcntp], avail, "(%%%s%s)", + *prefixes & idx_addr16 ? "" : "e", reg); + if ((size_t) needed > avail) + return (size_t) needed - avail; + *bufcntp += needed; + + return 0; +} + +static int +FCT_ds_bx (GElf_Addr addr __attribute__ ((unused)), + int *prefixes __attribute__ ((unused)), + const char *op1str __attribute__ ((unused)), + size_t opoff1 __attribute__ ((unused)), + size_t opoff2 __attribute__ ((unused)), + size_t opoff3 __attribute__ ((unused)), + char *bufp __attribute__ ((unused)), + size_t *bufcntp __attribute__ ((unused)), + size_t bufsize __attribute__ ((unused)), + const uint8_t *data __attribute__ ((unused)), + const uint8_t **param_start __attribute__ ((unused)), + const uint8_t *end __attribute__ ((unused)), + DisasmGetSymCB_t symcb __attribute__ ((unused)), + void *symcbarg __attribute__ ((unused))) +{ + return FCT_ds_xx ("bx", prefixes, bufp, bufcntp, bufsize); +} + +static int +FCT_ds_si (GElf_Addr addr __attribute__ ((unused)), + int *prefixes __attribute__ ((unused)), + const char *op1str __attribute__ ((unused)), + size_t opoff1 __attribute__ ((unused)), + size_t opoff2 __attribute__ ((unused)), + size_t opoff3 __attribute__ ((unused)), + char *bufp __attribute__ ((unused)), + size_t *bufcntp __attribute__ ((unused)), + size_t bufsize __attribute__ ((unused)), + const uint8_t *data __attribute__ ((unused)), + const uint8_t **param_start __attribute__ ((unused)), + const uint8_t *end __attribute__ ((unused)), + DisasmGetSymCB_t symcb __attribute__ ((unused)), + void *symcbarg __attribute__ ((unused))) +{ + return FCT_ds_xx ("si", prefixes, bufp, bufcntp, bufsize); +} + +static int +FCT_dx (GElf_Addr addr __attribute__ ((unused)), + int *prefixes __attribute__ ((unused)), + const char *op1str __attribute__ ((unused)), + size_t opoff1 __attribute__ ((unused)), + size_t opoff2 __attribute__ ((unused)), + size_t opoff3 __attribute__ ((unused)), + char *bufp __attribute__ ((unused)), + size_t *bufcntp __attribute__ ((unused)), + size_t bufsize __attribute__ ((unused)), + const uint8_t *data __attribute__ ((unused)), + const uint8_t **param_start __attribute__ ((unused)), + const uint8_t *end __attribute__ ((unused)), + DisasmGetSymCB_t symcb __attribute__ ((unused)), + void *symcbarg __attribute__ ((unused))) +{ + if (*bufcntp + 7 > bufsize) + return *bufcntp + 7 - bufsize; + + memcpy (&bufp[*bufcntp], "(%dx)", 5); + *bufcntp += 5; + + return 0; +} + +static int +FCT_es_di (GElf_Addr addr __attribute__ ((unused)), + int *prefixes __attribute__ ((unused)), + const char *op1str __attribute__ ((unused)), + size_t opoff1 __attribute__ ((unused)), + size_t opoff2 __attribute__ ((unused)), + size_t opoff3 __attribute__ ((unused)), + char *bufp __attribute__ ((unused)), + size_t *bufcntp __attribute__ ((unused)), + size_t bufsize __attribute__ ((unused)), + const uint8_t *data __attribute__ ((unused)), + const uint8_t **param_start __attribute__ ((unused)), + const uint8_t *end __attribute__ ((unused)), + DisasmGetSymCB_t symcb __attribute__ ((unused)), + void *symcbarg __attribute__ ((unused))) +{ + size_t avail = bufsize - *bufcntp; + int needed = snprintf (&bufp[*bufcntp], avail, "%%es:(%%%sdi)", + *prefixes & idx_addr16 ? "" : "e"); + if ((size_t) needed > avail) + return (size_t) needed - avail; + *bufcntp += needed; + + return 0; +} + +static int +FCT_imm (GElf_Addr addr __attribute__ ((unused)), + int *prefixes __attribute__ ((unused)), + const char *op1str __attribute__ ((unused)), + size_t opoff1 __attribute__ ((unused)), + size_t opoff2 __attribute__ ((unused)), + size_t opoff3 __attribute__ ((unused)), + char *bufp __attribute__ ((unused)), + size_t *bufcntp __attribute__ ((unused)), + size_t bufsize __attribute__ ((unused)), + const uint8_t *data __attribute__ ((unused)), + const uint8_t **param_start __attribute__ ((unused)), + const uint8_t *end __attribute__ ((unused)), + DisasmGetSymCB_t symcb __attribute__ ((unused)), + void *symcbarg __attribute__ ((unused))) +{ + size_t avail = bufsize - *bufcntp; + int needed; + if (*prefixes & has_data16) + { + if (*param_start + 2 > end) + return -1; + uint16_t word = read_2ubyte_unaligned_inc (*param_start); + needed = snprintf (&bufp[*bufcntp], avail, "$0x%" PRIx16, word); + } + else + { + if (*param_start + 4 > end) + return -1; + uint32_t word = read_4ubyte_unaligned_inc (*param_start); + needed = snprintf (&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 (GElf_Addr addr __attribute__ ((unused)), + int *prefixes __attribute__ ((unused)), + const char *op1str __attribute__ ((unused)), + size_t opoff1 __attribute__ ((unused)), + size_t opoff2 __attribute__ ((unused)), + size_t opoff3 __attribute__ ((unused)), + char *bufp __attribute__ ((unused)), + size_t *bufcntp __attribute__ ((unused)), + size_t bufsize __attribute__ ((unused)), + const uint8_t *data __attribute__ ((unused)), + const uint8_t **param_start __attribute__ ((unused)), + const uint8_t *end __attribute__ ((unused)), + DisasmGetSymCB_t symcb __attribute__ ((unused)), + void *symcbarg __attribute__ ((unused))) +{ + if ((data[opoff2 / 8] & (1 << (7 - (opoff2 & 7)))) != 0) + return FCT_imm (addr, prefixes, op1str, opoff1, opoff2, opoff3, bufp, + bufcntp, bufsize, data, param_start, end, symcb, symcbarg); + + size_t avail = bufsize - *bufcntp; + uint_fast8_t word = *(*param_start)++; + int needed = snprintf (&bufp[*bufcntp], avail, "$0x%" PRIxFAST8, word); + if ((size_t) needed > avail) + return (size_t) needed - avail; + *bufcntp += needed; + return 0; +} + +static int +FCT_imms (GElf_Addr addr __attribute__ ((unused)), + int *prefixes __attribute__ ((unused)), + const char *op1str __attribute__ ((unused)), + size_t opoff1 __attribute__ ((unused)), + size_t opoff2 __attribute__ ((unused)), + size_t opoff3 __attribute__ ((unused)), + char *bufp __attribute__ ((unused)), + size_t *bufcntp __attribute__ ((unused)), + size_t bufsize __attribute__ ((unused)), + const uint8_t *data __attribute__ ((unused)), + const uint8_t **param_start __attribute__ ((unused)), + const uint8_t *end __attribute__ ((unused)), + DisasmGetSymCB_t symcb __attribute__ ((unused)), + void *symcbarg __attribute__ ((unused))) +{ + size_t avail = bufsize - *bufcntp; + int_fast8_t byte = *(*param_start)++; + int needed = snprintf (&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_imm$s (GElf_Addr addr __attribute__ ((unused)), + int *prefixes __attribute__ ((unused)), + const char *op1str __attribute__ ((unused)), + size_t opoff1 __attribute__ ((unused)), + size_t opoff2 __attribute__ ((unused)), + size_t opoff3 __attribute__ ((unused)), + char *bufp __attribute__ ((unused)), + size_t *bufcntp __attribute__ ((unused)), + size_t bufsize __attribute__ ((unused)), + const uint8_t *data __attribute__ ((unused)), + const uint8_t **param_start __attribute__ ((unused)), + const uint8_t *end __attribute__ ((unused)), + DisasmGetSymCB_t symcb __attribute__ ((unused)), + void *symcbarg __attribute__ ((unused))) +{ + uint_fast8_t opcode = data[opoff2 / 8]; + size_t avail = bufsize - *bufcntp; + if ((opcode & 2) != 0) + return FCT_imms (addr, prefixes, op1str, opoff1, opoff2, opoff3, bufp, + bufcntp, bufsize, data, param_start, end, symcb, + symcbarg); + + if ((*prefixes & has_data16) == 0) + { + if (*param_start + 4 > end) + return -1; + uint32_t word = read_4ubyte_unaligned_inc (*param_start); + int needed = snprintf (&bufp[*bufcntp], avail, "$0x%" PRIx32, word); + if ((size_t) needed > avail) + return (size_t) needed - avail; + *bufcntp += needed; + } + else + { + if (*param_start + 2 > end) + return -1; + uint16_t word = read_2ubyte_unaligned_inc (*param_start); + int needed = snprintf (&bufp[*bufcntp], avail, "$0x%" PRIx16, word); + if ((size_t) needed > avail) + return (size_t) needed - avail; + *bufcntp += needed; + } + return 0; +} + +static int +FCT_imm16 (GElf_Addr addr __attribute__ ((unused)), + int *prefixes __attribute__ ((unused)), + const char *op1str __attribute__ ((unused)), + size_t opoff1 __attribute__ ((unused)), + size_t opoff2 __attribute__ ((unused)), + size_t opoff3 __attribute__ ((unused)), + char *bufp __attribute__ ((unused)), + size_t *bufcntp __attribute__ ((unused)), + size_t bufsize __attribute__ ((unused)), + const uint8_t *data __attribute__ ((unused)), + const uint8_t **param_start __attribute__ ((unused)), + const uint8_t *end __attribute__ ((unused)), + DisasmGetSymCB_t symcb __attribute__ ((unused)), + void *symcbarg __attribute__ ((unused))) +{ + if (*param_start + 2 > end) + return -1; + uint16_t word = read_2ubyte_unaligned_inc (*param_start); + size_t avail = bufsize - *bufcntp; + int needed = snprintf (&bufp[*bufcntp], avail, "$0x%" PRIx16, word); + if ((size_t) needed > avail) + return (size_t) needed - avail; + *bufcntp += needed; + return 0; +} + +static int +FCT_imms8 (GElf_Addr addr __attribute__ ((unused)), + int *prefixes __attribute__ ((unused)), + const char *op1str __attribute__ ((unused)), + size_t opoff1 __attribute__ ((unused)), + size_t opoff2 __attribute__ ((unused)), + size_t opoff3 __attribute__ ((unused)), + char *bufp __attribute__ ((unused)), + size_t *bufcntp __attribute__ ((unused)), + size_t bufsize __attribute__ ((unused)), + const uint8_t *data __attribute__ ((unused)), + const uint8_t **param_start __attribute__ ((unused)), + const uint8_t *end __attribute__ ((unused)), + DisasmGetSymCB_t symcb __attribute__ ((unused)), + void *symcbarg __attribute__ ((unused))) +{ + size_t avail = bufsize - *bufcntp; + int_fast8_t byte = *(*param_start)++; + int needed = snprintf (&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 (GElf_Addr addr __attribute__ ((unused)), + int *prefixes __attribute__ ((unused)), + const char *op1str __attribute__ ((unused)), + size_t opoff1 __attribute__ ((unused)), + size_t opoff2 __attribute__ ((unused)), + size_t opoff3 __attribute__ ((unused)), + char *bufp __attribute__ ((unused)), + size_t *bufcntp __attribute__ ((unused)), + size_t bufsize __attribute__ ((unused)), + const uint8_t *data __attribute__ ((unused)), + const uint8_t **param_start __attribute__ ((unused)), + const uint8_t *end __attribute__ ((unused)), + DisasmGetSymCB_t symcb __attribute__ ((unused)), + void *symcbarg __attribute__ ((unused))) +{ + size_t avail = bufsize - *bufcntp; + uint_fast8_t byte = *(*param_start)++; + int needed = snprintf (&bufp[*bufcntp], avail, "$0x%" PRIx32, + (uint32_t) byte); + if ((size_t) needed > avail) + return (size_t) needed - avail; + *bufcntp += needed; + return 0; +} + +#ifndef X86_64 +static int +FCT_rel (GElf_Addr addr __attribute__ ((unused)), + int *prefixes __attribute__ ((unused)), + const char *op1str __attribute__ ((unused)), + size_t opoff1 __attribute__ ((unused)), + size_t opoff2 __attribute__ ((unused)), + size_t opoff3 __attribute__ ((unused)), + char *bufp __attribute__ ((unused)), + size_t *bufcntp __attribute__ ((unused)), + size_t bufsize __attribute__ ((unused)), + const uint8_t *data __attribute__ ((unused)), + const uint8_t **param_start __attribute__ ((unused)), + const uint8_t *end __attribute__ ((unused)), + DisasmGetSymCB_t symcb __attribute__ ((unused)), + void *symcbarg __attribute__ ((unused))) +{ + size_t avail = bufsize - *bufcntp; + if (*param_start + 4 > end) + return -1; + uint32_t rel = read_4ubyte_unaligned_inc (*param_start); + int needed = snprintf (&bufp[*bufcntp], avail, "0x%" PRIx32, + (uint32_t) (addr + rel + (*param_start - data))); + if ((size_t) needed > avail) + return (size_t) needed - avail; + *bufcntp += needed; + return 0; +} +#endif + +static int +FCT_mmxreg (GElf_Addr addr __attribute__ ((unused)), + int *prefixes __attribute__ ((unused)), + const char *op1str __attribute__ ((unused)), + size_t opoff1 __attribute__ ((unused)), + size_t opoff2 __attribute__ ((unused)), + size_t opoff3 __attribute__ ((unused)), + char *bufp __attribute__ ((unused)), + size_t *bufcntp __attribute__ ((unused)), + size_t bufsize __attribute__ ((unused)), + const uint8_t *data __attribute__ ((unused)), + const uint8_t **param_start __attribute__ ((unused)), + const uint8_t *end __attribute__ ((unused)), + DisasmGetSymCB_t symcb __attribute__ ((unused)), + void *symcbarg __attribute__ ((unused))) +{ + uint_fast8_t byte = data[opoff1 / 8]; + assert (opoff1 % 8 == 2 || opoff1 % 8 == 5); + byte = (byte >> (5 - opoff1 % 8)) & 7; + size_t avail = bufsize - *bufcntp; + int needed = snprintf (&bufp[*bufcntp], avail, "%%mm%" PRIxFAST8, byte); + if ((size_t) needed > avail) + return needed - avail; + *bufcntp += needed; + return 0; +} + +static int +FCT_mmxreg2 (GElf_Addr addr __attribute__ ((unused)), + int *prefixes __attribute__ ((unused)), + const char *op1str __attribute__ ((unused)), + size_t opoff1 __attribute__ ((unused)), + size_t opoff2 __attribute__ ((unused)), + size_t opoff3 __attribute__ ((unused)), + char *bufp __attribute__ ((unused)), + size_t *bufcntp __attribute__ ((unused)), + size_t bufsize __attribute__ ((unused)), + const uint8_t *data __attribute__ ((unused)), + const uint8_t **param_start __attribute__ ((unused)), + const uint8_t *end __attribute__ ((unused)), + DisasmGetSymCB_t symcb __attribute__ ((unused)), + void *symcbarg __attribute__ ((unused))) +{ + uint_fast8_t byte = data[opoff1 / 8]; + assert (opoff1 % 8 == 2 || opoff1 % 8 == 5); + byte = (byte >> (5 - opoff1 % 8)) & 7; + size_t avail = bufsize - *bufcntp; + int needed; + if (*prefixes & (has_rep | has_repne)) + needed = snprintf (&bufp[*bufcntp], avail, "%%%s", regs[byte]); + else + needed = snprintf (&bufp[*bufcntp], avail, "%%mm%" PRIxFAST8, byte); + if ((size_t) needed > avail) + return needed - avail; + *bufcntp += needed; + return 0; +} + + +static int +FCT_mod$r_m (GElf_Addr addr __attribute__ ((unused)), + int *prefixes __attribute__ ((unused)), + const char *op1str __attribute__ ((unused)), + size_t opoff1 __attribute__ ((unused)), + size_t opoff2 __attribute__ ((unused)), + size_t opoff3 __attribute__ ((unused)), + char *bufp __attribute__ ((unused)), + size_t *bufcntp __attribute__ ((unused)), + size_t bufsize __attribute__ ((unused)), + const uint8_t *data __attribute__ ((unused)), + const uint8_t **param_start __attribute__ ((unused)), + const uint8_t *end __attribute__ ((unused)), + DisasmGetSymCB_t symcb __attribute__ ((unused)), + void *symcbarg __attribute__ ((unused))) +{ + assert (opoff1 % 8 == 0); + uint_fast8_t modrm = data[opoff1 / 8]; + if ((modrm & 0xc0) == 0xc0) + { + int is_16bit = (*prefixes & has_data16) != 0; + + if (*bufcntp + 5 - is_16bit > bufsize) + return *bufcntp + 5 - is_16bit - bufsize; + bufp[(*bufcntp)++] = '%'; + memcpy (&bufp[*bufcntp], regs[modrm & 7] + is_16bit, + sizeof (regs[0]) - is_16bit); + *bufcntp += 3 - is_16bit; + return 0; + } + + return general_mod$r_m (addr, prefixes, op1str, opoff1, opoff2, opoff3, bufp, + bufcntp, bufsize, data, param_start, end, + symcb, symcbarg); +} + + +#ifndef X86_64 +static int +FCT_moda$r_m (GElf_Addr addr __attribute__ ((unused)), + int *prefixes __attribute__ ((unused)), + const char *op1str __attribute__ ((unused)), + size_t opoff1 __attribute__ ((unused)), + size_t opoff2 __attribute__ ((unused)), + size_t opoff3 __attribute__ ((unused)), + char *bufp __attribute__ ((unused)), + size_t *bufcntp __attribute__ ((unused)), + size_t bufsize __attribute__ ((unused)), + const uint8_t *data __attribute__ ((unused)), + const uint8_t **param_start __attribute__ ((unused)), + const uint8_t *end __attribute__ ((unused)), + DisasmGetSymCB_t symcb __attribute__ ((unused)), + void *symcbarg __attribute__ ((unused))) +{ + int r = data_prefix (prefixes, bufp, bufcntp, bufsize); + if (r != 0) + return r; + + assert (opoff1 % 8 == 0); + uint_fast8_t modrm = data[opoff1 / 8]; + if ((modrm & 0xc0) == 0xc0) + { + if (*bufcntp + 3 > bufsize) + return *bufcntp + 3 - bufsize; + + memcpy (&bufp[*bufcntp], "???", 3); + *bufcntp += 3; + + return 0; + } + + return general_mod$r_m (addr, prefixes, op1str, opoff1, opoff2, opoff3, bufp, + bufcntp, bufsize, data, param_start, end, + symcb, symcbarg); +} +#endif + + +static int +FCT_mod$r_m$w (GElf_Addr addr __attribute__ ((unused)), + int *prefixes __attribute__ ((unused)), + const char *op1str __attribute__ ((unused)), + size_t opoff1 __attribute__ ((unused)), + size_t opoff2 __attribute__ ((unused)), + size_t opoff3 __attribute__ ((unused)), + char *bufp __attribute__ ((unused)), + size_t *bufcntp __attribute__ ((unused)), + size_t bufsize __attribute__ ((unused)), + const uint8_t *data __attribute__ ((unused)), + const uint8_t **param_start __attribute__ ((unused)), + const uint8_t *end __attribute__ ((unused)), + DisasmGetSymCB_t symcb __attribute__ ((unused)), + void *symcbarg __attribute__ ((unused))) +{ + int r = data_prefix (prefixes, bufp, bufcntp, bufsize); + if (r != 0) + return r; + + assert (opoff1 % 8 == 0); + uint_fast8_t modrm = data[opoff1 / 8]; + if ((modrm & 0xc0) == 0xc0) + { + if ((data[opoff3 / 8] & (1 << (7 - (opoff3 & 7)))) == 0) + { + if (*bufcntp + 3 > bufsize) + return *bufcntp + 3 - bufsize; + bufp[(*bufcntp)++] = '%'; + bufp[(*bufcntp)++] = "acdb"[modrm & 3]; + bufp[(*bufcntp)++] = "lh"[(modrm & 4) >> 2]; + } + else + { + int is_16bit = (*prefixes & has_data16) != 0; + + if (*bufcntp + 5 - is_16bit > bufsize) + return *bufcntp + 5 - is_16bit - bufsize; + bufp[(*bufcntp)++] = '%'; + memcpy (&bufp[*bufcntp], regs[modrm & 7] + is_16bit, + sizeof (regs[0]) - is_16bit); + *bufcntp += 3 - is_16bit; + } + return 0; + } + + return general_mod$r_m (addr, prefixes, op1str, opoff1, opoff2, opoff3, bufp, + bufcntp, bufsize, data, param_start, end, + symcb, symcbarg); +} + + +#ifndef X86_64 +static int +FCT_mod$8r_m (GElf_Addr addr __attribute__ ((unused)), + int *prefixes __attribute__ ((unused)), + const char *op1str __attribute__ ((unused)), + size_t opoff1 __attribute__ ((unused)), + size_t opoff2 __attribute__ ((unused)), + size_t opoff3 __attribute__ ((unused)), + char *bufp __attribute__ ((unused)), + size_t *bufcntp __attribute__ ((unused)), + size_t bufsize __attribute__ ((unused)), + const uint8_t *data __attribute__ ((unused)), + const uint8_t **param_start __attribute__ ((unused)), + const uint8_t *end __attribute__ ((unused)), + DisasmGetSymCB_t symcb __attribute__ ((unused)), + void *symcbarg __attribute__ ((unused))) +{ + assert (opoff1 % 8 == 0); + uint_fast8_t modrm = data[opoff1 / 8]; + if ((modrm & 0xc0) == 0xc0) + { + if (*bufcntp + 3 > bufsize) + return *bufcntp + 3 - bufsize; + bufp[(*bufcntp)++] = '%'; + bufp[(*bufcntp)++] = "acdb"[modrm & 3]; + bufp[(*bufcntp)++] = "lh"[(modrm & 4) >> 2]; + return 0; + } + + return general_mod$r_m (addr, prefixes, op1str, opoff1, opoff2, opoff3, bufp, + bufcntp, bufsize, data, param_start, end, + symcb, symcbarg); +} +#endif + +static int +FCT_mod$16r_m (GElf_Addr addr __attribute__ ((unused)), + int *prefixes __attribute__ ((unused)), + const char *op1str __attribute__ ((unused)), + size_t opoff1 __attribute__ ((unused)), + size_t opoff2 __attribute__ ((unused)), + size_t opoff3 __attribute__ ((unused)), + char *bufp __attribute__ ((unused)), + size_t *bufcntp __attribute__ ((unused)), + size_t bufsize __attribute__ ((unused)), + const uint8_t *data __attribute__ ((unused)), + const uint8_t **param_start __attribute__ ((unused)), + const uint8_t *end __attribute__ ((unused)), + DisasmGetSymCB_t symcb __attribute__ ((unused)), + void *symcbarg __attribute__ ((unused))) +{ + assert (opoff1 % 8 == 0); + uint_fast8_t modrm = data[opoff1 / 8]; + if ((modrm & 0xc0) == 0xc0) + { + uint_fast8_t byte = data[opoff1 / 8] & 7; + if (*bufcntp + 3 > bufsize) + return *bufcntp + 3 - bufsize; + bufp[(*bufcntp)++] = '%'; + memcpy (&bufp[*bufcntp], regs[byte] + 1, sizeof (regs[0]) - 1); + *bufcntp += 2; + return 0; + } + + return general_mod$r_m (addr, prefixes, op1str, opoff1, opoff2, opoff3, bufp, + bufcntp, bufsize, data, param_start, end, + symcb, symcbarg); +} + +static int +FCT_reg (GElf_Addr addr __attribute__ ((unused)), + int *prefixes __attribute__ ((unused)), + const char *op1str __attribute__ ((unused)), + size_t opoff1 __attribute__ ((unused)), + size_t opoff2 __attribute__ ((unused)), + size_t opoff3 __attribute__ ((unused)), + char *bufp __attribute__ ((unused)), + size_t *bufcntp __attribute__ ((unused)), + size_t bufsize __attribute__ ((unused)), + const uint8_t *data __attribute__ ((unused)), + const uint8_t **param_start __attribute__ ((unused)), + const uint8_t *end __attribute__ ((unused)), + DisasmGetSymCB_t symcb __attribute__ ((unused)), + void *symcbarg __attribute__ ((unused))) +{ + uint_fast8_t byte = data[opoff1 / 8]; + assert (opoff1 % 8 + 3 <= 8); + byte >>= 8 - (opoff1 % 8 + 3); + byte &= 7; + int is_16bit = (*prefixes & has_data16) != 0; + if (*bufcntp + 4 > bufsize) + return *bufcntp + 4 - bufsize; + bufp[(*bufcntp)++] = '%'; + memcpy (&bufp[*bufcntp], regs[byte] + is_16bit, sizeof (regs[0]) - is_16bit); + *bufcntp += 3 - is_16bit; + return 0; +} + +static int +FCT_reg$w (GElf_Addr addr __attribute__ ((unused)), + int *prefixes __attribute__ ((unused)), + const char *op1str __attribute__ ((unused)), + size_t opoff1 __attribute__ ((unused)), + size_t opoff2 __attribute__ ((unused)), + size_t opoff3 __attribute__ ((unused)), + char *bufp __attribute__ ((unused)), + size_t *bufcntp __attribute__ ((unused)), + size_t bufsize __attribute__ ((unused)), + const uint8_t *data __attribute__ ((unused)), + const uint8_t **param_start __attribute__ ((unused)), + const uint8_t *end __attribute__ ((unused)), + DisasmGetSymCB_t symcb __attribute__ ((unused)), + void *symcbarg __attribute__ ((unused))) +{ + if (data[opoff2 / 8] & (1 << (7 - (opoff2 & 7)))) + return FCT_reg (addr, prefixes, op1str, opoff1, opoff2, opoff3, bufp, + bufcntp, bufsize, data, param_start, end, symcb, symcbarg); + uint_fast8_t byte = data[opoff1 / 8]; + assert (opoff1 % 8 + 3 <= 8); + byte >>= 8 - (opoff1 % 8 + 3); + byte &= 7; + if (*bufcntp + 3 > bufsize) + return *bufcntp + 3 - bufsize; + bufp[(*bufcntp)++] = '%'; + bufp[(*bufcntp)++] = "acdb"[byte & 3]; + bufp[(*bufcntp)++] = "lh"[byte >> 2]; + return 0; +} + +#ifndef X86_64 +static int +FCT_freg (GElf_Addr addr __attribute__ ((unused)), + int *prefixes __attribute__ ((unused)), + const char *op1str __attribute__ ((unused)), + size_t opoff1 __attribute__ ((unused)), + size_t opoff2 __attribute__ ((unused)), + size_t opoff3 __attribute__ ((unused)), + char *bufp __attribute__ ((unused)), + size_t *bufcntp __attribute__ ((unused)), + size_t bufsize __attribute__ ((unused)), + const uint8_t *data __attribute__ ((unused)), + const uint8_t **param_start __attribute__ ((unused)), + const uint8_t *end __attribute__ ((unused)), + DisasmGetSymCB_t symcb __attribute__ ((unused)), + void *symcbarg __attribute__ ((unused))) +{ + assert (opoff1 / 8 == 1); + assert (opoff1 % 8 == 5); + size_t avail = bufsize - *bufcntp; + int needed = snprintf (&bufp[*bufcntp], avail, "%%st(%" PRIx32 ")", + (uint32_t) (data[1] & 7)); + if ((size_t) needed > avail) + return (size_t) needed - avail; + *bufcntp += needed; + return 0; +} + +static int +FCT_reg16 (GElf_Addr addr __attribute__ ((unused)), + int *prefixes __attribute__ ((unused)), + const char *op1str __attribute__ ((unused)), + size_t opoff1 __attribute__ ((unused)), + size_t opoff2 __attribute__ ((unused)), + size_t opoff3 __attribute__ ((unused)), + char *bufp __attribute__ ((unused)), + size_t *bufcntp __attribute__ ((unused)), + size_t bufsize __attribute__ ((unused)), + const uint8_t *data __attribute__ ((unused)), + const uint8_t **param_start __attribute__ ((unused)), + const uint8_t *end __attribute__ ((unused)), + DisasmGetSymCB_t symcb __attribute__ ((unused)), + void *symcbarg __attribute__ ((unused))) +{ + if (*prefixes & has_data16) + return -1; + + *prefixes |= has_data16; + return FCT_reg (addr, prefixes, op1str, opoff1, opoff2, opoff3, bufp, + bufcntp, bufsize, data, param_start, end, symcb, symcbarg); +} +#endif + +static int +FCT_sel (GElf_Addr addr __attribute__ ((unused)), + int *prefixes __attribute__ ((unused)), + const char *op1str __attribute__ ((unused)), + size_t opoff1 __attribute__ ((unused)), + size_t opoff2 __attribute__ ((unused)), + size_t opoff3 __attribute__ ((unused)), + char *bufp __attribute__ ((unused)), + size_t *bufcntp __attribute__ ((unused)), + size_t bufsize __attribute__ ((unused)), + const uint8_t *data __attribute__ ((unused)), + const uint8_t **param_start __attribute__ ((unused)), + const uint8_t *end __attribute__ ((unused)), + DisasmGetSymCB_t symcb __attribute__ ((unused)), + void *symcbarg __attribute__ ((unused))) +{ + assert (opoff1 % 8 == 0); + assert (opoff1 / 8 == 5); + if (*param_start + 2 > end) + return -1; + *param_start += 2; + uint16_t absval = read_2ubyte_unaligned (&data[5]); + size_t avail = bufsize - *bufcntp; + int needed = snprintf (&bufp[*bufcntp], avail, "$0x%" PRIx16, absval); + if ((size_t) needed > avail) + return needed - avail; + *bufcntp += needed; + return 0; +} + +static int +FCT_sreg2 (GElf_Addr addr __attribute__ ((unused)), + int *prefixes __attribute__ ((unused)), + const char *op1str __attribute__ ((unused)), + size_t opoff1 __attribute__ ((unused)), + size_t opoff2 __attribute__ ((unused)), + size_t opoff3 __attribute__ ((unused)), + char *bufp __attribute__ ((unused)), + size_t *bufcntp __attribute__ ((unused)), + size_t bufsize __attribute__ ((unused)), + const uint8_t *data __attribute__ ((unused)), + const uint8_t **param_start __attribute__ ((unused)), + const uint8_t *end __attribute__ ((unused)), + DisasmGetSymCB_t symcb __attribute__ ((unused)), + void *symcbarg __attribute__ ((unused))) +{ + uint_fast8_t byte = data[opoff1 / 8]; + assert (opoff1 % 8 + 3 <= 8); + byte >>= 8 - (opoff1 % 8 + 2); + + if (*bufcntp + 3 > bufsize) + return *bufcntp + 3 - bufsize; + + bufp[(*bufcntp)++] = '%'; + bufp[(*bufcntp)++] = "ecsd"[byte & 3]; + bufp[(*bufcntp)++] = 's'; + + return 0; +} + +static int +FCT_sreg3 (GElf_Addr addr __attribute__ ((unused)), + int *prefixes __attribute__ ((unused)), + const char *op1str __attribute__ ((unused)), + size_t opoff1 __attribute__ ((unused)), + size_t opoff2 __attribute__ ((unused)), + size_t opoff3 __attribute__ ((unused)), + char *bufp __attribute__ ((unused)), + size_t *bufcntp __attribute__ ((unused)), + size_t bufsize __attribute__ ((unused)), + const uint8_t *data __attribute__ ((unused)), + const uint8_t **param_start __attribute__ ((unused)), + const uint8_t *end __attribute__ ((unused)), + DisasmGetSymCB_t symcb __attribute__ ((unused)), + void *symcbarg __attribute__ ((unused))) +{ + uint_fast8_t byte = data[opoff1 / 8]; + assert (opoff1 % 8 + 4 <= 8); + byte >>= 8 - (opoff1 % 8 + 3); + + if ((byte & 7) >= 6) + return -1; + + if (*bufcntp + 3 > bufsize) + return *bufcntp + 3 - bufsize; + + bufp[(*bufcntp)++] = '%'; + bufp[(*bufcntp)++] = "ecsdfg"[byte & 7]; + bufp[(*bufcntp)++] = 's'; + + return 0; +} + +static int +FCT_string (GElf_Addr addr __attribute__ ((unused)), + int *prefixes __attribute__ ((unused)), + const char *op1str __attribute__ ((unused)), + size_t opoff1 __attribute__ ((unused)), + size_t opoff2 __attribute__ ((unused)), + size_t opoff3 __attribute__ ((unused)), + char *bufp __attribute__ ((unused)), + size_t *bufcntp __attribute__ ((unused)), + size_t bufsize __attribute__ ((unused)), + const uint8_t *data __attribute__ ((unused)), + const uint8_t **param_start __attribute__ ((unused)), + const uint8_t *end __attribute__ ((unused)), + DisasmGetSymCB_t symcb __attribute__ ((unused)), + void *symcbarg __attribute__ ((unused))) +{ + return 0; +} + +static int +FCT_xmmreg (GElf_Addr addr __attribute__ ((unused)), + int *prefixes __attribute__ ((unused)), + const char *op1str __attribute__ ((unused)), + size_t opoff1 __attribute__ ((unused)), + size_t opoff2 __attribute__ ((unused)), + size_t opoff3 __attribute__ ((unused)), + char *bufp __attribute__ ((unused)), + size_t *bufcntp __attribute__ ((unused)), + size_t bufsize __attribute__ ((unused)), + const uint8_t *data __attribute__ ((unused)), + const uint8_t **param_start __attribute__ ((unused)), + const uint8_t *end __attribute__ ((unused)), + DisasmGetSymCB_t symcb __attribute__ ((unused)), + void *symcbarg __attribute__ ((unused))) +{ + uint_fast8_t byte = data[opoff1 / 8]; + assert (opoff1 % 8 == 2 || opoff1 % 8 == 5); + byte = (byte >> (5 - opoff1 % 8)) & 7; + size_t avail = bufsize - *bufcntp; + int needed = snprintf (&bufp[*bufcntp], avail, "%%xmm%" PRIxFAST8, byte); + if ((size_t) needed > avail) + return needed - avail; + *bufcntp += needed; + return 0; +} diff --git a/libcpu/i386_disasm.c b/libcpu/i386_disasm.c new file mode 100644 index 000000000..ca173cc6d --- /dev/null +++ b/libcpu/i386_disasm.c @@ -0,0 +1,914 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../libebl/libeblP.h" + +#define MACHINE_ENCODING __LITTLE_ENDIAN +#include "memory-access.h" + + +#ifndef MNEFILE +# define MNEFILE "i386.mnemonics" +#endif + +#define MNESTRFIELD(line) MNESTRFIELD1 (line) +#define MNESTRFIELD1(line) str##line +static const union mnestr_t +{ + struct + { +#define MNE(name) char MNESTRFIELD (__LINE__)[sizeof (#name)]; +#include MNEFILE +#undef MNE + }; + char str[0]; +} mnestr = + { + { +#define MNE(name) #name, +#include MNEFILE +#undef MNE + } + }; + +/* The index can be stored in the instrtab. */ +enum + { +#define MNE(name) MNE_##name, +#include MNEFILE +#undef MNE + MNE_INVALID + }; + +static const unsigned short int mneidx[] = + { +#define MNE(name) \ + [MNE_##name] = offsetof (union mnestr_t, MNESTRFIELD (__LINE__)), +#include MNEFILE +#undef MNE + }; + + +enum + { + idx_cs = 0, + idx_ds, + idx_es, + idx_fs, + idx_gs, + idx_ss, + idx_data16, + idx_addr16, + idx_rep, + idx_repne, + idx_lock + }; + +enum + { +#define prefbit(pref) has_##pref = 1 << idx_##pref + prefbit (cs), + prefbit (ds), + prefbit (es), + prefbit (fs), + prefbit (gs), + prefbit (ss), + prefbit (data16), + prefbit (addr16), + prefbit (rep), + prefbit (repne), + prefbit (lock) +#undef prefbit + }; +#define SEGMENT_PREFIXES \ + (has_cs | has_ds | has_es | has_fs | has_gs | has_ss) + +#define prefix_cs 0x2e +#define prefix_ds 0x3e +#define prefix_es 0x26 +#define prefix_fs 0x64 +#define prefix_gs 0x65 +#define prefix_ss 0x36 +#define prefix_data16 0x66 +#define prefix_addr16 0x67 +#define prefix_rep 0xf3 +#define prefix_repne 0xf2 +#define prefix_lock 0xf0 + + +static const uint8_t known_prefixes[] = + { +#define newpref(pref) [idx_##pref] = prefix_##pref + newpref (cs), + newpref (ds), + newpref (es), + newpref (fs), + newpref (gs), + newpref (ss), + newpref (data16), + newpref (addr16), + newpref (rep), + newpref (repne), + newpref (lock) +#undef newpref + }; +#define nknown_prefixes (sizeof (known_prefixes) / sizeof (known_prefixes[0])) + + +#if 0 +static const char *prefix_str[] = + { +#define newpref(pref) [idx_##pref] = #pref + newpref (cs), + newpref (ds), + newpref (es), + newpref (fs), + newpref (gs), + newpref (ss), + newpref (data16), + newpref (addr16), + newpref (rep), + newpref (repne), + newpref (lock) +#undef newpref + }; +#endif + + +#ifndef DISFILE +# define DISFILE "i386_dis.h" +#endif +#include DISFILE + + +#define ADD_CHAR(ch) \ + do { \ + if (unlikely (bufcnt == bufsize)) \ + goto enomem; \ + buf[bufcnt++] = (ch); \ + } while (0) + +#define ADD_STRING(str) \ + do { \ + const char *_str = (str); \ + size_t _len = strlen (_str); \ + if (unlikely (bufcnt + _len > bufsize)) \ + goto enomem; \ + memcpy (buf + bufcnt, str, _len); \ + bufcnt += _len; \ + } while (0) + + +#include +int +i386_disasm (const uint8_t **startp, const uint8_t *end, GElf_Addr addr, + const char *fmt, DisasmOutputCB_t outcb, DisasmGetSymCB_t symcb, + void *outcbarg, void *symcbarg) +{ + const char *save_fmt = fmt; + + while (1) + { +#define BUFSIZE 512 + const size_t bufsize = BUFSIZE; + char initbuf[BUFSIZE]; + char *buf = initbuf; + size_t bufcnt = 0; + + int prefixes = 0; + + const uint8_t *data = *startp; + const uint8_t *begin = data; + + fmt = save_fmt; + + /* Recognize all prefixes. */ + while (data < end) + { + unsigned int i; + for (i = 0; i < nknown_prefixes; ++i) + if (known_prefixes[i] == *data) + break; + if (i == nknown_prefixes) + break; + + prefixes |= 1 << i; + + ++data; + } + + assert (data <= end); + if (data == end) + { + if (prefixes != 0) + goto print_prefix; + + return -1; + } + + const uint8_t *curr = match_data; + const uint8_t *const match_end = match_data + sizeof (match_data); + + enomem: + ; + + size_t cnt = 0; + while (curr < match_end) + { + const uint8_t *const start = curr; + + uint_fast8_t len = *curr++; + + assert (len > 0); + assert (curr + 2 * len + 2 <= match_end); + + const uint8_t *codep = data; + size_t avail = len; + + do + { + uint_fast8_t masked = *codep++ & *curr++; + if (masked != *curr++) + break; + + --avail; + if (codep == end && avail > 0) + return 0; + } + while (avail > 0); + + if (avail != 0) + { + not: + curr = start + 1 + 2 * len + 2; + ++cnt; + continue; + } + + if (len > end - data) + /* There is not enough data for the entire instruction. The + caller can figure this out by looking at the pointer into + the input data. */ + return 0; + + size_t prefix_size = 0; + + // XXXonly print as prefix if valid? + if ((prefixes & has_lock) != 0) + { + ADD_STRING ("lock "); + prefix_size += 5; + } + + if (instrtab[cnt].rep) + { + if ((prefixes & has_rep) != 0) + { + ADD_STRING ("rep "); + prefix_size += 4; + } + } + else if (instrtab[cnt].repe + && (prefixes & (has_rep | has_repne)) != 0) + { + if ((prefixes & has_repne) != 0) + { + ADD_STRING ("repne "); + prefix_size += 6; + } + else if ((prefixes & has_rep) != 0) + { + ADD_STRING ("repe "); + prefix_size += 5; + } + } + else if ((prefixes & (has_rep | has_repne)) != 0) + { + uint_fast8_t byte; + print_prefix: + bufcnt = 0; + byte = *begin; + /* This is a prefix byte. Print it. */ + switch (byte) + { + case prefix_rep: + ADD_STRING ("rep"); + break; + case prefix_repne: + ADD_STRING ("repne"); + break; + case prefix_cs: + ADD_STRING ("cs"); + break; + case prefix_ds: + ADD_STRING ("ds"); + break; + case prefix_es: + ADD_STRING ("es"); + break; + case prefix_fs: + ADD_STRING ("fs"); + break; + case prefix_gs: + ADD_STRING ("gs"); + break; + case prefix_ss: + ADD_STRING ("ss"); + break; + case prefix_data16: + ADD_STRING ("data16"); + break; + case prefix_addr16: + ADD_STRING ("addr16"); + break; + case prefix_lock: + ADD_STRING ("lock"); + break; + default: + /* Cannot happen. */ + abort (); + } + data = begin + 1; + ++addr; + + /* The string definitely fits. */ + buf[bufcnt++] = '\0'; + + goto out; + } + + /* We have a match. First determine how many bytes are + needed for the adressing mode. */ + const uint8_t *param_start = codep; + if (instrtab[cnt].modrm) + { + uint_fast8_t modrm = codep[-1]; + + if ((prefixes & has_addr16) == 0) + { + /* Account for SIB. */ + if ((modrm & 0xc0) != 0xc0 && (modrm & 0x7) == 0x4) + param_start += 1; + } + + /* Account for displacement. */ + if ((modrm & 0xc7) == 5 || (modrm & 0xc0) == 0x80 + || ((modrm & 0xc7) == 0x4 && (codep[0] & 0x7) == 0x5)) + param_start += 4; + else if ((modrm & 0xc0) == 0x40) + param_start += 1; + } + + unsigned long string_end_idx = 0; + while (*fmt != '\0') + { + if (*fmt != '%') + { + char ch = *fmt++; + if (ch == '\\') + { + switch ((ch = *fmt++)) + { + case '0' ... '7': + { + int val = ch - '0'; + ch = *fmt; + if (ch >= '0' && ch <= '7') + { + val *= 8; + val += ch - '0'; + ch = *++fmt; + if (ch >= '0' && ch <= '7' && val < 32) + { + val *= 8; + val += ch - '0'; + ++fmt; + } + } + ch = val; + } + break; + + case 'n': + ch = '\n'; + break; + + case 't': + ch = '\t'; + break; + + default: + return EINVAL; + } + } + ADD_CHAR (ch); + continue; + } + ++fmt; + + int width = 0; + while (isdigit (*fmt)) + width = width * 10 + (*fmt++ - '0'); + + int prec = 0; + if (*fmt == '.') + while (isdigit (*++fmt)) + prec = prec * 10 + (*fmt - '0'); + + size_t start_idx = bufcnt; + switch (*fmt++) + { + const char *str; + + case 'm': + /* Mnemonic. */ + + if (unlikely (instrtab[cnt].mnemonic == MNE_INVALID)) + { + switch (*data) + { + case 0x90: + if (prefixes & ~has_rep) + goto print_prefix; + /* Discard the 'rep' prefix string possibly + already in the buffer. */ + bufcnt = 0; + str = prefixes & has_rep ? "pause" : "nop"; + break; + + case 0x98: + if (prefixes & ~has_data16) + goto print_prefix; + str = prefixes & has_data16 ? "cbtw" : "cwtl"; + break; + + case 0x99: + if (prefixes & ~has_data16) + goto print_prefix; + str = prefixes & has_data16 ? "cwtd" : "cltd"; + break; + + case 0xe3: + if (prefixes & ~has_addr16) + goto print_prefix; + str = prefixes & has_addr16 ? "jcxz" : "jecxz"; + break; + + case 0x0f: + if (data[1] == 0x10 || data[1] == 0x11) + { + bufcnt = 0; + int mod = prefixes & (has_data16 | has_rep + | has_repne); + if (mod & (mod - 1)) + return -1; + str = (mod & has_data16 + ? "movupd" + : mod & has_rep + ? "movss" + : mod & has_repne + ? "movsd" + : "movups"); + break; + } + if (data[1] == 0x12) + { + bufcnt = 0; + int mod = prefixes & (has_data16 | has_rep + | has_repne); + if (mod & (mod - 1)) + return -1; + str = (mod & has_data16 + ? "movlpd" + : mod & has_rep + ? "movsldup" + : mod & has_repne + ? "movddup" + : (data[2] & 0xc0) == 0xc0 + ? "movhlps" + : "movlps"); + break; + } + if (data[1] == 0x13) + { + bufcnt = 0; + int mod = prefixes & has_data16; + if (mod & (mod - 1)) + return -1; + str = (mod & has_data16 + ? ((data[2] & 0xc0) == 0xc0 + ? "movhlpd" + : "movlpd") + : (data[2] & 0xc0) == 0xc0 + ? "movhlps" + : "movlps"); + break; + } + if (data[1] == 0x14) + { + bufcnt = 0; + int mod = prefixes & has_data16; + if (mod & (mod - 1)) + return -1; + str = (mod & has_data16 + ? "unpcklpd" : "unpcklps"); + break; + } + if (data[1] == 0x15) + { + bufcnt = 0; + int mod = prefixes & has_data16; + if (mod & (mod - 1)) + return -1; + str = (mod & has_data16 + ? "unpckhpd" : "unpckhps"); + break; + } + if (data[1] == 0x16) + { + bufcnt = 0; + int mod = prefixes & (has_data16 | has_rep); + if (mod & (mod - 1)) + return -1; + str = (mod & has_data16 + ? "movhpd" + : mod & has_rep + ? "movshdup" + : (data[2] & 0xc0) == 0xc0 + ? "movlhps" + : "movhps"); + break; + } + if (data[1] == 0x17) + { + bufcnt = 0; + int mod = prefixes & has_data16; + if (mod & (mod - 1)) + return -1; + str = (mod & has_data16 + ? ((data[2] & 0xc0) == 0xc0 + ? "movlhpd" : "movhpd") + : (data[2] & 0xc0) == 0xc0 + ? "movlhps" + : "movhps"); + break; + } + if (data[1] == 0x28 || data[1] == 0x29) + { + bufcnt = 0; + int mod = prefixes & has_data16; + if (mod & (mod - 1)) + return -1; + str = (mod & has_data16) ? "movapd" : "movaps"; + break; + } + if (data[1] == 0x2a) + { + bufcnt = 0; + int mod = prefixes & (has_data16 | has_rep + | has_repne); + if (mod & (mod - 1)) + return -1; + str = (mod & has_data16 + ? "cvtpi2pd" + : mod & has_rep + ? "cvtsi2ss" + : mod & has_repne + ? "cvtsi2sd" + : "cvtpi2ps"); + break; + } + if (data[1] == 0x2b) + { + bufcnt = 0; + int mod = prefixes & has_data16; + if (mod & (mod - 1)) + return -1; + prefixes ^= mod; + str = (mod & has_data16) ? "movntpd" : "movntps"; + break; + } + if (data[1] == 0x2c) + { + bufcnt = 0; + int mod = prefixes & (has_data16 | has_rep + | has_repne); + if (mod & (mod - 1)) + return -1; + str = (mod & has_data16 + ? "cvttpd2pi" + : mod & has_rep + ? "cvttss2si" + : mod & has_repne + ? "cvttsd2si" + : "cvttps2pi"); + break; + } + if (data[1] == 0x2d) + { + bufcnt = 0; + int mod = prefixes & (has_data16 | has_rep + | has_repne); + if (mod & (mod - 1)) + return -1; + str = (mod & has_data16 + ? "cvtpd2pi" + : mod & has_rep + ? "cvtss2si" + : mod & has_repne + ? "cvtsd2si" + : "cvtps2pi"); + break; + } + if (data[1] == 0x2e) + { + int mod = prefixes & has_data16; + if (mod & (mod - 1)) + return -1; + str = (mod & has_data16 + ? "ucomisd" : "ucomiss"); + break; + } + if (data[1] == 0x2f) + { + int mod = prefixes & has_data16; + if (mod & (mod - 1)) + return -1; + str = (mod & has_data16 + ? "comisd" : "comiss"); + break; + } + if (data[1] == 0x50) + { + int mod = prefixes & has_data16; + if (mod & (mod - 1)) + return -1; + prefixes ^= mod; + str = (mod & has_data16 + ? "movmskpd" : "movmskps"); + break; + } + if (data[1] == 0x51) + { + bufcnt = 0; + int mod = prefixes & (has_data16 | has_rep + | has_repne); + if (mod & (mod - 1)) + return -1; + str = (mod & has_data16 + ? "sqrtpd" + : mod & has_rep + ? "sqrtss" + : mod & has_repne + ? "sqrtsd" + : "sqrtps"); + break; + } + if (data[1] == 0x52) + { + bufcnt = 0; + int mod = prefixes & has_rep; + if (mod & (mod - 1)) + return -1; + str = mod & has_rep ? "rsqrtss" : "rsqrtps"; + break; + } + if (data[1] == 0x53) + { + bufcnt = 0; + int mod = prefixes & has_rep; + if (mod & (mod - 1)) + return -1; + str = mod & has_rep ? "rcpss" : "rcpps"; + break; + } + /* FALLTHROUGH */ + + default: + abort (); + } + } + else + str = mnestr.str + mneidx[instrtab[cnt].mnemonic]; + + ADD_STRING (str); + + switch (instrtab[cnt].suffix) + { + case suffix_none: + break; + case suffix_w: + if ((codep[-1] & 0xc0) != 0xc0) + { + char ch; + + if (data[0] & 1) + { + if (prefixes & has_data16) + ch = 'w'; + else + ch = 'l'; + } + else + ch = 'b'; + + ADD_CHAR (ch); + } + break; + case suffix_w0: + if ((codep[-1] & 0xc0) != 0xc0) + ADD_CHAR ('l'); + break; + case suffix_w1: + if ((data[0] & 0x4) == 0) + ADD_CHAR ('l'); + break; + case suffix_W: + if (prefixes & has_data16) + { + ADD_CHAR ('w'); + prefixes &= ~has_data16; + } +#ifdef x86_64 + else + abort (); +#endif + break; + case suffix_tttn:; + static const char tttn[16][3] = + { + "o", "no", "b", "ae", "e", "ne", "be", "a", + "s", "ns", "p", "np", "l", "ge", "le", "g" + }; + ADD_STRING (tttn[codep[-1 - instrtab[cnt].modrm] & 0x0f]); + break; + case suffix_D: + if ((codep[-1] & 0xc0) != 0xc0) + ADD_CHAR ((data[0] & 0x04) == 0 ? 's' : 'l'); + break; + default: + printf("unknown suffix %d\n", instrtab[cnt].suffix); + abort (); + } + + string_end_idx = bufcnt; + break; + + case 'o': + if (prec == 1 && instrtab[cnt].fct1 != 0) + { + /* First parameter. */ + if (instrtab[cnt].str1 != 0) + ADD_STRING (op1_str[instrtab[cnt].str1]); + + int r = op1_fct[instrtab[cnt].fct1] (addr + + (data - begin), + &prefixes, +#ifdef STR1_BITS + op1_str[instrtab[cnt].str1], +#else + NULL, +#endif + instrtab[cnt].off1_1 + OFF1_1_BIAS, + instrtab[cnt].off1_2 + OFF1_2_BIAS, + instrtab[cnt].off1_3 + OFF1_3_BIAS, + buf, &bufcnt, bufsize, + data, ¶m_start, + end, + symcb, symcbarg); + if (r < 0) + goto not; + if (r > 0) + goto enomem; + + string_end_idx = ~0ul; + } + else if (prec == 2 && instrtab[cnt].fct2 != 0) + { + /* Second parameter. */ +#ifdef STR2_BITS + // XXX Probably not needed once the instruction + // XXX tables are complete + if (instrtab[cnt].str2 != 0) + ADD_STRING (op2_str[instrtab[cnt].str2]); +#endif + + int r = op2_fct[instrtab[cnt].fct2] (addr + + (data - begin), + &prefixes, +#ifdef STR2_BITS + op2_str[instrtab[cnt].str2], +#else + NULL, +#endif + instrtab[cnt].off2_1 + OFF2_1_BIAS, + instrtab[cnt].off2_2 + OFF2_2_BIAS, + instrtab[cnt].off2_3 + OFF2_3_BIAS, + buf, &bufcnt, bufsize, + data, ¶m_start, + end, + symcb, symcbarg); + if (r < 0) + goto not; + if (r > 0) + goto enomem; + + string_end_idx = ~0ul; + } + else if (prec == 3 && instrtab[cnt].fct3 != 0) + { + /* Third parameter. */ +#ifdef STR3_BITS + // XXX Probably not needed once the instruction + // XXX tables are complete + if (instrtab[cnt].str3 != 0) + ADD_STRING (op3_str[instrtab[cnt].str3]); +#endif + + int r = op3_fct[instrtab[cnt].fct3] (addr + + (data - begin), + &prefixes, +#ifdef STR3_BITS + op3_str[instrtab[cnt].str3], +#else + NULL, +#endif + instrtab[cnt].off3_1 + OFF3_1_BIAS, +#ifdef OFF3_2_BITS + instrtab[cnt].off3_2 + OFF3_2_BIAS, +#else + 0, +#endif +#ifdef OFF3_3_BITS + instrtab[cnt].off3_3 + OFF3_3_BIAS, +#else + 0, +#endif + buf, &bufcnt, bufsize, + data, ¶m_start, + end, + symcb, symcbarg); + if (r < 0) + goto not; + if (r > 0) + goto enomem; + + string_end_idx = ~0ul; + } + break; + + case 'e': + /* String end marker. */ + if (string_end_idx == ~0ul) + string_end_idx = bufcnt; + /* No padding. */ + width = 0; + break; + } + + /* Pad according to the specified width. */ + while (bufcnt + prefix_size < start_idx + width) + ADD_CHAR (' '); + prefix_size = 0; + } + + if ((prefixes & SEGMENT_PREFIXES) != 0) + goto print_prefix; + + if (string_end_idx != ~0ul) + buf[string_end_idx] = '\0'; + + addr += param_start - begin; + data = param_start; + + goto out; + } + + /* Invalid (or at least unhandled) opcode. */ + if (prefixes != 0) + goto print_prefix; + assert (*startp == data); + ++data; + ADD_STRING ("(bad)"); + addr += data - begin; + + buf[bufcnt++] = '\0'; + + out: + *startp = data; + int res = outcb (buf, strlen (buf), outcbarg); + if (res != 0) + return res; + } + + return 0; +} diff --git a/libcpu/i386_gendis.c b/libcpu/i386_gendis.c new file mode 100644 index 000000000..ca01db8c9 --- /dev/null +++ b/libcpu/i386_gendis.c @@ -0,0 +1,39 @@ +#include +#include +#include +#include +#include + + +extern int i386_parse (void); + + +extern FILE *i386_in; +extern int i386_debug; +char *infname; + +FILE *outfile; + +int +main (int argc, char *argv[argc]) +{ + outfile = stdout; + + if (argc == 1) + error (EXIT_FAILURE, 0, "usage: %s ", argv[0]); + + //i386_debug = 1; + infname = argv[1]; + if (strcmp (infname, "-") == 0) + i386_in = stdin; + else + { + i386_in = fopen (infname, "r"); + if (i386_in == NULL) + error (EXIT_FAILURE, errno, "cannot open %s", argv[1]); + } + + i386_parse (); + + return error_message_count != 0; +} diff --git a/libcpu/i386_lex.l b/libcpu/i386_lex.l new file mode 100644 index 000000000..ea121909f --- /dev/null +++ b/libcpu/i386_lex.l @@ -0,0 +1,115 @@ +%{ +/* Copyright (C) 2004, 2005, 2007 Red Hat, Inc. + Written by Ulrich Drepper , 2004. + + This program is Open Source software; you can redistribute it and/or + modify it under the terms of the Open Software License version 1.0 as + published by the Open Source Initiative. + + You should have received a copy of the Open Software License along + with this program; if not, you may obtain a copy of the Open Software + License version 1.0 from http://www.opensource.org/licenses/osl.php or + by writing the Open Source Initiative c/o Lawrence Rosen, Esq., + 3001 King Ranch Road, Ukiah, CA 95482. */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include +#include + +#include +#include "i386_parse.h" + + +static void eat_to_eol (void); +static void invalid_char (int ch); +%} + +ID [a-zA-Z_][a-zA-Z0-9_/]* +ID2 [a-zA-Z0-9_:/]* +NUMBER [0-9]+ +WHITE [[:space:]]+ + +%option yylineno +%option never-interactive +%option noyywrap + + +%x MAIN + +%% + +"%mask" { return kMASK; } + +"%prefix" { return kPREFIX; } +"%suffix" { return kSUFFIX; } + +"%synonym" { return kSYNONYM; } + +{NUMBER} { i386_lval.num = strtoul (yytext, NULL, 10); + return kNUMBER; } + +"%%" { BEGIN (MAIN); return kPERCPERC; } + + +
"0" { return '0'; } +
"1" { return '1'; } + +"{"{ID2}"}" { i386_lval.str = xstrndup (yytext + 1, + yyleng - 2); + return kBITFIELD; } + +
"INVALID" { i386_lval.str = (void *) -1l; + return kID; } + +
{ID} { i386_lval.str = xstrndup (yytext, yyleng); + return kID; } + +
"," { return ','; } + +
":" { return ':'; } + +^"\n" { /* IGNORE */ } + +"\n" { return '\n'; } + +^"#" { eat_to_eol (); } + +{WHITE} { /* IGNORE */ } + +
{WHITE} { return kSPACE; } + +
. { i386_lval.ch = *yytext; return kCHAR; } + +. { invalid_char (*yytext); } + + +%% + +static void +eat_to_eol (void) +{ + while (1) + { + int c = input (); + + if (c == EOF || c == '\n') + break; + } +} + +static void +invalid_char (int ch) +{ + error (0, 0, (isascii (ch) + ? gettext ("invalid character '%c' at line %d; ignored") + : gettext ("invalid character '\\%o' at line %d; ignored")), + ch, yylineno); +} + +// Local Variables: +// mode: C +// End: diff --git a/libcpu/i386_parse.y b/libcpu/i386_parse.y new file mode 100644 index 000000000..9ad116224 --- /dev/null +++ b/libcpu/i386_parse.y @@ -0,0 +1,1641 @@ +%{ +/* Parser for i386 CPU description. + Copyright (C) 2004, 2005, 2007 Red Hat, Inc. + Written by Ulrich Drepper , 2004. + + This program is Open Source software; you can redistribute it and/or + modify it under the terms of the Open Software License version 1.0 as + published by the Open Source Initiative. + + You should have received a copy of the Open Software License along + with this program; if not, you may obtain a copy of the Open Software + License version 1.0 from http://www.opensource.org/licenses/osl.php or + by writing the Open Source Initiative c/o Lawrence Rosen, Esq., + 3001 King Ranch Road, Ukiah, CA 95482. */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define obstack_chunk_alloc xmalloc +#define obstack_chunk_free free + +/* The error handler. */ +static void yyerror (const char *s); + +extern int yylex (void); +extern int i386_lineno; +extern char *infname; + + +struct known_bitfield +{ + char *name; + unsigned long int bits; + int tmp; +}; + + +struct bitvalue +{ + enum bittype { zeroone, field, failure } type; + union + { + unsigned int value; + struct known_bitfield *field; + }; + struct bitvalue *next; +}; + + +struct argname +{ + enum nametype { string, nfield } type; + union + { + char *str; + struct known_bitfield *field; + }; + struct argname *next; +}; + + +struct argument +{ + struct argname *name; + struct argument *next; +}; + + +struct instruction +{ + /* The byte encoding. */ + struct bitvalue *bytes; + + /* Prefix possible. */ + int repe; + int rep; + + /* Mnemonic. */ + char *mnemonic; + + /* Suffix. */ + enum { suffix_none = 0, suffix_w, suffix_w0, suffix_W, suffix_tttn, + suffix_w1, suffix_gg, suffix_GG, suffix_0g, suffix_gG, + suffix_predpd, suffix_predps, suffix_predsd, suffix_predss, + suffix_D } suffix; + + /* Flag set if modr/m is used. */ + int modrm; + + /* Operands. */ + struct operand + { + char *fct; + char *str; + int off1; + int off2; + int off3; + } operands[3]; + + struct instruction *next; +}; + + +struct synonym +{ + char *from; + char *to; +}; + + +struct suffix +{ + char *name; + int idx; +}; + + +struct argstring +{ + char *str; + int idx; +}; + + +static struct known_bitfield ax_reg = + { + .name = "ax", .bits = 0, .tmp = 0 + }; + +static struct known_bitfield dx_reg = + { + .name = "dx", .bits = 0, .tmp = 0 + }; + +static struct known_bitfield di_reg = + { + .name = "es_di", .bits = 0, .tmp = 0 + }; + +static struct known_bitfield si_reg = + { + .name = "ds_si", .bits = 0, .tmp = 0 + }; + +static struct known_bitfield bx_reg = + { + .name = "ds_bx", .bits = 0, .tmp = 0 + }; + + +static int bitfield_compare (const void *p1, const void *p2); +static void new_bitfield (char *name, unsigned long int num); +static void check_bits (struct bitvalue *value); +static int check_duplicates (struct bitvalue *val); +static int check_argsdef (struct bitvalue *bitval, struct argument *args); +static int check_bitsused (struct bitvalue *bitval, + struct known_bitfield *suffix, + struct argument *args); +static struct argname *combine (struct argname *name); +static void fillin_arg (struct bitvalue *bytes, struct argname *name, + struct instruction *instr, int n); +static void find_numbers (void); +static int compare_syn (const void *p1, const void *p2); +static int compare_suf (const void *p1, const void *p2); +static void instrtable_out (void); +#if 0 +static void create_mnemonic_table (void); +#endif + +static void *bitfields; +static struct instruction *instructions; +static size_t ninstructions; +static void *synonyms; +static void *suffixes; +static int nsuffixes; +static void *mnemonics; +size_t nmnemonics; +extern FILE *outfile; + +/* Number of bits used mnemonics. */ +#if 0 +static size_t best_mnemonic_bits; +#endif +%} + +%union { + unsigned long int num; + char *str; + char ch; + struct known_bitfield *field; + struct bitvalue *bit; + struct argname *name; + struct argument *arg; +} + +%token kMASK +%token kPREFIX +%token kSUFFIX +%token kSYNONYM +%token kID +%token kNUMBER +%token kPERCPERC +%token kBITFIELD +%token kCHAR +%token kSPACE + +%type bit byte bytes +%type bitfieldopt +%type argcomp arg +%type args optargs + +%defines + +%% + +spec: masks kPERCPERC '\n' instrs + { + if (error_message_count != 0) + error (EXIT_FAILURE, 0, + "terminated due to previous error"); + + instrtable_out (); + } + ; + +masks: masks '\n' mask + | mask + ; + +mask: kMASK kBITFIELD kNUMBER + { new_bitfield ($2, $3); } + | kPREFIX kBITFIELD + { new_bitfield ($2, -1); } + | kSUFFIX kBITFIELD + { new_bitfield ($2, -2); } + | kSYNONYM kBITFIELD kBITFIELD + { + struct synonym *newp = xmalloc (sizeof (*newp)); + newp->from = $2; + newp->to = $3; + if (tfind (newp, &synonyms, compare_syn) != NULL) + error (0, 0, + "%d: duplicate definition for synonym '%s'", + i386_lineno, $2); + else if (tsearch ( newp, &synonyms, compare_syn) == NULL) + error (EXIT_FAILURE, 0, "tsearch"); + } + | + ; + +instrs: instrs '\n' instr + | instr + ; + +instr: bytes ':' bitfieldopt kID bitfieldopt optargs + { + if ($3 != NULL && strcmp ($3->name, "RE") != 0 + && strcmp ($3->name, "R") != 0) + { + error (0, 0, "%d: only 'R' and 'RE' prefix allowed", + i386_lineno - 1); + } + if (check_duplicates ($1) == 0 + && check_argsdef ($1, $6) == 0 + && check_bitsused ($1, $5, $6) == 0) + { + struct instruction *newp = xcalloc (sizeof (*newp), + 1); + if ($3 != NULL) + { + if (strcmp ($3->name, "RE") == 0) + newp->repe = 1; + else if (strcmp ($3->name, "R") == 0) + newp->rep = 1; + } + + newp->bytes = $1; + newp->mnemonic = $4; + if (newp->mnemonic != (void *) -1l + && tfind ($4, &mnemonics, + (comparison_fn_t) strcmp) == NULL) + { + if (tsearch ($4, &mnemonics, + (comparison_fn_t) strcmp) == NULL) + error (EXIT_FAILURE, errno, "tsearch"); + ++nmnemonics; + } + + if ($5 != NULL) + { + if (strcmp ($5->name, "w") == 0) + newp->suffix = suffix_w; + else if (strcmp ($5->name, "w0") == 0) + newp->suffix = suffix_w0; + else if (strcmp ($5->name, "tttn") == 0) + newp->suffix = suffix_tttn; + else if (strcmp ($5->name, "w1") == 0) + newp->suffix = suffix_w1; + else if (strcmp ($5->name, "W") == 0) + newp->suffix = suffix_W; + else if (strcmp ($5->name, "gg") == 0) + newp->suffix = suffix_gg; + else if (strcmp ($5->name, "GG") == 0) + newp->suffix = suffix_GG; + else if (strcmp ($5->name, "0g") == 0) + newp->suffix = suffix_0g; + else if (strcmp ($5->name, "gG") == 0) + newp->suffix = suffix_gG; + else if (strcmp ($5->name, "predpd") == 0) + newp->suffix = suffix_predpd; + else if (strcmp ($5->name, "predps") == 0) + newp->suffix = suffix_predps; + else if (strcmp ($5->name, "predsd") == 0) + newp->suffix = suffix_predsd; + else if (strcmp ($5->name, "predss") == 0) + newp->suffix = suffix_predss; + else if (strcmp ($5->name, "D") == 0) + newp->suffix = suffix_D; + else + error (EXIT_FAILURE, 0, + "%s: %d: unknown suffix '%s'", + infname, i386_lineno - 1, $5->name); + + struct suffix search = { .name = $5->name }; + if (tfind (&search, &suffixes, compare_suf) + == NULL) + { + struct suffix *ns = xmalloc (sizeof (*ns)); + ns->name = $5->name; + ns->idx = ++nsuffixes; + if (tsearch (ns, &suffixes, compare_suf) + == NULL) + error (EXIT_FAILURE, errno, "tsearch"); + } + } + + struct argument *args = $6; + int n = 0; + while (args != NULL) + { + fillin_arg ($1, args->name, newp, n); + + args = args->next; + ++n; + } + + newp->next = instructions; + instructions = newp; + ++ninstructions; + } + } + | + ; + +bitfieldopt: kBITFIELD + { + struct known_bitfield search; + search.name = $1; + struct known_bitfield **res; + res = tfind (&search, &bitfields, bitfield_compare); + if (res == NULL) + { + error (0, 0, "%d: unknown bitfield '%s'", + i386_lineno, search.name); + $$ = NULL; + } + else + $$ = *res; + } + | + { $$ = NULL; } + ; + +bytes: bytes ',' byte + { + check_bits ($3); + + struct bitvalue *runp = $1; + while (runp->next != NULL) + runp = runp->next; + runp->next = $3; + $$ = $1; + } + | byte + { + check_bits ($1); + $$ = $1; + } + ; + +byte: byte bit + { + struct bitvalue *runp = $1; + while (runp->next != NULL) + runp = runp->next; + runp->next = $2; + $$ = $1; + } + | bit + { $$ = $1; } + ; + +bit: '0' + { + $$ = xmalloc (sizeof (struct bitvalue)); + $$->type = zeroone; + $$->value = 0; + $$->next = NULL; + } + | '1' + { + $$ = xmalloc (sizeof (struct bitvalue)); + $$->type = zeroone; + $$->value = 1; + $$->next = NULL; + } + | kBITFIELD + { + $$ = xmalloc (sizeof (struct bitvalue)); + struct known_bitfield search; + search.name = $1; + struct known_bitfield **res; + res = tfind (&search, &bitfields, bitfield_compare); + if (res == NULL) + { + error (0, 0, "%d: unknown bitfield '%s'", + i386_lineno, search.name); + $$->type = failure; + } + else + { + $$->type = field; + $$->field = *res; + } + $$->next = NULL; + } + ; + +optargs: kSPACE args + { $$ = $2; } + | + { $$ = NULL; } + ; + +args: args ',' arg + { + struct argument *runp = $1; + while (runp->next != NULL) + runp = runp->next; + runp->next = xmalloc (sizeof (struct argument)); + runp->next->name = combine ($3); + runp->next->next = NULL; + $$ = $1; + } + | arg + { + $$ = xmalloc (sizeof (struct argument)); + $$->name = combine ($1); + $$->next = NULL; + } + ; + +arg: arg argcomp + { + struct argname *runp = $1; + while (runp->next != NULL) + runp = runp->next; + runp->next = $2; + $$ = $1; + } + | argcomp + { $$ = $1; } + ; +argcomp: kBITFIELD + { + $$ = xmalloc (sizeof (struct argname)); + $$->type = nfield; + $$->next = NULL; + + struct known_bitfield search; + search.name = $1; + struct known_bitfield **res; + res = tfind (&search, &bitfields, bitfield_compare); + if (res == NULL) + { + if (strcmp ($1, "ax") == 0) + $$->field = &ax_reg; + else if (strcmp ($1, "dx") == 0) + $$->field = &dx_reg; + else if (strcmp ($1, "es_di") == 0) + $$->field = &di_reg; + else if (strcmp ($1, "ds_si") == 0) + $$->field = &si_reg; + else if (strcmp ($1, "ds_bx") == 0) + $$->field = &bx_reg; + else + { + error (0, 0, "%d: unknown bitfield '%s'", + i386_lineno, search.name); + $$->field = NULL; + } + } + else + $$->field = *res; + } + | kCHAR + { + $$ = xmalloc (sizeof (struct argname)); + $$->type = string; + $$->next = NULL; + $$->str = xmalloc (2); + $$->str[0] = $1; + $$->str[1] = '\0'; + } + | kID + { + $$ = xmalloc (sizeof (struct argname)); + $$->type = string; + $$->next = NULL; + $$->str = $1; + } + | ':' + { + $$ = xmalloc (sizeof (struct argname)); + $$->type = string; + $$->next = NULL; + $$->str = xmalloc (2); + $$->str[0] = ':'; + $$->str[1] = '\0'; + } + ; + +%% + +static void +yyerror (const char *s) +{ + error (0, 0, gettext ("while reading i386 CPU description: %s at line %d"), + gettext (s), i386_lineno); +} + + +static int +bitfield_compare (const void *p1, const void *p2) +{ + struct known_bitfield *f1 = (struct known_bitfield *) p1; + struct known_bitfield *f2 = (struct known_bitfield *) p2; + + return strcmp (f1->name, f2->name); +} + + +static void +new_bitfield (char *name, unsigned long int num) +{ + struct known_bitfield *newp = xmalloc (sizeof (struct known_bitfield)); + newp->name = name; + newp->bits = num; + newp->tmp = 0; + + if (tfind (newp, &bitfields, bitfield_compare) != NULL) + { + error (0, 0, "%d: duplicated definition of bitfield '%s'", + i386_lineno, name); + free (name); + return; + } + + if (tsearch (newp, &bitfields, bitfield_compare) == NULL) + error (EXIT_FAILURE, errno, "%d: cannot insert new bitfield '%s'", + i386_lineno, name); +} + + +/* Check that the number of bits is a multiple of 8. */ +static void +check_bits (struct bitvalue *val) +{ + struct bitvalue *runp = val; + unsigned int total = 0; + + while (runp != NULL) + { + if (runp->type == zeroone) + ++total; + else if (runp->field == NULL) + /* No sense doing anything, the field is not known. */ + return; + else + total += runp->field->bits; + + runp = runp->next; + } + + if (total % 8 != 0) + { + struct obstack os; + obstack_init (&os); + + while (val != NULL) + { + if (val->type == zeroone) + obstack_printf (&os, "%u", val->value); + else + obstack_printf (&os, "{%s}", val->field->name); + val = val->next; + } + obstack_1grow (&os, '\0'); + + error (0, 0, "%d: field '%s' not a multiple of 8 bits in size", + i386_lineno, (char *) obstack_finish (&os)); + + obstack_free (&os, NULL); + } +} + + +static int +check_duplicates (struct bitvalue *val) +{ + static int testcnt; + ++testcnt; + + int result = 0; + while (val != NULL) + { + if (val->type == field && val->field != NULL) + { + if (val->field->tmp == testcnt) + { + error (0, 0, "%d: bitfield '%s' used more than once", + i386_lineno - 1, val->field->name); + result = 1; + } + val->field->tmp = testcnt; + } + + val = val->next; + } + + return result; +} + + +static int +check_argsdef (struct bitvalue *bitval, struct argument *args) +{ + int result = 0; + + while (args != NULL) + { + for (struct argname *name = args->name; name != NULL; name = name->next) + if (name->type == nfield && name->field != NULL + && name->field != &ax_reg && name->field != &dx_reg + && name->field != &di_reg && name->field != &si_reg + && name->field != &bx_reg) + { + struct bitvalue *runp = bitval; + + while (runp != NULL) + if (runp->type == field && runp->field == name->field) + break; + else + runp = runp->next; + + if (runp == NULL) + { + error (0, 0, "%d: unknown bitfield '%s' used in output format", + i386_lineno - 1, name->field->name); + result = 1; + } + } + + args = args->next; + } + + return result; +} + + +static int +check_bitsused (struct bitvalue *bitval, struct known_bitfield *suffix, + struct argument *args) +{ + int result = 0; + + while (bitval != NULL) + { + if (bitval->type == field && bitval->field != NULL + && bitval->field != suffix + /* {w} is handled special. */ + && strcmp (bitval->field->name, "w") != 0) + { + struct argument *runp; + for (runp = args; runp != NULL; runp = runp->next) + { + struct argname *name = runp->name; + + while (name != NULL) + if (name->type == nfield && name->field == bitval->field) + break; + else + name = name->next; + + if (name != NULL) + break; + } + +#if 0 + if (runp == NULL) + { + error (0, 0, "%d: bitfield '%s' not used", + i386_lineno - 1, bitval->field->name); + result = 1; + } +#endif + } + + bitval = bitval->next; + } + + return result; +} + + +static struct argname * +combine (struct argname *name) +{ + struct argname *last_str = NULL; + for (struct argname *runp = name; runp != NULL; runp = runp->next) + { + if (runp->type == string) + { + if (last_str == NULL) + last_str = runp; + else + { + last_str->str = xrealloc (last_str->str, + strlen (last_str->str) + + strlen (runp->str) + 1); + strcat (last_str->str, runp->str); + last_str->next = runp->next; + } + } + else + last_str = NULL; + } + return name; +} + + +#define obstack_grow_str(ob, str) obstack_grow (ob, str, strlen (str)) + + +static void +fillin_arg (struct bitvalue *bytes, struct argname *name, + struct instruction *instr, int n) +{ + static struct obstack ob; + static int initialized; + if (! initialized) + { + initialized = 1; + obstack_init (&ob); + } + + struct argname *runp = name; + int cnt = 0; + while (runp != NULL) + { + /* We ignore strings in the function name. */ + if (runp->type == string) + { + if (instr->operands[n].str != NULL) + error (EXIT_FAILURE, 0, + "%d: cannot have more than one string parameter", + i386_lineno - 1); + + instr->operands[n].str = runp->str; + } + else + { + assert (runp->type == nfield); + + /* Construct the function name. */ + if (cnt++ > 0) + obstack_1grow (&ob, '$'); + + if (runp->field == NULL) + /* Add some string which contains invalid characters. */ + obstack_grow_str (&ob, "!!!INVALID!!!"); + else + obstack_grow_str (&ob, runp->field->name); + + /* Now compute the bit offset of the field. */ + struct bitvalue *b = bytes; + int bitoff = 0; + if (runp->field != NULL) + while (b != NULL) + { + if (b->type == field && b->field != NULL) + { + if (strcmp (b->field->name, runp->field->name) == 0) + break; + bitoff += b->field->bits; + } + else + ++bitoff; + + b = b->next; + } + if (instr->operands[n].off1 == 0) + instr->operands[n].off1 = bitoff; + else if (instr->operands[n].off2 == 0) + instr->operands[n].off2 = bitoff; + else if (instr->operands[n].off3 == 0) + instr->operands[n].off3 = bitoff; + else + error (EXIT_FAILURE, 0, + "%d: cannot have more than three fields in parameter", + i386_lineno - 1); + + if (runp->field != NULL + && strncasecmp (runp->field->name, "mod", 3) == 0) + instr->modrm = 1; + } + + runp = runp->next; + } + if (obstack_object_size (&ob) == 0) + obstack_grow_str (&ob, "string"); + obstack_1grow (&ob, '\0'); + char *fct = obstack_finish (&ob); + + struct synonym search = { .from = fct }; + struct synonym **res = tfind (&search, &synonyms, compare_syn); + if (res != NULL) + fct = (*res)->to; + + instr->operands[n].fct = fct; +} + + +#if 0 +static void +nameout (const void *nodep, VISIT value, int level) +{ + if (value == leaf || value == postorder) + printf (" %s\n", *(const char **) nodep); +} +#endif + + +static int +compare_argstring (const void *p1, const void *p2) +{ + const struct argstring *a1 = (const struct argstring *) p1; + const struct argstring *a2 = (const struct argstring *) p2; + + return strcmp (a1->str, a2->str); +} + + +static int maxoff[3][3]; +static int minoff[3][3] = { { 1000, 1000, 1000 }, + { 1000, 1000, 1000 }, + { 1000, 1000, 1000 } }; +static int nbitoff[3][3]; +static void *fct_names[3]; +static int nbitfct[3]; +static int nbitsuf; +static void *strs[3]; +static int nbitstr[3]; +static int total_bits = 2; // Already counted the rep/repe bits. + +static void +find_numbers (void) +{ + int nfct_names[3] = { 0, 0, 0 }; + int nstrs[3] = { 0, 0, 0 }; + + /* We reverse the order of the instruction list while processing it. + Later phases need it in the order in which the input file has + them. */ + struct instruction *reversed = NULL; + + struct instruction *runp = instructions; + while (runp != NULL) + { + for (int i = 0; i < 3; ++i) + if (runp->operands[i].fct != NULL) + { + struct argstring search = { .str = runp->operands[i].fct }; + if (tfind (&search, &fct_names[i], compare_argstring) == NULL) + { + struct argstring *newp = xmalloc (sizeof (*newp)); + newp->str = runp->operands[i].fct; + newp->idx = 0; + if (tsearch (newp, &fct_names[i], compare_argstring) == NULL) + error (EXIT_FAILURE, errno, "tsearch"); + ++nfct_names[i]; + } + + if (runp->operands[i].str != NULL) + { + search.str = runp->operands[i].str; + if (tfind (&search, &strs[i], compare_argstring) == NULL) + { + struct argstring *newp = xmalloc (sizeof (*newp)); + newp->str = runp->operands[i].str; + newp->idx = 0; + if (tsearch (newp, &strs[i], compare_argstring) == NULL) + error (EXIT_FAILURE, errno, "tsearch"); + ++nstrs[i]; + } + } + + maxoff[i][0] = MAX (maxoff[i][0], runp->operands[i].off1); + maxoff[i][1] = MAX (maxoff[i][1], runp->operands[i].off2); + maxoff[i][2] = MAX (maxoff[i][2], runp->operands[i].off3); + + if (runp->operands[i].off1 > 0) + minoff[i][0] = MIN (minoff[i][0], runp->operands[i].off1); + if (runp->operands[i].off2 > 0) + minoff[i][1] = MIN (minoff[i][1], runp->operands[i].off2); + if (runp->operands[i].off3 > 0) + minoff[i][2] = MIN (minoff[i][2], runp->operands[i].off3); + } + + struct instruction *old = runp; + runp = runp->next; + + old->next = reversed; + reversed = old; + } + instructions = reversed; + + int d; + int c; + for (int i = 0; i < 3; ++i) + { + // printf ("min1 = %d, min2 = %d, min3 = %d\n", minoff[i][0], minoff[i][1], minoff[i][2]); + // printf ("max1 = %d, max2 = %d, max3 = %d\n", maxoff[i][0], maxoff[i][1], maxoff[i][2]); + + if (minoff[i][0] == 1000) + nbitoff[i][0] = 0; + else + { + nbitoff[i][0] = 1; + d = maxoff[i][0] - minoff[i][0]; + c = 1; + while (c < d) + { + ++nbitoff[i][0]; + c *= 2; + } + total_bits += nbitoff[i][0]; + } + + if (minoff[i][1] == 1000) + nbitoff[i][1] = 0; + else + { + nbitoff[i][1] = 1; + d = maxoff[i][1] - minoff[i][1]; + c = 1; + while (c < d) + { + ++nbitoff[i][1]; + c *= 2; + } + total_bits += nbitoff[i][1]; + } + + if (minoff[i][2] == 1000) + nbitoff[i][2] = 0; + else + { + nbitoff[i][2] = 1; + d = maxoff[i][2] - minoff[i][2]; + c = 1; + while (c < d) + { + ++nbitoff[i][2]; + c *= 2; + } + total_bits += nbitoff[i][2]; + } + // printf ("off1 = %d, off2 = %d, off3 = %d\n", nbitoff[i][0], nbitoff[i][1], nbitoff[i][2]); + + nbitfct[i] = 1; + d = nfct_names[i]; + c = 1; + while (c < d) + { + ++nbitfct[i]; + c *= 2; + } + total_bits += nbitfct[i]; + // printf ("%d fct[%d], %d bits\n", nfct_names[i], i, nbitfct[i]); + + if (nstrs[i] != 0) + { + nbitstr[i] = 1; + d = nstrs[i]; + c = 1; + while (c < d) + { + ++nbitstr[i]; + c *= 2; + } + total_bits += nbitstr[i]; + } + + // twalk (fct_names[i], nameout); + } + + nbitsuf = 0; + d = nsuffixes; + c = 1; + while (c < d) + { + ++nbitsuf; + c *= 2; + } + total_bits += nbitsuf; + // printf ("%d suffixes, %d bits\n", nsuffixes, nbitsuf); +} + + +static int +compare_syn (const void *p1, const void *p2) +{ + const struct synonym *s1 = (const struct synonym *) p1; + const struct synonym *s2 = (const struct synonym *) p2; + + return strcmp (s1->from, s2->from); +} + + +static int +compare_suf (const void *p1, const void *p2) +{ + const struct suffix *s1 = (const struct suffix *) p1; + const struct suffix *s2 = (const struct suffix *) p2; + + return strcmp (s1->name, s2->name); +} + + +static int count_op_str; +static void +print_op_str (const void *nodep, VISIT value, + int level __attribute__ ((unused))) +{ + if (value == leaf || value == postorder) + { + fprintf (outfile, " \"%s\",\n", (*(struct argstring **) nodep)->str); + (*(struct argstring **) nodep)->idx = ++count_op_str; + } +} + + +static void +print_op_fct (const void *nodep, VISIT value, + int level __attribute__ ((unused))) +{ + if (value == leaf || value == postorder) + { + fprintf (outfile, " FCT_%s,\n", (*(struct argstring **) nodep)->str); + (*(struct argstring **) nodep)->idx = ++count_op_str; + } +} + + +static void +instrtable_out (void) +{ + find_numbers (); + +#if 0 + create_mnemonic_table (); + + fprintf (outfile, "#define MNEMONIC_BITS %zu\n", best_mnemonic_bits); +#else + fprintf (outfile, "#define MNEMONIC_BITS %ld\n", + lrint (ceil (log2 (NMNES)))); +#endif + fprintf (outfile, "#define SUFFIX_BITS %d\n", nbitsuf); + for (int i = 0; i < 3; ++i) + { + fprintf (outfile, "#define FCT%d_BITS %d\n", i + 1, nbitfct[i]); + if (nbitstr[i] != 0) + fprintf (outfile, "#define STR%d_BITS %d\n", i + 1, nbitstr[i]); + fprintf (outfile, "#define OFF%d_1_BITS %d\n", i + 1, nbitoff[i][0]); + fprintf (outfile, "#define OFF%d_1_BIAS %d\n", i + 1, minoff[i][0]); + if (nbitoff[i][1] != 0) + { + fprintf (outfile, "#define OFF%d_2_BITS %d\n", i + 1, nbitoff[i][1]); + fprintf (outfile, "#define OFF%d_2_BIAS %d\n", i + 1, minoff[i][1]); + } + if (nbitoff[i][2] != 0) + { + fprintf (outfile, "#define OFF%d_3_BITS %d\n", i + 1, nbitoff[i][2]); + fprintf (outfile, "#define OFF%d_3_BIAS %d\n", i + 1, minoff[i][2]); + } + } + + fputs ("\n#include \n\n", outfile); + + +#define APPEND(a, b) APPEND_ (a, b) +#define APPEND_(a, b) a##b +#define EMIT_SUFFIX(suf) \ + fprintf (outfile, "#define suffix_%s %d\n", #suf, APPEND (suffix_, suf)) + EMIT_SUFFIX (none); + EMIT_SUFFIX (w); + EMIT_SUFFIX (w0); + EMIT_SUFFIX (W); + EMIT_SUFFIX (tttn); + EMIT_SUFFIX (D); + EMIT_SUFFIX (w1); + + fputc_unlocked ('\n', outfile); + + for (int i = 0; i < 3; ++i) + { + /* Functions. */ + count_op_str = 0; + fprintf (outfile, "static opfct_t op%d_fct[] =\n{\n NULL,\n", i + 1); + twalk (fct_names[i], print_op_fct); + fputs ("};\n", outfile); + + /* The operand strings. */ + if (nbitstr[i] != 0) + { + count_op_str = 0; + fprintf (outfile, "static const char *op%d_str[] =\n{\n NULL,\n", + i + 1); + twalk (strs[i], print_op_str); + fputs ("};\n", outfile); + } + } + + + fputs ("static const struct instr_enc instrtab[] =\n{\n", outfile); + struct instruction *instr; + for (instr = instructions; instr != NULL; instr = instr->next) + { + fputs (" {", outfile); + if (instr->mnemonic == (void *) -1l) + fputs (" .mnemonic = MNE_INVALID,", outfile); + else + fprintf (outfile, " .mnemonic = MNE_%s,", instr->mnemonic); + fprintf (outfile, " .rep = %d,", instr->rep); + fprintf (outfile, " .repe = %d,", instr->repe); + fprintf (outfile, " .suffix = %d,", instr->suffix); + fprintf (outfile, " .modrm = %d,", instr->modrm); + + for (int i = 0; i < 3; ++i) + { + int idx = 0; + if (instr->operands[i].fct != NULL) + { + struct argstring search = { .str = instr->operands[i].fct }; + struct argstring **res = tfind (&search, &fct_names[i], + compare_argstring); + assert (res != NULL); + idx = (*res)->idx; + } + fprintf (outfile, " .fct%d = %d,", i + 1, idx); + + idx = 0; + if (instr->operands[i].str != NULL) + { + struct argstring search = { .str = instr->operands[i].str }; + struct argstring **res = tfind (&search, &strs[i], + compare_argstring); + assert (res != NULL); + idx = (*res)->idx; + } + if (nbitstr[i] != 0) + fprintf (outfile, " .str%d = %d,", i + 1, idx); + + fprintf (outfile, " .off%d_1 = %d,", i + 1, + MAX (0, instr->operands[i].off1 - minoff[i][0])); + + if (nbitoff[i][1] != 0) + fprintf (outfile, " .off%d_2 = %d,", i + 1, + MAX (0, instr->operands[i].off2 - minoff[i][1])); + + if (nbitoff[i][2] != 0) + fprintf (outfile, " .off%d_3 = %d,", i + 1, + MAX (0, instr->operands[i].off3 - minoff[i][2])); + } + + fputs (" },\n", outfile); + } + fputs ("};\n", outfile); + + fputs ("static const uint8_t match_data[] =\n{\n", outfile); + size_t cnt = 0; + for (instr = instructions; instr != NULL; instr = instr->next, ++cnt) + { + /* First count the number of bytes. */ + size_t totalbits = 0; + size_t zerobits = 0; + struct bitvalue *b = instr->bytes; + while (b != NULL) + { + if (b->type == zeroone) + { + ++totalbits; + zerobits = 0; + } + else + { + totalbits += b->field->bits; + /* We must always count the mod/rm byte. */ + if (strncasecmp (b->field->name, "mod", 3) == 0) + zerobits = 0; + else + zerobits += b->field->bits; + } + b = b->next; + } + size_t nbytes = (totalbits - zerobits + 7) / 8; + assert (nbytes > 0); + + fprintf (outfile, " %#zx,", nbytes); + + /* Now create the mask and byte values. */ + uint8_t byte = 0; + uint8_t mask = 0; + int nbits = 0; + b = instr->bytes; + while (b != NULL) + { + if (b->type == zeroone) + { + byte = (byte << 1) | b->value; + mask = (mask << 1) | 1; + if (++nbits == 8) + { + fprintf (outfile, " %#" PRIx8 ", %#" PRIx8 ",", mask, byte); + byte = mask = nbits = 0; + if (--nbytes == 0) + break; + } + } + else + { + unsigned long int remaining = b->field->bits; + while (nbits + remaining > 8) + { + fprintf (outfile, " %#" PRIx8 ", %#" PRIx8 ",", + mask << (8 - nbits), byte << (8 - nbits)); + remaining = nbits + remaining - 8; + byte = mask = nbits = 0; + if (--nbytes == 0) + break; + } + byte <<= remaining; + mask <<= remaining; + nbits += remaining; + if (nbits == 8) + { + fprintf (outfile, " %#" PRIx8 ", %#" PRIx8 ",", mask, byte); + byte = mask = nbits = 0; + if (--nbytes == 0) + break; + } + } + b = b->next; + } + + fprintf (outfile, " %#zx, %#zx,\n", cnt & 0xff, cnt >> 8); + } + fputs ("};\n", outfile); +} + + +#if 0 +static size_t mnemonic_maxlen; +static size_t mnemonic_minlen; +static size_t +which_chars (const char *str[], size_t nstr) +{ + char used_char[256]; + memset (used_char, '\0', sizeof (used_char)); + mnemonic_maxlen = 0; + mnemonic_minlen = 10000; + for (size_t cnt = 0; cnt < nstr; ++cnt) + { + const unsigned char *cp = (const unsigned char *) str[cnt]; + mnemonic_maxlen = MAX (mnemonic_maxlen, strlen ((char *) cp)); + mnemonic_minlen = MIN (mnemonic_minlen, strlen ((char *) cp)); + do + used_char[*cp++] = 1; + while (*cp != '\0'); + } + size_t nused_char = 0; + for (size_t cnt = 0; cnt < 256; ++cnt) + if (used_char[cnt] != 0) + ++nused_char; + return nused_char; +} + + +static const char **mnemonic_strs; +static size_t nmnemonic_strs; +static void +add_mnemonics (const void *nodep, VISIT value, + int level __attribute__ ((unused))) +{ + if (value == leaf || value == postorder) + mnemonic_strs[nmnemonic_strs++] = *(const char **) nodep; +} + + +struct charfreq +{ + char ch; + int freq; +}; +static struct charfreq pfxfreq[256]; +static struct charfreq sfxfreq[256]; + + +static int +compare_freq (const void *p1, const void *p2) +{ + const struct charfreq *c1 = (const struct charfreq *) p1; + const struct charfreq *c2 = (const struct charfreq *) p2; + + if (c1->freq > c2->freq) + return -1; + if (c1->freq < c2->freq) + return 1; + return 0; +} + + +static size_t +compute_pfxfreq (const char *str[], size_t nstr) +{ + memset (pfxfreq, '\0', sizeof (pfxfreq)); + + for (size_t i = 0; i < nstr; ++i) + pfxfreq[i].ch = i; + + for (size_t i = 0; i < nstr; ++i) + ++pfxfreq[*((const unsigned char *) str[i])].freq; + + qsort (pfxfreq, 256, sizeof (struct charfreq), compare_freq); + + size_t n = 0; + while (n < 256 && pfxfreq[n].freq != 0) + ++n; + return n; +} + + +struct strsnlen +{ + const char *str; + size_t len; +}; + +static size_t +compute_sfxfreq (size_t nstr, struct strsnlen *strsnlen) +{ + memset (sfxfreq, '\0', sizeof (sfxfreq)); + + for (size_t i = 0; i < nstr; ++i) + sfxfreq[i].ch = i; + + for (size_t i = 0; i < nstr; ++i) + ++sfxfreq[((const unsigned char *) strchrnul (strsnlen[i].str, '\0'))[-1]].freq; + + qsort (sfxfreq, 256, sizeof (struct charfreq), compare_freq); + + size_t n = 0; + while (n < 256 && sfxfreq[n].freq != 0) + ++n; + return n; +} + + +static void +create_mnemonic_table (void) +{ + mnemonic_strs = xmalloc (nmnemonics * sizeof (char *)); + + twalk (mnemonics, add_mnemonics); + + (void) which_chars (mnemonic_strs, nmnemonic_strs); + + size_t best_so_far = 100000000; + char *best_prefix = NULL; + char *best_suffix = NULL; + char *best_table = NULL; + size_t best_table_size = 0; + size_t best_table_bits = 0; + size_t best_prefix_bits = 0; + + /* We can precompute the prefix characters. */ + size_t npfx_char = compute_pfxfreq (mnemonic_strs, nmnemonic_strs); + + /* Compute best size for string representation including explicit NUL. */ + for (size_t pfxbits = 0; (1u << pfxbits) < 2 * npfx_char; ++pfxbits) + { + char prefix[1 << pfxbits]; + size_t i; + for (i = 0; i < (1u << pfxbits) - 1; ++i) + prefix[i] = pfxfreq[i].ch; + prefix[i] = '\0'; + + struct strsnlen strsnlen[nmnemonic_strs]; + + for (i = 0; i < nmnemonic_strs; ++i) + { + if (strchr (prefix, *mnemonic_strs[i]) != NULL) + strsnlen[i].str = mnemonic_strs[i] + 1; + else + strsnlen[i].str = mnemonic_strs[i]; + strsnlen[i].len = strlen (strsnlen[i].str); + } + + /* With the prefixes gone, try to combine strings. */ + size_t nstrsnlen = 1; + for (i = 1; i < nmnemonic_strs; ++i) + { + size_t j; + for (j = 0; j < nstrsnlen; ++j) + if (strsnlen[i].len > strsnlen[j].len + && strcmp (strsnlen[j].str, + strsnlen[i].str + (strsnlen[i].len + - strsnlen[j].len)) == 0) + { + strsnlen[j] = strsnlen[i]; + break; + } + else if (strsnlen[i].len < strsnlen[j].len + && strcmp (strsnlen[i].str, + strsnlen[j].str + (strsnlen[j].len + - strsnlen[i].len)) == 0) + break; +; + if (j == nstrsnlen) + strsnlen[nstrsnlen++] = strsnlen[i]; + } + + size_t nsfx_char = compute_sfxfreq (nstrsnlen, strsnlen); + + for (size_t sfxbits = 0; (1u << sfxbits) < 2 * nsfx_char; ++sfxbits) + { + char suffix[1 << sfxbits]; + + for (i = 0; i < (1u << sfxbits) - 1; ++i) + suffix[i] = sfxfreq[i].ch; + suffix[i] = '\0'; + + size_t newlen[nstrsnlen]; + + for (i = 0; i < nstrsnlen; ++i) + if (strchr (suffix, strsnlen[i].str[strsnlen[i].len - 1]) != NULL) + newlen[i] = strsnlen[i].len - 1; + else + newlen[i] = strsnlen[i].len; + + char charused[256]; + memset (charused, '\0', sizeof (charused)); + size_t ncharused = 0; + + const char *tablestr[nstrsnlen]; + size_t ntablestr = 1; + tablestr[0] = strsnlen[0].str; + size_t table = newlen[0] + 1; + for (i = 1; i < nstrsnlen; ++i) + { + size_t j; + for (j = 0; j < ntablestr; ++j) + if (newlen[i] > newlen[j] + && memcmp (tablestr[j], + strsnlen[i].str + (newlen[i] - newlen[j]), + newlen[j]) == 0) + { + table += newlen[i] - newlen[j]; + tablestr[j] = strsnlen[i].str; + newlen[j] = newlen[i]; + break; + } + else if (newlen[i] < newlen[j] + && memcmp (strsnlen[i].str, + tablestr[j] + (newlen[j] - newlen[i]), + newlen[i]) == 0) + break; + + if (j == ntablestr) + { + table += newlen[i] + 1; + tablestr[ntablestr] = strsnlen[i].str; + newlen[ntablestr] = newlen[i]; + + ++ntablestr; + } + + for (size_t x = 0; x < newlen[j]; ++x) + if (charused[((const unsigned char *) tablestr[j])[x]]++ == 0) + ++ncharused; + } + + size_t ncharused_bits = 0; + i = 1; + while (i < ncharused) + { + i *= 2; + ++ncharused_bits; + } + + size_t table_bits = 0; + i = 1; + while (i < table) + { + i *= 2; + ++table_bits; + } + + size_t mnemonic_bits = table_bits + pfxbits + sfxbits; + size_t new_total = (((table + 7) / 8) * ncharused_bits + ncharused + + (pfxbits == 0 ? 0 : (1 << pfxbits) - 1) + + (sfxbits == 0 ? 0 : (1 << sfxbits) - 1) + + (((total_bits + mnemonic_bits + 7) / 8) + * ninstructions)); + + if (new_total < best_so_far) + { + best_so_far = new_total; + best_mnemonic_bits = mnemonic_bits; + + free (best_suffix); + best_suffix = xstrdup (suffix); + + free (best_prefix); + best_prefix = xstrdup (prefix); + best_prefix_bits = pfxbits; + + best_table_size = table; + best_table_bits = table_bits; + char *cp = best_table = xrealloc (best_table, table); + for (i = 0; i < ntablestr; ++i) + { + assert (cp + newlen[i] + 1 <= best_table + table); + cp = mempcpy (cp, tablestr[i], newlen[i]); + *cp++ = '\0'; + } + assert (cp == best_table + table); + } + } + } + + fputs ("static const char mnemonic_table[] =\n\"", outfile); + for (size_t i = 0; i < best_table_size; ++i) + { + if (((i + 1) % 60) == 0) + fputs ("\"\n\"", outfile); + if (!isascii (best_table[i]) || !isprint (best_table[i])) + fprintf (outfile, "\\%03o", best_table[i]); + else + fputc (best_table[i], outfile); + } + fputs ("\";\n", outfile); + + if (best_prefix[0] != '\0') + fprintf (outfile, + "static const char prefix[%zu] = \"%s\";\n" + "#define PREFIXCHAR_BITS %zu\n", + strlen (best_prefix), best_prefix, best_prefix_bits); + else + fputs ("#define NO_PREFIX\n", outfile); + + if (best_suffix[0] != '\0') + fprintf (outfile, "static const char suffix[%zu] = \"%s\";\n", + strlen (best_suffix), best_suffix); + else + fputs ("#define NO_SUFFIX\n", outfile); + + for (size_t i = 0; i < nmnemonic_strs; ++i) + { + const char *mne = mnemonic_strs[i]; + + size_t pfxval = 0; + char *cp = strchr (best_prefix, *mne); + if (cp != NULL) + { + pfxval = 1 + (cp - best_prefix); + ++mne; + } + + size_t l = strlen (mne); + + size_t sfxval = 0; + cp = strchr (best_suffix, mne[l - 1]); + if (cp != NULL) + { + sfxval = 1 + (cp - best_suffix); + --l; + } + + char *off = memmem (best_table, best_table_size, mne, l); + while (off[l] != '\0') + { + off = memmem (off + 1, best_table_size, mne, l); + assert (off != NULL); + } + + fprintf (outfile, "#define MNE_%s %#zx\n", + mnemonic_strs[i], + (off - best_table) + + ((pfxval + (sfxval << best_prefix_bits)) << best_table_bits)); + } +} +#endif diff --git a/libcpu/memory-access.h b/libcpu/memory-access.h new file mode 100644 index 000000000..0d61f4854 --- /dev/null +++ b/libcpu/memory-access.h @@ -0,0 +1,168 @@ +/* Unaligned memory access functionality. + Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005 Red Hat, Inc. + Written by Ulrich Drepper , 2001. + + This program is Open Source software; you can redistribute it and/or + modify it under the terms of the Open Software License version 1.0 as + published by the Open Source Initiative. + + You should have received a copy of the Open Software License along + with this program; if not, you may obtain a copy of the Open Software + License version 1.0 from http://www.opensource.org/licenses/osl.php or + by writing the Open Source Initiative c/o Lawrence Rosen, Esq., + 3001 King Ranch Road, Ukiah, CA 95482. */ + +#ifndef _MEMORY_ACCESS_H +#define _MEMORY_ACCESS_H 1 + +#include +#include +#include +#include + + +/* When loading this file we require the macro MACHINE_ENCODING to be + defined to signal the endianness of the architecture which is + defined. */ +#ifndef MACHINE_ENCODING +# error "MACHINE_ENCODING needs to be defined" +#endif +#if MACHINE_ENCODING != __BIG_ENDIAN && MACHINE_ENCODING != __LITTLE_ENDIAN +# error "MACHINE_ENCODING must signal either big or little endian" +#endif + + +/* We use simple memory access functions in case the hardware allows it. + The caller has to make sure we don't have alias problems. */ +#if ALLOW_UNALIGNED + +# define read_2ubyte_unaligned(Addr) \ + (unlikely (MACHINE_ENCODING != __BYTE_ORDER) \ + ? bswap_16 (*((const uint16_t *) (Addr))) \ + : *((const uint16_t *) (Addr))) +# define read_2sbyte_unaligned(Addr) \ + (unlikely (MACHINE_ENCODING != __BYTE_ORDER) \ + ? (int16_t) bswap_16 (*((const int16_t *) (Addr))) \ + : *((const int16_t *) (Addr))) + +# define read_4ubyte_unaligned_noncvt(Addr) \ + *((const uint32_t *) (Addr)) +# define read_4ubyte_unaligned(Addr) \ + (unlikely (MACHINE_ENCODING != __BYTE_ORDER) \ + ? bswap_32 (*((const uint32_t *) (Addr))) \ + : *((const uint32_t *) (Addr))) +# define read_4sbyte_unaligned(Addr) \ + (unlikely (MACHINE_ENCODING != __BYTE_ORDER) \ + ? (int32_t) bswap_32 (*((const int32_t *) (Addr))) \ + : *((const int32_t *) (Addr))) + +# define read_8ubyte_unaligned(Addr) \ + (unlikely (MACHINE_ENCODING != __BYTE_ORDER) \ + ? bswap_64 (*((const uint64_t *) (Addr))) \ + : *((const uint64_t *) (Addr))) +# define read_8sbyte_unaligned(Addr) \ + (unlikely (MACHINE_ENCODING != __BYTE_ORDER) \ + ? (int64_t) bswap_64 (*((const int64_t *) (Addr))) \ + : *((const int64_t *) (Addr))) + +#else + +union unaligned + { + void *p; + uint16_t u2; + uint32_t u4; + uint64_t u8; + int16_t s2; + int32_t s4; + int64_t s8; + } __attribute__ ((packed)); + +static inline uint16_t +read_2ubyte_unaligned (const void *p) +{ + const union unaligned *up = p; + if (MACHINE_ENCODING != __BYTE_ORDER) + return bswap_16 (up->u2); + return up->u2; +} +static inline int16_t +read_2sbyte_unaligned (const void *p) +{ + const union unaligned *up = p; + if (MACHINE_ENCODING != __BYTE_ORDER) + return (int16_t) bswap_16 (up->u2); + return up->s2; +} + +static inline uint32_t +read_4ubyte_unaligned_noncvt (const void *p) +{ + const union unaligned *up = p; + return up->u4; +} +static inline uint32_t +read_4ubyte_unaligned (const void *p) +{ + const union unaligned *up = p; + if (MACHINE_ENCODING != __BYTE_ORDER) + return bswap_32 (up->u4); + return up->u4; +} +static inline int32_t +read_4sbyte_unaligned (const void *p) +{ + const union unaligned *up = p; + if (MACHINE_ENCODING != __BYTE_ORDER) + return (int32_t) bswap_32 (up->u4); + return up->s4; +} + +static inline uint64_t +read_8ubyte_unaligned (const void *p) +{ + const union unaligned *up = p; + if (MACHINE_ENCODING != __BYTE_ORDER) + return bswap_64 (up->u8); + return up->u8; +} +static inline int64_t +read_8sbyte_unaligned (const void *p) +{ + const union unaligned *up = p; + if (MACHINE_ENCODING != __BYTE_ORDER) + return (int64_t) bswap_64 (up->u8); + return up->s8; +} + +#endif /* allow unaligned */ + + +#define read_2ubyte_unaligned_inc(Addr) \ + ({ uint16_t t_ = read_2ubyte_unaligned (Addr); \ + Addr = (__typeof (Addr)) (((uintptr_t) (Addr)) + 2); \ + t_; }) +#define read_2sbyte_unaligned_inc(Addr) \ + ({ int16_t t_ = read_2sbyte_unaligned (Addr); \ + Addr = (__typeof (Addr)) (((uintptr_t) (Addr)) + 2); \ + t_; }) + +#define read_4ubyte_unaligned_inc(Addr) \ + ({ uint32_t t_ = read_4ubyte_unaligned (Addr); \ + Addr = (__typeof (Addr)) (((uintptr_t) (Addr)) + 4); \ + t_; }) +#define read_4sbyte_unaligned_inc(Addr) \ + ({ int32_t t_ = read_4sbyte_unaligned (Addr); \ + Addr = (__typeof (Addr)) (((uintptr_t) (Addr)) + 4); \ + t_; }) + +#define read_8ubyte_unaligned_inc(Addr) \ + ({ uint64_t t_ = read_8ubyte_unaligned (Addr); \ + Addr = (__typeof (Addr)) (((uintptr_t) (Addr)) + 8); \ + t_; }) +#define read_8sbyte_unaligned_inc(Addr) \ + ({ int64_t t_ = read_8sbyte_unaligned (Addr); \ + Addr = (__typeof (Addr)) (((uintptr_t) (Addr)) + 8); \ + t_; }) + +#endif /* memory-access.h */ diff --git a/libcpu/x86_64_disasm.c b/libcpu/x86_64_disasm.c new file mode 100644 index 000000000..f6ae7957c --- /dev/null +++ b/libcpu/x86_64_disasm.c @@ -0,0 +1,5 @@ +#define i386_disasm x86_64_disasm +#define DISFILE "x86_64_dis.h" +#define MNEFILE "x86_64.mnemonics" +#define X86_64 +#include "i386_disasm.c" diff --git a/libebl/Makefile.am b/libebl/Makefile.am index 993800ca6..2cf570e7c 100644 --- a/libebl/Makefile.am +++ b/libebl/Makefile.am @@ -34,7 +34,7 @@ AM_CFLAGS += -fpic -Wall -Wshadow -Werror -Wunused -Wextra -Wformat=2 \ -std=gnu99 INCLUDES = -I$(srcdir) -I$(top_srcdir)/libelf -I$(top_srcdir)/libdw \ - -I$(top_srcdir)/lib -I.. + -I$(top_srcdir)/lib -I.. -I$(srcdir)/../libasm VERSION = 1 PACKAGE_VERSION = @PACKAGE_VERSION@ LIBEBL_SUBDIR = @LIBEBL_SUBDIR@ diff --git a/libebl/ebl-hooks.h b/libebl/ebl-hooks.h index d466a1f3b..c0c929bd1 100644 --- a/libebl/ebl-hooks.h +++ b/libebl/ebl-hooks.h @@ -148,6 +148,11 @@ ssize_t EBLHOOK(register_info) (Ebl *ebl, const char **prefix, const char **setname, int *bits, int *type); + /* Disassembler function. */ +int EBLHOOK(disasm) (const uint8_t **startp, const uint8_t *end, + GElf_Addr addr, const char *fmt, DisasmOutputCB_t outcb, + DisasmGetSymCB_t symcb, void *outcbarg, void *symcbarg); + /* Destructor for ELF backend handle. */ void EBLHOOK(destr) (struct ebl *); diff --git a/libebl/eblopenbackend.c b/libebl/eblopenbackend.c index 63e64a10e..8a44f904c 100644 --- a/libebl/eblopenbackend.c +++ b/libebl/eblopenbackend.c @@ -246,6 +246,7 @@ fill_defaults (Ebl *result) result->bss_plt_p = default_bss_plt_p; result->return_value_location = default_return_value_location; result->register_info = default_register_info; + result->disasm = NULL; result->destr = default_destr; result->sysvhash_entrysize = sizeof (Elf32_Word); } diff --git a/libebl/libeblP.h b/libebl/libeblP.h index 7bb4c376f..7bfa650f5 100644 --- a/libebl/libeblP.h +++ b/libebl/libeblP.h @@ -51,6 +51,7 @@ #define _LIBEBLP_H 1 #include +#include #include #include diff --git a/libelf/ChangeLog b/libelf/ChangeLog index 57a40d905..daa78e4cf 100644 --- a/libelf/ChangeLog +++ b/libelf/ChangeLog @@ -1,3 +1,14 @@ +2007-12-20 Ulrich Drepper + + * Makefile.am (libelf_a_SOURCES): Add elf_scnshndx. + * libelfP.h (struct Elf_Scn): Add shndx_index field. + Declare __elf_scnshndx_internal. + * elf32_getshdr.c: Record location of extended section header. + * elf_begin.c (file_read_elf): Likewise. + * elf_scnshndx.c: New file. + * libelf.h: Declare elf_scnshndx. + * libelf.map: Add elf_scnshndx to version ELFUTILS_1.4. + 2007-11-12 Roland McGrath * libelf.h: Replace off64_t with loff_t throughout. diff --git a/libelf/Makefile.am b/libelf/Makefile.am index 58c9b5a80..3cb858df8 100644 --- a/libelf/Makefile.am +++ b/libelf/Makefile.am @@ -99,7 +99,8 @@ libelf_a_SOURCES = elf_version.c elf_hash.c elf_error.c elf_fill.c \ gelf_getlib.c gelf_update_lib.c \ elf32_offscn.c elf64_offscn.c gelf_offscn.c \ elf_getaroff.c \ - elf_gnu_hash.c + elf_gnu_hash.c \ + elf_scnshndx.c if !MUDFLAP libelf_pic_a_SOURCES = diff --git a/libelf/elf32_getshdr.c b/libelf/elf32_getshdr.c index cafb1d4f1..c7f75bbb4 100644 --- a/libelf/elf32_getshdr.c +++ b/libelf/elf32_getshdr.c @@ -165,6 +165,20 @@ elfw2(LIBELFBITS,getshdr) (scn) CONVERT_TO (shdr[cnt].sh_addralign, notcvt[cnt].sh_addralign); CONVERT_TO (shdr[cnt].sh_entsize, notcvt[cnt].sh_entsize); + + /* If this is a section with an extended index add a + reference in the section which uses the extended + index. */ + if (shdr[cnt].sh_type == SHT_SYMTAB_SHNDX + && shdr[cnt].sh_link < shnum) + elf->state.ELFW(elf,LIBELFBITS).scns.data[shdr[cnt].sh_link].shndx_index + = cnt; + + /* Set the own shndx_index field in case it has not yet + been set. */ + if (elf->state.ELFW(elf,LIBELFBITS).scns.data[cnt].shndx_index == 0) + elf->state.ELFW(elf,LIBELFBITS).scns.data[cnt].shndx_index + = -1; } } } diff --git a/libelf/elf_begin.c b/libelf/elf_begin.c index 13f965f7c..d84f90478 100644 --- a/libelf/elf_begin.c +++ b/libelf/elf_begin.c @@ -327,6 +327,19 @@ file_read_elf (int fildes, void *map_address, unsigned char *e_ident, ((char *) map_address + offset + elf->state.elf32.shdr[cnt].sh_offset); elf->state.elf32.scns.data[cnt].list = &elf->state.elf32.scns; + + /* If this is a section with an extended index add a + reference in the section which uses the extended + index. */ + if (elf->state.elf32.shdr[cnt].sh_type == SHT_SYMTAB_SHNDX + && elf->state.elf32.shdr[cnt].sh_link < scncnt) + elf->state.elf32.scns.data[elf->state.elf32.shdr[cnt].sh_link].shndx_index + = cnt; + + /* Set the own shndx_index field in case it has not yet + been set. */ + if (elf->state.elf32.scns.data[cnt].shndx_index == 0) + elf->state.elf32.scns.data[cnt].shndx_index = -1; } } else @@ -402,6 +415,19 @@ file_read_elf (int fildes, void *map_address, unsigned char *e_ident, ((char *) map_address + offset + elf->state.elf64.shdr[cnt].sh_offset); elf->state.elf64.scns.data[cnt].list = &elf->state.elf64.scns; + + /* If this is a section with an extended index add a + reference in the section which uses the extended + index. */ + if (elf->state.elf64.shdr[cnt].sh_type == SHT_SYMTAB_SHNDX + && elf->state.elf64.shdr[cnt].sh_link < scncnt) + elf->state.elf64.scns.data[elf->state.elf64.shdr[cnt].sh_link].shndx_index + = cnt; + + /* Set the own shndx_index field in case it has not yet + been set. */ + if (elf->state.elf64.scns.data[cnt].shndx_index == 0) + elf->state.elf64.scns.data[cnt].shndx_index = -1; } } else diff --git a/libelf/elf_scnshndx.c b/libelf/elf_scnshndx.c new file mode 100644 index 000000000..987d23e4b --- /dev/null +++ b/libelf/elf_scnshndx.c @@ -0,0 +1,71 @@ +/* Get the section index of the extended section index table. + Copyright (C) 2007 Red Hat, Inc. + This file is part of Red Hat elfutils. + Contributed by Ulrich Drepper , 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. + + In addition, as a special exception, Red Hat, Inc. gives You the + additional right to link the code of Red Hat elfutils with code licensed + under any Open Source Initiative certified open source license + (http://www.opensource.org/licenses/index.php) which requires the + distribution of source code with any binary distribution and to + distribute linked combinations of the two. Non-GPL Code permitted under + this exception must only link to the code of Red Hat elfutils through + those well defined interfaces identified in the file named EXCEPTION + found in the source code files (the "Approved Interfaces"). The files + of Non-GPL Code may instantiate templates or use macros or inline + functions from the Approved Interfaces without causing the resulting + work to be covered by the GNU General Public License. Only Red Hat, + Inc. may make changes or additions to the list of Approved Interfaces. + Red Hat's grant of this exception is conditioned upon your not adding + any new exceptions. If you wish to add a new Approved Interface or + exception, please contact Red Hat. You must obey the GNU General Public + License in all respects for all of the Red Hat elfutils code and other + code used in conjunction with Red Hat elfutils except the Non-GPL Code + covered by this exception. If you modify this file, you may extend this + exception to your version of the file, but you are not obligated to do + so. If you do not wish to provide this exception without modification, + you must delete this exception statement from your version and license + this file solely under the GPL without exception. + + 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 + . */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include "libelfP.h" + + +int +elf_scnshndx (Elf_Scn *scn) +{ + if (unlikely (scn->shndx_index == 0)) + { + /* We do not have the value yet. We get it as a side effect of + getting a section header. */ + GElf_Shdr shdr_mem; + (void) INTUSE(gelf_getshdr) (scn, &shdr_mem); + } + + return scn->shndx_index; +} +INTDEF(elf_scnshndx) diff --git a/libelf/libelf.h b/libelf/libelf.h index 90a588ee9..54271228a 100644 --- a/libelf/libelf.h +++ b/libelf/libelf.h @@ -248,6 +248,10 @@ extern Elf_Scn *elf_nextscn (Elf *__elf, Elf_Scn *__scn); /* Create a new section and append it at the end of the table. */ extern Elf_Scn *elf_newscn (Elf *__elf); +/* Get the section index of the extended section index table for the + given symbol table. */ +extern int elf_scnshndx (Elf_Scn *__scn); + /* Get the number of sections in the ELF file. If the file uses more sections than can be represented in the e_shnum field of the ELF header the information from the sh_size field in the zeroth section diff --git a/libelf/libelf.map b/libelf/libelf.map index aaaf9164b..c2537774b 100644 --- a/libelf/libelf.map +++ b/libelf/libelf.map @@ -122,4 +122,9 @@ ELFUTILS_1.3 { gelf_getauxv; gelf_update_auxv; gelf_getnote; -}; +} ELFUTILS_1.2; + +ELFUTILS_1.4 { + global: + elf_scnshndx; +} ELFUTILS_1.3; diff --git a/libelf/libelfP.h b/libelf/libelfP.h index 7e6305cdd..5e3d57cb1 100644 --- a/libelf/libelfP.h +++ b/libelf/libelfP.h @@ -1,5 +1,5 @@ /* Internal interfaces for libelf. - Copyright (C) 1998,1999,2000,2001,2002,2003,2005,2006,2007 Red Hat, Inc. + Copyright (C) 1998-2003, 2005, 2006, 2007 Red Hat, Inc. This file is part of Red Hat elfutils. Contributed by Ulrich Drepper , 1998. @@ -224,6 +224,9 @@ struct Elf_Scn int data_read; /* Nonzero if the section was created by the user or if the data from the file/memory is read. */ + int shndx_index; /* Index of the extended section index + table for this symbol table (if this + section is a symbol table). */ size_t index; /* Index of this section. */ struct Elf *elf; /* The underlying ELF file. */ @@ -532,6 +535,7 @@ extern Elf_Scn *__elf_getscn_internal (Elf *__elf, size_t __index) attribute_hidden; extern Elf_Scn *__elf_nextscn_internal (Elf *__elf, Elf_Scn *__scn) attribute_hidden; +extern int __elf_scnshndx_internal (Elf_Scn *__scn) attribute_hidden; extern Elf_Data *__elf_getdata_internal (Elf_Scn *__scn, Elf_Data *__data) attribute_hidden; extern Elf_Data *__elf_rawdata_internal (Elf_Scn *__scn, Elf_Data *__data) diff --git a/src/ChangeLog b/src/ChangeLog index 3107b2f0e..79ce2e858 100644 --- a/src/ChangeLog +++ b/src/ChangeLog @@ -15,6 +15,40 @@ * strip.c: Likewise. * unstrip.c: Likewise. +2007-12-30 Ulrich Drepper + + * objdump (show_disasm): Use %e after third parameter. + +2007-12-21 Ulrich Drepper + + * strip.c: Fix wrong parenthesis in a few branch predictions. + * strings.c: Likewise. + +2007-12-20 Ulrich Drepper + + * Makefile.am (DEFS): Add DEBUGPRED. + * addr2line.c: Include debugpred.h. + * ar.c: Likewise. + * elfcmp.c: Likewise. + * elflint.c: Likewise. + * findtextrel.c: Likewise. + * nm.c: Likewise. + * objdump.c: Likewise. + * ranlib.c: Likewise. + * readelf.c: Likewise. + * size.c: Likewise. + * strings.c: Likewise. + * strip.c: Likewise. + * unstrip.c: Likewise. + * debugpred.h: New file. + + * readelf.c (handle_relocs_rel): Use elf_scnshndx. + (handle_relocs_rela): Likewise. + + * readelf.c: Add lots of likely/unlikely. + + * elflint.c: Minor cleanups. + 2007-11-19 Roland McGrath * readelf.c (print_ops): Handle all bad op codes gracefully. diff --git a/src/Makefile.am b/src/Makefile.am index 138be5a32..f72bb4589 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -24,7 +24,7 @@ ## Network licensing program, please visit www.openinventionnetwork.com ## . ## -DEFS = -D_GNU_SOURCE -DHAVE_CONFIG_H $(YYDEBUG) \ +DEFS = -D_GNU_SOURCE -DHAVE_CONFIG_H $(YYDEBUG) -DDEBUGPRED=@DEBUGPRED@ \ -DSRCDIR=\"$(shell cd $(srcdir);pwd)\" -DOBJDIR=\"$(shell pwd)\" if MUDFLAP AM_CFLAGS = -fmudflap @@ -38,7 +38,7 @@ AM_CFLAGS += -Wall -Wshadow -std=gnu99 $(native_ld_cflags) \ INCLUDES = -I$(srcdir) -I$(srcdir)/../libelf -I$(srcdir)/../libebl \ -I$(srcdir)/../libdw -I$(srcdir)/../libdwfl \ - -I$(srcdir)/../lib -I.. + -I$(srcdir)/../libasm -I$(srcdir)/../lib -I.. AM_LDFLAGS = -Wl,-rpath-link,../libelf:../libdw @@ -81,7 +81,8 @@ libar_a_SOURCES = arlib.c arlib2.c noinst_HEADERS = ld.h symbolhash.h sectionhash.h versionhash.h \ ldscript.h xelf.h unaligned.h -EXTRA_DIST = elf32-i386.script libld_elf_i386.map $(ld_modules) arlib.h +EXTRA_DIST = elf32-i386.script libld_elf_i386.map $(ld_modules) arlib.h \ + debugpred.h ld_modules = i386_ld.c bin_SCRIPTS = make-debug-archive @@ -93,9 +94,11 @@ libmudflap = -lmudflap endif if BUILD_STATIC +libasm = ../libasm/libasm.a libdw = ../libdw/libdw.a $(libelf) $(libebl) -ldl libelf = ../libelf/libelf.a else +libasm = ../libasm/libasm.so libdw = ../libdw/libdw.so libelf = ../libelf/libelf.so endif @@ -122,7 +125,7 @@ elflint_LDADD = $(libebl) $(libelf) $(libeu) $(libmudflap) -ldl findtextrel_LDADD = $(libdw) $(libelf) $(libmudflap) addr2line_LDADD = $(libdw) $(libmudflap) elfcmp_LDADD = $(libebl) $(libelf) $(libmudflap) -ldl -objdump_LDADD = $(libebl) $(libelf) $(libeu) $(libmudflap) -ldl +objdump_LDADD = $(libasm) $(libebl) $(libelf) $(libeu) $(libmudflap) -ldl ranlib_LDADD = libar.a $(libelf) $(libeu) $(libmudflap) strings_LDADD = $(libelf) $(libeu) $(libmudflap) ar_LDADD = libar.a $(libelf) $(libeu) $(libmudflap) diff --git a/src/addr2line.c b/src/addr2line.c index 5e0c2dad1..4b1d13e7b 100644 --- a/src/addr2line.c +++ b/src/addr2line.c @@ -373,3 +373,6 @@ handle_address (GElf_Addr addr, Dwfl *dwfl) else puts ("??:0"); } + + +#include "debugpred.h" diff --git a/src/ar.c b/src/ar.c index 8d3b91ac4..aade35111 100644 --- a/src/ar.c +++ b/src/ar.c @@ -1513,3 +1513,6 @@ do_oper_insert (int oper, const char *arfname, char **argv, int argc, return status; } + + +#include "debugpred.h" diff --git a/src/debugpred.h b/src/debugpred.h new file mode 100644 index 000000000..867f4ace3 --- /dev/null +++ b/src/debugpred.h @@ -0,0 +1,53 @@ +/* Support to debug branch prediction. + Copyright (C) 2007 Red Hat, Inc. + This file is part of Red Hat elfutils. + Written by Ulrich Drepper , 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 + . */ + +#include + +#if DEBUGPRED +extern const unsigned long int __start_predict_data; +extern const unsigned long int __stop_predict_data; +extern const unsigned long int __start_predict_line; +extern const char *__start_predict_file; + +static void +__attribute__ ((destructor)) +predprint (void) +{ + const unsigned long int *s = &__start_predict_data; + const unsigned long int *e = &__stop_predict_data; + const unsigned long int *sl = &__start_predict_line; + const char **sf = &__start_predict_file; + while (s < e) + { + if (s[0] != 0 || s[1] != 0) + printf ("%s:%lu: wrong=%lu, correct=%lu%s\n", *sf, *sl, s[0], s[1], + s[0] > s[1] ? " <==== WARNING" : ""); + ++sl; + ++sf; + s += 2; + } +} +#endif diff --git a/src/elfcmp.c b/src/elfcmp.c index cbc8cd8cb..be9aaccd5 100644 --- a/src/elfcmp.c +++ b/src/elfcmp.c @@ -744,3 +744,6 @@ hash_content_equivalent (size_t entsize, Elf_Data *data1, Elf_Data *data2) return false; } + + +#include "debugpred.h" diff --git a/src/elflint.c b/src/elflint.c index 0121832e8..85b249541 100644 --- a/src/elflint.c +++ b/src/elflint.c @@ -579,11 +579,10 @@ check_symtab (Ebl *ebl, GElf_Ehdr *ehdr, GElf_Shdr *shdr, int idx) idx, section_name (ebl, idx)); /* Search for an extended section index table section. */ - size_t cnt; Elf_Data *xndxdata = NULL; Elf32_Word xndxscnidx = 0; bool found_xndx = false; - for (cnt = 1; cnt < shnum; ++cnt) + for (size_t cnt = 1; cnt < shnum; ++cnt) if (cnt != (size_t) idx) { Elf_Scn *xndxscn = elf_getscn (ebl->elf, cnt); @@ -608,8 +607,8 @@ section [%2d] '%s': symbol table cannot have more than one extended index sectio if (shdr->sh_entsize != gelf_fsize (ebl->elf, ELF_T_SYM, 1, EV_CURRENT)) ERROR (gettext ("\ -section [%2zu] '%s': entry size is does not match ElfXX_Sym\n"), - cnt, section_name (ebl, cnt)); +section [%2u] '%s': entry size is does not match ElfXX_Sym\n"), + idx, section_name (ebl, idx)); /* Test the zeroth entry. */ GElf_Sym sym_mem; @@ -644,7 +643,7 @@ section [%2d] '%s': XINDEX for zeroth entry not zero\n"), xndxscnidx, section_name (ebl, xndxscnidx)); } - for (cnt = 1; cnt < shdr->sh_size / shdr->sh_entsize; ++cnt) + for (size_t cnt = 1; cnt < shdr->sh_size / shdr->sh_entsize; ++cnt) { sym = gelf_getsymshndx (data, xndxdata, cnt, &sym_mem, &xndx); if (sym == NULL) @@ -3958,3 +3957,6 @@ process_elf_file (Elf *elf, const char *prefix, const char *suffix, /* Free the resources. */ ebl_closebackend (ebl); } + + +#include "debugpred.h" diff --git a/src/findtextrel.c b/src/findtextrel.c index d2de3a65c..245db7fe6 100644 --- a/src/findtextrel.c +++ b/src/findtextrel.c @@ -606,3 +606,6 @@ a relocation modifies memory at offset %llu in a write-protected segment\n"), break; } } + + +#include "debugpred.h" diff --git a/src/ld.c b/src/ld.c index aae938e70..bede0e854 100644 --- a/src/ld.c +++ b/src/ld.c @@ -1525,3 +1525,6 @@ create_special_section_symbol (struct symbol **symp, const char *name) ++ld_state.nsymtab; } + + +#include "debugpred.h" diff --git a/src/nm.c b/src/nm.c index 6ddae6118..1bef49fcd 100644 --- a/src/nm.c +++ b/src/nm.c @@ -1294,3 +1294,6 @@ handle_elf (Elf *elf, const char *prefix, const char *fname, return result; } + + +#include "debugpred.h" diff --git a/src/objdump.c b/src/objdump.c index 8c62ee288..92ab84f82 100644 --- a/src/objdump.c +++ b/src/objdump.c @@ -681,12 +681,87 @@ show_full_content (Ebl *ebl, const char *fname, uint32_t shstrndx) } +struct disasm_info +{ + GElf_Addr addr; + const uint8_t *cur; + const uint8_t *last_end; +}; + + +// XXX This is not the preferred output for all architectures. Needs +// XXX customization, too. static int -show_disasm (Ebl *ebl __attribute__ ((unused)), - const char *fname __attribute__ ((unused)), - uint32_t shstrndx __attribute__ ((unused))) +disasm_output (char *buf, size_t buflen, void *arg) { - /// XXX For now nothing. + struct disasm_info *info = (struct disasm_info *) arg; + + printf ("%8" PRIx64 ": ", (uint64_t) info->addr); + size_t cnt; + for (cnt = 0; cnt < (size_t) MIN (info->cur - info->last_end, 8); ++cnt) + printf (" %02" PRIx8, info->last_end[cnt]); + printf ("%*s %.*s\n", + (int) (8 - cnt) * 3 + 1, "", (int) buflen, buf); + + info->addr += cnt; + + /* We limit the number of bytes printed before the mnemonic to 8. + Print the rest on a separate, following line. */ + if (info->cur - info->last_end > 8) + { + printf ("%8" PRIx64 ": ", (uint64_t) info->addr); + for (; cnt < (size_t) (info->cur - info->last_end); ++cnt) + printf (" %02" PRIx8, info->last_end[cnt]); + putchar_unlocked ('\n'); + info->addr += info->cur - info->last_end - 8; + } + + info->last_end = info->cur; + + return 0; +} + + +static int +show_disasm (Ebl *ebl, const char *fname, uint32_t shstrndx) +{ + DisasmCtx_t *ctx = disasm_begin (ebl, ebl->elf, NULL /* XXX TODO */); + if (ctx == NULL) + error (EXIT_FAILURE, 0, gettext ("cannot disassemble")); + + Elf_Scn *scn = NULL; + while ((scn = elf_nextscn (ebl->elf, scn)) != NULL) + { + GElf_Shdr shdr_mem; + GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem); + + if (shdr == NULL) + INTERNAL_ERROR (fname); + + if (shdr->sh_type == SHT_PROGBITS && shdr->sh_size > 0 + && (shdr->sh_flags & SHF_EXECINSTR) != 0) + { + if (! section_match (ebl->elf, elf_ndxscn (scn), shdr, shstrndx)) + continue; + + Elf_Data *data = elf_getdata (scn, NULL); + if (data == NULL) + continue; + + printf ("Disassembly of section %s:\n\n", + elf_strptr (ebl->elf, shstrndx, shdr->sh_name)); + + struct disasm_info info; + info.addr = shdr->sh_addr; + info.last_end = info.cur = data->d_buf; + + disasm_cb (ctx, &info.cur, info.cur + data->d_size, info.addr, + "%7m%e %.1o%e,%.2o%e,%.3o%e", disasm_output, &info, + NULL /* XXX */); + } + } + + (void) disasm_end (ctx); return 0; } @@ -735,3 +810,6 @@ handle_elf (Elf *elf, const char *prefix, const char *fname, return result; } + + +#include "debugpred.h" diff --git a/src/ranlib.c b/src/ranlib.c index f82b4f986..a915e558e 100644 --- a/src/ranlib.c +++ b/src/ranlib.c @@ -304,3 +304,6 @@ handle_file (const char *fname) return status; } + + +#include "debugpred.h" diff --git a/src/readelf.c b/src/readelf.c index 0a5556236..d65f81034 100644 --- a/src/readelf.c +++ b/src/readelf.c @@ -535,7 +535,7 @@ process_file (int fd, const char *fname, bool only_one) struct stat64 st; if (fstat64 (fd, &st) != 0) error (0, errno, gettext ("cannot stat input file")); - else if (st.st_size == 0) + else if (unlikely (st.st_size == 0)) error (0, 0, gettext ("input file is empty")); else error (0, 0, gettext ("failed reading '%s': %s"), @@ -575,7 +575,7 @@ process_elf_file (Dwfl_Module *dwflmod, int fd) } Ebl *ebl = ebl_openbackend (elf); - if (ebl == NULL) + if (unlikely (ebl == NULL)) { ebl_error: error (0, errno, gettext ("cannot create EBL handle")); @@ -583,7 +583,7 @@ process_elf_file (Dwfl_Module *dwflmod, int fd) } /* Determine the number of sections. */ - if (elf_getshnum (ebl->elf, &shnum) < 0) + if (unlikely (elf_getshnum (ebl->elf, &shnum) < 0)) error (EXIT_FAILURE, 0, gettext ("cannot determine number of sections: %s"), elf_errmsg (-1)); @@ -665,7 +665,7 @@ process_elf_file (Dwfl_Module *dwflmod, int fd) static void print_file_type (unsigned short int e_type) { - if (e_type <= ET_CORE) + if (likely (e_type <= ET_CORE)) { static const char *const knowntypes[] = { @@ -764,7 +764,7 @@ print_ehdr (Ebl *ebl, GElf_Ehdr *ehdr) } fputc_unlocked ('\n', stdout); - if (ehdr->e_shstrndx == SHN_XINDEX) + if (unlikely (ehdr->e_shstrndx == SHN_XINDEX)) { GElf_Shdr shdr_mem; GElf_Shdr *shdr = gelf_getshdr (elf_getscn (ebl->elf, 0), &shdr_mem); @@ -820,7 +820,7 @@ There are %d section headers, starting at offset %#" PRIx64 ":\n\ ehdr->e_shnum, ehdr->e_shoff); /* Get the section header string table index. */ - if (elf_getshstrndx (ebl->elf, &shstrndx) < 0) + if (unlikely (elf_getshstrndx (ebl->elf, &shstrndx) < 0)) error (EXIT_FAILURE, 0, gettext ("cannot get section header string table index")); @@ -835,14 +835,14 @@ There are %d section headers, starting at offset %#" PRIx64 ":\n\ { Elf_Scn *scn = elf_getscn (ebl->elf, cnt); - if (scn == NULL) + if (unlikely (scn == NULL)) error (EXIT_FAILURE, 0, gettext ("cannot get section: %s"), elf_errmsg (-1)); /* Get the section header. */ GElf_Shdr shdr_mem; GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem); - if (shdr == NULL) + if (unlikely (shdr == NULL)) error (EXIT_FAILURE, 0, gettext ("cannot get section header: %s"), elf_errmsg (-1)); @@ -920,7 +920,7 @@ print_phdr (Ebl *ebl, GElf_Ehdr *ehdr) GElf_Phdr *phdr = gelf_getphdr (ebl->elf, cnt, &mem); /* If for some reason the header cannot be returned show this. */ - if (phdr == NULL) + if (unlikely (phdr == NULL)) { puts (" ???"); continue; @@ -959,7 +959,7 @@ print_phdr (Ebl *ebl, GElf_Ehdr *ehdr) /* Get the section header string table index. */ size_t shstrndx; - if (elf_getshstrndx (ebl->elf, &shstrndx) < 0) + if (unlikely (elf_getshstrndx (ebl->elf, &shstrndx) < 0)) error (EXIT_FAILURE, 0, gettext ("cannot get section header string table index")); @@ -973,7 +973,7 @@ print_phdr (Ebl *ebl, GElf_Ehdr *ehdr) GElf_Phdr phdr_mem; GElf_Phdr *phdr = gelf_getphdr (ebl->elf, cnt, &phdr_mem); /* This must not happen. */ - if (phdr == NULL) + if (unlikely (phdr == NULL)) error (EXIT_FAILURE, 0, gettext ("cannot get program header: %s"), elf_errmsg (-1)); @@ -984,14 +984,14 @@ print_phdr (Ebl *ebl, GElf_Ehdr *ehdr) { Elf_Scn *scn = elf_getscn (ebl->elf, inner); /* This should not happen. */ - if (scn == NULL) + if (unlikely (scn == NULL)) error (EXIT_FAILURE, 0, gettext ("cannot get section: %s"), elf_errmsg (-1)); /* Get the section header. */ GElf_Shdr shdr_mem; GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem); - if (shdr == NULL) + if (unlikely (shdr == NULL)) error (EXIT_FAILURE, 0, gettext ("cannot get section header: %s"), elf_errmsg (-1)); @@ -1100,7 +1100,7 @@ handle_scngrp (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr) /* Get the section header string table index. */ size_t shstrndx; - if (elf_getshstrndx (ebl->elf, &shstrndx) < 0) + if (unlikely (elf_getshstrndx (ebl->elf, &shstrndx) < 0)) error (EXIT_FAILURE, 0, gettext ("cannot get section header string table index")); @@ -1282,7 +1282,7 @@ handle_dynamic (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr) return; /* Get the section header string table index. */ - if (elf_getshstrndx (ebl->elf, &shstrndx) < 0) + if (unlikely (elf_getshstrndx (ebl->elf, &shstrndx) < 0)) error (EXIT_FAILURE, 0, gettext ("cannot get section header string table index")); @@ -1431,7 +1431,7 @@ print_relocs (Ebl *ebl) GElf_Shdr shdr_mem; GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem); - if (shdr != NULL) + if (likely (shdr != NULL)) { if (shdr->sh_type == SHT_REL) handle_relocs_rel (ebl, scn, shdr); @@ -1465,7 +1465,7 @@ handle_relocs_rel (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr) GElf_Shdr *destshdr = gelf_getshdr (elf_getscn (ebl->elf, shdr->sh_info), &destshdr_mem); - if (symshdr == NULL || symdata == NULL || destshdr == NULL) + if (unlikely (symshdr == NULL || symdata == NULL || destshdr == NULL)) { printf (gettext ("\nInvalid symbol table at offset %#0" PRIx64 "\n"), shdr->sh_offset); @@ -1473,24 +1473,14 @@ handle_relocs_rel (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr) } /* Search for the optional extended section index table. */ - Elf_Scn *xndxscn = NULL; Elf_Data *xndxdata = NULL; - while ((xndxscn = elf_nextscn (ebl->elf, xndxscn)) != NULL) - { - GElf_Shdr xndxshdr_mem; - GElf_Shdr *xndxshdr = gelf_getshdr (xndxscn, &xndxshdr_mem); - if (xndxshdr != NULL && xndxshdr->sh_type == SHT_SYMTAB_SHNDX - && xndxshdr->sh_link == elf_ndxscn (symscn)) - { - /* Found it. */ - xndxdata = elf_getdata (xndxscn, NULL); - break; - } - } + int xndxscnidx = elf_scnshndx (scn); + if (unlikely (xndxscnidx > 0)) + xndxdata = elf_getdata (elf_getscn (ebl->elf, xndxscnidx), NULL); /* Get the section header string table index. */ size_t shstrndx; - if (elf_getshstrndx (ebl->elf, &shstrndx) < 0) + if (unlikely (elf_getshstrndx (ebl->elf, &shstrndx) < 0)) error (EXIT_FAILURE, 0, gettext ("cannot get section header string table index")); @@ -1530,7 +1520,7 @@ handle_relocs_rel (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr) { GElf_Rel relmem; GElf_Rel *rel = gelf_getrel (data, cnt, &relmem); - if (rel != NULL) + if (likely (rel != NULL)) { char buf[128]; GElf_Sym symmem; @@ -1538,7 +1528,7 @@ handle_relocs_rel (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr) GElf_Sym *sym = gelf_getsymshndx (symdata, xndxdata, GELF_R_SYM (rel->r_info), &symmem, &xndx); - if (sym == NULL) + if (unlikely (sym == NULL)) printf (" %#0*" PRIx64 " %-20s <%s %ld>\n", class == ELFCLASS32 ? 10 : 18, rel->r_offset, ebl_reloc_type_check (ebl, GELF_R_TYPE (rel->r_info)) @@ -1552,7 +1542,8 @@ handle_relocs_rel (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr) else if (GELF_ST_TYPE (sym->st_info) != STT_SECTION) printf (" %#0*" PRIx64 " %-20s %#0*" PRIx64 " %s\n", class == ELFCLASS32 ? 10 : 18, rel->r_offset, - ebl_reloc_type_check (ebl, GELF_R_TYPE (rel->r_info)) + likely (ebl_reloc_type_check (ebl, + GELF_R_TYPE (rel->r_info))) /* Avoid the leading R_ which isn't carrying any information. */ ? ebl_reloc_type_name (ebl, GELF_R_TYPE (rel->r_info), @@ -1567,7 +1558,7 @@ handle_relocs_rel (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr) ? xndx : sym->st_shndx), &destshdr_mem); - if (destshdr == NULL) + if (unlikely (destshdr == NULL)) printf (" %#0*" PRIx64 " %-20s <%s %ld>\n", class == ELFCLASS32 ? 10 : 18, rel->r_offset, ebl_reloc_type_check (ebl, GELF_R_TYPE (rel->r_info)) @@ -1619,7 +1610,7 @@ handle_relocs_rela (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr) GElf_Shdr *destshdr = gelf_getshdr (elf_getscn (ebl->elf, shdr->sh_info), &destshdr_mem); - if (symshdr == NULL || symdata == NULL || destshdr == NULL) + if (unlikely (symshdr == NULL || symdata == NULL || destshdr == NULL)) { printf (gettext ("\nInvalid symbol table at offset %#0" PRIx64 "\n"), shdr->sh_offset); @@ -1628,23 +1619,13 @@ handle_relocs_rela (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr) /* Search for the optional extended section index table. */ Elf_Data *xndxdata = NULL; - Elf_Scn *xndxscn = NULL; - while ((xndxscn = elf_nextscn (ebl->elf, xndxscn)) != NULL) - { - GElf_Shdr xndxshdr_mem; - GElf_Shdr *xndxshdr = gelf_getshdr (xndxscn, &xndxshdr_mem); - if (xndxshdr != NULL && xndxshdr->sh_type == SHT_SYMTAB_SHNDX - && xndxshdr->sh_link == elf_ndxscn (symscn)) - { - /* Found it. */ - xndxdata = elf_getdata (xndxscn, NULL); - break; - } - } + int xndxscnidx = elf_scnshndx (scn); + if (unlikely (xndxscnidx > 0)) + xndxdata = elf_getdata (elf_getscn (ebl->elf, xndxscnidx), NULL); /* Get the section header string table index. */ size_t shstrndx; - if (elf_getshstrndx (ebl->elf, &shstrndx) < 0) + if (unlikely (elf_getshstrndx (ebl->elf, &shstrndx) < 0)) error (EXIT_FAILURE, 0, gettext ("cannot get section header string table index")); @@ -1670,7 +1651,7 @@ handle_relocs_rela (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr) { GElf_Rela relmem; GElf_Rela *rel = gelf_getrela (data, cnt, &relmem); - if (rel != NULL) + if (likely (rel != NULL)) { char buf[64]; GElf_Sym symmem; @@ -1679,7 +1660,7 @@ handle_relocs_rela (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr) GELF_R_SYM (rel->r_info), &symmem, &xndx); - if (sym == NULL) + if (unlikely (sym == NULL)) printf (" %#0*" PRIx64 " %-15s <%s %ld>\n", class == ELFCLASS32 ? 10 : 18, rel->r_offset, ebl_reloc_type_check (ebl, GELF_R_TYPE (rel->r_info)) @@ -1694,7 +1675,8 @@ handle_relocs_rela (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr) printf ("\ %#0*" PRIx64 " %-15s %#0*" PRIx64 " %+6" PRId64 " %s\n", class == ELFCLASS32 ? 10 : 18, rel->r_offset, - ebl_reloc_type_check (ebl, GELF_R_TYPE (rel->r_info)) + likely (ebl_reloc_type_check (ebl, + GELF_R_TYPE (rel->r_info))) /* Avoid the leading R_ which isn't carrying any information. */ ? ebl_reloc_type_name (ebl, GELF_R_TYPE (rel->r_info), @@ -1710,7 +1692,7 @@ handle_relocs_rela (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr) ? xndx : sym->st_shndx), &destshdr_mem); - if (shdr == NULL) + if (unlikely (shdr == NULL)) printf (" %#0*" PRIx64 " %-15s <%s %ld>\n", class == ELFCLASS32 ? 10 : 18, rel->r_offset, ebl_reloc_type_check (ebl, GELF_R_TYPE (rel->r_info)) @@ -1784,7 +1766,7 @@ handle_symtab (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr) GElf_Shdr runshdr_mem; GElf_Shdr *runshdr = gelf_getshdr (runscn, &runshdr_mem); - if (runshdr != NULL) + if (likely (runshdr != NULL)) { if (runshdr->sh_type == SHT_GNU_versym && runshdr->sh_link == elf_ndxscn (scn)) @@ -1811,7 +1793,7 @@ handle_symtab (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr) /* Get the section header string table index. */ size_t shstrndx; - if (elf_getshstrndx (ebl->elf, &shstrndx) < 0) + if (unlikely (elf_getshstrndx (ebl->elf, &shstrndx) < 0)) error (EXIT_FAILURE, 0, gettext ("cannot get section header string table index")); @@ -1851,11 +1833,11 @@ handle_symtab (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr) GElf_Sym sym_mem; GElf_Sym *sym = gelf_getsymshndx (data, xndx_data, cnt, &sym_mem, &xndx); - if (sym == NULL) + if (unlikely (sym == NULL)) continue; /* Determine the real section index. */ - if (sym->st_shndx != SHN_XINDEX) + if (likely (sym->st_shndx != SHN_XINDEX)) xndx = sym->st_shndx; printf (gettext ("\ @@ -1945,7 +1927,7 @@ handle_symtab (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr) (unsigned int) vernaux->vna_other); check_def = 0; } - else if (! is_nobits) + else if (unlikely (! is_nobits)) error (0, 0, gettext ("bad dynamic symbol")); else check_def = 1; @@ -2008,7 +1990,7 @@ print_verinfo (Ebl *ebl) GElf_Shdr shdr_mem; GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem); - if (shdr != NULL) + if (likely (shdr != NULL)) { if (shdr->sh_type == SHT_GNU_verneed) handle_verneed (ebl, scn, shdr); @@ -2043,7 +2025,7 @@ get_ver_flags (unsigned int flags) endp = stpcpy (endp, "WEAK "); } - if (flags & ~(VER_FLG_BASE | VER_FLG_WEAK)) + if (unlikely (flags & ~(VER_FLG_BASE | VER_FLG_WEAK))) { strncpy (endp, gettext ("| "), buf + sizeof (buf) - endp); buf[sizeof (buf) - 1] = '\0'; @@ -2065,7 +2047,7 @@ handle_verneed (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr) /* Get the section header string table index. */ size_t shstrndx; - if (elf_getshstrndx (ebl->elf, &shstrndx) < 0) + if (unlikely (elf_getshstrndx (ebl->elf, &shstrndx) < 0)) error (EXIT_FAILURE, 0, gettext ("cannot get section header string table index")); @@ -2090,7 +2072,7 @@ handle_verneed (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr) /* Get the data at the next offset. */ GElf_Verneed needmem; GElf_Verneed *need = gelf_getverneed (data, offset, &needmem); - if (need == NULL) + if (unlikely (need == NULL)) break; printf (gettext (" %#06x: Version: %hu File: %s Cnt: %hu\n"), @@ -2103,7 +2085,7 @@ handle_verneed (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr) { GElf_Vernaux auxmem; GElf_Vernaux *aux = gelf_getvernaux (data, auxoffset, &auxmem); - if (aux == NULL) + if (unlikely (aux == NULL)) break; printf (gettext (" %#06x: Name: %s Flags: %s Version: %hu\n"), @@ -2131,7 +2113,7 @@ handle_verdef (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr) /* Get the section header string table index. */ size_t shstrndx; - if (elf_getshstrndx (ebl->elf, &shstrndx) < 0) + if (unlikely (elf_getshstrndx (ebl->elf, &shstrndx) < 0)) error (EXIT_FAILURE, 0, gettext ("cannot get section header string table index")); @@ -2158,13 +2140,13 @@ handle_verdef (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr) /* Get the data at the next offset. */ GElf_Verdef defmem; GElf_Verdef *def = gelf_getverdef (data, offset, &defmem); - if (def == NULL) + if (unlikely (def == NULL)) break; unsigned int auxoffset = offset + def->vd_aux; GElf_Verdaux auxmem; GElf_Verdaux *aux = gelf_getverdaux (data, auxoffset, &auxmem); - if (aux == NULL) + if (unlikely (aux == NULL)) break; printf (gettext ("\ @@ -2179,7 +2161,7 @@ handle_verdef (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr) for (int cnt2 = 1; cnt2 < def->vd_cnt; ++cnt2) { aux = gelf_getverdaux (data, auxoffset, &auxmem); - if (aux == NULL) + if (unlikely (aux == NULL)) break; printf (gettext (" %#06x: Parent %d: %s\n"), @@ -2209,7 +2191,7 @@ handle_versym (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr) /* Get the section header string table index. */ size_t shstrndx; - if (elf_getshstrndx (ebl->elf, &shstrndx) < 0) + if (unlikely (elf_getshstrndx (ebl->elf, &shstrndx) < 0)) error (EXIT_FAILURE, 0, gettext ("cannot get section header string table index")); @@ -2224,7 +2206,7 @@ handle_versym (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr) GElf_Shdr vershdr_mem; GElf_Shdr *vershdr = gelf_getshdr (verscn, &vershdr_mem); - if (vershdr != NULL) + if (likely (vershdr != NULL)) { if (vershdr->sh_type == SHT_GNU_verdef) defscn = verscn; @@ -2249,11 +2231,11 @@ handle_versym (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr) GElf_Shdr *defshdr; defdata = elf_getdata (defscn, NULL); - if (defdata == NULL) + if (unlikely (defdata == NULL)) return; defshdr = gelf_getshdr (defscn, &defshdrmem); - if (defshdr == NULL) + if (unlikely (defshdr == NULL)) return; for (unsigned int cnt = 0; cnt < defshdr->sh_info; ++cnt) @@ -2263,7 +2245,7 @@ handle_versym (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr) /* Get the data at the next offset. */ def = gelf_getverdef (defdata, offset, &defmem); - if (def == NULL) + if (unlikely (def == NULL)) break; nvername = MAX (nvername, (size_t) (def->vd_ndx & 0x7fff)); @@ -2279,11 +2261,11 @@ handle_versym (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr) GElf_Shdr *needshdr; needdata = elf_getdata (needscn, NULL); - if (needdata == NULL) + if (unlikely (needdata == NULL)) return; needshdr = gelf_getshdr (needscn, &needshdrmem); - if (needshdr == NULL) + if (unlikely (needshdr == NULL)) return; for (unsigned int cnt = 0; cnt < needshdr->sh_info; ++cnt) @@ -2295,7 +2277,7 @@ handle_versym (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr) /* Get the data at the next offset. */ need = gelf_getverneed (needdata, offset, &needmem); - if (need == NULL) + if (unlikely (need == NULL)) break; /* Run through the auxiliary entries. */ @@ -2306,7 +2288,7 @@ handle_versym (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr) GElf_Vernaux *aux; aux = gelf_getvernaux (needdata, auxoffset, &auxmem); - if (aux == NULL) + if (unlikely (aux == NULL)) break; nvername = MAX (nvername, @@ -2337,11 +2319,11 @@ handle_versym (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr) GElf_Shdr *defshdr; defdata = elf_getdata (defscn, NULL); - if (defdata == NULL) + if (unlikely (defdata == NULL)) return; defshdr = gelf_getshdr (defscn, &defshdrmem); - if (defshdr == NULL) + if (unlikely (defshdr == NULL)) return; for (unsigned int cnt = 0; cnt < defshdr->sh_info; ++cnt) @@ -2354,7 +2336,7 @@ handle_versym (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr) GElf_Verdaux *aux = gelf_getverdaux (defdata, offset + def->vd_aux, &auxmem); - if (def == NULL || aux == NULL) + if (unlikely (def == NULL || aux == NULL)) break; vername[def->vd_ndx & 0x7fff] @@ -2371,7 +2353,7 @@ handle_versym (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr) Elf_Data *needdata = elf_getdata (needscn, NULL); GElf_Shdr needshdrmem; GElf_Shdr *needshdr = gelf_getshdr (needscn, &needshdrmem); - if (needdata == NULL || needshdr == NULL) + if (unlikely (needdata == NULL || needshdr == NULL)) return; for (unsigned int cnt = 0; cnt < needshdr->sh_info; ++cnt) @@ -2380,7 +2362,7 @@ handle_versym (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr) GElf_Verneed needmem; GElf_Verneed *need = gelf_getverneed (needdata, offset, &needmem); - if (need == NULL) + if (unlikely (need == NULL)) break; /* Run through the auxiliary entries. */ @@ -2390,7 +2372,7 @@ handle_versym (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr) GElf_Vernaux auxmem; GElf_Vernaux *aux = gelf_getvernaux (needdata, auxoffset, &auxmem); - if (aux == NULL) + if (unlikely (aux == NULL)) break; vername[aux->vna_other & 0x7fff] @@ -2499,7 +2481,7 @@ print_hash_info (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr, size_t shstrndx, if (extrastr != NULL) fputs (extrastr, stdout); - if (nbucket > 0) + if (likely (nbucket > 0)) { uint64_t success = 0; @@ -2541,7 +2523,7 @@ static void handle_sysv_hash (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr, size_t shstrndx) { Elf_Data *data = elf_getdata (scn, NULL); - if (data == NULL) + if (unlikely (data == NULL)) { error (0, 0, gettext ("cannot get data for section %d: %s"), (int) elf_ndxscn (scn), elf_errmsg (-1)); @@ -2583,7 +2565,7 @@ static void handle_sysv_hash64 (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr, size_t shstrndx) { Elf_Data *data = elf_getdata (scn, NULL); - if (data == NULL) + if (unlikely (data == NULL)) { error (0, 0, gettext ("cannot get data for section %d: %s"), (int) elf_ndxscn (scn), elf_errmsg (-1)); @@ -2624,7 +2606,7 @@ static void handle_gnu_hash (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr, size_t shstrndx) { Elf_Data *data = elf_getdata (scn, NULL); - if (data == NULL) + if (unlikely (data == NULL)) { error (0, 0, gettext ("cannot get data for section %d: %s"), (int) elf_ndxscn (scn), elf_errmsg (-1)); @@ -2680,13 +2662,15 @@ handle_gnu_hash (Ebl *ebl, Elf_Scn *scn, GElf_Shdr *shdr, size_t shstrndx) } char *str; - if (asprintf (&str, gettext ("\ + if (unlikely (asprintf (&str, gettext ("\ Symbol Bias: %u\n\ Bitmask Size: %zu bytes %" PRIuFAST32 "%% bits set 2nd hash shift: %u\n"), - (unsigned int) symbias, bitmask_words * sizeof (Elf32_Word), - ((nbits * 100 + 50) - / (uint_fast32_t) (bitmask_words * sizeof (Elf32_Word) * 8)), - (unsigned int) shift) == -1) + (unsigned int) symbias, + bitmask_words * sizeof (Elf32_Word), + ((nbits * 100 + 50) + / (uint_fast32_t) (bitmask_words + * sizeof (Elf32_Word) * 8)), + (unsigned int) shift) == -1)) error (EXIT_FAILURE, 0, gettext ("memory exhausted")); print_hash_info (ebl, scn, shdr, shstrndx, maxlength, nbucket, nsyms, @@ -2704,7 +2688,7 @@ handle_hash (Ebl *ebl) { /* Get the section header string table index. */ size_t shstrndx; - if (elf_getshstrndx (ebl->elf, &shstrndx) < 0) + if (unlikely (elf_getshstrndx (ebl->elf, &shstrndx) < 0)) error (EXIT_FAILURE, 0, gettext ("cannot get section header string table index")); @@ -2715,7 +2699,7 @@ handle_hash (Ebl *ebl) GElf_Shdr shdr_mem; GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem); - if (shdr != NULL) + if (likely (shdr != NULL)) { if (shdr->sh_type == SHT_HASH) { @@ -2740,7 +2724,7 @@ print_liblist (Ebl *ebl) /* Get the section header string table index. */ size_t shstrndx; - if (elf_getshstrndx (ebl->elf, &shstrndx) < 0) + if (unlikely (elf_getshstrndx (ebl->elf, &shstrndx) < 0)) error (EXIT_FAILURE, 0, gettext ("cannot get section header string table index")); @@ -2773,12 +2757,12 @@ print_liblist (Ebl *ebl) { GElf_Lib lib_mem; GElf_Lib *lib = gelf_getlib (data, cnt, &lib_mem); - if (lib == NULL) + if (unlikely (lib == NULL)) continue; time_t t = (time_t) lib->l_time_stamp; struct tm *tm = gmtime (&t); - if (tm == NULL) + if (unlikely (tm == NULL)) continue; printf (" [%2d] %-29s %04u-%02u-%02uT%02u:%02u:%02u %08x %-7u %u\n", @@ -2936,10 +2920,10 @@ dwarf_tag_string (unsigned int tag) static char buf[40]; const char *result = NULL; - if (tag < nknown_tags) + if (likely (tag < nknown_tags)) result = known_tags[tag]; - if (result == NULL) + if (unlikely (result == NULL)) /* There are a few known extensions. */ switch (tag) { @@ -3072,10 +3056,10 @@ dwarf_attr_string (unsigned int attrnum) static char buf[40]; const char *result = NULL; - if (attrnum < nknown_attrs) + if (likely (attrnum < nknown_attrs)) result = known_attrs[attrnum]; - if (result == NULL) + if (unlikely (result == NULL)) /* There are a few known extensions. */ switch (attrnum) { @@ -3218,10 +3202,10 @@ dwarf_form_string (unsigned int form) static char buf[40]; const char *result = NULL; - if (form < nknown_forms) + if (likely (form < nknown_forms)) result = known_forms[form]; - if (result == NULL) + if (unlikely (result == NULL)) snprintf (buf, sizeof buf, gettext ("unknown form %" PRIx64), (uint64_t) form); @@ -3255,7 +3239,7 @@ dwarf_lang_string (unsigned int lang) [DW_LANG_D] = "D", }; - if (lang < sizeof (known) / sizeof (known[0])) + if (likely (lang < sizeof (known) / sizeof (known[0]))) return known[lang]; else if (lang == DW_LANG_Mips_Assembler) /* This language tag is used for assembler in general. */ @@ -3283,7 +3267,7 @@ dwarf_inline_string (unsigned int code) [DW_INL_declared_inlined] = "declared_inlined" }; - if (code < sizeof (known) / sizeof (known[0])) + if (likely (code < sizeof (known) / sizeof (known[0]))) return known[code]; return "???"; @@ -3313,7 +3297,7 @@ dwarf_encoding_string (unsigned int code) [DW_ATE_decimal_float] = "decimal_float", }; - if (code < sizeof (known) / sizeof (known[0])) + if (likely (code < sizeof (known) / sizeof (known[0]))) return known[code]; if (code >= DW_ATE_lo_user && code <= DW_ATE_hi_user) @@ -3337,7 +3321,7 @@ dwarf_access_string (unsigned int code) [DW_ACCESS_private] = "private" }; - if (code < sizeof (known) / sizeof (known[0])) + if (likely (code < sizeof (known) / sizeof (known[0]))) return known[code]; return "???"; @@ -3354,7 +3338,7 @@ dwarf_visibility_string (unsigned int code) [DW_VIS_qualified] = "qualified" }; - if (code < sizeof (known) / sizeof (known[0])) + if (likely (code < sizeof (known) / sizeof (known[0]))) return known[code]; return "???"; @@ -3371,7 +3355,7 @@ dwarf_virtuality_string (unsigned int code) [DW_VIRTUALITY_pure_virtual] = "pure_virtual" }; - if (code < sizeof (known) / sizeof (known[0])) + if (likely (code < sizeof (known) / sizeof (known[0]))) return known[code]; return "???"; @@ -3389,7 +3373,7 @@ dwarf_identifier_case_string (unsigned int code) [DW_ID_case_insensitive] = "insensitive" }; - if (code < sizeof (known) / sizeof (known[0])) + if (likely (code < sizeof (known) / sizeof (known[0]))) return known[code]; return "???"; @@ -3406,7 +3390,7 @@ dwarf_calling_convention_string (unsigned int code) [DW_CC_nocall] = "nocall", }; - if (code < sizeof (known) / sizeof (known[0])) + if (likely (code < sizeof (known) / sizeof (known[0]))) return known[code]; if (code >= DW_CC_lo_user && code <= DW_CC_hi_user) @@ -3429,7 +3413,7 @@ dwarf_ordering_string (unsigned int code) [DW_ORD_col_major] = "col_major" }; - if (code < sizeof (known) / sizeof (known[0])) + if (likely (code < sizeof (known) / sizeof (known[0]))) return known[code]; return "???"; @@ -3445,7 +3429,7 @@ dwarf_discr_list_string (unsigned int code) [DW_DSC_range] = "range" }; - if (code < sizeof (known) / sizeof (known[0])) + if (likely (code < sizeof (known) / sizeof (known[0]))) return known[code]; return "???"; @@ -3828,7 +3812,7 @@ print_debug_abbrev_section (Dwfl_Module *dwflmod __attribute__ ((unused)), int res = dwarf_offabbrev (dbg, offset, &length, &abbrev); if (res != 0) { - if (res < 0) + if (unlikely (res < 0)) { printf (gettext ("\ *** error while reading abbreviation: %s\n"), @@ -3884,7 +3868,7 @@ print_debug_aranges_section (Dwfl_Module *dwflmod __attribute__ ((unused)), { Dwarf_Aranges *aranges; size_t cnt; - if (dwarf_getaranges (dbg, &aranges, &cnt) != 0) + if (unlikely (dwarf_getaranges (dbg, &aranges, &cnt) != 0)) { error (0, 0, gettext ("cannot get .debug_aranges content: %s"), dwarf_errmsg (-1)); @@ -3910,7 +3894,7 @@ print_debug_aranges_section (Dwfl_Module *dwflmod __attribute__ ((unused)), for (size_t n = 0; n < cnt; ++n) { Dwarf_Arange *runp = dwarf_onearange (aranges, n); - if (runp == NULL) + if (unlikely (runp == NULL)) { printf ("cannot get arange %zu: %s\n", n, dwarf_errmsg (-1)); return; @@ -3920,7 +3904,7 @@ print_debug_aranges_section (Dwfl_Module *dwflmod __attribute__ ((unused)), Dwarf_Word length; Dwarf_Off offset; - if (dwarf_getarangeinfo (runp, &start, &length, &offset) != 0) + if (unlikely (dwarf_getarangeinfo (runp, &start, &length, &offset) != 0)) printf (gettext (" [%*zu] ???\n"), digits, n); else printf (gettext (" [%*zu] start: %0#*" PRIx64 @@ -3940,7 +3924,7 @@ print_debug_ranges_section (Dwfl_Module *dwflmod, { Elf_Data *data = elf_rawdata (scn, NULL); - if (data == NULL) + if (unlikely (data == NULL)) { error (0, 0, gettext ("cannot get .debug_ranges content: %s"), elf_errmsg (-1)); @@ -3959,7 +3943,7 @@ print_debug_ranges_section (Dwfl_Module *dwflmod, { ptrdiff_t offset = readp - (unsigned char *) data->d_buf; - if (data->d_size - offset < address_size * 2) + if (unlikely (data->d_size - offset < address_size * 2)) { printf (gettext (" [%6tx] \n"), offset); break; @@ -4291,7 +4275,7 @@ print_debug_info_section (Dwfl_Module *dwflmod, do { offset = dwarf_dieoffset (&dies[level]); - if (offset == ~0ul) + if (unlikely (offset == ~0ul)) { error (0, 0, gettext ("cannot get DIE offset: %s"), dwarf_errmsg (-1)); @@ -4299,7 +4283,7 @@ print_debug_info_section (Dwfl_Module *dwflmod, } int tag = dwarf_tag (&dies[level]); - if (tag == DW_TAG_invalid) + if (unlikely (tag == DW_TAG_invalid)) { error (0, 0, gettext ("cannot get tag of DIE at offset %" PRIu64 " in section '%s': %s"), @@ -4328,7 +4312,7 @@ print_debug_info_section (Dwfl_Module *dwflmod, if (level-- == 0) break; - if (res == -1) + if (unlikely (res == -1)) { error (0, 0, gettext ("cannot get next DIE: %s\n"), dwarf_errmsg (-1)); @@ -4370,7 +4354,7 @@ print_debug_line_section (Dwfl_Module *dwflmod, Ebl *ebl, /* There is no functionality in libdw to read the information in the way it is represented here. Hardcode the decoder. */ Elf_Data *data = elf_getdata (scn, NULL); - if (data == NULL || data->d_buf == NULL) + if (unlikely (data == NULL || data->d_buf == NULL)) { error (0, 0, gettext ("cannot get line data section data: %s"), elf_errmsg (-1)); @@ -4483,7 +4467,7 @@ print_debug_line_section (Dwfl_Module *dwflmod, Ebl *ebl, while (*linep != 0) { unsigned char *endp = memchr (linep, '\0', lineendp - linep); - if (endp == NULL) + if (unlikely (endp == NULL)) goto invalid_unit; printf (" %s\n", (char *) linep); @@ -4502,7 +4486,7 @@ print_debug_line_section (Dwfl_Module *dwflmod, Ebl *ebl, /* First comes the file name. */ char *fname = (char *) linep; unsigned char *endp = memchr (fname, '\0', lineendp - linep); - if (endp == NULL) + if (unlikely (endp == NULL)) goto invalid_unit; linep = endp + 1; @@ -4635,7 +4619,7 @@ print_debug_line_section (Dwfl_Module *dwflmod, Ebl *ebl, char *fname = (char *) linep; unsigned char *endp = memchr (linep, '\0', lineendp - linep); - if (endp == NULL) + if (unlikely (endp == NULL)) goto invalid_unit; linep = endp + 1; @@ -4801,7 +4785,7 @@ print_debug_loc_section (Dwfl_Module *dwflmod, { Elf_Data *data = elf_rawdata (scn, NULL); - if (data == NULL) + if (unlikely (data == NULL)) { error (0, 0, gettext ("cannot get .debug_loc content: %s"), elf_errmsg (-1)); @@ -4820,7 +4804,7 @@ print_debug_loc_section (Dwfl_Module *dwflmod, { ptrdiff_t offset = readp - (unsigned char *) data->d_buf; - if (data->d_size - offset < address_size * 2) + if (unlikely (data->d_size - offset < address_size * 2)) { printf (gettext (" [%6tx] \n"), offset); break; @@ -4911,7 +4895,7 @@ print_debug_macinfo_section (Dwfl_Module *dwflmod __attribute__ ((unused)), /* There is no function in libdw to iterate over the raw content of the section but it is easy enough to do. */ Elf_Data *data = elf_getdata (scn, NULL); - if (data == NULL || data->d_buf == NULL) + if (unlikely (data == NULL || data->d_buf == NULL)) { error (0, 0, gettext ("cannot get macro information section data: %s"), elf_errmsg (-1)); @@ -4990,7 +4974,7 @@ print_debug_macinfo_section (Dwfl_Module *dwflmod __attribute__ ((unused)), get_uleb128 (u128, readp); endp = memchr (readp, '\0', readendp - readp); - if (endp == NULL) + if (unlikely (endp == NULL)) { printf (gettext ("\ %*s*** non-terminated string at end of section"), @@ -5045,7 +5029,7 @@ print_debug_macinfo_section (Dwfl_Module *dwflmod __attribute__ ((unused)), default: // XXX gcc seems to generate files with a trailing zero. - if (opcode != 0 || readp != readendp) + if (unlikely (opcode != 0 || readp != readendp)) printf ("%*s*** invalid opcode %u\n", level, "", opcode); break; } @@ -5112,7 +5096,7 @@ print_debug_str_section (Dwfl_Module *dwflmod __attribute__ ((unused)), { size_t len; const char *str = dwarf_getstring (dbg, offset, &len); - if (str == NULL) + if (unlikely (str == NULL)) { printf (gettext (" *** error while reading strings: %s\n"), dwarf_errmsg (-1)); @@ -5140,7 +5124,7 @@ print_debug (Dwfl_Module *dwflmod, Ebl *ebl, GElf_Ehdr *ehdr) /* Get the section header string table index. */ size_t shstrndx; - if (elf_getshstrndx (ebl->elf, &shstrndx) < 0) + if (unlikely (elf_getshstrndx (ebl->elf, &shstrndx) < 0)) error (EXIT_FAILURE, 0, gettext ("cannot get section header string table index")); @@ -6052,7 +6036,7 @@ for_each_section_argument (Elf *elf, const struct section_argument *list, { Elf_Scn *scn; GElf_Shdr shdr_mem; - const char *name; + const char *name = NULL; char *endp = NULL; unsigned long int shndx = strtoul (a->arg, &endp, 0); @@ -6085,7 +6069,7 @@ for_each_section_argument (Elf *elf, const struct section_argument *list, break; } - if (scn == NULL) + if (unlikely (scn == NULL)) { error (0, 0, gettext ("\nsection '%s' does not exist"), a->arg); continue; @@ -6186,3 +6170,5 @@ dump_archive_index (Elf *elf, const char *fname) printf ("\t%s\n", s->as_name); } } + +#include "debugpred.h" diff --git a/src/size.c b/src/size.c index 825f3e977..d226f4021 100644 --- a/src/size.c +++ b/src/size.c @@ -685,3 +685,6 @@ handle_elf (Elf *elf, const char *prefix, const char *fname) show_bsd (elf, prefix, fname, fullname); } } + + +#include "debugpred.h" diff --git a/src/strings.c b/src/strings.c index b9c559b31..b21099615 100644 --- a/src/strings.c +++ b/src/strings.c @@ -422,13 +422,13 @@ process_chunk (const char *fname, const unsigned char *buf, off64_t to, if (curlen >= min_len) { /* We found a match. */ - if (unlikely (fname != NULL)) + if (likely (fname != NULL)) { fputs_unlocked (fname, stdout); fputs_unlocked (": ", stdout); } - if (unlikely (locfmt != NULL)) + if (likely (locfmt != NULL)) printf (locfmt, (int64_t) to - len - (buf - start)); if (unlikely (*unprinted != NULL)) @@ -739,3 +739,6 @@ read_elf (Elf *elf, int fd, const char *fname, off64_t fdlen) return result; } + + +#include "debugpred.h" diff --git a/src/strip.c b/src/strip.c index 7858e8bf2..1e61911a7 100644 --- a/src/strip.c +++ b/src/strip.c @@ -843,7 +843,7 @@ handle_elf (int fd, Elf *elf, const char *prefix, const char *fname, if (discard_section) debugshdr.sh_type = SHT_NOBITS; - if (unlikely (gelf_update_shdr (scn, &debugshdr)) == 0) + if (unlikely (gelf_update_shdr (scn, &debugshdr) == 0)) /* There cannot be any overflows. */ INTERNAL_ERROR (fname); @@ -881,7 +881,7 @@ handle_elf (int fd, Elf *elf, const char *prefix, const char *fname, debugehdr->e_flags = ehdr->e_flags; debugehdr->e_shstrndx = ehdr->e_shstrndx; - if (unlikely (gelf_update_ehdr (debugelf, debugehdr)) == 0) + if (unlikely (gelf_update_ehdr (debugelf, debugehdr) == 0)) { error (0, 0, gettext ("%s: error while creating ELF header: %s"), debug_fname, elf_errmsg (-1)); @@ -1547,7 +1547,7 @@ handle_elf (int fd, Elf *elf, const char *prefix, const char *fname, }; /* Finally write the file. */ - if (unlikely (elf_update (debugelf, ELF_C_WRITE)) == -1) + if (unlikely (elf_update (debugelf, ELF_C_WRITE) == -1)) { error (0, 0, gettext ("while writing '%s': %s"), debug_fname, elf_errmsg (-1)); @@ -1772,3 +1772,6 @@ cannot set access and modification date of '%s'"), fname); return result; } + + +#include "debugpred.h" diff --git a/src/unstrip.c b/src/unstrip.c index 53ea3b877..676a0c7fe 100644 --- a/src/unstrip.c +++ b/src/unstrip.c @@ -2312,3 +2312,6 @@ or - if no debuginfo was found, or . if FILE contains the debug information.\ return 0; } + + +#include "debugpred.h" diff --git a/tests/ChangeLog b/tests/ChangeLog index 888f15659..2c7ed9143 100644 --- a/tests/ChangeLog +++ b/tests/ChangeLog @@ -1,3 +1,52 @@ +2008-01-01 Ulrich Drepper + + * testfile44.expect.bz2: New tests. + * testfile44.expect.bz2: Adjust. + +2007-12-31 Ulrich Drepper + + * testfile44.expect.bz2: New tests. + * testfile44.expect.bz2: Adjust. + +2007-12-30 Ulrich Drepper + + * testfile44.expect.bz2: New tests. + * testfile44.expect.bz2: Adjust. + +2007-12-29 Ulrich Drepper + + * testfile44.expect.bz2: New tests. + * testfile44.expect.bz2: Adjust. + +2007-12-28 Ulrich Drepper + + * testfile44.S.bz2: New tests. + * testfile44.expect.bz2: Adjust. + +2007-12-27 Ulrich Drepper + + * testfile44.S.bz2: New tests. + * testfile44.expect.bz2: Adjust. + +2007-12-26 Ulrich Drepper + + * testfile44.S.bz2: New tests. + * testfile44.expect.bz2: Adjust + +2007-12-21 Ulrich Drepper + + * testfile44.S.bz2: More tests. + * testfile44.expect.bz2: Adjust appropriately. + +2007-12-19 Ulrich Drepper + + * Makefile.am (TESTS): Add run-disasm.sh. + (EXTRA_DIST): Add run-disasm.sh, testfile44.S.bz2, and + testfile44.expect.bz2. + * run-disasm.sh: New file. + * testfile44.S.bz2: New file. + * testfile44.expect.bz2: New file. + 2007-12-15 Roland McGrath * run-allregs.sh: Change expected output for powerpc spefscr. diff --git a/tests/Makefile.am b/tests/Makefile.am index 455607888..25631e3ca 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -83,7 +83,8 @@ TESTS = run-arextract.sh run-arsymtest.sh newfile test-nlist \ run-native-test.sh run-bug1-test.sh \ dwfl-bug-addr-overflow run-addrname-test.sh \ dwfl-bug-fd-leak dwfl-bug-report \ - run-dwfl-bug-offline-rel.sh run-dwfl-addr-sect.sh + run-dwfl-bug-offline-rel.sh run-dwfl-addr-sect.sh \ + run-disasm.sh # run-show-ciefde.sh if !STANDALONE @@ -133,7 +134,8 @@ EXTRA_DIST = run-arextract.sh run-arsymtest.sh \ testfile36.bz2 testfile36.debug.bz2 \ testfile37.bz2 testfile37.debug.bz2 \ testfile38.bz2 testfile39.bz2 testfile40.bz2 testfile40.debug.bz2 \ - testfile41.bz2 testfile42.bz2 testfile43.bz2 + testfile41.bz2 testfile42.bz2 testfile43.bz2 \ + testfile44.S.bz2 testfile44.expect.bz2 run-disasm.sh installed_TESTS_ENVIRONMENT = libdir=$(DESTDIR)$(libdir) \ bindir=$(DESTDIR)$(bindir) \ diff --git a/tests/run-disasm.sh b/tests/run-disasm.sh new file mode 100755 index 000000000..efbb81f82 --- /dev/null +++ b/tests/run-disasm.sh @@ -0,0 +1,36 @@ +#! /bin/sh +# Copyright (C) 2007 Red Hat, Inc. +# This file is part of Red Hat elfutils. +# +# 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 +# . + +. $srcdir/test-subr.sh + +# Run x86 test. +case "$(arch)" in + x86_64 | i?86 ) + tempfiles testfile44.o + testfiles testfile44.S testfile44.expect + gcc -m32 -c -o testfile44.o testfile44.S + testrun_compare ../src/objdump -d testfile44.o < testfile44.expect + ;; +esac diff --git a/tests/testfile44.S.bz2 b/tests/testfile44.S.bz2 new file mode 100644 index 0000000000000000000000000000000000000000..30b07709456be5379e1e4ca79275af144762287c GIT binary patch literal 10255 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 DWu$w2 literal 0 Hc-jL100001 diff --git a/tests/testfile44.expect.bz2 b/tests/testfile44.expect.bz2 new file mode 100644 index 0000000000000000000000000000000000000000..6e0d65346797bf57b5e29d7fbe47be8d18f4cfa0 GIT binary patch literal 32107 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 J;@->E{~yMbXDR>y literal 0 Hc-jL100001 -- 2.47.2