--- /dev/null
+create-diff-object
+*.o
+*.d
--- /dev/null
+ 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.
--- /dev/null
+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
--- /dev/null
+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.
--- /dev/null
+/*
+ * 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, §ions_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;
+}
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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 */
--- /dev/null
+/* 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,
+};
--- /dev/null
+/*
+ * 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];
+}
+
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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_ */
--- /dev/null
+/*
+ * 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
--- /dev/null
+#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_ */
--- /dev/null
+#!/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
--- /dev/null
+#!/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"