]> xenbits.xensource.com Git - livepatch-build-tools.git/commitdiff
Add project files
authorRoss Lagerwall <ross.lagerwall@citrix.com>
Mon, 12 Oct 2015 11:25:14 +0000 (12:25 +0100)
committerRoss Lagerwall <ross.lagerwall@citrix.com>
Mon, 12 Oct 2015 11:30:44 +0000 (12:30 +0100)
xsplice-build can build live patches for most XSAs. It is, however,
still prototype code.

Signed-off-by: Ross Lagerwall <ross.lagerwall@citrix.com>
16 files changed:
.gitignore [new file with mode: 0644]
COPYING [new file with mode: 0644]
Makefile [new file with mode: 0644]
README.md [new file with mode: 0644]
create-diff-object.c [new file with mode: 0644]
insn/asm/inat.h [new file with mode: 0644]
insn/asm/inat_types.h [new file with mode: 0644]
insn/asm/insn.h [new file with mode: 0644]
insn/inat-tables.c [new file with mode: 0644]
insn/inat.c [new file with mode: 0644]
insn/insn.c [new file with mode: 0644]
list.h [new file with mode: 0644]
lookup.c [new file with mode: 0644]
lookup.h [new file with mode: 0644]
xsplice-build [new file with mode: 0755]
xsplice-gcc [new file with mode: 0755]

diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..a594867
--- /dev/null
@@ -0,0 +1,3 @@
+create-diff-object
+*.o
+*.d
diff --git a/COPYING b/COPYING
new file mode 100644 (file)
index 0000000..d159169
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,339 @@
+                    GNU GENERAL PUBLIC LICENSE
+                       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                            Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                    GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+                            NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+                     END OF TERMS AND CONDITIONS
+
+            How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License along
+    with this program; if not, write to the Free Software Foundation, Inc.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
diff --git a/Makefile b/Makefile
new file mode 100644 (file)
index 0000000..3741f6d
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,25 @@
+SHELL = /bin/sh
+CC    = gcc
+
+.PHONY: all install clean
+.DEFAULT: all
+
+CFLAGS  += -Iinsn -Wall -g
+LDFLAGS = -lelf
+
+TARGETS = create-diff-object
+OBJS = create-diff-object.o lookup.o insn/insn.o insn/inat.o
+SOURCES = create-diff-object.c lookup.c insn/insn.c insn/inat.c
+
+all: $(TARGETS)
+
+-include $(SOURCES:.c=.d)
+
+%.o : %.c
+       $(CC) -MMD -MP $(CFLAGS) -c -o $@ $<
+
+create-diff-object: $(OBJS)
+       $(CC) $(CFLAGS) $^ -o $@ $(LDFLAGS)
+
+clean:
+       $(RM) $(TARGETS) $(OBJS) *.d insn/*.d
diff --git a/README.md b/README.md
new file mode 100644 (file)
index 0000000..c39b376
--- /dev/null
+++ b/README.md
@@ -0,0 +1,72 @@
+xsplice-build
+=============
+
+xsplice-build is a tool for building xSplice patches from source code
+patches.  It takes as input, a Xen tree and a patch and outputs an
+`.xsplice` module containing containing the live patch.
+
+Quick start
+-----------
+First checkout the code, and then run `make` to build it.
+
+Here is an example of building a patch for XSA-106:
+```
+$ cd ~/src/xen
+$ git reset --hard
+$ git clean -x -f -d
+$ git checkout 346d4545569928b652c40c7815c1732676f8587c^
+$ cd ~/src/xsplice-build
+$ wget -q 'http://xenbits.xen.org/xsa/xsa106.patch'
+$ ./xsplice-build --xen-debug -s ~/src/xen -p xsa106.patch -o out
+Building xSplice patch: xsa106
+
+Xen directory: /home/ross/src/xen
+Patch file: /home/ross/src/xsplice-build/xsa106.patch
+Output directory: /home/ross/src/xsplice-build/out
+================================================
+
+Testing patch file...
+Perform full initial build with 4 CPU(s)...
+Apply patch and build with 4 CPU(s)...
+Unapply patch and build with 4 CPU(s)...
+Extracting new and modified ELF sections...
+Processing xen/arch/x86/x86_emulate.o
+Creating patch module...
+xsa106.xsplice created successfully
+
+$ ls -lh out/xsa106.xsplice
+-rw-rw-r--. 1 ross ross 418K Oct 12 12:02 out/xsa106.xsplice
+```
+
+Project Status
+--------------
+This is prototype code:
+ * There's no way to apply built patches
+ * Patches cannot be built for some source patches
+ * The output format does not correspond to the latest xSplice design
+
+With no source patch modifications, live patches can be built for every
+XSA that applies to x86 back to XSA-90 except for XSA-97, XSA-111,
+XSA-112, and XSA-114 (83% success rate).
+
+License
+-------
+xSplice is under the GPLv2 license.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+Credits
+-------
+Most of this code is copied from [kPatch](https://github.com/dynup/kpatch).
+All credits to the kPatch authors.
diff --git a/create-diff-object.c b/create-diff-object.c
new file mode 100644 (file)
index 0000000..5b8fb09
--- /dev/null
@@ -0,0 +1,2589 @@
+/*
+ * Copyright (C) 2014 Seth Jennings <sjenning@redhat.com>
+ * Copyright (C) 2013-2014 Josh Poimboeuf <jpoimboe@redhat.com>
+ * Copyright (C) 2015 Ross Lagerwall <ross.lagerwall@citrix.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/* Large amounts of this code have been copied from kpatch.
+ * The insn directory is copied from kpatch, which was in turn copied
+ * from the Linux kernel's arch/x86/lib.
+ */
+
+/*
+ * This file contains the heart of the ELF object differencing engine.
+ *
+ * The tool takes two ELF objects from two versions of the same source
+ * file; a "base" object and a "patched" object.  These object need to have
+ * been compiled with the -ffunction-sections and -fdata-sections GCC options.
+ *
+ * The tool compares the objects at a section level to determine what
+ * sections have changed.  Once a list of changed sections has been generated,
+ * various rules are applied to determine any object local sections that
+ * are dependencies of the changed section and also need to be included in
+ * the output object.
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <libgen.h>
+#include <argp.h>
+#include <error.h>
+#include <unistd.h>
+#include <gelf.h>
+
+#include "list.h"
+#include "lookup.h"
+#include "asm/insn.h"
+
+#define ERROR(format, ...) \
+       error(1, 0, "ERROR: %s: %s: %d: " format, childobj, __FUNCTION__, __LINE__, ##__VA_ARGS__)
+
+#define DIFF_FATAL(format, ...) \
+({ \
+       fprintf(stderr, "ERROR: %s: " format "\n", childobj, ##__VA_ARGS__); \
+       error(2, 0, "unreconcilable difference"); \
+})
+
+#define log_debug(format, ...) log(DEBUG, format, ##__VA_ARGS__)
+#define log_normal(format, ...) log(NORMAL, "%s: " format, childobj, ##__VA_ARGS__)
+
+#define log(level, format, ...) \
+({ \
+       if (loglevel <= (level)) \
+               printf(format, ##__VA_ARGS__); \
+})
+
+char *childobj;
+
+enum loglevel {
+       DEBUG,
+       NORMAL
+};
+
+static enum loglevel loglevel = NORMAL;
+
+/*******************
+ * Data structures
+ * ****************/
+struct section;
+struct symbol;
+struct rela;
+
+enum status {
+       NEW,
+       CHANGED,
+       SAME
+};
+
+struct section {
+       struct list_head list;
+       struct section *twin;
+       GElf_Shdr sh;
+       Elf_Data *data;
+       char *name;
+       int index;
+       enum status status;
+       int include;
+       int ignore;
+       int grouped;
+       union {
+               struct { /* if (is_rela_section()) */
+                       struct section *base;
+                       struct list_head relas;
+               };
+               struct { /* else */
+                       struct section *rela;
+                       struct symbol *secsym, *sym;
+               };
+       };
+};
+
+struct symbol {
+       struct list_head list;
+       struct symbol *twin;
+       struct section *sec;
+       GElf_Sym sym;
+       char *name;
+       int index;
+       unsigned char bind, type;
+       enum status status;
+       union {
+               int include; /* used in the patched elf */
+               int strip; /* used in the output elf */
+       };
+};
+
+struct rela {
+       struct list_head list;
+       GElf_Rela rela;
+       struct symbol *sym;
+       unsigned int type;
+       int addend;
+       int offset;
+       char *string;
+};
+
+struct string {
+       struct list_head list;
+       char *name;
+};
+
+struct xsplice_elf {
+       Elf *elf;
+       struct list_head sections;
+       struct list_head symbols;
+       struct list_head strings;
+       int fd;
+};
+
+struct xsplice_patch_func {
+       unsigned long new_addr;
+       unsigned long new_size;
+       unsigned long old_addr;
+       unsigned long old_size;
+       char *name;
+};
+
+struct special_section {
+       char *name;
+       int (*group_size)(struct xsplice_elf *kelf, int offset);
+};
+
+/*******************
+ * Helper functions
+ ******************/
+
+char *status_str(enum status status)
+{
+       switch(status) {
+       case NEW:
+               return "NEW";
+       case CHANGED:
+               return "CHANGED";
+       case SAME:
+               return "SAME";
+       default:
+               ERROR("status_str");
+       }
+       /* never reached */
+       return NULL;
+}
+
+int is_rela_section(struct section *sec)
+{
+       return (sec->sh.sh_type == SHT_RELA);
+}
+
+int is_text_section(struct section *sec)
+{
+       return (sec->sh.sh_type == SHT_PROGBITS &&
+               (sec->sh.sh_flags & SHF_EXECINSTR));
+}
+
+int is_debug_section(struct section *sec)
+{
+       char *name;
+       if (is_rela_section(sec))
+               name = sec->base->name;
+       else
+               name = sec->name;
+       return !strncmp(name, ".debug_", 7);
+}
+
+struct section *find_section_by_index(struct list_head *list, unsigned int index)
+{
+       struct section *sec;
+
+       list_for_each_entry(sec, list, list)
+               if (sec->index == index)
+                       return sec;
+
+       return NULL;
+}
+
+struct section *find_section_by_name(struct list_head *list, const char *name)
+{
+       struct section *sec;
+
+       list_for_each_entry(sec, list, list)
+               if (!strcmp(sec->name, name))
+                       return sec;
+
+       return NULL;
+}
+
+struct symbol *find_symbol_by_index(struct list_head *list, size_t index)
+{
+       struct symbol *sym;
+
+       list_for_each_entry(sym, list, list)
+               if (sym->index == index)
+                       return sym;
+
+       return NULL;
+}
+
+struct symbol *find_symbol_by_name(struct list_head *list, const char *name)
+{
+       struct symbol *sym;
+
+       list_for_each_entry(sym, list, list)
+               if (sym->name && !strcmp(sym->name, name))
+                       return sym;
+
+       return NULL;
+}
+
+#define ALLOC_LINK(_new, _list) \
+{ \
+       (_new) = malloc(sizeof(*(_new))); \
+       if (!(_new)) \
+               ERROR("malloc"); \
+       memset((_new), 0, sizeof(*(_new))); \
+       INIT_LIST_HEAD(&(_new)->list); \
+       list_add_tail(&(_new)->list, (_list)); \
+}
+
+/* returns the offset of the string in the string table */
+int offset_of_string(struct list_head *list, char *name)
+{
+       struct string *string;
+       int index = 0;
+
+       /* try to find string in the string list */
+       list_for_each_entry(string, list, list) {
+               if (!strcmp(string->name, name))
+                       return index;
+               index += strlen(string->name) + 1;
+       }
+
+       /* allocate a new string */
+       ALLOC_LINK(string, list);
+       string->name = name;
+       return index;
+}
+
+void rela_insn(struct section *sec, struct rela *rela, struct insn *insn)
+{
+       unsigned long insn_addr, start, end, rela_addr;
+
+       start = (unsigned long)sec->base->data->d_buf;
+       end = start + sec->base->sh.sh_size;
+       rela_addr = start + rela->offset;
+       for (insn_addr = start; insn_addr < end; insn_addr += insn->length) {
+               insn_init(insn, (void *)insn_addr, 1);
+               insn_get_length(insn);
+               if (!insn->length)
+                       ERROR("can't decode instruction in section %s at offset 0x%lx",
+                             sec->base->name, insn_addr);
+               if (rela_addr >= insn_addr &&
+                   rela_addr < insn_addr + insn->length)
+                       return;
+       }
+}
+
+/*************
+ * Functions
+ * **********/
+void xsplice_create_rela_list(struct xsplice_elf *kelf, struct section *sec)
+{
+       int rela_nr, index = 0, skip = 0;
+       struct rela *rela;
+       unsigned int symndx;
+
+       /* find matching base (text/data) section */
+       sec->base = find_section_by_name(&kelf->sections, sec->name + 5);
+       if (!sec->base)
+               ERROR("can't find base section for rela section %s", sec->name);
+
+       /* create reverse link from base section to this rela section */
+       sec->base->rela = sec;
+               
+       rela_nr = sec->sh.sh_size / sec->sh.sh_entsize;
+
+       log_debug("\n=== rela list for %s (%d entries) ===\n",
+               sec->base->name, rela_nr);
+
+       if (is_debug_section(sec)) {
+               log_debug("skipping rela listing for .debug_* section\n");
+               skip = 1;
+       }
+
+       /* read and store the rela entries */
+       while (rela_nr--) {
+               ALLOC_LINK(rela, &sec->relas);
+
+               if (!gelf_getrela(sec->data, index, &rela->rela))
+                       ERROR("gelf_getrela");
+               index++;
+
+               rela->type = GELF_R_TYPE(rela->rela.r_info);
+               rela->addend = rela->rela.r_addend;
+               rela->offset = rela->rela.r_offset;
+               symndx = GELF_R_SYM(rela->rela.r_info);
+               rela->sym = find_symbol_by_index(&kelf->symbols, symndx);
+               if (!rela->sym)
+                       ERROR("could not find rela entry symbol\n");
+               if (rela->sym->sec &&
+                   (rela->sym->sec->sh.sh_flags & SHF_STRINGS)) {
+                       /* XXX This differs from upstream. Send a pull request. */
+                       rela->string = rela->sym->sec->data->d_buf +
+                                      rela->sym->sym.st_value + rela->addend;
+
+                       /*
+                        * XXX This is a bit of a hack. Investigate with
+                        * upstream.
+                        */
+                       /*
+                        * If the relocation is relative to %rip, the addend
+                        * includes the length of the instruction, so offset by
+                        * the length of the instruction to get the actual
+                        * string (if the relocation is to a .text section).
+                        * If the relocation is to a debug frame, ignore the
+                        * since it encodes the line number.
+                        */
+                       if (rela->type == R_X86_64_PC32) {
+                               if (!strncmp(sec->base->name, ".text", 5)) {
+                                       struct insn insn;
+                                       rela_insn(sec, rela, &insn);
+                                       rela->string += (long)insn.next_byte -
+                                                       (long)sec->base->data->d_buf -
+                                                       rela->offset;
+                               } else {
+                                       rela->string -= rela->addend;
+                               }
+                       }
+                       if (!rela->string)
+                               ERROR("could not lookup rela string for %s+%d",
+                                     rela->sym->name, rela->addend);
+               }
+
+               if (skip)
+                       continue;
+               log_debug("offset %d, type %d, %s %s %d", rela->offset,
+                       rela->type, rela->sym->name,
+                       (rela->addend < 0)?"-":"+", abs(rela->addend));
+               if (rela->string)
+                       log_debug(" (string = %s)", rela->string);
+               log_debug("\n");
+       }
+}
+
+void xsplice_create_section_list(struct xsplice_elf *kelf)
+{
+       Elf_Scn *scn = NULL;
+       struct section *sec;
+       size_t shstrndx, sections_nr;
+
+       if (elf_getshdrnum(kelf->elf, &sections_nr))
+               ERROR("elf_getshdrnum");
+
+       /*
+        * elf_getshdrnum() includes section index 0 but elf_nextscn
+        * doesn't return that section so subtract one.
+        */
+       sections_nr--;
+
+       if (elf_getshdrstrndx(kelf->elf, &shstrndx))
+               ERROR("elf_getshdrstrndx");
+
+       log_debug("=== section list (%zu) ===\n", sections_nr);
+
+       while (sections_nr--) {
+               ALLOC_LINK(sec, &kelf->sections);
+
+               scn = elf_nextscn(kelf->elf, scn);
+               if (!scn)
+                       ERROR("scn NULL");
+
+               if (!gelf_getshdr(scn, &sec->sh))
+                       ERROR("gelf_getshdr");
+
+               sec->name = elf_strptr(kelf->elf, shstrndx, sec->sh.sh_name);
+               if (!sec->name)
+                       ERROR("elf_strptr");
+
+               sec->data = elf_getdata(scn, NULL);
+               if (!sec->data)
+                       ERROR("elf_getdata");
+
+               sec->index = elf_ndxscn(scn);
+
+               log_debug("ndx %02d, data %p, size %zu, name %s\n",
+                       sec->index, sec->data->d_buf, sec->data->d_size,
+                       sec->name);
+       }
+
+       /* Sanity check, one more call to elf_nextscn() should return NULL */
+       if (elf_nextscn(kelf->elf, scn))
+               ERROR("expected NULL");
+}
+
+int is_bundleable(struct symbol *sym)
+{
+       if (sym->type == STT_FUNC &&
+           !strncmp(sym->sec->name, ".text.",6) &&
+           !strcmp(sym->sec->name + 6, sym->name))
+               return 1;
+
+       if (sym->type == STT_FUNC &&
+           !strncmp(sym->sec->name, ".text.unlikely.",15) &&
+           !strcmp(sym->sec->name + 15, sym->name))
+               return 1;
+
+       if (sym->type == STT_OBJECT &&
+          !strncmp(sym->sec->name, ".data.",6) &&
+          !strcmp(sym->sec->name + 6, sym->name))
+               return 1;
+
+       if (sym->type == STT_OBJECT &&
+          !strncmp(sym->sec->name, ".rodata.",8) &&
+          !strcmp(sym->sec->name + 8, sym->name))
+               return 1;
+
+       if (sym->type == STT_OBJECT &&
+          !strncmp(sym->sec->name, ".bss.",5) &&
+          !strcmp(sym->sec->name + 5, sym->name))
+               return 1;
+
+       return 0;
+}
+
+void xsplice_create_symbol_list(struct xsplice_elf *kelf)
+{
+       struct section *symtab;
+       struct symbol *sym;
+       int symbols_nr, index = 0;
+
+       symtab = find_section_by_name(&kelf->sections, ".symtab");
+       if (!symtab)
+               ERROR("missing symbol table");
+
+       symbols_nr = symtab->sh.sh_size / symtab->sh.sh_entsize;
+
+       log_debug("\n=== symbol list (%d entries) ===\n", symbols_nr);
+
+       while (symbols_nr--) {
+               ALLOC_LINK(sym, &kelf->symbols);
+
+               sym->index = index;
+               if (!gelf_getsym(symtab->data, index, &sym->sym))
+                       ERROR("gelf_getsym");
+               index++;
+
+               sym->name = elf_strptr(kelf->elf, symtab->sh.sh_link,
+                                      sym->sym.st_name);
+               if (!sym->name)
+                       ERROR("elf_strptr");
+
+               sym->type = GELF_ST_TYPE(sym->sym.st_info);
+               sym->bind = GELF_ST_BIND(sym->sym.st_info);
+
+               if (sym->sym.st_shndx > SHN_UNDEF &&
+                   sym->sym.st_shndx < SHN_LORESERVE) {
+                       sym->sec = find_section_by_index(&kelf->sections,
+                                       sym->sym.st_shndx);
+                       if (!sym->sec)
+                               ERROR("couldn't find section for symbol %s\n",
+                                       sym->name);
+
+                       if (is_bundleable(sym)) {
+                               if (sym->sym.st_value != 0)
+                                       ERROR("symbol %s at offset %lu within section %s, expected 0",
+                                             sym->name, sym->sym.st_value, sym->sec->name);
+                               sym->sec->sym = sym;
+                       } else if (sym->type == STT_SECTION) {
+                               sym->sec->secsym = sym;
+                               /* use the section name as the symbol name */
+                               sym->name = sym->sec->name;
+                       }
+               }
+
+               log_debug("sym %02d, type %d, bind %d, ndx %02d, name %s",
+                       sym->index, sym->type, sym->bind, sym->sym.st_shndx,
+                       sym->name);
+               if (sym->sec)
+                       log_debug(" -> %s", sym->sec->name);
+               log_debug("\n");
+       }
+
+}
+
+struct xsplice_elf *xsplice_elf_open(const char *name)
+{
+       Elf *elf;
+       int fd;
+       struct xsplice_elf *kelf;
+       struct section *sec;
+
+       fd = open(name, O_RDONLY);
+       if (fd == -1)
+               ERROR("open");
+
+       elf = elf_begin(fd, ELF_C_READ_MMAP, NULL);
+       if (!elf)
+               ERROR("elf_begin");
+
+       kelf = malloc(sizeof(*kelf));
+       if (!kelf)
+               ERROR("malloc");
+       memset(kelf, 0, sizeof(*kelf));
+       INIT_LIST_HEAD(&kelf->sections);
+       INIT_LIST_HEAD(&kelf->symbols);
+       INIT_LIST_HEAD(&kelf->strings);
+
+       /* read and store section, symbol entries from file */
+       kelf->elf = elf;
+       kelf->fd = fd;
+       xsplice_create_section_list(kelf);
+       xsplice_create_symbol_list(kelf);
+
+       /* for each rela section, read and store the rela entries */
+       list_for_each_entry(sec, &kelf->sections, list) {
+               if (!is_rela_section(sec))
+                       continue;
+               INIT_LIST_HEAD(&sec->relas);
+               xsplice_create_rela_list(kelf, sec);
+       }
+
+       return kelf;
+}
+
+void xsplice_compare_elf_headers(Elf *elf1, Elf *elf2)
+{
+       GElf_Ehdr eh1, eh2;
+
+       if (!gelf_getehdr(elf1, &eh1))
+               ERROR("gelf_getehdr");
+
+       if (!gelf_getehdr(elf2, &eh2))
+               ERROR("gelf_getehdr");
+
+       if (memcmp(eh1.e_ident, eh2.e_ident, EI_NIDENT) ||
+           eh1.e_type != eh2.e_type ||
+           eh1.e_machine != eh2.e_machine ||
+           eh1.e_version != eh2.e_version ||
+           eh1.e_entry != eh2.e_entry ||
+           eh1.e_phoff != eh2.e_phoff ||
+           eh1.e_flags != eh2.e_flags ||
+           eh1.e_ehsize != eh2.e_ehsize ||
+           eh1.e_phentsize != eh2.e_phentsize ||
+           eh1.e_shentsize != eh2.e_shentsize)
+               DIFF_FATAL("ELF headers differ");
+}
+
+void xsplice_check_program_headers(Elf *elf)
+{
+       size_t ph_nr;
+
+       if (elf_getphdrnum(elf, &ph_nr))
+               ERROR("elf_getphdrnum");
+
+       if (ph_nr != 0)
+               DIFF_FATAL("ELF contains program header");
+}
+
+void xsplice_mark_grouped_sections(struct xsplice_elf *kelf)
+{
+       struct section *groupsec, *sec;
+       unsigned int *data, *end;
+
+       list_for_each_entry(groupsec, &kelf->sections, list) {
+               if (groupsec->sh.sh_type != SHT_GROUP)
+                       continue;
+               data = groupsec->data->d_buf;
+               end = groupsec->data->d_buf + groupsec->data->d_size;
+               data++; /* skip first flag word (e.g. GRP_COMDAT) */
+               while (data < end) {
+                       sec = find_section_by_index(&kelf->sections, *data);
+                       if (!sec)
+                               ERROR("group section not found");
+                       sec->grouped = 1;
+                       log_debug("marking section %s (%d) as grouped\n",
+                                 sec->name, sec->index);
+                       data++;
+               }
+       }
+}
+
+/*
+ * Mangle the relas a little.  The compiler will sometimes use section symbols
+ * to reference local objects and functions rather than the object or function
+ * symbols themselves.  We substitute the object/function symbols for the
+ * section symbol in this case so that the relas can be properly correlated and
+ * so that the existing object/function in vmlinux can be linked to.
+ */
+void xsplice_replace_sections_syms(struct xsplice_elf *kelf)
+{
+       struct section *sec;
+       struct rela *rela;
+       struct symbol *sym;
+       int add_off;
+
+       log_debug("\n");
+
+       list_for_each_entry(sec, &kelf->sections, list) {
+               if (!is_rela_section(sec) ||
+                   is_debug_section(sec))
+                       continue;
+
+               list_for_each_entry(rela, &sec->relas, list) {
+
+                       if (rela->sym->type != STT_SECTION)
+                               continue;
+
+                       /*
+                        * Replace references to bundled sections with their
+                        * symbols.
+                        */
+                       if (rela->sym->sec && rela->sym->sec->sym) {
+                               rela->sym = rela->sym->sec->sym;
+                               continue;
+                       }
+
+                       if (rela->type == R_X86_64_PC32) {
+                               struct insn insn;
+                               rela_insn(sec, rela, &insn);
+                               add_off = (long)insn.next_byte -
+                                         (long)sec->base->data->d_buf -
+                                         rela->offset;
+                       } else if (rela->type == R_X86_64_64 ||
+                                  rela->type == R_X86_64_32S)
+                               add_off = 0;
+                       else
+                               continue;
+
+                       /*
+                        * Attempt to replace references to unbundled sections
+                        * with their symbols.
+                        */
+                       list_for_each_entry(sym, &kelf->symbols, list) {
+                               int start, end;
+
+                               if (sym->type == STT_SECTION ||
+                                   sym->sec != rela->sym->sec)
+                                       continue;
+
+                               start = sym->sym.st_value;
+                               end = sym->sym.st_value + sym->sym.st_size;
+
+                               if (rela->addend + add_off < start ||
+                                   rela->addend + add_off >= end)
+                                       continue;
+
+                               log_debug("%s: replacing %s+%d reference with %s+%d\n",
+                                         sec->name,
+                                         rela->sym->name, rela->addend,
+                                         sym->name, rela->addend - start);
+
+                               rela->sym = sym;
+                               rela->addend -= start;
+                               break;
+                       }
+               }
+       }
+       log_debug("\n");
+}
+
+/*
+ * This is like strcmp, but for gcc-mangled symbols.  It skips the comparison
+ * of any substring which consists of '.' followed by any number of digits.
+ */
+static int xsplice_mangled_strcmp(char *s1, char *s2)
+{
+       while (*s1 == *s2) {
+               if (!*s1)
+                       return 0;
+               if (*s1 == '.' && isdigit(s1[1])) {
+                       if (!isdigit(s2[1]))
+                               return 1;
+                       while (isdigit(*++s1))
+                               ;
+                       while (isdigit(*++s2))
+                               ;
+               } else {
+                       s1++;
+                       s2++;
+               }
+       }
+       return 1;
+}
+
+/*
+ * When gcc makes compiler optimizations which affect a function's calling
+ * interface, it mangles the function's name.  For example, sysctl_print_dir is
+ * renamed to sysctl_print_dir.isra.2.  The problem is that the trailing number
+ * is chosen arbitrarily, and the patched version of the function may end up
+ * with a different trailing number.  Rename any mangled patched functions to
+ * match their base counterparts.
+ */
+void xsplice_rename_mangled_functions(struct xsplice_elf *base,
+                                    struct xsplice_elf *patched)
+{
+       struct symbol *sym, *basesym;
+       char name[256], *origname;
+       struct section *sec, *basesec;
+       int found;
+
+       list_for_each_entry(sym, &patched->symbols, list) {
+               if (sym->type != STT_FUNC)
+                       continue;
+
+               if (!strstr(sym->name, ".isra.") &&
+                   !strstr(sym->name, ".constprop.") &&
+                   !strstr(sym->name, ".part."))
+                       continue;
+
+               found = 0;
+               list_for_each_entry(basesym, &base->symbols, list) {
+                       if (!xsplice_mangled_strcmp(basesym->name, sym->name)) {
+                               found = 1;
+                               break;
+                       }
+               }
+
+               if (!found)
+                       continue;
+
+               if (!strcmp(sym->name, basesym->name))
+                       continue;
+
+               log_debug("renaming %s to %s\n", sym->name, basesym->name);
+               origname = sym->name;
+               sym->name = strdup(basesym->name);
+
+               if (sym != sym->sec->sym)
+                       continue;
+
+               sym->sec->name = strdup(basesym->sec->name);
+               if (sym->sec->rela)
+                       sym->sec->rela->name = strdup(basesym->sec->rela->name);
+
+               /*
+                * When function foo.isra.1 has a switch statement, it might
+                * have a corresponding bundled .rodata.foo.isra.1 section (in
+                * addition to .text.foo.isra.1 which we renamed above).
+                */
+               sprintf(name, ".rodata.%s", origname);
+               sec = find_section_by_name(&patched->sections, name);
+               if (!sec)
+                       continue;
+               sprintf(name, ".rodata.%s", basesym->name);
+               basesec = find_section_by_name(&base->sections, name);
+               if (!basesec)
+                       continue;
+               sec->name = strdup(basesec->name);
+               sec->secsym->name = sec->name;
+               if (sec->rela)
+                       sec->rela->name = strdup(basesec->rela->name);
+       }
+}
+
+/*
+ * This function detects whether the given symbol is a "special" static local
+ * variable (for lack of a better term).
+ *
+ * Special static local variables should never be correlated and should always
+ * be included if they are referenced by an included function.
+ */
+static int is_special_static(struct symbol *sym)
+{
+       static char *prefixes[] = {
+               "__key.",
+               "__warned.",
+               "descriptor.",
+               "__func__.",
+               "_rs.",
+               NULL,
+       };
+       char **prefix;
+
+       if (!sym)
+               return 0;
+
+       if (sym->type == STT_SECTION) {
+               /* __verbose section contains the descriptor variables */
+               if (!strcmp(sym->name, "__verbose"))
+                       return 1;
+
+               /* otherwise make sure section is bundled */
+               if (!sym->sec->sym)
+                       return 0;
+
+               /* use bundled object/function symbol for matching */
+               sym = sym->sec->sym;
+       }
+
+       if (sym->type != STT_OBJECT || sym->bind != STB_LOCAL)
+               return 0;
+
+       for (prefix = prefixes; *prefix; prefix++)
+               if (!strncmp(sym->name, *prefix, strlen(*prefix)))
+                       return 1;
+
+       return 0;
+}
+
+static int is_constant_label(struct symbol *sym)
+{
+       const char *str;
+
+       if (sym->bind != STB_LOCAL)
+               return 0;
+
+       if (strncmp(sym->name, ".LC", 3))
+               return 0;
+
+       str = sym->name + 3;
+       do {
+               if (!isdigit(*str))
+                       return 0;
+       } while (*++str);
+
+       return 1;
+}
+
+void xsplice_correlate_sections(struct list_head *seclist1, struct list_head *seclist2)
+{
+       struct section *sec1, *sec2;
+
+       list_for_each_entry(sec1, seclist1, list) {
+               list_for_each_entry(sec2, seclist2, list) {
+                       if (strcmp(sec1->name, sec2->name))
+                               continue;
+
+                       if (is_special_static(is_rela_section(sec1) ?
+                                             sec1->base->secsym :
+                                             sec1->secsym))
+                               continue;
+
+                       /*
+                        * Group sections must match exactly to be correlated.
+                        * Changed group sections are currently not supported.
+                        */
+                       if (sec1->sh.sh_type == SHT_GROUP) {
+                               if (sec1->data->d_size != sec2->data->d_size)
+                                       continue;
+                               if (memcmp(sec1->data->d_buf, sec2->data->d_buf,
+                                          sec1->data->d_size))
+                                       continue;
+                       }
+                       log_debug("Found section twins: %s\n", sec1->name);
+                       sec1->twin = sec2;
+                       sec2->twin = sec1;
+                       /* set initial status, might change */
+                       sec1->status = sec2->status = SAME;
+                       break;
+               }
+       }
+}
+
+void xsplice_correlate_symbols(struct list_head *symlist1, struct list_head *symlist2)
+{
+       struct symbol *sym1, *sym2;
+
+       list_for_each_entry(sym1, symlist1, list) {
+               list_for_each_entry(sym2, symlist2, list) {
+                       if (strcmp(sym1->name, sym2->name) ||
+                           sym1->type != sym2->type)
+                               continue;
+
+                       if (is_special_static(sym1))
+                               continue;
+
+                       if (is_constant_label(sym1))
+                               continue;
+
+                       /* group section symbols must have correlated sections */
+                       if (sym1->sec &&
+                           sym1->sec->sh.sh_type == SHT_GROUP &&
+                           sym1->sec->twin != sym2->sec)
+                               continue;
+
+                       log_debug("Found symbol twins: %s\n", sym1->name);
+                       sym1->twin = sym2;
+                       sym2->twin = sym1;
+                       /* set initial status, might change */
+                       sym1->status = sym2->status = SAME;
+                       break;
+               }
+       }
+}
+
+void xsplice_correlate_elfs(struct xsplice_elf *kelf1, struct xsplice_elf *kelf2)
+{
+       xsplice_correlate_sections(&kelf1->sections, &kelf2->sections);
+       xsplice_correlate_symbols(&kelf1->symbols, &kelf2->symbols);
+}
+
+static char *xsplice_section_function_name(struct section *sec)
+{
+       if (is_rela_section(sec))
+               sec = sec->base;
+       return sec->sym ? sec->sym->name : sec->name;
+}
+
+/*
+ * Given a static local variable symbol and a section which references it in
+ * the patched object, find a corresponding usage of a similarly named symbol
+ * in the base object.
+ */
+static struct symbol *xsplice_find_static_twin(struct section *sec,
+                                             struct symbol *sym)
+{
+       struct rela *rela;
+       struct symbol *basesym;
+
+       if (!sec->twin)
+               return NULL;
+
+       /*
+        * Ensure there are no other orphaned static variables with the
+        * same name in the function.  This is possible if the
+        * variables are in different scopes or if one of them is part of an
+        * inlined function.
+        */
+       list_for_each_entry(rela, &sec->relas, list) {
+               if (rela->sym == sym || rela->sym->twin)
+                       continue;
+               if (!xsplice_mangled_strcmp(rela->sym->name, sym->name))
+                       ERROR("found another static local variable matching %s in patched %s",
+                             sym->name, xsplice_section_function_name(sec));
+       }
+
+       /* find the base object's corresponding variable */
+       basesym = NULL;
+       list_for_each_entry(rela, &sec->twin->relas, list) {
+               if (rela->sym->twin)
+                       continue;
+               if (xsplice_mangled_strcmp(rela->sym->name, sym->name))
+                       continue;
+               if (basesym && basesym != rela->sym)
+                       ERROR("found two static local variables matching %s in orig %s",
+                             sym->name, xsplice_section_function_name(sec));
+
+               basesym = rela->sym;
+       }
+
+       return basesym;
+}
+
+/*
+ * gcc renames static local variables by appending a period and a number.  For
+ * example, __foo could be renamed to __foo.31452.  Unfortunately this number
+ * can arbitrarily change.  Try to rename the patched version of the symbol to
+ * match the base version and then correlate them.
+ */
+void xsplice_correlate_static_local_variables(struct xsplice_elf *base,
+                                            struct xsplice_elf *patched)
+{
+       struct symbol *sym, *basesym, *tmpsym;
+       struct section *tmpsec, *sec;
+       struct rela *rela;
+       int bundled, basebundled;
+
+       list_for_each_entry(sym, &patched->symbols, list) {
+               if (sym->type != STT_OBJECT || sym->bind != STB_LOCAL ||
+                   sym->twin)
+                       continue;
+
+               if (is_special_static(sym))
+                       continue;
+
+               if (!strchr(sym->name, '.'))
+                       continue;
+
+               /*
+                * For each function which uses the variable in the patched
+                * object, look for a corresponding use in the function's twin
+                * in the base object.
+                *
+                * It's possible for multiple functions to use the same static
+                * local variable if the variable is defined in an inlined
+                * function.
+                */
+               sec = NULL;
+               basesym = NULL;
+               list_for_each_entry(tmpsec, &patched->sections, list) {
+                       if (!is_rela_section(tmpsec) ||
+                           !is_text_section(tmpsec->base) ||
+                           is_debug_section(tmpsec))
+                               continue;
+                       list_for_each_entry(rela, &tmpsec->relas, list) {
+                               if (rela->sym != sym)
+                                       continue;
+
+                               tmpsym = xsplice_find_static_twin(tmpsec, sym);
+                               if (basesym && tmpsym && basesym != tmpsym)
+                                       ERROR("found two twins for static local variable %s: %s and %s",
+                                             sym->name, basesym->name,
+                                             tmpsym->name);
+                               if (tmpsym && !basesym)
+                                       basesym = tmpsym;
+
+                               sec = tmpsec;
+                               break;
+                       }
+               }
+
+               if (!sec)
+                       ERROR("static local variable %s not used", sym->name);
+
+               if (!basesym) {
+                       log_normal("WARNING: unable to correlate static local variable %s used by %s, assuming variable is new\n",
+                                  sym->name,
+                                  xsplice_section_function_name(sec));
+                       continue;
+               }
+
+
+               bundled = sym == sym->sec->sym;
+               basebundled = basesym == basesym->sec->sym;
+               if (bundled != basebundled)
+                       ERROR("bundle mismatch for symbol %s", sym->name);
+               if (!bundled && sym->sec->twin != basesym->sec)
+                       ERROR("sections %s and %s aren't correlated",
+                             sym->sec->name, basesym->sec->name);
+
+               log_debug("renaming and correlating %s to %s\n",
+                         sym->name, basesym->name);
+               sym->name = strdup(basesym->name);
+               sym->twin = basesym;
+               basesym->twin = sym;
+               sym->status = basesym->status = SAME;
+
+               if (bundled) {
+                       sym->sec->twin = basesym->sec;
+                       basesym->sec->twin = sym->sec;
+               }
+       }
+}
+
+int rela_equal(struct rela *rela1, struct rela *rela2)
+{
+       log_debug("Comparing rela %s with %s\n", rela1->sym->name, rela2->sym->name);
+       if (rela1->type != rela2->type ||
+           rela1->offset != rela2->offset)
+               return 0;
+
+       if (rela1->string)
+               return rela2->string && !strcmp(rela1->string, rela2->string);
+
+       if (rela1->addend != rela2->addend)
+               return 0;
+
+       if (is_constant_label(rela1->sym) && is_constant_label(rela2->sym))
+               return 1;
+
+       if (is_special_static(rela1->sym))
+               return !xsplice_mangled_strcmp(rela1->sym->name,
+                                             rela2->sym->name);
+
+       return !strcmp(rela1->sym->name, rela2->sym->name);
+}
+
+void xsplice_compare_correlated_rela_section(struct section *sec)
+{
+       struct rela *rela1, *rela2 = NULL;
+
+       rela2 = list_entry(sec->twin->relas.next, struct rela, list);
+       list_for_each_entry(rela1, &sec->relas, list) {
+               if (rela_equal(rela1, rela2)) {
+                       rela2 = list_entry(rela2->list.next, struct rela, list);
+                       continue;
+               }
+               sec->status = CHANGED;
+               return;
+       }
+
+       sec->status = SAME;
+}
+
+void xsplice_compare_correlated_nonrela_section(struct section *sec)
+{
+       struct section *sec1 = sec, *sec2 = sec->twin;
+
+       if (sec1->sh.sh_type != SHT_NOBITS &&
+           memcmp(sec1->data->d_buf, sec2->data->d_buf, sec1->data->d_size))
+               sec->status = CHANGED;
+       else
+               sec->status = SAME;
+}
+
+void xsplice_compare_correlated_section(struct section *sec)
+{
+       struct section *sec1 = sec, *sec2 = sec->twin;
+
+       log_debug("Compare correlated section: %s\n", sec->name);
+
+       /* Compare section headers (must match or fatal) */
+       if (sec1->sh.sh_type != sec2->sh.sh_type ||
+           sec1->sh.sh_flags != sec2->sh.sh_flags ||
+           sec1->sh.sh_addr != sec2->sh.sh_addr ||
+           sec1->sh.sh_addralign != sec2->sh.sh_addralign ||
+           sec1->sh.sh_entsize != sec2->sh.sh_entsize)
+               DIFF_FATAL("%s section header details differ", sec1->name);
+
+       if (sec1->sh.sh_size != sec2->sh.sh_size ||
+           sec1->data->d_size != sec2->data->d_size) {
+               sec->status = CHANGED;
+               goto out;
+       }
+
+       if (is_rela_section(sec))
+               xsplice_compare_correlated_rela_section(sec);
+       else
+               xsplice_compare_correlated_nonrela_section(sec);
+out:
+       if (sec->status == CHANGED)
+               log_debug("section %s has changed\n", sec->name);
+}
+
+void xsplice_compare_sections(struct list_head *seclist)
+{
+       struct section *sec;
+
+       /* compare all sections */
+       list_for_each_entry(sec, seclist, list) {
+               if (sec->twin)
+                       xsplice_compare_correlated_section(sec);
+               else
+                       sec->status = NEW;
+       }
+
+       /* sync symbol status */
+       list_for_each_entry(sec, seclist, list) {
+               if (is_rela_section(sec)) {
+                       if (sec->base->sym && sec->base->sym->status != CHANGED)
+                               sec->base->sym->status = sec->status;
+               } else {
+                       if (sec->sym && sec->sym->status != CHANGED)
+                               sec->sym->status = sec->status;
+               }
+       }
+}
+
+void xsplice_compare_correlated_symbol(struct symbol *sym)
+{
+       struct symbol *sym1 = sym, *sym2 = sym->twin;
+
+       if (sym1->sym.st_info != sym2->sym.st_info ||
+           sym1->sym.st_other != sym2->sym.st_other ||
+           (sym1->sec && !sym2->sec) ||
+           (sym2->sec && !sym1->sec))
+               DIFF_FATAL("symbol info mismatch: %s", sym1->name);
+
+       /*
+        * If two symbols are correlated but their sections are not, then the
+        * symbol has changed sections.  This is only allowed if the symbol is
+        * moving out of an ignored section.
+        */
+       if (sym1->sec && sym2->sec && sym1->sec->twin != sym2->sec) {
+               if (sym2->sec->twin && sym2->sec->twin->ignore)
+                       sym->status = CHANGED;
+               else
+                       DIFF_FATAL("symbol changed sections: %s, %s, %s, %s", sym1->name, sym2->name, sym1->sec->name, sym2->sec->name);
+       }
+
+       if (sym1->type == STT_OBJECT &&
+           sym1->sym.st_size != sym2->sym.st_size)
+               DIFF_FATAL("object size mismatch: %s", sym1->name);
+
+       if (sym1->sym.st_shndx == SHN_UNDEF ||
+            sym1->sym.st_shndx == SHN_ABS)
+               sym1->status = SAME;
+
+       /*
+        * The status of LOCAL symbols is dependent on the status of their
+        * matching section and is set during section comparison.
+        */
+}
+
+void xsplice_compare_symbols(struct list_head *symlist)
+{
+       struct symbol *sym;
+
+       list_for_each_entry(sym, symlist, list) {
+               if (sym->twin)
+                       xsplice_compare_correlated_symbol(sym);
+               else
+                       sym->status = NEW;
+
+               log_debug("symbol %s is %s\n", sym->name, status_str(sym->status));
+       }
+}
+
+void xsplice_compare_correlated_elements(struct xsplice_elf *kelf)
+{
+       /* lists are already correlated at this point */
+       log_debug("Compare sections\n");
+       xsplice_compare_sections(&kelf->sections);
+       log_debug("Compare symbols\n");
+       xsplice_compare_symbols(&kelf->symbols);
+}
+
+/*
+ * While this is a one-shot program without a lot of proper cleanup in case
+ * of an error, this function serves a debugging purpose: to break down and
+ * zero data structures we shouldn't be accessing anymore.  This should
+ * help cause an immediate and obvious issue when a logic error leads to
+ * accessing data that is not intended to be accessed past a particular point.
+ */
+void xsplice_elf_teardown(struct xsplice_elf *kelf)
+{
+       struct section *sec, *safesec;
+       struct symbol *sym, *safesym;
+       struct rela *rela, *saferela;
+
+       list_for_each_entry_safe(sec, safesec, &kelf->sections, list) {
+               if (is_rela_section(sec)) {
+                       list_for_each_entry_safe(rela, saferela, &sec->relas, list) {
+                               memset(rela, 0, sizeof(*rela));
+                               free(rela);
+                       }
+                       memset(sec, 0, sizeof(*sec));
+                       free(sec);
+               }
+       }
+
+       list_for_each_entry_safe(sym, safesym, &kelf->symbols, list) {
+               memset(sym, 0, sizeof(*sym));
+               free(sym);
+       }
+
+       INIT_LIST_HEAD(&kelf->sections);
+       INIT_LIST_HEAD(&kelf->symbols);
+}
+
+void xsplice_elf_free(struct xsplice_elf *kelf)
+{
+       elf_end(kelf->elf);
+       close(kelf->fd);
+       memset(kelf, 0, sizeof(*kelf));
+       free(kelf);
+}
+
+void xsplice_mark_ignored_sections_same(struct xsplice_elf *kelf)
+{
+       struct section *sec;
+       struct symbol *sym;
+
+       list_for_each_entry(sec, &kelf->sections, list) {
+               if (!sec->ignore)
+                       continue;
+               sec->status = SAME;
+               if (sec->secsym)
+                       sec->secsym->status = SAME;
+               if (sec->rela)
+                       sec->rela->status = SAME;
+               list_for_each_entry(sym, &kelf->symbols, list) {
+                       if (sym->sec != sec)
+                               continue;
+                       sym->status = SAME;
+               }
+       }
+}
+
+void xsplice_mark_constant_labels_same(struct xsplice_elf *kelf)
+{
+       struct symbol *sym;
+
+       list_for_each_entry(sym, &kelf->symbols, list) {
+               if (is_constant_label(sym))
+                       sym->status = SAME;
+       }
+}
+
+int bug_frames_0_group_size(struct xsplice_elf *kelf, int offset) { return 8; }
+int bug_frames_1_group_size(struct xsplice_elf *kelf, int offset) { return 8; }
+int bug_frames_2_group_size(struct xsplice_elf *kelf, int offset) { return 8; }
+int bug_frames_3_group_size(struct xsplice_elf *kelf, int offset) { return 16; }
+int ex_table_group_size(struct xsplice_elf *kelf, int offset) { return 8; }
+int altinstructions_group_size(struct xsplice_elf *kelf, int offset) { return 12; }
+
+/*
+ * The rela groups in the .fixup section vary in size.  The beginning of each
+ * .fixup rela group is referenced by the .ex_table section. To find the size
+ * of a .fixup rela group, we have to traverse the .ex_table relas.
+ */
+int fixup_group_size(struct xsplice_elf *kelf, int offset)
+{
+       struct section *sec;
+       struct rela *rela;
+       int found;
+
+       sec = find_section_by_name(&kelf->sections, ".rela.ex_table");
+       if (!sec)
+               ERROR("missing .rela.ex_table section");
+
+       /* find beginning of this group */
+       found = 0;
+       list_for_each_entry(rela, &sec->relas, list) {
+               if (!strcmp(rela->sym->name, ".fixup") &&
+                   rela->addend == offset) {
+                               found = 1;
+                               break;
+               }
+       }
+
+       if (!found)
+               ERROR("can't find .fixup rela group at offset %d\n", offset);
+
+       /* find beginning of next group */
+       found = 0;
+       list_for_each_entry_continue(rela, &sec->relas, list) {
+               if (!strcmp(rela->sym->name, ".fixup") &&
+                   rela->addend > offset) {
+                       found = 1;
+                       break;
+               }
+       }
+
+       if (!found) {
+               /* last group */
+               struct section *fixupsec;
+               fixupsec = find_section_by_name(&kelf->sections, ".fixup");
+               return fixupsec->sh.sh_size - offset;
+       }
+
+       return rela->addend - offset;
+}
+
+struct special_section special_sections[] = {
+       {
+               .name           = ".bug_frames.0",
+               .group_size     = bug_frames_0_group_size,
+       },
+       {
+               .name           = ".bug_frames.1",
+               .group_size     = bug_frames_1_group_size,
+       },
+       {
+               .name           = ".bug_frames.2",
+               .group_size     = bug_frames_2_group_size,
+       },
+       {
+               .name           = ".bug_frames.3",
+               .group_size     = bug_frames_3_group_size,
+       },
+       {
+               .name           = ".fixup",
+               .group_size     = fixup_group_size,
+       },
+       {
+               .name           = ".ex_table",
+               .group_size     = ex_table_group_size,
+       },
+       {
+               .name           = ".altinstructions",
+               .group_size     = altinstructions_group_size,
+       },
+       {},
+};
+
+int should_keep_rela_group(struct section *sec, int start, int size)
+{
+       struct rela *rela;
+       int found = 0;
+
+       /* check if any relas in the group reference any changed functions */
+       list_for_each_entry(rela, &sec->relas, list) {
+               if (rela->offset >= start &&
+                   rela->offset < start + size &&
+                   rela->sym->type == STT_FUNC &&
+                   rela->sym->sec->include) {
+                       found = 1;
+                       log_debug("new/changed symbol %s found in special section %s\n",
+                                 rela->sym->name, sec->name);
+               }
+       }
+
+       return found;
+}
+
+void xsplice_regenerate_special_section(struct xsplice_elf *kelf,
+                                      struct special_section *special,
+                                      struct section *sec)
+{
+       struct rela *rela, *safe;
+       char *src, *dest;
+       int group_size, src_offset, dest_offset, include, align, aligned_size;
+
+       LIST_HEAD(newrelas);
+
+       src = sec->base->data->d_buf;
+       /* alloc buffer for new base section */
+       dest = malloc(sec->base->sh.sh_size);
+       if (!dest)
+               ERROR("malloc");
+
+       group_size = 0;
+       src_offset = 0;
+       dest_offset = 0;
+       for ( ; src_offset < sec->base->sh.sh_size; src_offset += group_size) {
+
+               group_size = special->group_size(kelf, src_offset);
+               include = should_keep_rela_group(sec, src_offset, group_size);
+
+               if (!include)
+                       continue;
+
+               /*
+                * Copy all relas in the group.  It's possible that the relas
+                * aren't sorted (e.g. .rela.fixup), so go through the entire
+                * rela list each time.
+                */
+               list_for_each_entry_safe(rela, safe, &sec->relas, list) {
+                       if (rela->offset >= src_offset &&
+                           rela->offset < src_offset + group_size) {
+                               /* copy rela entry */
+                               list_del(&rela->list);
+                               list_add_tail(&rela->list, &newrelas);
+
+                               rela->offset -= src_offset - dest_offset;
+                               rela->rela.r_offset = rela->offset;
+
+                               rela->sym->include = 1;
+                       }
+               }
+
+               /* copy base section group */
+               memcpy(dest + dest_offset, src + src_offset, group_size);
+               dest_offset += group_size;
+       }
+
+       /* verify that group_size is a divisor of aligned section size */
+       align = sec->base->sh.sh_addralign;
+       aligned_size = ((sec->base->sh.sh_size + align - 1) / align) * align;
+       if (src_offset != aligned_size)
+               ERROR("group size mismatch for section %s\n", sec->base->name);
+
+       if (!dest_offset) {
+               /* no changed or global functions referenced */
+               sec->status = sec->base->status = SAME;
+               sec->include = sec->base->include = 0;
+               free(dest);
+               return;
+       }
+
+       /* overwrite with new relas list */
+       list_replace(&newrelas, &sec->relas);
+
+       /* include both rela and base sections */
+       sec->include = 1;
+       sec->base->include = 1;
+
+       /*
+        * Update text section data buf and size.
+        *
+        * The rela section's data buf and size will be regenerated in
+        * xsplice_rebuild_rela_section_data().
+        */
+       sec->base->data->d_buf = dest;
+       sec->base->data->d_size = dest_offset;
+}
+
+void xsplice_process_special_sections(struct xsplice_elf *kelf)
+{
+       struct special_section *special;
+       struct section *sec;
+       struct symbol *sym;
+       struct rela *rela;
+
+       for (special = special_sections; special->name; special++) {
+               sec = find_section_by_name(&kelf->sections, special->name);
+               if (!sec)
+                       continue;
+
+               sec = sec->rela;
+               if (!sec)
+                       continue;
+
+               xsplice_regenerate_special_section(kelf, special, sec);
+       }
+
+       /*
+        * The following special sections don't have relas which reference
+        * non-included symbols, so their entire rela section can be included.
+        */
+       list_for_each_entry(sec, &kelf->sections, list) {
+               if (strcmp(sec->name, ".altinstr_replacement"))
+                       continue;
+
+               /* include base section */
+               sec->include = 1;
+
+               /* include all symbols in the section */
+               list_for_each_entry(sym, &kelf->symbols, list)
+                       if (sym->sec == sec)
+                               sym->include = 1;
+
+               /* include rela section */
+               if (sec->rela) {
+                       sec->rela->include = 1;
+                       /* include all symbols referenced by relas */
+                       list_for_each_entry(rela, &sec->rela->relas, list)
+                               rela->sym->include = 1;
+               }
+       }
+}
+
+void xsplice_include_standard_elements(struct xsplice_elf *kelf)
+{
+       struct section *sec;
+
+       list_for_each_entry(sec, &kelf->sections, list) {
+               /* include these sections even if they haven't changed */
+               if (!strcmp(sec->name, ".shstrtab") ||
+                   !strcmp(sec->name, ".strtab") ||
+                   !strcmp(sec->name, ".symtab") ||
+                   !strncmp(sec->name, ".rodata.str1.", 13)) {
+                       sec->include = 1;
+                       if (sec->secsym)
+                               sec->secsym->include = 1;
+               }
+       }
+
+       /* include the NULL symbol */
+       list_entry(kelf->symbols.next, struct symbol, list)->include = 1;
+}
+
+#define inc_printf(fmt, ...) \
+       log_debug("%*s" fmt, recurselevel, "", ##__VA_ARGS__);
+
+void xsplice_include_symbol(struct symbol *sym, int recurselevel)
+{
+       struct rela *rela;
+       struct section *sec;
+
+       inc_printf("start include_symbol(%s)\n", sym->name);
+       sym->include = 1;
+       inc_printf("symbol %s is included\n", sym->name);
+       /*
+        * Check if sym is a non-local symbol (sym->sec is NULL) or
+        * if an unchanged local symbol.  This a base case for the
+        * inclusion recursion.
+        */
+       if (!sym->sec || sym->sec->include ||
+           (sym->type != STT_SECTION && sym->status == SAME))
+               goto out;
+       sec = sym->sec;
+       sec->include = 1;
+       inc_printf("section %s is included\n", sec->name);
+       if (sec->secsym && sec->secsym != sym) {
+               sec->secsym->include = 1;
+               inc_printf("section symbol %s is included\n", sec->secsym->name);
+       }
+       if (!sec->rela)
+               goto out;
+       sec->rela->include = 1;
+       inc_printf("section %s is included\n", sec->rela->name);
+       list_for_each_entry(rela, &sec->rela->relas, list)
+               xsplice_include_symbol(rela->sym, recurselevel+1);
+out:
+       inc_printf("end include_symbol(%s)\n", sym->name);
+       return;
+}
+
+int xsplice_include_changed_functions(struct xsplice_elf *kelf)
+{
+       struct symbol *sym;
+       int changed_nr = 0;
+
+       log_debug("\n=== Inclusion Tree ===\n");
+
+       list_for_each_entry(sym, &kelf->symbols, list) {
+               if (sym->status == CHANGED &&
+                   sym->type == STT_FUNC) {
+                       changed_nr++;
+                       xsplice_include_symbol(sym, 0);
+               }
+
+               if (sym->type == STT_FILE)
+                       sym->include = 1;
+       }
+
+       return changed_nr;
+}
+
+void xsplice_include_debug_sections(struct xsplice_elf *kelf)
+{
+       struct section *sec;
+       struct rela *rela, *saferela;
+
+       /* include all .debug_* sections */
+       list_for_each_entry(sec, &kelf->sections, list) {
+               if (is_debug_section(sec)) {
+                       sec->include = 1;
+                       if (!is_rela_section(sec))
+                               sec->secsym->include = 1;
+               }
+       }
+
+       /*
+        * Go through the .rela.debug_ sections and strip entries
+        * referencing unchanged symbols
+        */
+       list_for_each_entry(sec, &kelf->sections, list) {
+               if (!is_rela_section(sec) || !is_debug_section(sec))
+                       continue;
+               list_for_each_entry_safe(rela, saferela, &sec->relas, list)
+                       if (!rela->sym->sec->include)
+                               list_del(&rela->list);
+       }
+}
+
+int xsplice_include_new_globals(struct xsplice_elf *kelf)
+{
+       struct symbol *sym;
+       int nr = 0;
+
+       list_for_each_entry(sym, &kelf->symbols, list) {
+               if (sym->bind == STB_GLOBAL && sym->sec &&
+                   sym->status == NEW) {
+                       xsplice_include_symbol(sym, 0);
+                       nr++;
+               }
+       }
+
+       return nr;
+}
+
+void xsplice_print_changes(struct xsplice_elf *kelf)
+{
+       struct symbol *sym;
+
+       list_for_each_entry(sym, &kelf->symbols, list) {
+               if (!sym->include || !sym->sec || sym->type != STT_FUNC)
+                       continue;
+               if (sym->status == NEW)
+                       log_normal("new function: %s\n", sym->name);
+               else if (sym->status == CHANGED)
+                       log_normal("changed function: %s\n", sym->name);
+       }
+}
+
+void xsplice_dump_kelf(struct xsplice_elf *kelf)
+{
+       struct section *sec;
+       struct symbol *sym;
+       struct rela *rela;
+
+       if (loglevel > DEBUG)
+               return;
+
+       printf("\n=== Sections ===\n");
+       list_for_each_entry(sec, &kelf->sections, list) {
+               printf("%02d %s (%s)", sec->index, sec->name, status_str(sec->status));
+               if (is_rela_section(sec)) {
+                       printf(", base-> %s\n", sec->base->name);
+                       /* skip .debug_* sections */
+                       if (is_debug_section(sec))
+                               goto next;
+                       printf("rela section expansion\n");
+                       list_for_each_entry(rela, &sec->relas, list) {
+                               printf("sym %d, offset %d, type %d, %s %s %d\n",
+                                      rela->sym->index, rela->offset,
+                                      rela->type, rela->sym->name,
+                                      (rela->addend < 0)?"-":"+",
+                                      abs(rela->addend));
+                       }
+               } else {
+                       if (sec->sym)
+                               printf(", sym-> %s", sec->sym->name);
+                       if (sec->secsym)
+                               printf(", secsym-> %s", sec->secsym->name);
+                       if (sec->rela)
+                               printf(", rela-> %s", sec->rela->name);
+               }
+next:
+               printf("\n");
+       }
+
+       printf("\n=== Symbols ===\n");
+       list_for_each_entry(sym, &kelf->symbols, list) {
+               printf("sym %02d, type %d, bind %d, ndx %02d, name %s (%s)",
+                       sym->index, sym->type, sym->bind, sym->sym.st_shndx,
+                       sym->name, status_str(sym->status));
+               if (sym->sec && (sym->type == STT_FUNC || sym->type == STT_OBJECT))
+                       printf(" -> %s", sym->sec->name);
+               printf("\n");
+       }
+}
+
+void xsplice_verify_patchability(struct xsplice_elf *kelf)
+{
+       struct section *sec;
+       int errs = 0;
+
+       list_for_each_entry(sec, &kelf->sections, list) {
+               if (sec->status == CHANGED && !sec->include) {
+                       log_normal("changed section %s not selected for inclusion\n",
+                                  sec->name);
+                       errs++;
+               }
+
+               if (sec->status != SAME && sec->grouped) {
+                       log_normal("changed section %s is part of a section group\n",
+                                  sec->name);
+                       errs++;
+               }
+
+               if (sec->sh.sh_type == SHT_GROUP && sec->status == NEW) {
+                       log_normal("new/changed group sections are not supported\n");
+                       errs++;
+               }
+
+               /*
+                * ensure we aren't including .data.* or .bss.*
+                * (.data.unlikely is ok b/c it only has __warned vars)
+                */
+               if (sec->include && sec->status != NEW &&
+                   (!strncmp(sec->name, ".data", 5) ||
+                    !strncmp(sec->name, ".bss", 4)) &&
+                   strcmp(sec->name, ".data.unlikely")) {
+                       log_normal("data section %s selected for inclusion\n",
+                                  sec->name);
+                       errs++;
+               }
+       }
+
+       if (errs)
+               DIFF_FATAL("%d unsupported section change(s)", errs);
+}
+
+void xsplice_migrate_included_elements(struct xsplice_elf *kelf, struct xsplice_elf **kelfout)
+{
+       struct section *sec, *safesec;
+       struct symbol *sym, *safesym;
+       struct xsplice_elf *out;
+
+       /* allocate output kelf */
+       out = malloc(sizeof(*out));
+       if (!out)
+               ERROR("malloc");
+       memset(out, 0, sizeof(*out));
+       INIT_LIST_HEAD(&out->sections);
+       INIT_LIST_HEAD(&out->symbols);
+       INIT_LIST_HEAD(&out->strings);
+
+       /* migrate included sections from kelf to out */
+       list_for_each_entry_safe(sec, safesec, &kelf->sections, list) {
+               if (!sec->include)
+                       continue;
+               list_del(&sec->list);
+               list_add_tail(&sec->list, &out->sections);
+               sec->index = 0;
+               if (!is_rela_section(sec) && sec->secsym && !sec->secsym->include)
+                       /* break link to non-included section symbol */
+                       sec->secsym = NULL;
+       }
+
+       /* migrate included symbols from kelf to out */
+       list_for_each_entry_safe(sym, safesym, &kelf->symbols, list) {
+               if (!sym->include)
+                       continue;
+               list_del(&sym->list);
+               list_add_tail(&sym->list, &out->symbols);
+               sym->index = 0;
+               sym->strip = 0;
+               if (sym->sec && !sym->sec->include)
+                       /* break link to non-included section */
+                       sym->sec = NULL;
+                       
+       }
+
+       *kelfout = out;
+}
+
+void xsplice_migrate_symbols(struct list_head *src,
+                                    struct list_head *dst,
+                                    int (*select)(struct symbol *))
+{
+       struct symbol *sym, *safe;
+
+       list_for_each_entry_safe(sym, safe, src, list) {
+               if (select && !select(sym))
+                       continue;
+
+               list_del(&sym->list);
+               list_add_tail(&sym->list, dst);
+       }
+}
+
+void xsplice_create_strings_elements(struct xsplice_elf *kelf)
+{
+       struct section *sec;
+       struct symbol *sym;
+
+       /* create .xsplice.strings */
+
+       /* allocate section resources */
+       ALLOC_LINK(sec, &kelf->sections);
+       sec->name = ".xsplice.strings";
+
+       /* set data */
+       sec->data = malloc(sizeof(*sec->data));
+       if (!sec->data)
+               ERROR("malloc");
+       sec->data->d_type = ELF_T_BYTE;
+
+       /* set section header */
+       sec->sh.sh_type = SHT_PROGBITS;
+       sec->sh.sh_entsize = 1;
+       sec->sh.sh_addralign = 1;
+       sec->sh.sh_flags = SHF_ALLOC;
+
+       /* create .xsplice.strings section symbol (reuse sym variable) */
+
+       ALLOC_LINK(sym, &kelf->symbols);
+       sym->sec = sec;
+       sym->sym.st_info = GELF_ST_INFO(STB_LOCAL, STT_SECTION);
+       sym->type = STT_SECTION;
+       sym->bind = STB_LOCAL;
+       sym->name = ".xsplice.strings";
+}
+
+void xsplice_build_strings_section_data(struct xsplice_elf *kelf)
+{
+       struct string *string;
+       struct section *sec;
+       int size;
+       char *strtab;
+
+       sec = find_section_by_name(&kelf->sections, ".xsplice.strings");
+       if (!sec)
+               ERROR("can't find .xsplice.strings");
+
+       /* determine size */
+       size = 0;
+       list_for_each_entry(string, &kelf->strings, list)
+               size += strlen(string->name) + 1;
+
+       /* allocate section resources */
+       strtab = malloc(size);
+       if (!strtab)
+               ERROR("malloc");
+       sec->data->d_buf = strtab;
+       sec->data->d_size = size;
+
+       /* populate strings section data */
+       list_for_each_entry(string, &kelf->strings, list) {
+               strcpy(strtab, string->name);
+               strtab += strlen(string->name) + 1;
+       }
+}
+
+struct section *create_section_pair(struct xsplice_elf *kelf, char *name,
+                                    int entsize, int nr)
+{
+       char *relaname;
+       struct section *sec, *relasec;
+       int size = entsize * nr;
+
+       relaname = malloc(strlen(name) + strlen(".rela") + 1);
+       if (!relaname)
+               ERROR("malloc");
+       strcpy(relaname, ".rela");
+       strcat(relaname, name);
+
+       /* allocate text section resources */
+       ALLOC_LINK(sec, &kelf->sections);
+       sec->name = name;
+
+       /* set data */
+       sec->data = malloc(sizeof(*sec->data));
+       if (!sec->data)
+               ERROR("malloc");
+       sec->data->d_buf = malloc(size);
+       if (!sec->data->d_buf)
+               ERROR("malloc");
+       sec->data->d_size = size;
+       sec->data->d_type = ELF_T_BYTE;
+
+       /* set section header */
+       sec->sh.sh_type = SHT_PROGBITS;
+       sec->sh.sh_entsize = entsize;
+       sec->sh.sh_addralign = 8;
+       sec->sh.sh_flags = SHF_ALLOC;
+       sec->sh.sh_size = size;
+
+       /* allocate rela section resources */
+       ALLOC_LINK(relasec, &kelf->sections);
+       relasec->name = relaname;
+       relasec->base = sec;
+       INIT_LIST_HEAD(&relasec->relas);
+
+       /* set data, buffers generated by xsplice_rebuild_rela_section_data() */
+       relasec->data = malloc(sizeof(*relasec->data));
+       if (!relasec->data)
+               ERROR("malloc");
+
+       /* set section header */
+       relasec->sh.sh_type = SHT_RELA;
+       relasec->sh.sh_entsize = sizeof(GElf_Rela);
+       relasec->sh.sh_addralign = 8;
+
+       /* set text rela section pointer */
+       sec->rela = relasec;
+
+       return sec;
+}
+
+void xsplice_create_patches_sections(struct xsplice_elf *kelf,
+                                    struct lookup_table *table, char *hint)
+{
+       int nr, index;
+       struct section *sec, *relasec;
+       struct symbol *sym, *strsym;
+       struct rela *rela;
+       struct lookup_result result;
+       struct xsplice_patch_func *funcs;
+
+       /* count patched functions */
+       nr = 0;
+       list_for_each_entry(sym, &kelf->symbols, list)
+               if (sym->type == STT_FUNC && sym->status == CHANGED)
+                       nr++;
+
+       /* create text/rela section pair */
+       sec = create_section_pair(kelf, ".xsplice.funcs", sizeof(*funcs), nr);
+       relasec = sec->rela;
+       funcs = sec->data->d_buf;
+
+       /* lookup strings symbol */
+       strsym = find_symbol_by_name(&kelf->symbols, ".xsplice.strings");
+       if (!strsym)
+               ERROR("can't find .xsplice.strings symbol");
+
+       /* populate sections */
+       index = 0;
+       list_for_each_entry(sym, &kelf->symbols, list) {
+               if (sym->type == STT_FUNC && sym->status == CHANGED) {
+                       if (sym->bind == STB_LOCAL) {
+                               if (lookup_local_symbol(table, sym->name,
+                                                       hint, &result))
+                                       ERROR("lookup_local_symbol %s (%s)",
+                                             sym->name, hint);
+                       } else {
+                               if (lookup_global_symbol(table, sym->name,
+                                                       &result))
+                                       ERROR("lookup_global_symbol %s",
+                                             sym->name);
+                       }
+                       log_debug("lookup for %s @ 0x%016lx len %lu\n",
+                                 sym->name, result.value, result.size);
+
+                       /* add entry in text section */
+                       funcs[index].old_addr = result.value;
+                       funcs[index].old_size = result.size;
+                       funcs[index].new_addr = 0;
+                       funcs[index].new_size = sym->sym.st_size;
+
+                       /*
+                        * Add a relocation that will populate
+                        * the funcs[index].new_addr field at
+                        * module load time.
+                        */
+                       ALLOC_LINK(rela, &relasec->relas);
+                       rela->sym = sym;
+                       rela->type = R_X86_64_64;
+                       rela->addend = 0;
+                       rela->offset = index * sizeof(*funcs);
+
+                       /*
+                        * Add a relocation that will populate
+                        * the funcs[index].name field.
+                        */
+                       ALLOC_LINK(rela, &relasec->relas);
+                       rela->sym = strsym;
+                       rela->type = R_X86_64_64;
+                       rela->addend = offset_of_string(&kelf->strings, sym->name);
+                       rela->offset = index * sizeof(*funcs) +
+                                      offsetof(struct xsplice_patch_func, name);
+
+                       index++;
+               }
+       }
+
+       /* sanity check, index should equal nr */
+       if (index != nr)
+               ERROR("size mismatch in funcs sections");
+
+}
+
+/* Resolve symbols using xen-syms */
+void xsplice_resolve_symbols(struct xsplice_elf *kelf,
+                            struct lookup_table *table, char *hint)
+{
+       struct symbol *sym;
+       struct lookup_result result;
+
+       list_for_each_entry(sym, &kelf->symbols, list) {
+               /* ignore NULL symbol */
+               if (!strlen(sym->name))
+                       continue;
+               if (sym->sec)
+                       continue;
+               if (sym->sym.st_shndx != SHN_UNDEF)
+                       continue;
+
+               if (sym->bind == STB_LOCAL) {
+                       if (lookup_local_symbol(table, sym->name,
+                                               hint, &result))
+                               ERROR("lookup_local_symbol %s (%s)",
+                                     sym->name, hint);
+               } else {
+                       if (lookup_global_symbol(table, sym->name,
+                                               &result))
+                               ERROR("lookup_global_symbol %s",
+                                     sym->name);
+               }
+               log_debug("lookup for %s @ 0x%016lx len %lu\n",
+                         sym->name, result.value, result.size);
+               sym->sym.st_value += result.value;
+               sym->sym.st_shndx = SHN_ABS;
+       }
+}
+
+int is_null_sym(struct symbol *sym)
+{
+       return !strlen(sym->name);
+}
+
+int is_file_sym(struct symbol *sym)
+{
+       return sym->type == STT_FILE;
+}
+
+int is_local_func_sym(struct symbol *sym)
+{
+       return sym->bind == STB_LOCAL && sym->type == STT_FUNC;
+}
+
+int is_local_sym(struct symbol *sym)
+{
+       return sym->bind == STB_LOCAL;
+}
+
+void xsplice_reorder_symbols(struct xsplice_elf *kelf)
+{
+       LIST_HEAD(symbols);
+
+       /* migrate NULL sym */
+       xsplice_migrate_symbols(&kelf->symbols, &symbols, is_null_sym);
+       /* migrate LOCAL FILE sym */
+       xsplice_migrate_symbols(&kelf->symbols, &symbols, is_file_sym);
+       /* migrate LOCAL FUNC syms */
+       xsplice_migrate_symbols(&kelf->symbols, &symbols, is_local_func_sym);
+       /* migrate all other LOCAL syms */
+       xsplice_migrate_symbols(&kelf->symbols, &symbols, is_local_sym);
+       /* migrate all other (GLOBAL) syms */
+       xsplice_migrate_symbols(&kelf->symbols, &symbols, NULL);
+
+       list_replace(&symbols, &kelf->symbols);
+}
+
+void xsplice_reindex_elements(struct xsplice_elf *kelf)
+{
+       struct section *sec;
+       struct symbol *sym;
+       int index;
+
+       index = 1; /* elf write function handles NULL section 0 */
+       list_for_each_entry(sec, &kelf->sections, list)
+               sec->index = index++;
+
+       index = 0;
+       list_for_each_entry(sym, &kelf->symbols, list) {
+               sym->index = index++;
+               if (sym->sec)
+                       sym->sym.st_shndx = sym->sec->index;
+               else if (sym->sym.st_shndx != SHN_ABS)
+                       sym->sym.st_shndx = SHN_UNDEF;
+       }
+}
+
+void xsplice_rebuild_rela_section_data(struct section *sec)
+{
+       struct rela *rela;
+       int nr = 0, index = 0, size;
+       GElf_Rela *relas;
+
+       list_for_each_entry(rela, &sec->relas, list)
+               nr++;
+
+       size = nr * sizeof(*relas);
+       relas = malloc(size);
+       if (!relas)
+               ERROR("malloc");
+
+       sec->data->d_buf = relas;
+       sec->data->d_size = size;
+       /* d_type remains ELF_T_RELA */
+
+       sec->sh.sh_size = size;
+
+       list_for_each_entry(rela, &sec->relas, list) {
+               relas[index].r_offset = rela->offset;
+               relas[index].r_addend = rela->addend;
+               relas[index].r_info = GELF_R_INFO(rela->sym->index, rela->type);
+               index++;
+       }
+
+       /* sanity check, index should equal nr */
+       if (index != nr)
+               ERROR("size mismatch in rebuilt rela section");
+}
+
+void print_strtab(char *buf, size_t size)
+{
+       int i;
+
+       for (i = 0; i < size; i++) {
+               if (buf[i] == 0)
+                       printf("\\0");
+               else
+                       printf("%c",buf[i]);
+       }
+}
+
+void xsplice_create_shstrtab(struct xsplice_elf *kelf)
+{
+       struct section *shstrtab, *sec;
+       size_t size, offset, len;
+       char *buf;
+
+       shstrtab = find_section_by_name(&kelf->sections, ".shstrtab");
+       if (!shstrtab)
+               ERROR("find_section_by_name");
+
+       /* determine size of string table */
+       size = 1; /* for initial NULL terminator */
+       list_for_each_entry(sec, &kelf->sections, list)
+               size += strlen(sec->name) + 1; /* include NULL terminator */
+
+       /* allocate data buffer */
+       buf = malloc(size);
+       if (!buf)
+               ERROR("malloc");
+       memset(buf, 0, size);
+
+       /* populate string table and link with section header */
+       offset = 1;
+       list_for_each_entry(sec, &kelf->sections, list) {
+               len = strlen(sec->name) + 1;
+               sec->sh.sh_name = offset;
+               memcpy(buf + offset, sec->name, len);
+               offset += len;
+       }
+
+       if (offset != size)
+               ERROR("shstrtab size mismatch");
+
+       shstrtab->data->d_buf = buf;
+       shstrtab->data->d_size = size;
+
+       if (loglevel <= DEBUG) {
+               printf("shstrtab: ");
+               print_strtab(buf, size);
+               printf("\n");
+
+               list_for_each_entry(sec, &kelf->sections, list)
+                       printf("%s @ shstrtab offset %d\n",
+                              sec->name, sec->sh.sh_name);
+       }
+}
+
+void xsplice_create_strtab(struct xsplice_elf *kelf)
+{
+       struct section *strtab;
+       struct symbol *sym;
+       size_t size = 0, offset = 0, len;
+       char *buf;
+
+       strtab = find_section_by_name(&kelf->sections, ".strtab");
+       if (!strtab)
+               ERROR("find_section_by_name");
+
+       /* determine size of string table */
+       list_for_each_entry(sym, &kelf->symbols, list) {
+               if (sym->type == STT_SECTION)
+                       continue;
+               size += strlen(sym->name) + 1; /* include NULL terminator */
+       }
+
+       /* allocate data buffer */
+       buf = malloc(size);
+       if (!buf)
+               ERROR("malloc");
+       memset(buf, 0, size);
+
+       /* populate string table and link with section header */
+       list_for_each_entry(sym, &kelf->symbols, list) {
+               if (sym->type == STT_SECTION) {
+                       sym->sym.st_name = 0;
+                       continue;
+               }
+               len = strlen(sym->name) + 1;
+               sym->sym.st_name = offset;
+               memcpy(buf + offset, sym->name, len);
+               offset += len;
+       }
+
+       if (offset != size)
+               ERROR("shstrtab size mismatch");
+
+       strtab->data->d_buf = buf;
+       strtab->data->d_size = size;
+
+       if (loglevel <= DEBUG) {
+               printf("strtab: ");
+               print_strtab(buf, size);
+               printf("\n");
+
+               list_for_each_entry(sym, &kelf->symbols, list)
+                       printf("%s @ strtab offset %d\n",
+                              sym->name, sym->sym.st_name);
+       }
+}
+
+void xsplice_create_symtab(struct xsplice_elf *kelf)
+{
+       struct section *symtab;
+       struct symbol *sym;
+       char *buf;
+       size_t size;
+       int nr = 0, offset = 0, nr_local = 0;
+
+       symtab = find_section_by_name(&kelf->sections, ".symtab");
+       if (!symtab)
+               ERROR("find_section_by_name");
+
+       /* count symbols */
+       list_for_each_entry(sym, &kelf->symbols, list)
+               nr++;
+
+       /* create new symtab buffer */
+       size = nr * symtab->sh.sh_entsize;
+       buf = malloc(size);
+       if (!buf)
+               ERROR("malloc");
+       memset(buf, 0, size);
+
+       offset = 0;
+       list_for_each_entry(sym, &kelf->symbols, list) {
+               memcpy(buf + offset, &sym->sym, symtab->sh.sh_entsize);
+               offset += symtab->sh.sh_entsize;
+
+               if (is_local_sym(sym))
+                       nr_local++;
+       }
+
+       symtab->data->d_buf = buf;
+       symtab->data->d_size = size;
+
+       /* update symtab section header */
+       symtab->sh.sh_link = find_section_by_name(&kelf->sections, ".strtab")->index;
+       symtab->sh.sh_info = nr_local;
+}
+
+void xsplice_write_output_elf(struct xsplice_elf *kelf, Elf *elf, char *outfile)
+{
+       int fd;
+       struct section *sec;
+       Elf *elfout;
+       GElf_Ehdr eh, ehout;
+       Elf_Scn *scn;
+       Elf_Data *data;
+       GElf_Shdr sh;
+
+       /* TODO make this argv */
+       fd = creat(outfile, 0777);
+       if (fd == -1)
+               ERROR("creat");
+
+       elfout = elf_begin(fd, ELF_C_WRITE, NULL);
+       if (!elfout)
+               ERROR("elf_begin");
+
+       if (!gelf_newehdr(elfout, gelf_getclass(kelf->elf)))
+               ERROR("gelf_newehdr");
+
+       if (!gelf_getehdr(elfout, &ehout))
+               ERROR("gelf_getehdr");
+
+       if (!gelf_getehdr(elf, &eh))
+               ERROR("gelf_getehdr");
+
+       memset(&ehout, 0, sizeof(ehout));
+       ehout.e_ident[EI_DATA] = eh.e_ident[EI_DATA];
+       ehout.e_machine = eh.e_machine;
+       ehout.e_type = eh.e_type;
+       ehout.e_version = EV_CURRENT;
+       ehout.e_shstrndx = find_section_by_name(&kelf->sections, ".shstrtab")->index;
+
+       /* add changed sections */
+       list_for_each_entry(sec, &kelf->sections, list) {
+               scn = elf_newscn(elfout);
+               if (!scn)
+                       ERROR("elf_newscn");
+
+               data = elf_newdata(scn);
+               if (!data)
+                       ERROR("elf_newdata");
+
+               if (!elf_flagdata(data, ELF_C_SET, ELF_F_DIRTY))
+                       ERROR("elf_flagdata");
+
+               data->d_type = sec->data->d_type;
+               data->d_buf = sec->data->d_buf;
+               data->d_size = sec->data->d_size;
+
+               if(!gelf_getshdr(scn, &sh))
+                       ERROR("gelf_getshdr");
+
+               sh = sec->sh;
+
+               if (!gelf_update_shdr(scn, &sh))
+                       ERROR("gelf_update_shdr");      
+       }
+
+       if (!gelf_update_ehdr(elfout, &ehout))
+               ERROR("gelf_update_ehdr");
+
+       if (elf_update(elfout, ELF_C_WRITE) < 0) {
+               printf("%s\n",elf_errmsg(-1));
+               ERROR("elf_update");
+       }
+}
+
+struct arguments {
+       char *args[4];
+       int debug;
+};
+
+static char args_doc[] = "original.o patched.o kernel-object output.o";
+
+static struct argp_option options[] = {
+       {"debug", 'd', 0, 0, "Show debug output" },
+       { 0 }
+};
+
+static error_t parse_opt (int key, char *arg, struct argp_state *state)
+{
+       /* Get the input argument from argp_parse, which we
+          know is a pointer to our arguments structure. */
+       struct arguments *arguments = state->input;
+
+       switch (key)
+       {
+               case 'd':
+                       arguments->debug = 1;
+                       break;
+               case ARGP_KEY_ARG:
+                       if (state->arg_num >= 4)
+                               /* Too many arguments. */
+                               argp_usage (state);
+                       arguments->args[state->arg_num] = arg;
+                       break;
+               case ARGP_KEY_END:
+                       if (state->arg_num < 4)
+                               /* Not enough arguments. */
+                               argp_usage (state);
+                       break;
+               default:
+                       return ARGP_ERR_UNKNOWN;
+       }
+       return 0;
+}
+
+static struct argp argp = { options, parse_opt, args_doc, 0 };
+
+int main(int argc, char *argv[])
+{
+       struct xsplice_elf *kelf_base, *kelf_patched, *kelf_out;
+       struct arguments arguments;
+       int num_changed, new_globals_exist;
+       struct lookup_table *lookup;
+       struct section *sec, *symtab;
+       struct symbol *sym;
+       char *hint = NULL;
+
+       arguments.debug = 0;
+       argp_parse (&argp, argc, argv, 0, 0, &arguments);
+       if (arguments.debug)
+               loglevel = DEBUG;
+
+       elf_version(EV_CURRENT);
+
+       childobj = basename(arguments.args[0]);
+
+       log_debug("Open base\n");
+       kelf_base = xsplice_elf_open(arguments.args[0]);
+       log_debug("Open patched\n");
+       kelf_patched = xsplice_elf_open(arguments.args[1]);
+
+       log_debug("Compare elf headers\n");
+       xsplice_compare_elf_headers(kelf_base->elf, kelf_patched->elf);
+       log_debug("Check program headers of base\n");
+       xsplice_check_program_headers(kelf_base->elf);
+       log_debug("Check program headers of patched\n");
+       xsplice_check_program_headers(kelf_patched->elf);
+
+       log_debug("Mark grouped sections\n");
+       xsplice_mark_grouped_sections(kelf_patched);
+       log_debug("Replace sections syms base\n");
+       xsplice_replace_sections_syms(kelf_base);
+       log_debug("Replace sections syms patched\n");
+       xsplice_replace_sections_syms(kelf_patched);
+       log_debug("Rename mangled functions\n");
+       xsplice_rename_mangled_functions(kelf_base, kelf_patched);
+
+       log_debug("Correlate elfs\n");
+       xsplice_correlate_elfs(kelf_base, kelf_patched);
+       log_debug("Correlate static local variables\n");
+       xsplice_correlate_static_local_variables(kelf_base, kelf_patched);
+
+       /*
+        * After this point, we don't care about kelf_base anymore.
+        * We access its sections via the twin pointers in the
+        * section, symbol, and rela lists of kelf_patched.
+        */
+       log_debug("Compare correlated elements\n");
+       xsplice_compare_correlated_elements(kelf_patched);
+       log_debug("Elf teardown base\n");
+       xsplice_elf_teardown(kelf_base);
+       log_debug("Elf free base\n");
+       xsplice_elf_free(kelf_base);
+
+       log_debug("Mark ignored sections same\n");
+       xsplice_mark_ignored_sections_same(kelf_patched);
+       log_debug("Mark constant labels same\n");
+       xsplice_mark_constant_labels_same(kelf_patched);
+
+       log_debug("Include standard elements\n");
+       xsplice_include_standard_elements(kelf_patched);
+       log_debug("Include changed functions\n");
+       num_changed = xsplice_include_changed_functions(kelf_patched);
+       log_debug("num_changed = %d\n", num_changed);
+       log_debug("Include debug sections\n");
+       xsplice_include_debug_sections(kelf_patched);
+       log_debug("Include new globals\n");
+       new_globals_exist = xsplice_include_new_globals(kelf_patched);
+       log_debug("new_globals_exist = %d\n", new_globals_exist);
+
+       log_debug("Print changes\n");
+       xsplice_print_changes(kelf_patched);
+       log_debug("Dump patched elf status\n");
+       xsplice_dump_kelf(kelf_patched);
+
+       if (!num_changed && !new_globals_exist) {
+               log_debug("no changed functions were found\n");
+               return 3; /* 1 is ERROR, 2 is DIFF_FATAL */
+       }
+
+       log_debug("Process special sections\n");
+       xsplice_process_special_sections(kelf_patched);
+       log_debug("Verify patchability\n");
+       xsplice_verify_patchability(kelf_patched);
+
+       /* this is destructive to kelf_patched */
+       log_debug("Migrate included elements\n");
+       xsplice_migrate_included_elements(kelf_patched, &kelf_out);
+
+       /*
+        * Teardown kelf_patched since we shouldn't access sections or symbols
+        * through it anymore.  Don't free however, since our section and symbol
+        * name fields still point to strings in the Elf object owned by
+        * xsplice_patched.
+        */
+       log_debug("Elf teardown patched\n");
+       xsplice_elf_teardown(kelf_patched);
+
+       log_debug("Search for source file name\n");
+       list_for_each_entry(sym, &kelf_out->symbols, list) {
+               if (sym->type == STT_FILE) {
+                       hint = sym->name;
+                       break;
+               }
+       }
+       if (!hint)
+               ERROR("FILE symbol not found in output. Stripped?\n");
+       log_debug("hint = %s\n", hint);
+
+       /* create symbol lookup table */
+       log_debug("Lookup xen-syms\n");
+       lookup = lookup_open(arguments.args[2]);
+
+       /* create strings, patches, and dynrelas sections */
+       log_debug("Create strings elements\n");
+       xsplice_create_strings_elements(kelf_out);
+       log_debug("Create patches sections\n");
+       xsplice_create_patches_sections(kelf_out, lookup, hint);
+       /* log_debug("Resolve symbols\n"); */
+       /* xsplice_resolve_symbols(kelf_out, lookup, hint); */
+       xsplice_build_strings_section_data(kelf_out);
+
+       /*
+        *  At this point, the set of output sections and symbols is
+        *  finalized.  Reorder the symbols into linker-compliant
+        *  order and index all the symbols and sections.  After the
+        *  indexes have been established, update index data
+        *  throughout the structure.
+        */
+       log_debug("Reorder symbols\n");
+       xsplice_reorder_symbols(kelf_out);
+       log_debug("Reindex elements\n");
+       xsplice_reindex_elements(kelf_out);
+
+       /*
+        * Update rela section headers and rebuild the rela section data
+        * buffers from the relas lists.
+        */
+       symtab = find_section_by_name(&kelf_out->sections, ".symtab");
+       list_for_each_entry(sec, &kelf_out->sections, list) {
+               if (!is_rela_section(sec))
+                       continue;
+               sec->sh.sh_link = symtab->index;
+               sec->sh.sh_info = sec->base->index;
+               log_debug("Rebuild rela section data for %s\n", sec->name);
+               xsplice_rebuild_rela_section_data(sec);
+       }
+
+       log_debug("Create shstrtab\n");
+       xsplice_create_shstrtab(kelf_out);
+       log_debug("Create strtab\n");
+       xsplice_create_strtab(kelf_out);
+       log_debug("Create symtab\n");
+       xsplice_create_symtab(kelf_out);
+       log_debug("Dump out elf status\n");
+       xsplice_dump_kelf(kelf_out);
+       log_debug("Write out elf\n");
+       xsplice_write_output_elf(kelf_out, kelf_patched->elf, arguments.args[3]);
+
+       log_debug("Elf free patched\n");
+       xsplice_elf_free(kelf_patched);
+       log_debug("Elf teardown out\n");
+       xsplice_elf_teardown(kelf_out);
+       log_debug("Elf free out\n");
+       xsplice_elf_free(kelf_out);
+
+       return 0;
+}
diff --git a/insn/asm/inat.h b/insn/asm/inat.h
new file mode 100644 (file)
index 0000000..74a2e31
--- /dev/null
@@ -0,0 +1,221 @@
+#ifndef _ASM_X86_INAT_H
+#define _ASM_X86_INAT_H
+/*
+ * x86 instruction attributes
+ *
+ * Written by Masami Hiramatsu <mhiramat@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+#include <asm/inat_types.h>
+
+/*
+ * Internal bits. Don't use bitmasks directly, because these bits are
+ * unstable. You should use checking functions.
+ */
+
+#define INAT_OPCODE_TABLE_SIZE 256
+#define INAT_GROUP_TABLE_SIZE 8
+
+/* Legacy last prefixes */
+#define INAT_PFX_OPNDSZ        1       /* 0x66 */ /* LPFX1 */
+#define INAT_PFX_REPE  2       /* 0xF3 */ /* LPFX2 */
+#define INAT_PFX_REPNE 3       /* 0xF2 */ /* LPFX3 */
+/* Other Legacy prefixes */
+#define INAT_PFX_LOCK  4       /* 0xF0 */
+#define INAT_PFX_CS    5       /* 0x2E */
+#define INAT_PFX_DS    6       /* 0x3E */
+#define INAT_PFX_ES    7       /* 0x26 */
+#define INAT_PFX_FS    8       /* 0x64 */
+#define INAT_PFX_GS    9       /* 0x65 */
+#define INAT_PFX_SS    10      /* 0x36 */
+#define INAT_PFX_ADDRSZ        11      /* 0x67 */
+/* x86-64 REX prefix */
+#define INAT_PFX_REX   12      /* 0x4X */
+/* AVX VEX prefixes */
+#define INAT_PFX_VEX2  13      /* 2-bytes VEX prefix */
+#define INAT_PFX_VEX3  14      /* 3-bytes VEX prefix */
+
+#define INAT_LSTPFX_MAX        3
+#define INAT_LGCPFX_MAX        11
+
+/* Immediate size */
+#define INAT_IMM_BYTE          1
+#define INAT_IMM_WORD          2
+#define INAT_IMM_DWORD         3
+#define INAT_IMM_QWORD         4
+#define INAT_IMM_PTR           5
+#define INAT_IMM_VWORD32       6
+#define INAT_IMM_VWORD         7
+
+/* Legacy prefix */
+#define INAT_PFX_OFFS  0
+#define INAT_PFX_BITS  4
+#define INAT_PFX_MAX    ((1 << INAT_PFX_BITS) - 1)
+#define INAT_PFX_MASK  (INAT_PFX_MAX << INAT_PFX_OFFS)
+/* Escape opcodes */
+#define INAT_ESC_OFFS  (INAT_PFX_OFFS + INAT_PFX_BITS)
+#define INAT_ESC_BITS  2
+#define INAT_ESC_MAX   ((1 << INAT_ESC_BITS) - 1)
+#define INAT_ESC_MASK  (INAT_ESC_MAX << INAT_ESC_OFFS)
+/* Group opcodes (1-16) */
+#define INAT_GRP_OFFS  (INAT_ESC_OFFS + INAT_ESC_BITS)
+#define INAT_GRP_BITS  5
+#define INAT_GRP_MAX   ((1 << INAT_GRP_BITS) - 1)
+#define INAT_GRP_MASK  (INAT_GRP_MAX << INAT_GRP_OFFS)
+/* Immediates */
+#define INAT_IMM_OFFS  (INAT_GRP_OFFS + INAT_GRP_BITS)
+#define INAT_IMM_BITS  3
+#define INAT_IMM_MASK  (((1 << INAT_IMM_BITS) - 1) << INAT_IMM_OFFS)
+/* Flags */
+#define INAT_FLAG_OFFS (INAT_IMM_OFFS + INAT_IMM_BITS)
+#define INAT_MODRM     (1 << (INAT_FLAG_OFFS))
+#define INAT_FORCE64   (1 << (INAT_FLAG_OFFS + 1))
+#define INAT_SCNDIMM   (1 << (INAT_FLAG_OFFS + 2))
+#define INAT_MOFFSET   (1 << (INAT_FLAG_OFFS + 3))
+#define INAT_VARIANT   (1 << (INAT_FLAG_OFFS + 4))
+#define INAT_VEXOK     (1 << (INAT_FLAG_OFFS + 5))
+#define INAT_VEXONLY   (1 << (INAT_FLAG_OFFS + 6))
+/* Attribute making macros for attribute tables */
+#define INAT_MAKE_PREFIX(pfx)  (pfx << INAT_PFX_OFFS)
+#define INAT_MAKE_ESCAPE(esc)  (esc << INAT_ESC_OFFS)
+#define INAT_MAKE_GROUP(grp)   ((grp << INAT_GRP_OFFS) | INAT_MODRM)
+#define INAT_MAKE_IMM(imm)     (imm << INAT_IMM_OFFS)
+
+/* Attribute search APIs */
+extern insn_attr_t inat_get_opcode_attribute(insn_byte_t opcode);
+extern int inat_get_last_prefix_id(insn_byte_t last_pfx);
+extern insn_attr_t inat_get_escape_attribute(insn_byte_t opcode,
+                                            int lpfx_id,
+                                            insn_attr_t esc_attr);
+extern insn_attr_t inat_get_group_attribute(insn_byte_t modrm,
+                                           int lpfx_id,
+                                           insn_attr_t esc_attr);
+extern insn_attr_t inat_get_avx_attribute(insn_byte_t opcode,
+                                         insn_byte_t vex_m,
+                                         insn_byte_t vex_pp);
+
+/* Attribute checking functions */
+static inline int inat_is_legacy_prefix(insn_attr_t attr)
+{
+       attr &= INAT_PFX_MASK;
+       return attr && attr <= INAT_LGCPFX_MAX;
+}
+
+static inline int inat_is_address_size_prefix(insn_attr_t attr)
+{
+       return (attr & INAT_PFX_MASK) == INAT_PFX_ADDRSZ;
+}
+
+static inline int inat_is_operand_size_prefix(insn_attr_t attr)
+{
+       return (attr & INAT_PFX_MASK) == INAT_PFX_OPNDSZ;
+}
+
+static inline int inat_is_rex_prefix(insn_attr_t attr)
+{
+       return (attr & INAT_PFX_MASK) == INAT_PFX_REX;
+}
+
+static inline int inat_last_prefix_id(insn_attr_t attr)
+{
+       if ((attr & INAT_PFX_MASK) > INAT_LSTPFX_MAX)
+               return 0;
+       else
+               return attr & INAT_PFX_MASK;
+}
+
+static inline int inat_is_vex_prefix(insn_attr_t attr)
+{
+       attr &= INAT_PFX_MASK;
+       return attr == INAT_PFX_VEX2 || attr == INAT_PFX_VEX3;
+}
+
+static inline int inat_is_vex3_prefix(insn_attr_t attr)
+{
+       return (attr & INAT_PFX_MASK) == INAT_PFX_VEX3;
+}
+
+static inline int inat_is_escape(insn_attr_t attr)
+{
+       return attr & INAT_ESC_MASK;
+}
+
+static inline int inat_escape_id(insn_attr_t attr)
+{
+       return (attr & INAT_ESC_MASK) >> INAT_ESC_OFFS;
+}
+
+static inline int inat_is_group(insn_attr_t attr)
+{
+       return attr & INAT_GRP_MASK;
+}
+
+static inline int inat_group_id(insn_attr_t attr)
+{
+       return (attr & INAT_GRP_MASK) >> INAT_GRP_OFFS;
+}
+
+static inline int inat_group_common_attribute(insn_attr_t attr)
+{
+       return attr & ~INAT_GRP_MASK;
+}
+
+static inline int inat_has_immediate(insn_attr_t attr)
+{
+       return attr & INAT_IMM_MASK;
+}
+
+static inline int inat_immediate_size(insn_attr_t attr)
+{
+       return (attr & INAT_IMM_MASK) >> INAT_IMM_OFFS;
+}
+
+static inline int inat_has_modrm(insn_attr_t attr)
+{
+       return attr & INAT_MODRM;
+}
+
+static inline int inat_is_force64(insn_attr_t attr)
+{
+       return attr & INAT_FORCE64;
+}
+
+static inline int inat_has_second_immediate(insn_attr_t attr)
+{
+       return attr & INAT_SCNDIMM;
+}
+
+static inline int inat_has_moffset(insn_attr_t attr)
+{
+       return attr & INAT_MOFFSET;
+}
+
+static inline int inat_has_variant(insn_attr_t attr)
+{
+       return attr & INAT_VARIANT;
+}
+
+static inline int inat_accept_vex(insn_attr_t attr)
+{
+       return attr & INAT_VEXOK;
+}
+
+static inline int inat_must_vex(insn_attr_t attr)
+{
+       return attr & INAT_VEXONLY;
+}
+#endif
diff --git a/insn/asm/inat_types.h b/insn/asm/inat_types.h
new file mode 100644 (file)
index 0000000..cb3c20c
--- /dev/null
@@ -0,0 +1,29 @@
+#ifndef _ASM_X86_INAT_TYPES_H
+#define _ASM_X86_INAT_TYPES_H
+/*
+ * x86 instruction attributes
+ *
+ * Written by Masami Hiramatsu <mhiramat@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+/* Instruction attributes */
+typedef unsigned int insn_attr_t;
+typedef unsigned char insn_byte_t;
+typedef signed int insn_value_t;
+
+#endif
diff --git a/insn/asm/insn.h b/insn/asm/insn.h
new file mode 100644 (file)
index 0000000..48eb30a
--- /dev/null
@@ -0,0 +1,199 @@
+#ifndef _ASM_X86_INSN_H
+#define _ASM_X86_INSN_H
+/*
+ * x86 instruction analysis
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Copyright (C) IBM Corporation, 2009
+ */
+
+/* insn_attr_t is defined in inat.h */
+#include <asm/inat.h>
+
+struct insn_field {
+       union {
+               insn_value_t value;
+               insn_byte_t bytes[4];
+       };
+       /* !0 if we've run insn_get_xxx() for this field */
+       unsigned char got;
+       unsigned char nbytes;
+};
+
+struct insn {
+       struct insn_field prefixes;     /*
+                                        * Prefixes
+                                        * prefixes.bytes[3]: last prefix
+                                        */
+       struct insn_field rex_prefix;   /* REX prefix */
+       struct insn_field vex_prefix;   /* VEX prefix */
+       struct insn_field opcode;       /*
+                                        * opcode.bytes[0]: opcode1
+                                        * opcode.bytes[1]: opcode2
+                                        * opcode.bytes[2]: opcode3
+                                        */
+       struct insn_field modrm;
+       struct insn_field sib;
+       struct insn_field displacement;
+       union {
+               struct insn_field immediate;
+               struct insn_field moffset1;     /* for 64bit MOV */
+               struct insn_field immediate1;   /* for 64bit imm or off16/32 */
+       };
+       union {
+               struct insn_field moffset2;     /* for 64bit MOV */
+               struct insn_field immediate2;   /* for 64bit imm or seg16 */
+       };
+
+       insn_attr_t attr;
+       unsigned char opnd_bytes;
+       unsigned char addr_bytes;
+       unsigned char length;
+       unsigned char x86_64;
+
+       const insn_byte_t *kaddr;       /* kernel address of insn to analyze */
+       const insn_byte_t *next_byte;
+};
+
+#define MAX_INSN_SIZE  16
+
+#define X86_MODRM_MOD(modrm) (((modrm) & 0xc0) >> 6)
+#define X86_MODRM_REG(modrm) (((modrm) & 0x38) >> 3)
+#define X86_MODRM_RM(modrm) ((modrm) & 0x07)
+
+#define X86_SIB_SCALE(sib) (((sib) & 0xc0) >> 6)
+#define X86_SIB_INDEX(sib) (((sib) & 0x38) >> 3)
+#define X86_SIB_BASE(sib) ((sib) & 0x07)
+
+#define X86_REX_W(rex) ((rex) & 8)
+#define X86_REX_R(rex) ((rex) & 4)
+#define X86_REX_X(rex) ((rex) & 2)
+#define X86_REX_B(rex) ((rex) & 1)
+
+/* VEX bit flags  */
+#define X86_VEX_W(vex) ((vex) & 0x80)  /* VEX3 Byte2 */
+#define X86_VEX_R(vex) ((vex) & 0x80)  /* VEX2/3 Byte1 */
+#define X86_VEX_X(vex) ((vex) & 0x40)  /* VEX3 Byte1 */
+#define X86_VEX_B(vex) ((vex) & 0x20)  /* VEX3 Byte1 */
+#define X86_VEX_L(vex) ((vex) & 0x04)  /* VEX3 Byte2, VEX2 Byte1 */
+/* VEX bit fields */
+#define X86_VEX3_M(vex)        ((vex) & 0x1f)          /* VEX3 Byte1 */
+#define X86_VEX2_M     1                       /* VEX2.M always 1 */
+#define X86_VEX_V(vex) (((vex) & 0x78) >> 3)   /* VEX3 Byte2, VEX2 Byte1 */
+#define X86_VEX_P(vex) ((vex) & 0x03)          /* VEX3 Byte2, VEX2 Byte1 */
+#define X86_VEX_M_MAX  0x1f                    /* VEX3.M Maximum value */
+
+extern void insn_init(struct insn *insn, const void *kaddr, int x86_64);
+extern void insn_get_prefixes(struct insn *insn);
+extern void insn_get_opcode(struct insn *insn);
+extern void insn_get_modrm(struct insn *insn);
+extern void insn_get_sib(struct insn *insn);
+extern void insn_get_displacement(struct insn *insn);
+extern void insn_get_immediate(struct insn *insn);
+extern void insn_get_length(struct insn *insn);
+
+/* Attribute will be determined after getting ModRM (for opcode groups) */
+static inline void insn_get_attribute(struct insn *insn)
+{
+       insn_get_modrm(insn);
+}
+
+/* Instruction uses RIP-relative addressing */
+extern int insn_rip_relative(struct insn *insn);
+
+/* Init insn for kernel text */
+static inline void kernel_insn_init(struct insn *insn, const void *kaddr)
+{
+#ifdef CONFIG_X86_64
+       insn_init(insn, kaddr, 1);
+#else /* CONFIG_X86_32 */
+       insn_init(insn, kaddr, 0);
+#endif
+}
+
+static inline int insn_is_avx(struct insn *insn)
+{
+       if (!insn->prefixes.got)
+               insn_get_prefixes(insn);
+       return (insn->vex_prefix.value != 0);
+}
+
+/* Ensure this instruction is decoded completely */
+static inline int insn_complete(struct insn *insn)
+{
+       return insn->opcode.got && insn->modrm.got && insn->sib.got &&
+               insn->displacement.got && insn->immediate.got;
+}
+
+static inline insn_byte_t insn_vex_m_bits(struct insn *insn)
+{
+       if (insn->vex_prefix.nbytes == 2)       /* 2 bytes VEX */
+               return X86_VEX2_M;
+       else
+               return X86_VEX3_M(insn->vex_prefix.bytes[1]);
+}
+
+static inline insn_byte_t insn_vex_p_bits(struct insn *insn)
+{
+       if (insn->vex_prefix.nbytes == 2)       /* 2 bytes VEX */
+               return X86_VEX_P(insn->vex_prefix.bytes[1]);
+       else
+               return X86_VEX_P(insn->vex_prefix.bytes[2]);
+}
+
+/* Get the last prefix id from last prefix or VEX prefix */
+static inline int insn_last_prefix_id(struct insn *insn)
+{
+       if (insn_is_avx(insn))
+               return insn_vex_p_bits(insn);   /* VEX_p is a SIMD prefix id */
+
+       if (insn->prefixes.bytes[3])
+               return inat_get_last_prefix_id(insn->prefixes.bytes[3]);
+
+       return 0;
+}
+
+/* Offset of each field from kaddr */
+static inline int insn_offset_rex_prefix(struct insn *insn)
+{
+       return insn->prefixes.nbytes;
+}
+static inline int insn_offset_vex_prefix(struct insn *insn)
+{
+       return insn_offset_rex_prefix(insn) + insn->rex_prefix.nbytes;
+}
+static inline int insn_offset_opcode(struct insn *insn)
+{
+       return insn_offset_vex_prefix(insn) + insn->vex_prefix.nbytes;
+}
+static inline int insn_offset_modrm(struct insn *insn)
+{
+       return insn_offset_opcode(insn) + insn->opcode.nbytes;
+}
+static inline int insn_offset_sib(struct insn *insn)
+{
+       return insn_offset_modrm(insn) + insn->modrm.nbytes;
+}
+static inline int insn_offset_displacement(struct insn *insn)
+{
+       return insn_offset_sib(insn) + insn->sib.nbytes;
+}
+static inline int insn_offset_immediate(struct insn *insn)
+{
+       return insn_offset_displacement(insn) + insn->displacement.nbytes;
+}
+
+#endif /* _ASM_X86_INSN_H */
diff --git a/insn/inat-tables.c b/insn/inat-tables.c
new file mode 100644 (file)
index 0000000..10de26c
--- /dev/null
@@ -0,0 +1,1146 @@
+/* x86 opcode map generated from x86-opcode-map.txt */
+/* Do not change this code. */
+
+/* Table: one byte opcode */
+const insn_attr_t inat_primary_table[INAT_OPCODE_TABLE_SIZE] = {
+       [0x00] = INAT_MODRM,
+       [0x01] = INAT_MODRM,
+       [0x02] = INAT_MODRM,
+       [0x03] = INAT_MODRM,
+       [0x04] = INAT_MAKE_IMM(INAT_IMM_BYTE),
+       [0x05] = INAT_MAKE_IMM(INAT_IMM_VWORD32),
+       [0x08] = INAT_MODRM,
+       [0x09] = INAT_MODRM,
+       [0x0a] = INAT_MODRM,
+       [0x0b] = INAT_MODRM,
+       [0x0c] = INAT_MAKE_IMM(INAT_IMM_BYTE),
+       [0x0d] = INAT_MAKE_IMM(INAT_IMM_VWORD32),
+       [0x0f] = INAT_MAKE_ESCAPE(1),
+       [0x10] = INAT_MODRM,
+       [0x11] = INAT_MODRM,
+       [0x12] = INAT_MODRM,
+       [0x13] = INAT_MODRM,
+       [0x14] = INAT_MAKE_IMM(INAT_IMM_BYTE),
+       [0x15] = INAT_MAKE_IMM(INAT_IMM_VWORD32),
+       [0x18] = INAT_MODRM,
+       [0x19] = INAT_MODRM,
+       [0x1a] = INAT_MODRM,
+       [0x1b] = INAT_MODRM,
+       [0x1c] = INAT_MAKE_IMM(INAT_IMM_BYTE),
+       [0x1d] = INAT_MAKE_IMM(INAT_IMM_VWORD32),
+       [0x20] = INAT_MODRM,
+       [0x21] = INAT_MODRM,
+       [0x22] = INAT_MODRM,
+       [0x23] = INAT_MODRM,
+       [0x24] = INAT_MAKE_IMM(INAT_IMM_BYTE),
+       [0x25] = INAT_MAKE_IMM(INAT_IMM_VWORD32),
+       [0x26] = INAT_MAKE_PREFIX(INAT_PFX_ES),
+       [0x28] = INAT_MODRM,
+       [0x29] = INAT_MODRM,
+       [0x2a] = INAT_MODRM,
+       [0x2b] = INAT_MODRM,
+       [0x2c] = INAT_MAKE_IMM(INAT_IMM_BYTE),
+       [0x2d] = INAT_MAKE_IMM(INAT_IMM_VWORD32),
+       [0x2e] = INAT_MAKE_PREFIX(INAT_PFX_CS),
+       [0x30] = INAT_MODRM,
+       [0x31] = INAT_MODRM,
+       [0x32] = INAT_MODRM,
+       [0x33] = INAT_MODRM,
+       [0x34] = INAT_MAKE_IMM(INAT_IMM_BYTE),
+       [0x35] = INAT_MAKE_IMM(INAT_IMM_VWORD32),
+       [0x36] = INAT_MAKE_PREFIX(INAT_PFX_SS),
+       [0x38] = INAT_MODRM,
+       [0x39] = INAT_MODRM,
+       [0x3a] = INAT_MODRM,
+       [0x3b] = INAT_MODRM,
+       [0x3c] = INAT_MAKE_IMM(INAT_IMM_BYTE),
+       [0x3d] = INAT_MAKE_IMM(INAT_IMM_VWORD32),
+       [0x3e] = INAT_MAKE_PREFIX(INAT_PFX_DS),
+       [0x40] = INAT_MAKE_PREFIX(INAT_PFX_REX),
+       [0x41] = INAT_MAKE_PREFIX(INAT_PFX_REX),
+       [0x42] = INAT_MAKE_PREFIX(INAT_PFX_REX),
+       [0x43] = INAT_MAKE_PREFIX(INAT_PFX_REX),
+       [0x44] = INAT_MAKE_PREFIX(INAT_PFX_REX),
+       [0x45] = INAT_MAKE_PREFIX(INAT_PFX_REX),
+       [0x46] = INAT_MAKE_PREFIX(INAT_PFX_REX),
+       [0x47] = INAT_MAKE_PREFIX(INAT_PFX_REX),
+       [0x48] = INAT_MAKE_PREFIX(INAT_PFX_REX),
+       [0x49] = INAT_MAKE_PREFIX(INAT_PFX_REX),
+       [0x4a] = INAT_MAKE_PREFIX(INAT_PFX_REX),
+       [0x4b] = INAT_MAKE_PREFIX(INAT_PFX_REX),
+       [0x4c] = INAT_MAKE_PREFIX(INAT_PFX_REX),
+       [0x4d] = INAT_MAKE_PREFIX(INAT_PFX_REX),
+       [0x4e] = INAT_MAKE_PREFIX(INAT_PFX_REX),
+       [0x4f] = INAT_MAKE_PREFIX(INAT_PFX_REX),
+       [0x50] = INAT_FORCE64,
+       [0x51] = INAT_FORCE64,
+       [0x52] = INAT_FORCE64,
+       [0x53] = INAT_FORCE64,
+       [0x54] = INAT_FORCE64,
+       [0x55] = INAT_FORCE64,
+       [0x56] = INAT_FORCE64,
+       [0x57] = INAT_FORCE64,
+       [0x58] = INAT_FORCE64,
+       [0x59] = INAT_FORCE64,
+       [0x5a] = INAT_FORCE64,
+       [0x5b] = INAT_FORCE64,
+       [0x5c] = INAT_FORCE64,
+       [0x5d] = INAT_FORCE64,
+       [0x5e] = INAT_FORCE64,
+       [0x5f] = INAT_FORCE64,
+       [0x62] = INAT_MODRM,
+       [0x63] = INAT_MODRM | INAT_MODRM,
+       [0x64] = INAT_MAKE_PREFIX(INAT_PFX_FS),
+       [0x65] = INAT_MAKE_PREFIX(INAT_PFX_GS),
+       [0x66] = INAT_MAKE_PREFIX(INAT_PFX_OPNDSZ),
+       [0x67] = INAT_MAKE_PREFIX(INAT_PFX_ADDRSZ),
+       [0x68] = INAT_MAKE_IMM(INAT_IMM_VWORD32) | INAT_FORCE64,
+       [0x69] = INAT_MAKE_IMM(INAT_IMM_VWORD32) | INAT_MODRM,
+       [0x6a] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_FORCE64,
+       [0x6b] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM,
+       [0x70] = INAT_MAKE_IMM(INAT_IMM_BYTE),
+       [0x71] = INAT_MAKE_IMM(INAT_IMM_BYTE),
+       [0x72] = INAT_MAKE_IMM(INAT_IMM_BYTE),
+       [0x73] = INAT_MAKE_IMM(INAT_IMM_BYTE),
+       [0x74] = INAT_MAKE_IMM(INAT_IMM_BYTE),
+       [0x75] = INAT_MAKE_IMM(INAT_IMM_BYTE),
+       [0x76] = INAT_MAKE_IMM(INAT_IMM_BYTE),
+       [0x77] = INAT_MAKE_IMM(INAT_IMM_BYTE),
+       [0x78] = INAT_MAKE_IMM(INAT_IMM_BYTE),
+       [0x79] = INAT_MAKE_IMM(INAT_IMM_BYTE),
+       [0x7a] = INAT_MAKE_IMM(INAT_IMM_BYTE),
+       [0x7b] = INAT_MAKE_IMM(INAT_IMM_BYTE),
+       [0x7c] = INAT_MAKE_IMM(INAT_IMM_BYTE),
+       [0x7d] = INAT_MAKE_IMM(INAT_IMM_BYTE),
+       [0x7e] = INAT_MAKE_IMM(INAT_IMM_BYTE),
+       [0x7f] = INAT_MAKE_IMM(INAT_IMM_BYTE),
+       [0x80] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_MAKE_GROUP(1),
+       [0x81] = INAT_MAKE_IMM(INAT_IMM_VWORD32) | INAT_MODRM | INAT_MAKE_GROUP(1),
+       [0x82] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_MAKE_GROUP(1),
+       [0x83] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_MAKE_GROUP(1),
+       [0x84] = INAT_MODRM,
+       [0x85] = INAT_MODRM,
+       [0x86] = INAT_MODRM,
+       [0x87] = INAT_MODRM,
+       [0x88] = INAT_MODRM,
+       [0x89] = INAT_MODRM,
+       [0x8a] = INAT_MODRM,
+       [0x8b] = INAT_MODRM,
+       [0x8c] = INAT_MODRM,
+       [0x8d] = INAT_MODRM,
+       [0x8e] = INAT_MODRM,
+       [0x8f] = INAT_MAKE_GROUP(2) | INAT_MODRM | INAT_FORCE64,
+       [0x9a] = INAT_MAKE_IMM(INAT_IMM_PTR),
+       [0x9c] = INAT_FORCE64,
+       [0x9d] = INAT_FORCE64,
+       [0xa0] = INAT_MOFFSET,
+       [0xa1] = INAT_MOFFSET,
+       [0xa2] = INAT_MOFFSET,
+       [0xa3] = INAT_MOFFSET,
+       [0xa8] = INAT_MAKE_IMM(INAT_IMM_BYTE),
+       [0xa9] = INAT_MAKE_IMM(INAT_IMM_VWORD32),
+       [0xb0] = INAT_MAKE_IMM(INAT_IMM_BYTE),
+       [0xb1] = INAT_MAKE_IMM(INAT_IMM_BYTE),
+       [0xb2] = INAT_MAKE_IMM(INAT_IMM_BYTE),
+       [0xb3] = INAT_MAKE_IMM(INAT_IMM_BYTE),
+       [0xb4] = INAT_MAKE_IMM(INAT_IMM_BYTE),
+       [0xb5] = INAT_MAKE_IMM(INAT_IMM_BYTE),
+       [0xb6] = INAT_MAKE_IMM(INAT_IMM_BYTE),
+       [0xb7] = INAT_MAKE_IMM(INAT_IMM_BYTE),
+       [0xb8] = INAT_MAKE_IMM(INAT_IMM_VWORD),
+       [0xb9] = INAT_MAKE_IMM(INAT_IMM_VWORD),
+       [0xba] = INAT_MAKE_IMM(INAT_IMM_VWORD),
+       [0xbb] = INAT_MAKE_IMM(INAT_IMM_VWORD),
+       [0xbc] = INAT_MAKE_IMM(INAT_IMM_VWORD),
+       [0xbd] = INAT_MAKE_IMM(INAT_IMM_VWORD),
+       [0xbe] = INAT_MAKE_IMM(INAT_IMM_VWORD),
+       [0xbf] = INAT_MAKE_IMM(INAT_IMM_VWORD),
+       [0xc0] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_MAKE_GROUP(3),
+       [0xc1] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_MAKE_GROUP(3),
+       [0xc2] = INAT_MAKE_IMM(INAT_IMM_WORD) | INAT_FORCE64,
+       [0xc4] = INAT_MODRM | INAT_MAKE_PREFIX(INAT_PFX_VEX3),
+       [0xc5] = INAT_MODRM | INAT_MAKE_PREFIX(INAT_PFX_VEX2),
+       [0xc6] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_MAKE_GROUP(4),
+       [0xc7] = INAT_MAKE_IMM(INAT_IMM_VWORD32) | INAT_MODRM | INAT_MAKE_GROUP(5),
+       [0xc8] = INAT_MAKE_IMM(INAT_IMM_WORD) | INAT_SCNDIMM,
+       [0xc9] = INAT_FORCE64,
+       [0xca] = INAT_MAKE_IMM(INAT_IMM_WORD),
+       [0xcd] = INAT_MAKE_IMM(INAT_IMM_BYTE),
+       [0xd0] = INAT_MODRM | INAT_MAKE_GROUP(3),
+       [0xd1] = INAT_MODRM | INAT_MAKE_GROUP(3),
+       [0xd2] = INAT_MODRM | INAT_MAKE_GROUP(3),
+       [0xd3] = INAT_MODRM | INAT_MAKE_GROUP(3),
+       [0xd4] = INAT_MAKE_IMM(INAT_IMM_BYTE),
+       [0xd5] = INAT_MAKE_IMM(INAT_IMM_BYTE),
+       [0xd8] = INAT_MODRM,
+       [0xd9] = INAT_MODRM,
+       [0xda] = INAT_MODRM,
+       [0xdb] = INAT_MODRM,
+       [0xdc] = INAT_MODRM,
+       [0xdd] = INAT_MODRM,
+       [0xde] = INAT_MODRM,
+       [0xdf] = INAT_MODRM,
+       [0xe0] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_FORCE64,
+       [0xe1] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_FORCE64,
+       [0xe2] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_FORCE64,
+       [0xe3] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_FORCE64,
+       [0xe4] = INAT_MAKE_IMM(INAT_IMM_BYTE),
+       [0xe5] = INAT_MAKE_IMM(INAT_IMM_BYTE),
+       [0xe6] = INAT_MAKE_IMM(INAT_IMM_BYTE),
+       [0xe7] = INAT_MAKE_IMM(INAT_IMM_BYTE),
+       [0xe8] = INAT_MAKE_IMM(INAT_IMM_VWORD32) | INAT_FORCE64,
+       [0xe9] = INAT_MAKE_IMM(INAT_IMM_VWORD32) | INAT_FORCE64,
+       [0xea] = INAT_MAKE_IMM(INAT_IMM_PTR),
+       [0xeb] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_FORCE64,
+       [0xf0] = INAT_MAKE_PREFIX(INAT_PFX_LOCK),
+       [0xf2] = INAT_MAKE_PREFIX(INAT_PFX_REPNE) | INAT_MAKE_PREFIX(INAT_PFX_REPNE),
+       [0xf3] = INAT_MAKE_PREFIX(INAT_PFX_REPE) | INAT_MAKE_PREFIX(INAT_PFX_REPE),
+       [0xf6] = INAT_MODRM | INAT_MAKE_GROUP(6),
+       [0xf7] = INAT_MODRM | INAT_MAKE_GROUP(7),
+       [0xfe] = INAT_MAKE_GROUP(8),
+       [0xff] = INAT_MAKE_GROUP(9),
+};
+
+/* Table: 2-byte opcode (0x0f) */
+const insn_attr_t inat_escape_table_1[INAT_OPCODE_TABLE_SIZE] = {
+       [0x00] = INAT_MAKE_GROUP(10),
+       [0x01] = INAT_MAKE_GROUP(11),
+       [0x02] = INAT_MODRM,
+       [0x03] = INAT_MODRM,
+       [0x0d] = INAT_MAKE_GROUP(12),
+       [0x0f] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM,
+       [0x10] = INAT_MODRM | INAT_VEXOK | INAT_VARIANT,
+       [0x11] = INAT_MODRM | INAT_VEXOK | INAT_VARIANT,
+       [0x12] = INAT_MODRM | INAT_VEXOK | INAT_MODRM | INAT_VEXOK | INAT_VARIANT,
+       [0x13] = INAT_MODRM | INAT_VEXOK | INAT_VARIANT,
+       [0x14] = INAT_MODRM | INAT_VEXOK | INAT_VARIANT,
+       [0x15] = INAT_MODRM | INAT_VEXOK | INAT_VARIANT,
+       [0x16] = INAT_MODRM | INAT_VEXOK | INAT_MODRM | INAT_VEXOK | INAT_VARIANT,
+       [0x17] = INAT_MODRM | INAT_VEXOK | INAT_VARIANT,
+       [0x18] = INAT_MAKE_GROUP(13),
+       [0x1a] = INAT_MODRM | INAT_MODRM | INAT_MODRM | INAT_MODRM,
+       [0x1b] = INAT_MODRM | INAT_MODRM | INAT_MODRM | INAT_MODRM,
+       [0x1f] = INAT_MODRM,
+       [0x20] = INAT_MODRM,
+       [0x21] = INAT_MODRM,
+       [0x22] = INAT_MODRM,
+       [0x23] = INAT_MODRM,
+       [0x28] = INAT_MODRM | INAT_VEXOK | INAT_VARIANT,
+       [0x29] = INAT_MODRM | INAT_VEXOK | INAT_VARIANT,
+       [0x2a] = INAT_MODRM | INAT_VARIANT,
+       [0x2b] = INAT_MODRM | INAT_VEXOK | INAT_VARIANT,
+       [0x2c] = INAT_MODRM | INAT_VARIANT,
+       [0x2d] = INAT_MODRM | INAT_VARIANT,
+       [0x2e] = INAT_MODRM | INAT_VEXOK | INAT_VARIANT,
+       [0x2f] = INAT_MODRM | INAT_VEXOK | INAT_VARIANT,
+       [0x38] = INAT_MAKE_ESCAPE(2),
+       [0x3a] = INAT_MAKE_ESCAPE(3),
+       [0x40] = INAT_MODRM,
+       [0x41] = INAT_MODRM,
+       [0x42] = INAT_MODRM,
+       [0x43] = INAT_MODRM,
+       [0x44] = INAT_MODRM,
+       [0x45] = INAT_MODRM,
+       [0x46] = INAT_MODRM,
+       [0x47] = INAT_MODRM,
+       [0x48] = INAT_MODRM,
+       [0x49] = INAT_MODRM,
+       [0x4a] = INAT_MODRM,
+       [0x4b] = INAT_MODRM,
+       [0x4c] = INAT_MODRM,
+       [0x4d] = INAT_MODRM,
+       [0x4e] = INAT_MODRM,
+       [0x4f] = INAT_MODRM,
+       [0x50] = INAT_MODRM | INAT_VEXOK | INAT_VARIANT,
+       [0x51] = INAT_MODRM | INAT_VEXOK | INAT_VARIANT,
+       [0x52] = INAT_MODRM | INAT_VEXOK | INAT_VARIANT,
+       [0x53] = INAT_MODRM | INAT_VEXOK | INAT_VARIANT,
+       [0x54] = INAT_MODRM | INAT_VEXOK | INAT_VARIANT,
+       [0x55] = INAT_MODRM | INAT_VEXOK | INAT_VARIANT,
+       [0x56] = INAT_MODRM | INAT_VEXOK | INAT_VARIANT,
+       [0x57] = INAT_MODRM | INAT_VEXOK | INAT_VARIANT,
+       [0x58] = INAT_MODRM | INAT_VEXOK | INAT_VARIANT,
+       [0x59] = INAT_MODRM | INAT_VEXOK | INAT_VARIANT,
+       [0x5a] = INAT_MODRM | INAT_VEXOK | INAT_VARIANT,
+       [0x5b] = INAT_MODRM | INAT_VEXOK | INAT_VARIANT,
+       [0x5c] = INAT_MODRM | INAT_VEXOK | INAT_VARIANT,
+       [0x5d] = INAT_MODRM | INAT_VEXOK | INAT_VARIANT,
+       [0x5e] = INAT_MODRM | INAT_VEXOK | INAT_VARIANT,
+       [0x5f] = INAT_MODRM | INAT_VEXOK | INAT_VARIANT,
+       [0x60] = INAT_MODRM | INAT_VARIANT,
+       [0x61] = INAT_MODRM | INAT_VARIANT,
+       [0x62] = INAT_MODRM | INAT_VARIANT,
+       [0x63] = INAT_MODRM | INAT_VARIANT,
+       [0x64] = INAT_MODRM | INAT_VARIANT,
+       [0x65] = INAT_MODRM | INAT_VARIANT,
+       [0x66] = INAT_MODRM | INAT_VARIANT,
+       [0x67] = INAT_MODRM | INAT_VARIANT,
+       [0x68] = INAT_MODRM | INAT_VARIANT,
+       [0x69] = INAT_MODRM | INAT_VARIANT,
+       [0x6a] = INAT_MODRM | INAT_VARIANT,
+       [0x6b] = INAT_MODRM | INAT_VARIANT,
+       [0x6c] = INAT_VARIANT,
+       [0x6d] = INAT_VARIANT,
+       [0x6e] = INAT_MODRM | INAT_VARIANT,
+       [0x6f] = INAT_MODRM | INAT_VARIANT,
+       [0x70] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VARIANT,
+       [0x71] = INAT_MAKE_GROUP(14),
+       [0x72] = INAT_MAKE_GROUP(15),
+       [0x73] = INAT_MAKE_GROUP(16),
+       [0x74] = INAT_MODRM | INAT_VARIANT,
+       [0x75] = INAT_MODRM | INAT_VARIANT,
+       [0x76] = INAT_MODRM | INAT_VARIANT,
+       [0x77] = INAT_VEXOK | INAT_VEXOK,
+       [0x78] = INAT_MODRM,
+       [0x79] = INAT_MODRM,
+       [0x7c] = INAT_VARIANT,
+       [0x7d] = INAT_VARIANT,
+       [0x7e] = INAT_MODRM | INAT_VARIANT,
+       [0x7f] = INAT_MODRM | INAT_VARIANT,
+       [0x80] = INAT_MAKE_IMM(INAT_IMM_VWORD32) | INAT_FORCE64,
+       [0x81] = INAT_MAKE_IMM(INAT_IMM_VWORD32) | INAT_FORCE64,
+       [0x82] = INAT_MAKE_IMM(INAT_IMM_VWORD32) | INAT_FORCE64,
+       [0x83] = INAT_MAKE_IMM(INAT_IMM_VWORD32) | INAT_FORCE64,
+       [0x84] = INAT_MAKE_IMM(INAT_IMM_VWORD32) | INAT_FORCE64,
+       [0x85] = INAT_MAKE_IMM(INAT_IMM_VWORD32) | INAT_FORCE64,
+       [0x86] = INAT_MAKE_IMM(INAT_IMM_VWORD32) | INAT_FORCE64,
+       [0x87] = INAT_MAKE_IMM(INAT_IMM_VWORD32) | INAT_FORCE64,
+       [0x88] = INAT_MAKE_IMM(INAT_IMM_VWORD32) | INAT_FORCE64,
+       [0x89] = INAT_MAKE_IMM(INAT_IMM_VWORD32) | INAT_FORCE64,
+       [0x8a] = INAT_MAKE_IMM(INAT_IMM_VWORD32) | INAT_FORCE64,
+       [0x8b] = INAT_MAKE_IMM(INAT_IMM_VWORD32) | INAT_FORCE64,
+       [0x8c] = INAT_MAKE_IMM(INAT_IMM_VWORD32) | INAT_FORCE64,
+       [0x8d] = INAT_MAKE_IMM(INAT_IMM_VWORD32) | INAT_FORCE64,
+       [0x8e] = INAT_MAKE_IMM(INAT_IMM_VWORD32) | INAT_FORCE64,
+       [0x8f] = INAT_MAKE_IMM(INAT_IMM_VWORD32) | INAT_FORCE64,
+       [0x90] = INAT_MODRM,
+       [0x91] = INAT_MODRM,
+       [0x92] = INAT_MODRM,
+       [0x93] = INAT_MODRM,
+       [0x94] = INAT_MODRM,
+       [0x95] = INAT_MODRM,
+       [0x96] = INAT_MODRM,
+       [0x97] = INAT_MODRM,
+       [0x98] = INAT_MODRM,
+       [0x99] = INAT_MODRM,
+       [0x9a] = INAT_MODRM,
+       [0x9b] = INAT_MODRM,
+       [0x9c] = INAT_MODRM,
+       [0x9d] = INAT_MODRM,
+       [0x9e] = INAT_MODRM,
+       [0x9f] = INAT_MODRM,
+       [0xa0] = INAT_FORCE64,
+       [0xa1] = INAT_FORCE64,
+       [0xa3] = INAT_MODRM,
+       [0xa4] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM,
+       [0xa5] = INAT_MODRM,
+       [0xa6] = INAT_MAKE_GROUP(17),
+       [0xa7] = INAT_MAKE_GROUP(18),
+       [0xa8] = INAT_FORCE64,
+       [0xa9] = INAT_FORCE64,
+       [0xab] = INAT_MODRM,
+       [0xac] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM,
+       [0xad] = INAT_MODRM,
+       [0xae] = INAT_MAKE_GROUP(19),
+       [0xaf] = INAT_MODRM,
+       [0xb0] = INAT_MODRM,
+       [0xb1] = INAT_MODRM,
+       [0xb2] = INAT_MODRM,
+       [0xb3] = INAT_MODRM,
+       [0xb4] = INAT_MODRM,
+       [0xb5] = INAT_MODRM,
+       [0xb6] = INAT_MODRM,
+       [0xb7] = INAT_MODRM,
+       [0xb8] = INAT_VARIANT,
+       [0xb9] = INAT_MAKE_GROUP(20),
+       [0xba] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_MAKE_GROUP(21),
+       [0xbb] = INAT_MODRM,
+       [0xbc] = INAT_MODRM | INAT_VARIANT,
+       [0xbd] = INAT_MODRM | INAT_VARIANT,
+       [0xbe] = INAT_MODRM,
+       [0xbf] = INAT_MODRM,
+       [0xc0] = INAT_MODRM,
+       [0xc1] = INAT_MODRM,
+       [0xc2] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK | INAT_VARIANT,
+       [0xc3] = INAT_MODRM,
+       [0xc4] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VARIANT,
+       [0xc5] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VARIANT,
+       [0xc6] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK | INAT_VARIANT,
+       [0xc7] = INAT_MAKE_GROUP(22),
+       [0xd0] = INAT_VARIANT,
+       [0xd1] = INAT_MODRM | INAT_VARIANT,
+       [0xd2] = INAT_MODRM | INAT_VARIANT,
+       [0xd3] = INAT_MODRM | INAT_VARIANT,
+       [0xd4] = INAT_MODRM | INAT_VARIANT,
+       [0xd5] = INAT_MODRM | INAT_VARIANT,
+       [0xd6] = INAT_VARIANT,
+       [0xd7] = INAT_MODRM | INAT_VARIANT,
+       [0xd8] = INAT_MODRM | INAT_VARIANT,
+       [0xd9] = INAT_MODRM | INAT_VARIANT,
+       [0xda] = INAT_MODRM | INAT_VARIANT,
+       [0xdb] = INAT_MODRM | INAT_VARIANT,
+       [0xdc] = INAT_MODRM | INAT_VARIANT,
+       [0xdd] = INAT_MODRM | INAT_VARIANT,
+       [0xde] = INAT_MODRM | INAT_VARIANT,
+       [0xdf] = INAT_MODRM | INAT_VARIANT,
+       [0xe0] = INAT_MODRM | INAT_VARIANT,
+       [0xe1] = INAT_MODRM | INAT_VARIANT,
+       [0xe2] = INAT_MODRM | INAT_VARIANT,
+       [0xe3] = INAT_MODRM | INAT_VARIANT,
+       [0xe4] = INAT_MODRM | INAT_VARIANT,
+       [0xe5] = INAT_MODRM | INAT_VARIANT,
+       [0xe6] = INAT_VARIANT,
+       [0xe7] = INAT_MODRM | INAT_VARIANT,
+       [0xe8] = INAT_MODRM | INAT_VARIANT,
+       [0xe9] = INAT_MODRM | INAT_VARIANT,
+       [0xea] = INAT_MODRM | INAT_VARIANT,
+       [0xeb] = INAT_MODRM | INAT_VARIANT,
+       [0xec] = INAT_MODRM | INAT_VARIANT,
+       [0xed] = INAT_MODRM | INAT_VARIANT,
+       [0xee] = INAT_MODRM | INAT_VARIANT,
+       [0xef] = INAT_MODRM | INAT_VARIANT,
+       [0xf0] = INAT_VARIANT,
+       [0xf1] = INAT_MODRM | INAT_VARIANT,
+       [0xf2] = INAT_MODRM | INAT_VARIANT,
+       [0xf3] = INAT_MODRM | INAT_VARIANT,
+       [0xf4] = INAT_MODRM | INAT_VARIANT,
+       [0xf5] = INAT_MODRM | INAT_VARIANT,
+       [0xf6] = INAT_MODRM | INAT_VARIANT,
+       [0xf7] = INAT_MODRM | INAT_VARIANT,
+       [0xf8] = INAT_MODRM | INAT_VARIANT,
+       [0xf9] = INAT_MODRM | INAT_VARIANT,
+       [0xfa] = INAT_MODRM | INAT_VARIANT,
+       [0xfb] = INAT_MODRM | INAT_VARIANT,
+       [0xfc] = INAT_MODRM | INAT_VARIANT,
+       [0xfd] = INAT_MODRM | INAT_VARIANT,
+       [0xfe] = INAT_MODRM | INAT_VARIANT,
+};
+const insn_attr_t inat_escape_table_1_1[INAT_OPCODE_TABLE_SIZE] = {
+       [0x10] = INAT_MODRM | INAT_VEXOK,
+       [0x11] = INAT_MODRM | INAT_VEXOK,
+       [0x12] = INAT_MODRM | INAT_VEXOK,
+       [0x13] = INAT_MODRM | INAT_VEXOK,
+       [0x14] = INAT_MODRM | INAT_VEXOK,
+       [0x15] = INAT_MODRM | INAT_VEXOK,
+       [0x16] = INAT_MODRM | INAT_VEXOK,
+       [0x17] = INAT_MODRM | INAT_VEXOK,
+       [0x28] = INAT_MODRM | INAT_VEXOK,
+       [0x29] = INAT_MODRM | INAT_VEXOK,
+       [0x2a] = INAT_MODRM,
+       [0x2b] = INAT_MODRM | INAT_VEXOK,
+       [0x2c] = INAT_MODRM,
+       [0x2d] = INAT_MODRM,
+       [0x2e] = INAT_MODRM | INAT_VEXOK,
+       [0x2f] = INAT_MODRM | INAT_VEXOK,
+       [0x50] = INAT_MODRM | INAT_VEXOK,
+       [0x51] = INAT_MODRM | INAT_VEXOK,
+       [0x54] = INAT_MODRM | INAT_VEXOK,
+       [0x55] = INAT_MODRM | INAT_VEXOK,
+       [0x56] = INAT_MODRM | INAT_VEXOK,
+       [0x57] = INAT_MODRM | INAT_VEXOK,
+       [0x58] = INAT_MODRM | INAT_VEXOK,
+       [0x59] = INAT_MODRM | INAT_VEXOK,
+       [0x5a] = INAT_MODRM | INAT_VEXOK,
+       [0x5b] = INAT_MODRM | INAT_VEXOK,
+       [0x5c] = INAT_MODRM | INAT_VEXOK,
+       [0x5d] = INAT_MODRM | INAT_VEXOK,
+       [0x5e] = INAT_MODRM | INAT_VEXOK,
+       [0x5f] = INAT_MODRM | INAT_VEXOK,
+       [0x60] = INAT_MODRM | INAT_VEXOK,
+       [0x61] = INAT_MODRM | INAT_VEXOK,
+       [0x62] = INAT_MODRM | INAT_VEXOK,
+       [0x63] = INAT_MODRM | INAT_VEXOK,
+       [0x64] = INAT_MODRM | INAT_VEXOK,
+       [0x65] = INAT_MODRM | INAT_VEXOK,
+       [0x66] = INAT_MODRM | INAT_VEXOK,
+       [0x67] = INAT_MODRM | INAT_VEXOK,
+       [0x68] = INAT_MODRM | INAT_VEXOK,
+       [0x69] = INAT_MODRM | INAT_VEXOK,
+       [0x6a] = INAT_MODRM | INAT_VEXOK,
+       [0x6b] = INAT_MODRM | INAT_VEXOK,
+       [0x6c] = INAT_MODRM | INAT_VEXOK,
+       [0x6d] = INAT_MODRM | INAT_VEXOK,
+       [0x6e] = INAT_MODRM | INAT_VEXOK,
+       [0x6f] = INAT_MODRM | INAT_VEXOK,
+       [0x70] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK,
+       [0x74] = INAT_MODRM | INAT_VEXOK,
+       [0x75] = INAT_MODRM | INAT_VEXOK,
+       [0x76] = INAT_MODRM | INAT_VEXOK,
+       [0x7c] = INAT_MODRM | INAT_VEXOK,
+       [0x7d] = INAT_MODRM | INAT_VEXOK,
+       [0x7e] = INAT_MODRM | INAT_VEXOK,
+       [0x7f] = INAT_MODRM | INAT_VEXOK,
+       [0xbc] = INAT_MODRM,
+       [0xbd] = INAT_MODRM,
+       [0xc2] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK,
+       [0xc4] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK,
+       [0xc5] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK,
+       [0xc6] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK,
+       [0xd0] = INAT_MODRM | INAT_VEXOK,
+       [0xd1] = INAT_MODRM | INAT_VEXOK,
+       [0xd2] = INAT_MODRM | INAT_VEXOK,
+       [0xd3] = INAT_MODRM | INAT_VEXOK,
+       [0xd4] = INAT_MODRM | INAT_VEXOK,
+       [0xd5] = INAT_MODRM | INAT_VEXOK,
+       [0xd6] = INAT_MODRM | INAT_VEXOK,
+       [0xd7] = INAT_MODRM | INAT_VEXOK,
+       [0xd8] = INAT_MODRM | INAT_VEXOK,
+       [0xd9] = INAT_MODRM | INAT_VEXOK,
+       [0xda] = INAT_MODRM | INAT_VEXOK,
+       [0xdb] = INAT_MODRM | INAT_VEXOK,
+       [0xdc] = INAT_MODRM | INAT_VEXOK,
+       [0xdd] = INAT_MODRM | INAT_VEXOK,
+       [0xde] = INAT_MODRM | INAT_VEXOK,
+       [0xdf] = INAT_MODRM | INAT_VEXOK,
+       [0xe0] = INAT_MODRM | INAT_VEXOK,
+       [0xe1] = INAT_MODRM | INAT_VEXOK,
+       [0xe2] = INAT_MODRM | INAT_VEXOK,
+       [0xe3] = INAT_MODRM | INAT_VEXOK,
+       [0xe4] = INAT_MODRM | INAT_VEXOK,
+       [0xe5] = INAT_MODRM | INAT_VEXOK,
+       [0xe6] = INAT_MODRM | INAT_VEXOK,
+       [0xe7] = INAT_MODRM | INAT_VEXOK,
+       [0xe8] = INAT_MODRM | INAT_VEXOK,
+       [0xe9] = INAT_MODRM | INAT_VEXOK,
+       [0xea] = INAT_MODRM | INAT_VEXOK,
+       [0xeb] = INAT_MODRM | INAT_VEXOK,
+       [0xec] = INAT_MODRM | INAT_VEXOK,
+       [0xed] = INAT_MODRM | INAT_VEXOK,
+       [0xee] = INAT_MODRM | INAT_VEXOK,
+       [0xef] = INAT_MODRM | INAT_VEXOK,
+       [0xf1] = INAT_MODRM | INAT_VEXOK,
+       [0xf2] = INAT_MODRM | INAT_VEXOK,
+       [0xf3] = INAT_MODRM | INAT_VEXOK,
+       [0xf4] = INAT_MODRM | INAT_VEXOK,
+       [0xf5] = INAT_MODRM | INAT_VEXOK,
+       [0xf6] = INAT_MODRM | INAT_VEXOK,
+       [0xf7] = INAT_MODRM | INAT_VEXOK,
+       [0xf8] = INAT_MODRM | INAT_VEXOK,
+       [0xf9] = INAT_MODRM | INAT_VEXOK,
+       [0xfa] = INAT_MODRM | INAT_VEXOK,
+       [0xfb] = INAT_MODRM | INAT_VEXOK,
+       [0xfc] = INAT_MODRM | INAT_VEXOK,
+       [0xfd] = INAT_MODRM | INAT_VEXOK,
+       [0xfe] = INAT_MODRM | INAT_VEXOK,
+};
+const insn_attr_t inat_escape_table_1_2[INAT_OPCODE_TABLE_SIZE] = {
+       [0x10] = INAT_MODRM | INAT_VEXOK,
+       [0x11] = INAT_MODRM | INAT_VEXOK,
+       [0x12] = INAT_MODRM | INAT_VEXOK,
+       [0x16] = INAT_MODRM | INAT_VEXOK,
+       [0x2a] = INAT_MODRM | INAT_VEXOK,
+       [0x2c] = INAT_MODRM | INAT_VEXOK,
+       [0x2d] = INAT_MODRM | INAT_VEXOK,
+       [0x51] = INAT_MODRM | INAT_VEXOK,
+       [0x52] = INAT_MODRM | INAT_VEXOK,
+       [0x53] = INAT_MODRM | INAT_VEXOK,
+       [0x58] = INAT_MODRM | INAT_VEXOK,
+       [0x59] = INAT_MODRM | INAT_VEXOK,
+       [0x5a] = INAT_MODRM | INAT_VEXOK,
+       [0x5b] = INAT_MODRM | INAT_VEXOK,
+       [0x5c] = INAT_MODRM | INAT_VEXOK,
+       [0x5d] = INAT_MODRM | INAT_VEXOK,
+       [0x5e] = INAT_MODRM | INAT_VEXOK,
+       [0x5f] = INAT_MODRM | INAT_VEXOK,
+       [0x6f] = INAT_MODRM | INAT_VEXOK,
+       [0x70] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK,
+       [0x7e] = INAT_MODRM | INAT_VEXOK,
+       [0x7f] = INAT_MODRM | INAT_VEXOK,
+       [0xb8] = INAT_MODRM,
+       [0xbc] = INAT_MODRM,
+       [0xbd] = INAT_MODRM,
+       [0xc2] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK,
+       [0xd6] = INAT_MODRM,
+       [0xe6] = INAT_MODRM | INAT_VEXOK,
+};
+const insn_attr_t inat_escape_table_1_3[INAT_OPCODE_TABLE_SIZE] = {
+       [0x10] = INAT_MODRM | INAT_VEXOK,
+       [0x11] = INAT_MODRM | INAT_VEXOK,
+       [0x12] = INAT_MODRM | INAT_VEXOK,
+       [0x2a] = INAT_MODRM | INAT_VEXOK,
+       [0x2c] = INAT_MODRM | INAT_VEXOK,
+       [0x2d] = INAT_MODRM | INAT_VEXOK,
+       [0x51] = INAT_MODRM | INAT_VEXOK,
+       [0x58] = INAT_MODRM | INAT_VEXOK,
+       [0x59] = INAT_MODRM | INAT_VEXOK,
+       [0x5a] = INAT_MODRM | INAT_VEXOK,
+       [0x5c] = INAT_MODRM | INAT_VEXOK,
+       [0x5d] = INAT_MODRM | INAT_VEXOK,
+       [0x5e] = INAT_MODRM | INAT_VEXOK,
+       [0x5f] = INAT_MODRM | INAT_VEXOK,
+       [0x70] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK,
+       [0x7c] = INAT_MODRM | INAT_VEXOK,
+       [0x7d] = INAT_MODRM | INAT_VEXOK,
+       [0xbc] = INAT_MODRM,
+       [0xbd] = INAT_MODRM,
+       [0xc2] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK,
+       [0xd0] = INAT_MODRM | INAT_VEXOK,
+       [0xd6] = INAT_MODRM,
+       [0xe6] = INAT_MODRM | INAT_VEXOK,
+       [0xf0] = INAT_MODRM | INAT_VEXOK,
+};
+
+/* Table: 3-byte opcode 1 (0x0f 0x38) */
+const insn_attr_t inat_escape_table_2[INAT_OPCODE_TABLE_SIZE] = {
+       [0x00] = INAT_MODRM | INAT_VARIANT,
+       [0x01] = INAT_MODRM | INAT_VARIANT,
+       [0x02] = INAT_MODRM | INAT_VARIANT,
+       [0x03] = INAT_MODRM | INAT_VARIANT,
+       [0x04] = INAT_MODRM | INAT_VARIANT,
+       [0x05] = INAT_MODRM | INAT_VARIANT,
+       [0x06] = INAT_MODRM | INAT_VARIANT,
+       [0x07] = INAT_MODRM | INAT_VARIANT,
+       [0x08] = INAT_MODRM | INAT_VARIANT,
+       [0x09] = INAT_MODRM | INAT_VARIANT,
+       [0x0a] = INAT_MODRM | INAT_VARIANT,
+       [0x0b] = INAT_MODRM | INAT_VARIANT,
+       [0x0c] = INAT_VARIANT,
+       [0x0d] = INAT_VARIANT,
+       [0x0e] = INAT_VARIANT,
+       [0x0f] = INAT_VARIANT,
+       [0x10] = INAT_VARIANT,
+       [0x13] = INAT_VARIANT,
+       [0x14] = INAT_VARIANT,
+       [0x15] = INAT_VARIANT,
+       [0x16] = INAT_VARIANT,
+       [0x17] = INAT_VARIANT,
+       [0x18] = INAT_VARIANT,
+       [0x19] = INAT_VARIANT,
+       [0x1a] = INAT_VARIANT,
+       [0x1c] = INAT_MODRM | INAT_VARIANT,
+       [0x1d] = INAT_MODRM | INAT_VARIANT,
+       [0x1e] = INAT_MODRM | INAT_VARIANT,
+       [0x20] = INAT_VARIANT,
+       [0x21] = INAT_VARIANT,
+       [0x22] = INAT_VARIANT,
+       [0x23] = INAT_VARIANT,
+       [0x24] = INAT_VARIANT,
+       [0x25] = INAT_VARIANT,
+       [0x28] = INAT_VARIANT,
+       [0x29] = INAT_VARIANT,
+       [0x2a] = INAT_VARIANT,
+       [0x2b] = INAT_VARIANT,
+       [0x2c] = INAT_VARIANT,
+       [0x2d] = INAT_VARIANT,
+       [0x2e] = INAT_VARIANT,
+       [0x2f] = INAT_VARIANT,
+       [0x30] = INAT_VARIANT,
+       [0x31] = INAT_VARIANT,
+       [0x32] = INAT_VARIANT,
+       [0x33] = INAT_VARIANT,
+       [0x34] = INAT_VARIANT,
+       [0x35] = INAT_VARIANT,
+       [0x36] = INAT_VARIANT,
+       [0x37] = INAT_VARIANT,
+       [0x38] = INAT_VARIANT,
+       [0x39] = INAT_VARIANT,
+       [0x3a] = INAT_VARIANT,
+       [0x3b] = INAT_VARIANT,
+       [0x3c] = INAT_VARIANT,
+       [0x3d] = INAT_VARIANT,
+       [0x3e] = INAT_VARIANT,
+       [0x3f] = INAT_VARIANT,
+       [0x40] = INAT_VARIANT,
+       [0x41] = INAT_VARIANT,
+       [0x45] = INAT_VARIANT,
+       [0x46] = INAT_VARIANT,
+       [0x47] = INAT_VARIANT,
+       [0x58] = INAT_VARIANT,
+       [0x59] = INAT_VARIANT,
+       [0x5a] = INAT_VARIANT,
+       [0x78] = INAT_VARIANT,
+       [0x79] = INAT_VARIANT,
+       [0x80] = INAT_VARIANT,
+       [0x81] = INAT_VARIANT,
+       [0x82] = INAT_VARIANT,
+       [0x8c] = INAT_VARIANT,
+       [0x8e] = INAT_VARIANT,
+       [0x90] = INAT_VARIANT,
+       [0x91] = INAT_VARIANT,
+       [0x92] = INAT_VARIANT,
+       [0x93] = INAT_VARIANT,
+       [0x96] = INAT_VARIANT,
+       [0x97] = INAT_VARIANT,
+       [0x98] = INAT_VARIANT,
+       [0x99] = INAT_VARIANT,
+       [0x9a] = INAT_VARIANT,
+       [0x9b] = INAT_VARIANT,
+       [0x9c] = INAT_VARIANT,
+       [0x9d] = INAT_VARIANT,
+       [0x9e] = INAT_VARIANT,
+       [0x9f] = INAT_VARIANT,
+       [0xa6] = INAT_VARIANT,
+       [0xa7] = INAT_VARIANT,
+       [0xa8] = INAT_VARIANT,
+       [0xa9] = INAT_VARIANT,
+       [0xaa] = INAT_VARIANT,
+       [0xab] = INAT_VARIANT,
+       [0xac] = INAT_VARIANT,
+       [0xad] = INAT_VARIANT,
+       [0xae] = INAT_VARIANT,
+       [0xaf] = INAT_VARIANT,
+       [0xb6] = INAT_VARIANT,
+       [0xb7] = INAT_VARIANT,
+       [0xb8] = INAT_VARIANT,
+       [0xb9] = INAT_VARIANT,
+       [0xba] = INAT_VARIANT,
+       [0xbb] = INAT_VARIANT,
+       [0xbc] = INAT_VARIANT,
+       [0xbd] = INAT_VARIANT,
+       [0xbe] = INAT_VARIANT,
+       [0xbf] = INAT_VARIANT,
+       [0xdb] = INAT_VARIANT,
+       [0xdc] = INAT_VARIANT,
+       [0xdd] = INAT_VARIANT,
+       [0xde] = INAT_VARIANT,
+       [0xdf] = INAT_VARIANT,
+       [0xf0] = INAT_MODRM | INAT_MODRM | INAT_VARIANT,
+       [0xf1] = INAT_MODRM | INAT_MODRM | INAT_VARIANT,
+       [0xf2] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY,
+       [0xf3] = INAT_MAKE_GROUP(23),
+       [0xf5] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY | INAT_VARIANT,
+       [0xf6] = INAT_VARIANT,
+       [0xf7] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY | INAT_VARIANT,
+};
+const insn_attr_t inat_escape_table_2_1[INAT_OPCODE_TABLE_SIZE] = {
+       [0x00] = INAT_MODRM | INAT_VEXOK,
+       [0x01] = INAT_MODRM | INAT_VEXOK,
+       [0x02] = INAT_MODRM | INAT_VEXOK,
+       [0x03] = INAT_MODRM | INAT_VEXOK,
+       [0x04] = INAT_MODRM | INAT_VEXOK,
+       [0x05] = INAT_MODRM | INAT_VEXOK,
+       [0x06] = INAT_MODRM | INAT_VEXOK,
+       [0x07] = INAT_MODRM | INAT_VEXOK,
+       [0x08] = INAT_MODRM | INAT_VEXOK,
+       [0x09] = INAT_MODRM | INAT_VEXOK,
+       [0x0a] = INAT_MODRM | INAT_VEXOK,
+       [0x0b] = INAT_MODRM | INAT_VEXOK,
+       [0x0c] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY,
+       [0x0d] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY,
+       [0x0e] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY,
+       [0x0f] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY,
+       [0x10] = INAT_MODRM,
+       [0x13] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK | INAT_VEXONLY,
+       [0x14] = INAT_MODRM,
+       [0x15] = INAT_MODRM,
+       [0x16] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY,
+       [0x17] = INAT_MODRM | INAT_VEXOK,
+       [0x18] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY,
+       [0x19] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY,
+       [0x1a] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY,
+       [0x1c] = INAT_MODRM | INAT_VEXOK,
+       [0x1d] = INAT_MODRM | INAT_VEXOK,
+       [0x1e] = INAT_MODRM | INAT_VEXOK,
+       [0x20] = INAT_MODRM | INAT_VEXOK,
+       [0x21] = INAT_MODRM | INAT_VEXOK,
+       [0x22] = INAT_MODRM | INAT_VEXOK,
+       [0x23] = INAT_MODRM | INAT_VEXOK,
+       [0x24] = INAT_MODRM | INAT_VEXOK,
+       [0x25] = INAT_MODRM | INAT_VEXOK,
+       [0x28] = INAT_MODRM | INAT_VEXOK,
+       [0x29] = INAT_MODRM | INAT_VEXOK,
+       [0x2a] = INAT_MODRM | INAT_VEXOK,
+       [0x2b] = INAT_MODRM | INAT_VEXOK,
+       [0x2c] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY,
+       [0x2d] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY,
+       [0x2e] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY,
+       [0x2f] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY,
+       [0x30] = INAT_MODRM | INAT_VEXOK,
+       [0x31] = INAT_MODRM | INAT_VEXOK,
+       [0x32] = INAT_MODRM | INAT_VEXOK,
+       [0x33] = INAT_MODRM | INAT_VEXOK,
+       [0x34] = INAT_MODRM | INAT_VEXOK,
+       [0x35] = INAT_MODRM | INAT_VEXOK,
+       [0x36] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY,
+       [0x37] = INAT_MODRM | INAT_VEXOK,
+       [0x38] = INAT_MODRM | INAT_VEXOK,
+       [0x39] = INAT_MODRM | INAT_VEXOK,
+       [0x3a] = INAT_MODRM | INAT_VEXOK,
+       [0x3b] = INAT_MODRM | INAT_VEXOK,
+       [0x3c] = INAT_MODRM | INAT_VEXOK,
+       [0x3d] = INAT_MODRM | INAT_VEXOK,
+       [0x3e] = INAT_MODRM | INAT_VEXOK,
+       [0x3f] = INAT_MODRM | INAT_VEXOK,
+       [0x40] = INAT_MODRM | INAT_VEXOK,
+       [0x41] = INAT_MODRM | INAT_VEXOK,
+       [0x45] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY,
+       [0x46] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY,
+       [0x47] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY,
+       [0x58] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY,
+       [0x59] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY,
+       [0x5a] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY,
+       [0x78] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY,
+       [0x79] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY,
+       [0x80] = INAT_MODRM,
+       [0x81] = INAT_MODRM,
+       [0x82] = INAT_MODRM,
+       [0x8c] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY,
+       [0x8e] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY,
+       [0x90] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY,
+       [0x91] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY,
+       [0x92] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY,
+       [0x93] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY,
+       [0x96] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY,
+       [0x97] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY,
+       [0x98] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY,
+       [0x99] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY,
+       [0x9a] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY,
+       [0x9b] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY,
+       [0x9c] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY,
+       [0x9d] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY,
+       [0x9e] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY,
+       [0x9f] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY,
+       [0xa6] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY,
+       [0xa7] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY,
+       [0xa8] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY,
+       [0xa9] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY,
+       [0xaa] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY,
+       [0xab] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY,
+       [0xac] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY,
+       [0xad] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY,
+       [0xae] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY,
+       [0xaf] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY,
+       [0xb6] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY,
+       [0xb7] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY,
+       [0xb8] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY,
+       [0xb9] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY,
+       [0xba] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY,
+       [0xbb] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY,
+       [0xbc] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY,
+       [0xbd] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY,
+       [0xbe] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY,
+       [0xbf] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY,
+       [0xdb] = INAT_MODRM | INAT_VEXOK,
+       [0xdc] = INAT_MODRM | INAT_VEXOK,
+       [0xdd] = INAT_MODRM | INAT_VEXOK,
+       [0xde] = INAT_MODRM | INAT_VEXOK,
+       [0xdf] = INAT_MODRM | INAT_VEXOK,
+       [0xf0] = INAT_MODRM,
+       [0xf1] = INAT_MODRM,
+       [0xf6] = INAT_MODRM,
+       [0xf7] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY,
+};
+const insn_attr_t inat_escape_table_2_2[INAT_OPCODE_TABLE_SIZE] = {
+       [0xf5] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY,
+       [0xf6] = INAT_MODRM,
+       [0xf7] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY,
+};
+const insn_attr_t inat_escape_table_2_3[INAT_OPCODE_TABLE_SIZE] = {
+       [0xf0] = INAT_MODRM | INAT_MODRM,
+       [0xf1] = INAT_MODRM | INAT_MODRM,
+       [0xf5] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY,
+       [0xf6] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY,
+       [0xf7] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY,
+};
+
+/* Table: 3-byte opcode 2 (0x0f 0x3a) */
+const insn_attr_t inat_escape_table_3[INAT_OPCODE_TABLE_SIZE] = {
+       [0x00] = INAT_VARIANT,
+       [0x01] = INAT_VARIANT,
+       [0x02] = INAT_VARIANT,
+       [0x04] = INAT_VARIANT,
+       [0x05] = INAT_VARIANT,
+       [0x06] = INAT_VARIANT,
+       [0x08] = INAT_VARIANT,
+       [0x09] = INAT_VARIANT,
+       [0x0a] = INAT_VARIANT,
+       [0x0b] = INAT_VARIANT,
+       [0x0c] = INAT_VARIANT,
+       [0x0d] = INAT_VARIANT,
+       [0x0e] = INAT_VARIANT,
+       [0x0f] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VARIANT,
+       [0x14] = INAT_VARIANT,
+       [0x15] = INAT_VARIANT,
+       [0x16] = INAT_VARIANT,
+       [0x17] = INAT_VARIANT,
+       [0x18] = INAT_VARIANT,
+       [0x19] = INAT_VARIANT,
+       [0x1d] = INAT_VARIANT,
+       [0x20] = INAT_VARIANT,
+       [0x21] = INAT_VARIANT,
+       [0x22] = INAT_VARIANT,
+       [0x38] = INAT_VARIANT,
+       [0x39] = INAT_VARIANT,
+       [0x40] = INAT_VARIANT,
+       [0x41] = INAT_VARIANT,
+       [0x42] = INAT_VARIANT,
+       [0x44] = INAT_VARIANT,
+       [0x46] = INAT_VARIANT,
+       [0x4a] = INAT_VARIANT,
+       [0x4b] = INAT_VARIANT,
+       [0x4c] = INAT_VARIANT,
+       [0x60] = INAT_VARIANT,
+       [0x61] = INAT_VARIANT,
+       [0x62] = INAT_VARIANT,
+       [0x63] = INAT_VARIANT,
+       [0xdf] = INAT_VARIANT,
+       [0xf0] = INAT_VARIANT,
+};
+const insn_attr_t inat_escape_table_3_1[INAT_OPCODE_TABLE_SIZE] = {
+       [0x00] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK | INAT_VEXONLY,
+       [0x01] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK | INAT_VEXONLY,
+       [0x02] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK | INAT_VEXONLY,
+       [0x04] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK | INAT_VEXONLY,
+       [0x05] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK | INAT_VEXONLY,
+       [0x06] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK | INAT_VEXONLY,
+       [0x08] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK,
+       [0x09] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK,
+       [0x0a] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK,
+       [0x0b] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK,
+       [0x0c] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK,
+       [0x0d] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK,
+       [0x0e] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK,
+       [0x0f] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK,
+       [0x14] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK,
+       [0x15] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK,
+       [0x16] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK,
+       [0x17] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK,
+       [0x18] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK | INAT_VEXONLY,
+       [0x19] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK | INAT_VEXONLY,
+       [0x1d] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK | INAT_VEXONLY,
+       [0x20] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK,
+       [0x21] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK,
+       [0x22] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK,
+       [0x38] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK | INAT_VEXONLY,
+       [0x39] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK | INAT_VEXONLY,
+       [0x40] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK,
+       [0x41] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK,
+       [0x42] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK,
+       [0x44] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK,
+       [0x46] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK | INAT_VEXONLY,
+       [0x4a] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK | INAT_VEXONLY,
+       [0x4b] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK | INAT_VEXONLY,
+       [0x4c] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK,
+       [0x60] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK,
+       [0x61] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK,
+       [0x62] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK,
+       [0x63] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK,
+       [0xdf] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK,
+};
+const insn_attr_t inat_escape_table_3_3[INAT_OPCODE_TABLE_SIZE] = {
+       [0xf0] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK | INAT_VEXONLY,
+};
+
+/* GrpTable: Grp1 */
+
+/* GrpTable: Grp1A */
+
+/* GrpTable: Grp2 */
+
+/* GrpTable: Grp3_1 */
+const insn_attr_t inat_group_table_6[INAT_GROUP_TABLE_SIZE] = {
+       [0x0] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM,
+       [0x2] = INAT_MODRM,
+       [0x3] = INAT_MODRM,
+       [0x4] = INAT_MODRM,
+       [0x5] = INAT_MODRM,
+       [0x6] = INAT_MODRM,
+       [0x7] = INAT_MODRM,
+};
+
+/* GrpTable: Grp3_2 */
+const insn_attr_t inat_group_table_7[INAT_GROUP_TABLE_SIZE] = {
+       [0x0] = INAT_MAKE_IMM(INAT_IMM_VWORD32) | INAT_MODRM,
+       [0x2] = INAT_MODRM,
+       [0x3] = INAT_MODRM,
+       [0x4] = INAT_MODRM,
+       [0x5] = INAT_MODRM,
+       [0x6] = INAT_MODRM,
+       [0x7] = INAT_MODRM,
+};
+
+/* GrpTable: Grp4 */
+const insn_attr_t inat_group_table_8[INAT_GROUP_TABLE_SIZE] = {
+       [0x0] = INAT_MODRM,
+       [0x1] = INAT_MODRM,
+};
+
+/* GrpTable: Grp5 */
+const insn_attr_t inat_group_table_9[INAT_GROUP_TABLE_SIZE] = {
+       [0x0] = INAT_MODRM,
+       [0x1] = INAT_MODRM,
+       [0x2] = INAT_MODRM | INAT_FORCE64,
+       [0x3] = INAT_MODRM,
+       [0x4] = INAT_MODRM | INAT_FORCE64,
+       [0x5] = INAT_MODRM,
+       [0x6] = INAT_MODRM | INAT_FORCE64,
+};
+
+/* GrpTable: Grp6 */
+const insn_attr_t inat_group_table_10[INAT_GROUP_TABLE_SIZE] = {
+       [0x0] = INAT_MODRM,
+       [0x1] = INAT_MODRM,
+       [0x2] = INAT_MODRM,
+       [0x3] = INAT_MODRM,
+       [0x4] = INAT_MODRM,
+       [0x5] = INAT_MODRM,
+};
+
+/* GrpTable: Grp7 */
+const insn_attr_t inat_group_table_11[INAT_GROUP_TABLE_SIZE] = {
+       [0x0] = INAT_MODRM,
+       [0x1] = INAT_MODRM,
+       [0x2] = INAT_MODRM,
+       [0x3] = INAT_MODRM,
+       [0x4] = INAT_MODRM,
+       [0x6] = INAT_MODRM,
+       [0x7] = INAT_MODRM,
+};
+
+/* GrpTable: Grp8 */
+
+/* GrpTable: Grp9 */
+const insn_attr_t inat_group_table_22[INAT_GROUP_TABLE_SIZE] = {
+       [0x1] = INAT_MODRM,
+       [0x6] = INAT_MODRM | INAT_MODRM | INAT_VARIANT,
+       [0x7] = INAT_MODRM | INAT_MODRM | INAT_VARIANT,
+};
+const insn_attr_t inat_group_table_22_1[INAT_GROUP_TABLE_SIZE] = {
+       [0x6] = INAT_MODRM,
+};
+const insn_attr_t inat_group_table_22_2[INAT_GROUP_TABLE_SIZE] = {
+       [0x6] = INAT_MODRM,
+       [0x7] = INAT_MODRM,
+};
+
+/* GrpTable: Grp10 */
+
+/* GrpTable: Grp11A */
+const insn_attr_t inat_group_table_4[INAT_GROUP_TABLE_SIZE] = {
+       [0x0] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM,
+       [0x7] = INAT_MAKE_IMM(INAT_IMM_BYTE),
+};
+
+/* GrpTable: Grp11B */
+const insn_attr_t inat_group_table_5[INAT_GROUP_TABLE_SIZE] = {
+       [0x0] = INAT_MAKE_IMM(INAT_IMM_VWORD32) | INAT_MODRM,
+       [0x7] = INAT_MAKE_IMM(INAT_IMM_VWORD32),
+};
+
+/* GrpTable: Grp12 */
+const insn_attr_t inat_group_table_14[INAT_GROUP_TABLE_SIZE] = {
+       [0x2] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VARIANT,
+       [0x4] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VARIANT,
+       [0x6] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VARIANT,
+};
+const insn_attr_t inat_group_table_14_1[INAT_GROUP_TABLE_SIZE] = {
+       [0x2] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK,
+       [0x4] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK,
+       [0x6] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK,
+};
+
+/* GrpTable: Grp13 */
+const insn_attr_t inat_group_table_15[INAT_GROUP_TABLE_SIZE] = {
+       [0x2] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VARIANT,
+       [0x4] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VARIANT,
+       [0x6] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VARIANT,
+};
+const insn_attr_t inat_group_table_15_1[INAT_GROUP_TABLE_SIZE] = {
+       [0x2] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK,
+       [0x4] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK,
+       [0x6] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK,
+};
+
+/* GrpTable: Grp14 */
+const insn_attr_t inat_group_table_16[INAT_GROUP_TABLE_SIZE] = {
+       [0x2] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VARIANT,
+       [0x3] = INAT_VARIANT,
+       [0x6] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VARIANT,
+       [0x7] = INAT_VARIANT,
+};
+const insn_attr_t inat_group_table_16_1[INAT_GROUP_TABLE_SIZE] = {
+       [0x2] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK,
+       [0x3] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK,
+       [0x6] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK,
+       [0x7] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK,
+};
+
+/* GrpTable: Grp15 */
+const insn_attr_t inat_group_table_19[INAT_GROUP_TABLE_SIZE] = {
+       [0x0] = INAT_VARIANT,
+       [0x1] = INAT_VARIANT,
+       [0x2] = INAT_MODRM | INAT_VEXOK | INAT_VARIANT,
+       [0x3] = INAT_MODRM | INAT_VEXOK | INAT_VARIANT,
+};
+const insn_attr_t inat_group_table_19_2[INAT_GROUP_TABLE_SIZE] = {
+       [0x0] = INAT_MODRM,
+       [0x1] = INAT_MODRM,
+       [0x2] = INAT_MODRM,
+       [0x3] = INAT_MODRM,
+};
+
+/* GrpTable: Grp16 */
+const insn_attr_t inat_group_table_13[INAT_GROUP_TABLE_SIZE] = {
+       [0x0] = INAT_MODRM,
+       [0x1] = INAT_MODRM,
+       [0x2] = INAT_MODRM,
+       [0x3] = INAT_MODRM,
+};
+
+/* GrpTable: Grp17 */
+const insn_attr_t inat_group_table_23[INAT_GROUP_TABLE_SIZE] = {
+       [0x1] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY,
+       [0x2] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY,
+       [0x3] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY,
+};
+
+/* GrpTable: GrpP */
+
+/* GrpTable: GrpPDLK */
+
+/* GrpTable: GrpRNG */
+
+/* Escape opcode map array */
+const insn_attr_t * const inat_escape_tables[INAT_ESC_MAX + 1][INAT_LSTPFX_MAX + 1] = {
+       [1][0] = inat_escape_table_1,
+       [1][1] = inat_escape_table_1_1,
+       [1][2] = inat_escape_table_1_2,
+       [1][3] = inat_escape_table_1_3,
+       [2][0] = inat_escape_table_2,
+       [2][1] = inat_escape_table_2_1,
+       [2][2] = inat_escape_table_2_2,
+       [2][3] = inat_escape_table_2_3,
+       [3][0] = inat_escape_table_3,
+       [3][1] = inat_escape_table_3_1,
+       [3][3] = inat_escape_table_3_3,
+};
+
+/* Group opcode map array */
+const insn_attr_t * const inat_group_tables[INAT_GRP_MAX + 1][INAT_LSTPFX_MAX + 1] = {
+       [4][0] = inat_group_table_4,
+       [5][0] = inat_group_table_5,
+       [6][0] = inat_group_table_6,
+       [7][0] = inat_group_table_7,
+       [8][0] = inat_group_table_8,
+       [9][0] = inat_group_table_9,
+       [10][0] = inat_group_table_10,
+       [11][0] = inat_group_table_11,
+       [13][0] = inat_group_table_13,
+       [14][0] = inat_group_table_14,
+       [14][1] = inat_group_table_14_1,
+       [15][0] = inat_group_table_15,
+       [15][1] = inat_group_table_15_1,
+       [16][0] = inat_group_table_16,
+       [16][1] = inat_group_table_16_1,
+       [19][0] = inat_group_table_19,
+       [19][2] = inat_group_table_19_2,
+       [22][0] = inat_group_table_22,
+       [22][1] = inat_group_table_22_1,
+       [22][2] = inat_group_table_22_2,
+       [23][0] = inat_group_table_23,
+};
+
+/* AVX opcode map array */
+const insn_attr_t * const inat_avx_tables[X86_VEX_M_MAX + 1][INAT_LSTPFX_MAX + 1] = {
+       [1][0] = inat_escape_table_1,
+       [1][1] = inat_escape_table_1_1,
+       [1][2] = inat_escape_table_1_2,
+       [1][3] = inat_escape_table_1_3,
+       [2][0] = inat_escape_table_2,
+       [2][1] = inat_escape_table_2_1,
+       [2][2] = inat_escape_table_2_2,
+       [2][3] = inat_escape_table_2_3,
+       [3][0] = inat_escape_table_3,
+       [3][1] = inat_escape_table_3_1,
+       [3][3] = inat_escape_table_3_3,
+};
diff --git a/insn/inat.c b/insn/inat.c
new file mode 100644 (file)
index 0000000..c1f01a8
--- /dev/null
@@ -0,0 +1,97 @@
+/*
+ * x86 instruction attribute tables
+ *
+ * Written by Masami Hiramatsu <mhiramat@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+#include <asm/insn.h>
+
+/* Attribute tables are generated from opcode map */
+#include "inat-tables.c"
+
+/* Attribute search APIs */
+insn_attr_t inat_get_opcode_attribute(insn_byte_t opcode)
+{
+       return inat_primary_table[opcode];
+}
+
+int inat_get_last_prefix_id(insn_byte_t last_pfx)
+{
+       insn_attr_t lpfx_attr;
+
+       lpfx_attr = inat_get_opcode_attribute(last_pfx);
+       return inat_last_prefix_id(lpfx_attr);
+}
+
+insn_attr_t inat_get_escape_attribute(insn_byte_t opcode, int lpfx_id,
+                                     insn_attr_t esc_attr)
+{
+       const insn_attr_t *table;
+       int n;
+
+       n = inat_escape_id(esc_attr);
+
+       table = inat_escape_tables[n][0];
+       if (!table)
+               return 0;
+       if (inat_has_variant(table[opcode]) && lpfx_id) {
+               table = inat_escape_tables[n][lpfx_id];
+               if (!table)
+                       return 0;
+       }
+       return table[opcode];
+}
+
+insn_attr_t inat_get_group_attribute(insn_byte_t modrm, int lpfx_id,
+                                    insn_attr_t grp_attr)
+{
+       const insn_attr_t *table;
+       int n;
+
+       n = inat_group_id(grp_attr);
+
+       table = inat_group_tables[n][0];
+       if (!table)
+               return inat_group_common_attribute(grp_attr);
+       if (inat_has_variant(table[X86_MODRM_REG(modrm)]) && lpfx_id) {
+               table = inat_group_tables[n][lpfx_id];
+               if (!table)
+                       return inat_group_common_attribute(grp_attr);
+       }
+       return table[X86_MODRM_REG(modrm)] |
+              inat_group_common_attribute(grp_attr);
+}
+
+insn_attr_t inat_get_avx_attribute(insn_byte_t opcode, insn_byte_t vex_m,
+                                  insn_byte_t vex_p)
+{
+       const insn_attr_t *table;
+       if (vex_m > X86_VEX_M_MAX || vex_p > INAT_LSTPFX_MAX)
+               return 0;
+       /* At first, this checks the master table */
+       table = inat_avx_tables[vex_m][0];
+       if (!table)
+               return 0;
+       if (!inat_is_group(table[opcode]) && vex_p) {
+               /* If this is not a group, get attribute directly */
+               table = inat_avx_tables[vex_m][vex_p];
+               if (!table)
+                       return 0;
+       }
+       return table[opcode];
+}
+
diff --git a/insn/insn.c b/insn/insn.c
new file mode 100644 (file)
index 0000000..7acdc96
--- /dev/null
@@ -0,0 +1,582 @@
+/*
+ * x86 instruction analysis
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Copyright (C) IBM Corporation, 2002, 2004, 2009
+ */
+
+#ifdef __KERNEL__
+#include <linux/string.h>
+#else
+#include <string.h>
+#endif
+#include <asm/inat.h>
+#include <asm/insn.h>
+
+#define unlikely(a) a
+
+/* Verify next sizeof(t) bytes can be on the same instruction */
+#define validate_next(t, insn, n)      \
+       ((insn)->next_byte + sizeof(t) + n - (insn)->kaddr <= MAX_INSN_SIZE)
+
+#define __get_next(t, insn)    \
+       ({ t r = *(t*)insn->next_byte; insn->next_byte += sizeof(t); r; })
+
+#define __peek_nbyte_next(t, insn, n)  \
+       ({ t r = *(t*)((insn)->next_byte + n); r; })
+
+#define get_next(t, insn)      \
+       ({ if (unlikely(!validate_next(t, insn, 0))) goto err_out; __get_next(t, insn); })
+
+#define peek_nbyte_next(t, insn, n)    \
+       ({ if (unlikely(!validate_next(t, insn, n))) goto err_out; __peek_nbyte_next(t, insn, n); })
+
+#define peek_next(t, insn)     peek_nbyte_next(t, insn, 0)
+
+/**
+ * insn_init() - initialize struct insn
+ * @insn:      &struct insn to be initialized
+ * @kaddr:     address (in kernel memory) of instruction (or copy thereof)
+ * @x86_64:    !0 for 64-bit kernel or 64-bit app
+ */
+void insn_init(struct insn *insn, const void *kaddr, int x86_64)
+{
+       memset(insn, 0, sizeof(*insn));
+       insn->kaddr = kaddr;
+       insn->next_byte = kaddr;
+       insn->x86_64 = x86_64 ? 1 : 0;
+       insn->opnd_bytes = 4;
+       if (x86_64)
+               insn->addr_bytes = 8;
+       else
+               insn->addr_bytes = 4;
+}
+
+/**
+ * insn_get_prefixes - scan x86 instruction prefix bytes
+ * @insn:      &struct insn containing instruction
+ *
+ * Populates the @insn->prefixes bitmap, and updates @insn->next_byte
+ * to point to the (first) opcode.  No effect if @insn->prefixes.got
+ * is already set.
+ */
+void insn_get_prefixes(struct insn *insn)
+{
+       struct insn_field *prefixes = &insn->prefixes;
+       insn_attr_t attr;
+       insn_byte_t b, lb;
+       int i, nb;
+
+       if (prefixes->got)
+               return;
+
+       nb = 0;
+       lb = 0;
+       b = peek_next(insn_byte_t, insn);
+       attr = inat_get_opcode_attribute(b);
+       while (inat_is_legacy_prefix(attr)) {
+               /* Skip if same prefix */
+               for (i = 0; i < nb; i++)
+                       if (prefixes->bytes[i] == b)
+                               goto found;
+               if (nb == 4)
+                       /* Invalid instruction */
+                       break;
+               prefixes->bytes[nb++] = b;
+               if (inat_is_address_size_prefix(attr)) {
+                       /* address size switches 2/4 or 4/8 */
+                       if (insn->x86_64)
+                               insn->addr_bytes ^= 12;
+                       else
+                               insn->addr_bytes ^= 6;
+               } else if (inat_is_operand_size_prefix(attr)) {
+                       /* oprand size switches 2/4 */
+                       insn->opnd_bytes ^= 6;
+               }
+found:
+               prefixes->nbytes++;
+               insn->next_byte++;
+               lb = b;
+               b = peek_next(insn_byte_t, insn);
+               attr = inat_get_opcode_attribute(b);
+       }
+       /* Set the last prefix */
+       if (lb && lb != insn->prefixes.bytes[3]) {
+               if (unlikely(insn->prefixes.bytes[3])) {
+                       /* Swap the last prefix */
+                       b = insn->prefixes.bytes[3];
+                       for (i = 0; i < nb; i++)
+                               if (prefixes->bytes[i] == lb)
+                                       prefixes->bytes[i] = b;
+               }
+               insn->prefixes.bytes[3] = lb;
+       }
+
+       /* Decode REX prefix */
+       if (insn->x86_64) {
+               b = peek_next(insn_byte_t, insn);
+               attr = inat_get_opcode_attribute(b);
+               if (inat_is_rex_prefix(attr)) {
+                       insn->rex_prefix.value = b;
+                       insn->rex_prefix.nbytes = 1;
+                       insn->next_byte++;
+                       if (X86_REX_W(b))
+                               /* REX.W overrides opnd_size */
+                               insn->opnd_bytes = 8;
+               }
+       }
+       insn->rex_prefix.got = 1;
+
+       /* Decode VEX prefix */
+       b = peek_next(insn_byte_t, insn);
+       attr = inat_get_opcode_attribute(b);
+       if (inat_is_vex_prefix(attr)) {
+               insn_byte_t b2 = peek_nbyte_next(insn_byte_t, insn, 1);
+               if (!insn->x86_64) {
+                       /*
+                        * In 32-bits mode, if the [7:6] bits (mod bits of
+                        * ModRM) on the second byte are not 11b, it is
+                        * LDS or LES.
+                        */
+                       if (X86_MODRM_MOD(b2) != 3)
+                               goto vex_end;
+               }
+               insn->vex_prefix.bytes[0] = b;
+               insn->vex_prefix.bytes[1] = b2;
+               if (inat_is_vex3_prefix(attr)) {
+                       b2 = peek_nbyte_next(insn_byte_t, insn, 2);
+                       insn->vex_prefix.bytes[2] = b2;
+                       insn->vex_prefix.nbytes = 3;
+                       insn->next_byte += 3;
+                       if (insn->x86_64 && X86_VEX_W(b2))
+                               /* VEX.W overrides opnd_size */
+                               insn->opnd_bytes = 8;
+               } else {
+                       insn->vex_prefix.nbytes = 2;
+                       insn->next_byte += 2;
+               }
+       }
+vex_end:
+       insn->vex_prefix.got = 1;
+
+       prefixes->got = 1;
+
+err_out:
+       return;
+}
+
+/**
+ * insn_get_opcode - collect opcode(s)
+ * @insn:      &struct insn containing instruction
+ *
+ * Populates @insn->opcode, updates @insn->next_byte to point past the
+ * opcode byte(s), and set @insn->attr (except for groups).
+ * If necessary, first collects any preceding (prefix) bytes.
+ * Sets @insn->opcode.value = opcode1.  No effect if @insn->opcode.got
+ * is already 1.
+ */
+void insn_get_opcode(struct insn *insn)
+{
+       struct insn_field *opcode = &insn->opcode;
+       insn_byte_t op;
+       int pfx_id;
+       if (opcode->got)
+               return;
+       if (!insn->prefixes.got)
+               insn_get_prefixes(insn);
+
+       /* Get first opcode */
+       op = get_next(insn_byte_t, insn);
+       opcode->bytes[0] = op;
+       opcode->nbytes = 1;
+
+       /* Check if there is VEX prefix or not */
+       if (insn_is_avx(insn)) {
+               insn_byte_t m, p;
+               m = insn_vex_m_bits(insn);
+               p = insn_vex_p_bits(insn);
+               insn->attr = inat_get_avx_attribute(op, m, p);
+               if (!inat_accept_vex(insn->attr) && !inat_is_group(insn->attr))
+                       insn->attr = 0; /* This instruction is bad */
+               goto end;       /* VEX has only 1 byte for opcode */
+       }
+
+       insn->attr = inat_get_opcode_attribute(op);
+       while (inat_is_escape(insn->attr)) {
+               /* Get escaped opcode */
+               op = get_next(insn_byte_t, insn);
+               opcode->bytes[opcode->nbytes++] = op;
+               pfx_id = insn_last_prefix_id(insn);
+               insn->attr = inat_get_escape_attribute(op, pfx_id, insn->attr);
+       }
+       if (inat_must_vex(insn->attr))
+               insn->attr = 0; /* This instruction is bad */
+end:
+       opcode->got = 1;
+
+err_out:
+       return;
+}
+
+/**
+ * insn_get_modrm - collect ModRM byte, if any
+ * @insn:      &struct insn containing instruction
+ *
+ * Populates @insn->modrm and updates @insn->next_byte to point past the
+ * ModRM byte, if any.  If necessary, first collects the preceding bytes
+ * (prefixes and opcode(s)).  No effect if @insn->modrm.got is already 1.
+ */
+void insn_get_modrm(struct insn *insn)
+{
+       struct insn_field *modrm = &insn->modrm;
+       insn_byte_t pfx_id, mod;
+       if (modrm->got)
+               return;
+       if (!insn->opcode.got)
+               insn_get_opcode(insn);
+
+       if (inat_has_modrm(insn->attr)) {
+               mod = get_next(insn_byte_t, insn);
+               modrm->value = mod;
+               modrm->nbytes = 1;
+               if (inat_is_group(insn->attr)) {
+                       pfx_id = insn_last_prefix_id(insn);
+                       insn->attr = inat_get_group_attribute(mod, pfx_id,
+                                                             insn->attr);
+                       if (insn_is_avx(insn) && !inat_accept_vex(insn->attr))
+                               insn->attr = 0; /* This is bad */
+               }
+       }
+
+       if (insn->x86_64 && inat_is_force64(insn->attr))
+               insn->opnd_bytes = 8;
+       modrm->got = 1;
+
+err_out:
+       return;
+}
+
+
+/**
+ * insn_rip_relative() - Does instruction use RIP-relative addressing mode?
+ * @insn:      &struct insn containing instruction
+ *
+ * If necessary, first collects the instruction up to and including the
+ * ModRM byte.  No effect if @insn->x86_64 is 0.
+ */
+int insn_rip_relative(struct insn *insn)
+{
+       struct insn_field *modrm = &insn->modrm;
+
+       if (!insn->x86_64)
+               return 0;
+       if (!modrm->got)
+               insn_get_modrm(insn);
+       /*
+        * For rip-relative instructions, the mod field (top 2 bits)
+        * is zero and the r/m field (bottom 3 bits) is 0x5.
+        */
+       return (modrm->nbytes && (modrm->value & 0xc7) == 0x5);
+}
+
+/**
+ * insn_get_sib() - Get the SIB byte of instruction
+ * @insn:      &struct insn containing instruction
+ *
+ * If necessary, first collects the instruction up to and including the
+ * ModRM byte.
+ */
+void insn_get_sib(struct insn *insn)
+{
+       insn_byte_t modrm;
+
+       if (insn->sib.got)
+               return;
+       if (!insn->modrm.got)
+               insn_get_modrm(insn);
+       if (insn->modrm.nbytes) {
+               modrm = (insn_byte_t)insn->modrm.value;
+               if (insn->addr_bytes != 2 &&
+                   X86_MODRM_MOD(modrm) != 3 && X86_MODRM_RM(modrm) == 4) {
+                       insn->sib.value = get_next(insn_byte_t, insn);
+                       insn->sib.nbytes = 1;
+               }
+       }
+       insn->sib.got = 1;
+
+err_out:
+       return;
+}
+
+
+/**
+ * insn_get_displacement() - Get the displacement of instruction
+ * @insn:      &struct insn containing instruction
+ *
+ * If necessary, first collects the instruction up to and including the
+ * SIB byte.
+ * Displacement value is sign-expanded.
+ */
+void insn_get_displacement(struct insn *insn)
+{
+       insn_byte_t mod, rm, base;
+
+       if (insn->displacement.got)
+               return;
+       if (!insn->sib.got)
+               insn_get_sib(insn);
+       if (insn->modrm.nbytes) {
+               /*
+                * Interpreting the modrm byte:
+                * mod = 00 - no displacement fields (exceptions below)
+                * mod = 01 - 1-byte displacement field
+                * mod = 10 - displacement field is 4 bytes, or 2 bytes if
+                *      address size = 2 (0x67 prefix in 32-bit mode)
+                * mod = 11 - no memory operand
+                *
+                * If address size = 2...
+                * mod = 00, r/m = 110 - displacement field is 2 bytes
+                *
+                * If address size != 2...
+                * mod != 11, r/m = 100 - SIB byte exists
+                * mod = 00, SIB base = 101 - displacement field is 4 bytes
+                * mod = 00, r/m = 101 - rip-relative addressing, displacement
+                *      field is 4 bytes
+                */
+               mod = X86_MODRM_MOD(insn->modrm.value);
+               rm = X86_MODRM_RM(insn->modrm.value);
+               base = X86_SIB_BASE(insn->sib.value);
+               if (mod == 3)
+                       goto out;
+               if (mod == 1) {
+                       insn->displacement.value = get_next(char, insn);
+                       insn->displacement.nbytes = 1;
+               } else if (insn->addr_bytes == 2) {
+                       if ((mod == 0 && rm == 6) || mod == 2) {
+                               insn->displacement.value =
+                                        get_next(short, insn);
+                               insn->displacement.nbytes = 2;
+                       }
+               } else {
+                       if ((mod == 0 && rm == 5) || mod == 2 ||
+                           (mod == 0 && base == 5)) {
+                               insn->displacement.value = get_next(int, insn);
+                               insn->displacement.nbytes = 4;
+                       }
+               }
+       }
+out:
+       insn->displacement.got = 1;
+
+err_out:
+       return;
+}
+
+/* Decode moffset16/32/64. Return 0 if failed */
+static int __get_moffset(struct insn *insn)
+{
+       switch (insn->addr_bytes) {
+       case 2:
+               insn->moffset1.value = get_next(short, insn);
+               insn->moffset1.nbytes = 2;
+               break;
+       case 4:
+               insn->moffset1.value = get_next(int, insn);
+               insn->moffset1.nbytes = 4;
+               break;
+       case 8:
+               insn->moffset1.value = get_next(int, insn);
+               insn->moffset1.nbytes = 4;
+               insn->moffset2.value = get_next(int, insn);
+               insn->moffset2.nbytes = 4;
+               break;
+       default:        /* opnd_bytes must be modified manually */
+               goto err_out;
+       }
+       insn->moffset1.got = insn->moffset2.got = 1;
+
+       return 1;
+
+err_out:
+       return 0;
+}
+
+/* Decode imm v32(Iz). Return 0 if failed */
+static int __get_immv32(struct insn *insn)
+{
+       switch (insn->opnd_bytes) {
+       case 2:
+               insn->immediate.value = get_next(short, insn);
+               insn->immediate.nbytes = 2;
+               break;
+       case 4:
+       case 8:
+               insn->immediate.value = get_next(int, insn);
+               insn->immediate.nbytes = 4;
+               break;
+       default:        /* opnd_bytes must be modified manually */
+               goto err_out;
+       }
+
+       return 1;
+
+err_out:
+       return 0;
+}
+
+/* Decode imm v64(Iv/Ov), Return 0 if failed */
+static int __get_immv(struct insn *insn)
+{
+       switch (insn->opnd_bytes) {
+       case 2:
+               insn->immediate1.value = get_next(short, insn);
+               insn->immediate1.nbytes = 2;
+               break;
+       case 4:
+               insn->immediate1.value = get_next(int, insn);
+               insn->immediate1.nbytes = 4;
+               break;
+       case 8:
+               insn->immediate1.value = get_next(int, insn);
+               insn->immediate1.nbytes = 4;
+               insn->immediate2.value = get_next(int, insn);
+               insn->immediate2.nbytes = 4;
+               break;
+       default:        /* opnd_bytes must be modified manually */
+               goto err_out;
+       }
+       insn->immediate1.got = insn->immediate2.got = 1;
+
+       return 1;
+err_out:
+       return 0;
+}
+
+/* Decode ptr16:16/32(Ap) */
+static int __get_immptr(struct insn *insn)
+{
+       switch (insn->opnd_bytes) {
+       case 2:
+               insn->immediate1.value = get_next(short, insn);
+               insn->immediate1.nbytes = 2;
+               break;
+       case 4:
+               insn->immediate1.value = get_next(int, insn);
+               insn->immediate1.nbytes = 4;
+               break;
+       case 8:
+               /* ptr16:64 is not exist (no segment) */
+               return 0;
+       default:        /* opnd_bytes must be modified manually */
+               goto err_out;
+       }
+       insn->immediate2.value = get_next(unsigned short, insn);
+       insn->immediate2.nbytes = 2;
+       insn->immediate1.got = insn->immediate2.got = 1;
+
+       return 1;
+err_out:
+       return 0;
+}
+
+/**
+ * insn_get_immediate() - Get the immediates of instruction
+ * @insn:      &struct insn containing instruction
+ *
+ * If necessary, first collects the instruction up to and including the
+ * displacement bytes.
+ * Basically, most of immediates are sign-expanded. Unsigned-value can be
+ * get by bit masking with ((1 << (nbytes * 8)) - 1)
+ */
+void insn_get_immediate(struct insn *insn)
+{
+       if (insn->immediate.got)
+               return;
+       if (!insn->displacement.got)
+               insn_get_displacement(insn);
+
+       if (inat_has_moffset(insn->attr)) {
+               if (!__get_moffset(insn))
+                       goto err_out;
+               goto done;
+       }
+
+       if (!inat_has_immediate(insn->attr))
+               /* no immediates */
+               goto done;
+
+       switch (inat_immediate_size(insn->attr)) {
+       case INAT_IMM_BYTE:
+               insn->immediate.value = get_next(char, insn);
+               insn->immediate.nbytes = 1;
+               break;
+       case INAT_IMM_WORD:
+               insn->immediate.value = get_next(short, insn);
+               insn->immediate.nbytes = 2;
+               break;
+       case INAT_IMM_DWORD:
+               insn->immediate.value = get_next(int, insn);
+               insn->immediate.nbytes = 4;
+               break;
+       case INAT_IMM_QWORD:
+               insn->immediate1.value = get_next(int, insn);
+               insn->immediate1.nbytes = 4;
+               insn->immediate2.value = get_next(int, insn);
+               insn->immediate2.nbytes = 4;
+               break;
+       case INAT_IMM_PTR:
+               if (!__get_immptr(insn))
+                       goto err_out;
+               break;
+       case INAT_IMM_VWORD32:
+               if (!__get_immv32(insn))
+                       goto err_out;
+               break;
+       case INAT_IMM_VWORD:
+               if (!__get_immv(insn))
+                       goto err_out;
+               break;
+       default:
+               /* Here, insn must have an immediate, but failed */
+               goto err_out;
+       }
+       if (inat_has_second_immediate(insn->attr)) {
+               insn->immediate2.value = get_next(char, insn);
+               insn->immediate2.nbytes = 1;
+       }
+done:
+       insn->immediate.got = 1;
+
+err_out:
+       return;
+}
+
+/**
+ * insn_get_length() - Get the length of instruction
+ * @insn:      &struct insn containing instruction
+ *
+ * If necessary, first collects the instruction up to and including the
+ * immediates bytes.
+ */
+void insn_get_length(struct insn *insn)
+{
+       if (insn->length)
+               return;
+       if (!insn->immediate.got)
+               insn_get_immediate(insn);
+       insn->length = (unsigned char)((unsigned long)insn->next_byte
+                                    - (unsigned long)insn->kaddr);
+}
diff --git a/list.h b/list.h
new file mode 100644 (file)
index 0000000..e95593c
--- /dev/null
+++ b/list.h
@@ -0,0 +1,221 @@
+/*
+ * list.h
+ *
+ * Adapted from http://www.mcs.anl.gov/~kazutomo/list/list.h which is a
+ * userspace port of the Linux kernel implementation in include/linux/list.h
+ *
+ * Thus licensed as GPLv2.
+ *
+ * Copyright (C) 2014 Seth Jennings <sjenning@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA,
+ * 02110-1301, USA.
+ */
+
+#ifndef _LIST_H
+#define _LIST_H
+
+/**
+ * Get offset of a member
+ */
+#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
+
+/**
+ * Casts a member of a structure out to the containing structure
+ * @param ptr        the pointer to the member.
+ * @param type       the type of the container struct this is embedded in.
+ * @param member     the name of the member within the struct.
+ *
+ */
+#define container_of(ptr, type, member) ({                      \
+        const typeof( ((type *)0)->member ) *__mptr = (ptr);    \
+        (type *)( (char *)__mptr - offsetof(type,member) );})
+
+/*
+ * These are non-NULL pointers that will result in page faults
+ * under normal circumstances, used to verify that nobody uses
+ * non-initialized list entries.
+ */
+#define LIST_POISON1  ((void *) 0x00100100)
+#define LIST_POISON2  ((void *) 0x00200200)
+
+/**
+ * Simple doubly linked list implementation.
+ *
+ * Some of the internal functions ("__xxx") are useful when
+ * manipulating whole lists rather than single entries, as
+ * sometimes we already know the next/prev entries and we can
+ * generate better code by using them directly rather than
+ * using the generic single-entry routines.
+ */
+struct list_head {
+       struct list_head *next, *prev;
+};
+
+#define LIST_HEAD_INIT(name) { &(name), &(name) }
+
+#define LIST_HEAD(name) \
+       struct list_head name = LIST_HEAD_INIT(name)
+
+#define INIT_LIST_HEAD(ptr) do { \
+       (ptr)->next = (ptr); (ptr)->prev = (ptr); \
+} while (0)
+
+/*
+ * Insert a new entry between two known consecutive entries.
+ *
+ * This is only for internal list manipulation where we know
+ * the prev/next entries already!
+ */
+static inline void __list_add(struct list_head *new,
+                             struct list_head *prev,
+                             struct list_head *next)
+{
+       next->prev = new;
+       new->next = next;
+       new->prev = prev;
+       prev->next = new;
+}
+
+/**
+ * list_add - add a new entry
+ * @new: new entry to be added
+ * @head: list head to add it after
+ *
+ * Insert a new entry after the specified head.
+ * This is good for implementing stacks.
+ */
+static inline void list_add(struct list_head *new, struct list_head *head)
+{
+       __list_add(new, head, head->next);
+}
+
+/**
+ * list_add_tail - add a new entry
+ * @new: new entry to be added
+ * @head: list head to add it before
+ *
+ * Insert a new entry before the specified head.
+ * This is useful for implementing queues.
+ */
+static inline void list_add_tail(struct list_head *new, struct list_head *head)
+{
+       __list_add(new, head->prev, head);
+}
+
+
+/*
+ * Delete a list entry by making the prev/next entries
+ * point to each other.
+ *
+ * This is only for internal list manipulation where we know
+ * the prev/next entries already!
+ */
+static inline void __list_del(struct list_head * prev, struct list_head * next)
+{
+       next->prev = prev;
+       prev->next = next;
+}
+
+/**
+ * list_del - deletes entry from list.
+ * @entry: the element to delete from the list.
+ * Note: list_empty on entry does not return true after this, the entry is
+ * in an undefined state.
+ */
+static inline void list_del(struct list_head *entry)
+{
+       __list_del(entry->prev, entry->next);
+       entry->next = LIST_POISON1;
+       entry->prev = LIST_POISON2;
+}
+
+/**
+ * list_replace - replace old entry by new one
+ * @old : the element to be replaced
+ * @new : the new element to insert
+ *
+ * If @old was empty, it will be overwritten.
+ */
+static inline void list_replace(struct list_head *old,
+                               struct list_head *new)
+{
+       new->next = old->next;
+       new->next->prev = new;
+       new->prev = old->prev;
+       new->prev->next = new;
+}
+
+#define list_entry(ptr, type, member) \
+       container_of(ptr, type, member)
+
+/**
+ * list_first_entry - get the first element from a list
+ * @ptr:       the list head to take the element from.
+ * @type:      the type of the struct this is embedded in.
+ * @member:    the name of the list_struct within the struct.
+ *
+ * Note, that list is expected to be not empty.
+ */
+#define list_first_entry(ptr, type, member) \
+       list_entry((ptr)->next, type, member)
+
+/**
+ * list_next_entry - get the next element in list
+ * @pos:       the type * to cursor
+ * @member:    the name of the list_struct within the struct.
+ */
+#define list_next_entry(pos, member) \
+       list_entry((pos)->member.next, typeof(*(pos)), member)
+
+/**
+ * list_for_each_entry -       iterate over list of given type
+ * @pos:       the type * to use as a loop counter.
+ * @head:      the head for your list.
+ * @member:    the name of the list_struct within the struct.
+ */
+#define list_for_each_entry(pos, head, member)                         \
+       for (pos = list_entry((head)->next, typeof(*pos), member);      \
+            &pos->member != (head);                                    \
+            pos = list_entry(pos->member.next, typeof(*pos), member))
+
+/**
+ * list_for_each_entry_continue - continue iteration over list of given type
+ * @pos:       the type * to use as a loop cursor.
+ * @head:      the head for your list.
+ * @member:    the name of the list_struct within the struct.
+ *
+ * Continue to iterate over list of given type, continuing after
+ * the current position.
+ */
+#define list_for_each_entry_continue(pos, head, member)                \
+       for (pos = list_next_entry(pos, member);                        \
+            &pos->member != (head);                                    \
+            pos = list_next_entry(pos, member))
+
+/**
+ * list_for_each_entry_safe - iterate over list of given type safe against removal of list entry
+ * @pos:       the type * to use as a loop counter.
+ * @n:         another type * to use as temporary storage
+ * @head:      the head for your list.
+ * @member:    the name of the list_struct within the struct.
+ */
+#define list_for_each_entry_safe(pos, n, head, member)                 \
+       for (pos = list_entry((head)->next, typeof(*pos), member),      \
+               n = list_entry(pos->member.next, typeof(*pos), member); \
+            &pos->member != (head);                                    \
+            pos = n, n = list_entry(n->member.next, typeof(*n), member))
+
+#endif /* _LIST_H_ */
diff --git a/lookup.c b/lookup.c
new file mode 100644 (file)
index 0000000..39125c6
--- /dev/null
+++ b/lookup.c
@@ -0,0 +1,251 @@
+/*
+ * lookup.c
+ *
+ * This file contains functions that assist in the reading and searching
+ * the symbol table of an ELF object.
+ *
+ * Copyright (C) 2014 Seth Jennings <sjenning@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA,
+ * 02110-1301, USA.
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <error.h>
+#include <gelf.h>
+#include <unistd.h>
+
+#include "lookup.h"
+
+#define ERROR(format, ...) \
+       error(1, 0, "%s: %d: " format, __FUNCTION__, __LINE__, ##__VA_ARGS__)
+
+struct symbol {
+       unsigned long value;
+       unsigned long size;
+       char *name;
+       int type, bind, skip;
+};
+
+struct lookup_table {
+       int fd, nr;
+       Elf *elf;
+       struct symbol *syms;
+};
+
+#define for_each_symbol(ndx, iter, table) \
+       for (ndx = 0, iter = table->syms; ndx < table->nr; ndx++, iter++)
+
+struct lookup_table *lookup_open(char *path)
+{
+       Elf *elf;
+       int fd, i, len;
+       Elf_Scn *scn;
+       GElf_Shdr sh;
+       GElf_Sym sym;
+       Elf_Data *data;
+       char *name;
+       struct lookup_table *table;
+       struct symbol *mysym;
+       size_t shstrndx;
+
+       if ((fd = open(path, O_RDONLY, 0)) < 0)
+               ERROR("open");
+
+       elf_version(EV_CURRENT);
+
+       elf = elf_begin(fd, ELF_C_READ_MMAP, NULL);
+       if (!elf) {
+               printf("%s\n", elf_errmsg(-1));
+               ERROR("elf_begin");
+       }
+
+       if (elf_getshdrstrndx(elf, &shstrndx))
+               ERROR("elf_getshdrstrndx");
+
+       scn = NULL;
+       while ((scn = elf_nextscn(elf, scn))) {
+               if (!gelf_getshdr(scn, &sh))
+                       ERROR("gelf_getshdr");
+
+               name = elf_strptr(elf, shstrndx, sh.sh_name);
+               if (!name)
+                       ERROR("elf_strptr scn");
+
+               if (!strcmp(name, ".symtab"))
+                       break;
+       }
+
+       if (!scn)
+               ERROR(".symtab section not found");
+
+       data = elf_getdata(scn, NULL);
+       if (!data)
+               ERROR("elf_getdata");
+
+       len = sh.sh_size / sh.sh_entsize;
+
+       table = malloc(sizeof(*table));
+       if (!table)
+               ERROR("malloc table");
+       table->syms = malloc(len * sizeof(struct symbol));
+       if (!table->syms)
+               ERROR("malloc table.syms");
+       memset(table->syms, 0, len * sizeof(struct symbol));
+       table->nr = len;
+       table->fd = fd;
+       table->elf = elf;
+
+       for_each_symbol(i, mysym, table) {
+               if (!gelf_getsym(data, i, &sym))
+                       ERROR("gelf_getsym");
+
+               if (sym.st_shndx == SHN_UNDEF) {
+                       mysym->skip = 1;
+                       continue;
+               }
+
+               name = elf_strptr(elf, sh.sh_link, sym.st_name);
+               if(!name)
+                       ERROR("elf_strptr sym");
+
+               mysym->value = sym.st_value;
+               mysym->size = sym.st_size;
+               mysym->type = GELF_ST_TYPE(sym.st_info);
+               mysym->bind = GELF_ST_BIND(sym.st_info);
+               mysym->name = name;
+       }
+
+       return table;
+}
+
+void lookup_close(struct lookup_table *table)
+{
+       elf_end(table->elf);
+       close(table->fd);
+       free(table);
+}
+
+int lookup_local_symbol(struct lookup_table *table, char *name, char *hint,
+                        struct lookup_result *result)
+{
+       struct symbol *sym, *match = NULL;
+       int i;
+       char *curfile = NULL;
+
+       memset(result, 0, sizeof(*result));
+       for_each_symbol(i, sym, table) {
+               if (sym->type == STT_FILE) {
+                       if (!strcmp(sym->name, hint)) {
+                               curfile = sym->name;
+                               continue; /* begin hint file symbols */
+                       } else if (curfile)
+                               curfile = NULL; /* end hint file symbols */
+               }
+               if (!curfile)
+                       continue;
+               if (sym->bind == STB_LOCAL && !strcmp(sym->name, name)) {
+                       if (match)
+                               /* dup file+symbol, unresolvable ambiguity */
+                               return 1;
+                       match = sym;
+               }
+       }
+
+       if (!match)
+               return 1;
+
+       result->value = match->value;
+       result->size = match->size;
+       return 0;
+}
+
+int lookup_global_symbol(struct lookup_table *table, char *name,
+                         struct lookup_result *result)
+{
+       struct symbol *sym;
+       int i;
+
+       memset(result, 0, sizeof(*result));
+       for_each_symbol(i, sym, table)
+               if (!sym->skip && sym->bind == STB_GLOBAL &&
+                   !strcmp(sym->name, name)) {
+                       result->value = sym->value;
+                       result->size = sym->size;
+                       return 0;
+               }
+
+       return 1;
+}
+
+int lookup_is_exported_symbol(struct lookup_table *table, char *name)
+{
+       struct symbol *sym;
+       int i;
+       char export[255] = "__ksymtab_";
+
+       strncat(export, name, 254);
+
+       for_each_symbol(i, sym, table)
+               if (!sym->skip && !strcmp(sym->name, export))
+                       return 1;
+
+       return 0;
+}
+
+#if 0 /* for local testing */
+static void find_this(struct lookup_table *table, char *sym, char *hint)
+{
+       struct lookup_result result;
+
+       if (hint)
+               lookup_local_symbol(table, sym, hint, &result);
+       else
+               lookup_global_symbol(table, sym, &result);
+
+       printf("%s %s w/ %s hint at 0x%016lx len %lu\n",
+              hint ? "local" : "global", sym, hint ? hint : "no",
+              result.value, result.size);
+}
+
+int main(int argc, char **argv)
+{
+       struct lookup_table *vmlinux;
+
+       if (argc != 2)
+               return 1;
+
+       vmlinux = lookup_open(argv[1]);
+
+       printf("printk is%s exported\n",
+               lookup_is_exported_symbol(vmlinux, "__fentry__") ? "" : " not");
+       printf("meminfo_proc_show is%s exported\n",
+               lookup_is_exported_symbol(vmlinux, "meminfo_proc_show") ? "" : " not");
+
+       find_this(vmlinux, "printk", NULL);
+       find_this(vmlinux, "pages_to_scan_show", "ksm.c");
+       find_this(vmlinux, "pages_to_scan_show", "huge_memory.c");
+       find_this(vmlinux, "pages_to_scan_show", NULL); /* should fail */
+
+       lookup_close(vmlinux);
+
+       return 0;
+}
+#endif
diff --git a/lookup.h b/lookup.h
new file mode 100644 (file)
index 0000000..cbb3dae
--- /dev/null
+++ b/lookup.h
@@ -0,0 +1,19 @@
+#ifndef _LOOKUP_H_
+#define _LOOKUP_H_
+
+struct lookup_table;
+
+struct lookup_result {
+       unsigned long value;
+       unsigned long size;
+};
+
+struct lookup_table *lookup_open(char *path);
+void lookup_close(struct lookup_table *table);
+int lookup_local_symbol(struct lookup_table *table, char *name, char *hint,
+                        struct lookup_result *result);
+int lookup_global_symbol(struct lookup_table *table, char *name,
+                         struct lookup_result *result);
+int lookup_is_exported_symbol(struct lookup_table *table, char *name);
+
+#endif /* _LOOKUP_H_ */
diff --git a/xsplice-build b/xsplice-build
new file mode 100755 (executable)
index 0000000..ca414b1
--- /dev/null
@@ -0,0 +1,252 @@
+#!/bin/bash
+#
+# xsplice build script
+#
+# Copyright (C) 2014 Seth Jennings <sjenning@redhat.com>
+# Copyright (C) 2013,2014 Josh Poimboeuf <jpoimboe@redhat.com>
+# Copyright (C) 2015 Ross Lagerwall <ross.lagerwall@citrix.com>
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+# This script takes a Xen tree, and a patch and outputs an xsplice
+# module intended to patch Xen at runtime.
+# Large amounts of this script are taken from kpatch's kpatch-build
+# script.
+
+SCRIPTDIR="$(readlink -f $(dirname $(type -p $0)))"
+CPUS="$(getconf _NPROCESSORS_ONLN)"
+DEBUG=n
+XEN_DEBUG=n
+SKIP=
+
+warn() {
+    echo "ERROR: $1" >&2
+}
+
+die() {
+    if [[ -z $1 ]]; then
+        msg="xSplice build failed"
+    else
+        msg="$1"
+    fi
+
+    warn "$msg."
+
+    exit 1
+}
+
+function make_patch_name()
+{
+    PATCHNAME=$(basename "$1")
+    if [[ "$PATCHNAME" =~ \.patch ]] || [[ "$PATCHNAME" =~ \.diff ]]; then
+            PATCHNAME="${PATCHNAME%.*}"
+    fi
+
+    # Only allow alphanumerics and '_' and '-' in the patch name.  Everything
+    # else is replaced with '-'.  Truncate to 48 chars.
+    echo ${PATCHNAME//[^a-zA-Z0-9_-]/-} |cut -c 1-48
+}
+
+# Do a full normal build
+function build_full()
+{
+    cd "${SRCDIR}/xen" || die
+    make "-j$CPUS" clean &> "${OUTPUT}/build_full_clean.log" || die
+    make "-j$CPUS" debug="$XEN_DEBUG" &> "${OUTPUT}/build_full_compile.log" || die
+    cp xen-syms "$OUTPUT"
+}
+
+# Build with special GCC flags
+function build_special()
+{
+    name=$1
+
+    cd "${SRCDIR}" || die
+
+    # Capture .o files from the patched build
+    export CROSS_COMPILE="${SCRIPTDIR}/xsplice-gcc "
+    export XSPLICE_BUILD_DIR="$(pwd)/"
+    export XSPLICE_CAPTURE_DIR="$OUTPUT/${name}"
+    mkdir -p "$XSPLICE_CAPTURE_DIR"
+
+    # Build with special GCC flags
+    cd "${SRCDIR}/xen" || die
+    sed -i 's/CFLAGS += -nostdinc/CFLAGS += -nostdinc -ffunction-sections -fdata-sections/' Rules.mk
+    make "-j$CPUS" debug="$XEN_DEBUG" &> "${OUTPUT}/build_${name}_compile.log" || die
+    sed -i 's/CFLAGS += -nostdinc -ffunction-sections -fdata-sections/CFLAGS += -nostdinc/' Rules.mk
+
+    unset XSPLICE_BUILD_DIR
+    unset XSPLICE_CAPTURE_DIR
+}
+
+function create_patch()
+{
+    echo "Extracting new and modified ELF sections..."
+
+    [[ -e "${OUTPUT}/original/changed_objs" ]] || die "no changed objects found"
+    [[ -e "${OUTPUT}/patched/changed_objs" ]] || die "no changed objects found"
+
+    cd "${OUTPUT}/original" || die
+    FILES="$(find xen -type f -name "*.o")"
+    cd "${OUTPUT}" || die
+    CHANGED=0
+    ERROR=0
+    debugopt=
+    [[ $DEBUG -eq 1 ]] && debugopt=-d
+
+    for i in $FILES; do
+        mkdir -p "output/$(dirname $i)" || die
+        echo "Processing ${i}"
+        echo "Run create-diff-object on $i" >> "${OUTPUT}/create-diff-object.log"
+        "${SCRIPTDIR}"/create-diff-object $debugopt "original/$i" "patched/$i" xen-syms "output/$i" &>> "${OUTPUT}/create-diff-object.log"
+        rc="${PIPESTATUS[0]}"
+        if [[ $rc = 139 ]]; then
+            warn "create-diff-object SIGSEGV"
+            if ls core* &> /dev/null; then
+                cp core* /tmp
+                die "core file at /tmp/$(ls core*)"
+            fi
+            die "no core file found, run 'ulimit -c unlimited' and try to recreate"
+        fi
+        # create-diff-object returns 3 if no functional change is found
+        [[ $rc -eq 0 ]] || [[ $rc -eq 3 ]] || ERROR=$(expr $ERROR "+" 1)
+        if [[ $rc -eq 0 ]]; then
+            CHANGED=1
+        fi
+    done
+
+    if [[ $ERROR -ne 0 ]]; then
+        die "$ERROR error(s) encountered"
+    fi
+
+    if [[ $CHANGED -eq 0 ]]; then
+        die "no functional changes found"
+    fi
+
+    echo "Creating patch module..."
+    ld -r -o "${PATCHNAME}.xsplice" $(find output -type f -name "*.o") || die
+}
+
+usage() {
+       echo "usage: $(basename $0) [options]" >&2
+       echo "          -h, --help         Show this help message" >&2
+       echo "          -s, --srcdir       Xen source directory" >&2
+       echo "          -p, --patch        Patch file" >&2
+       echo "          -o, --output       Output directory" >&2
+       echo "          -j, --cpus         Number of CPUs to use" >&2
+       echo "          -k, --skip         Skip build or diff phase" >&2
+       echo "          -d, --debug        Enable debug logging" >&2
+       echo "          --xen-debug        Build debug Xen" >&2
+}
+
+options=$(getopt -o hs:p:o:j:k:d -l "help,srcdir:patch:output:cpus:,skip:,debug,xen-debug" -- "$@") || die "getopt failed"
+
+eval set -- "$options"
+
+while [[ $# -gt 0 ]]; do
+    case "$1" in
+        -h|--help)
+            usage
+            exit 0
+            ;;
+        -j|--cpus)
+            shift
+            CPUS="$1"
+            shift
+            ;;
+        -k|--skip)
+            shift
+            SKIP="$1"
+            shift
+            ;;
+        -d|--debug)
+            DEBUG=1
+            shift
+            ;;
+        --xen-debug)
+            XEN_DEBUG=y
+            shift
+            ;;
+        -s|--srcdir)
+            shift
+            srcarg="$1"
+            shift
+            ;;
+        -p|--patch)
+            shift
+            patcharg="$1"
+            shift
+            ;;
+        -o|--output)
+            shift
+            outputarg="$1"
+            shift
+            ;;
+        --)
+            shift
+            break
+            ;;
+    esac
+done
+
+[ -z "$srcarg" ] && die "Xen directory not given"
+[ -z "$patcharg" ] && die "Patchfile not given"
+[ -z "$outputarg" ] && die "Output directory not given"
+
+SRCDIR="$(realpath -m -- "$srcarg")"
+PATCHFILE="$(realpath -m -- "$patcharg")"
+OUTPUT="$(realpath -m -- "$outputarg")"
+
+[ -d "${SRCDIR}" ] || die "Xen directory does not exist"
+[ -f "${PATCHFILE}" ] || die "Patchfile does not exist"
+
+PATCHNAME=$(make_patch_name "${PATCHFILE}")
+
+echo "Building xSplice patch: ${PATCHNAME}"
+echo
+echo "Xen directory: ${SRCDIR}"
+echo "Patch file: ${PATCHFILE}"
+echo "Output directory: ${OUTPUT}"
+echo "================================================"
+echo
+
+if [ "${SKIP}" != "build" ]; then
+    [ -e "${OUTPUT}" ] && die "Output directory exists"
+    mkdir -p "${OUTPUT}" || die
+
+    echo "Testing patch file..."
+    cd "$SRCDIR" || die
+    patch -s -N -p1 --dry-run < "$PATCHFILE" || die "source patch file failed to apply"
+
+    echo "Perform full initial build with ${CPUS} CPU(s)..."
+    build_full
+
+    echo "Apply patch and build with ${CPUS} CPU(s)..."
+    cd "$SRCDIR" || die
+    patch -s -N -p1 < "$PATCHFILE" || die
+    build_special original
+
+    echo "Unapply patch and build with ${CPUS} CPU(s)..."
+    cd "$SRCDIR" || die
+    patch -s -R -p1 < "$PATCHFILE" || die
+    build_special patched
+fi
+
+if [ "${SKIP}" != "diff" ]; then
+    [ -d "${OUTPUT}" ] || die "Output directory does not exist"
+
+    cd "${OUTPUT}" || die
+    create_patch
+    echo "${PATCHNAME}.xsplice created successfully"
+fi
diff --git a/xsplice-gcc b/xsplice-gcc
new file mode 100755 (executable)
index 0000000..8b63e50
--- /dev/null
@@ -0,0 +1,66 @@
+#!/bin/bash
+#
+# xsplice build script
+#
+# Copyright (C) 2015 Ross Lagerwall <ross.lagerwall@citrix.com>
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+# Based on kpatch's kpatch-gcc script.
+
+TOOLCHAINCMD="$1"
+shift
+
+declare -a args=("$@")
+keep=no
+
+if [[ "$TOOLCHAINCMD" = "gcc" ]] ; then
+    while [ "$#" -gt 0 ]; do
+        if [ "$1" = "-o" ]; then
+            obj=$2
+            [[ $2 = */.tmp_*.o ]] && obj=${2/.tmp_/}
+            case "$obj" in
+            version.o|\
+            debug.o|\
+            *.xen-syms.*.o|\
+            .*.o)
+                break
+                ;;
+            *.o)
+                path="$(pwd)/$(dirname $obj)"
+                dir="${path#$XSPLICE_BUILD_DIR}"
+                if [ -n "$XSPLICE_CAPTURE_DIR" -a -d "$XSPLICE_CAPTURE_DIR" ]; then
+                    echo "$dir/$obj" >> "${XSPLICE_CAPTURE_DIR}/changed_objs"
+                    keep=yes
+                fi
+                break
+                ;;
+            *)
+                break
+                ;;
+        esac
+    fi
+    shift
+done
+fi
+
+"$TOOLCHAINCMD" "${args[@]}"
+ret="$?"
+
+if [[ "$keep" = "yes" ]] ; then
+    mkdir -p "$(dirname $XSPLICE_CAPTURE_DIR/$dir/$obj)"
+    cp "$obj" "$XSPLICE_CAPTURE_DIR/$dir/$obj"
+fi
+
+exit "$ret"