From cf59a534b7f987720820ef94ac15e5491c34ba86 Mon Sep 17 00:00:00 2001 From: Ross Lagerwall Date: Mon, 12 Oct 2015 12:25:14 +0100 Subject: [PATCH 1/1] Add project files xsplice-build can build live patches for most XSAs. It is, however, still prototype code. Signed-off-by: Ross Lagerwall --- .gitignore | 3 + COPYING | 339 ++++++ Makefile | 25 + README.md | 72 ++ create-diff-object.c | 2589 +++++++++++++++++++++++++++++++++++++++++ insn/asm/inat.h | 221 ++++ insn/asm/inat_types.h | 29 + insn/asm/insn.h | 199 ++++ insn/inat-tables.c | 1146 ++++++++++++++++++ insn/inat.c | 97 ++ insn/insn.c | 582 +++++++++ list.h | 221 ++++ lookup.c | 251 ++++ lookup.h | 19 + xsplice-build | 252 ++++ xsplice-gcc | 66 ++ 16 files changed, 6111 insertions(+) create mode 100644 .gitignore create mode 100644 COPYING create mode 100644 Makefile create mode 100644 README.md create mode 100644 create-diff-object.c create mode 100644 insn/asm/inat.h create mode 100644 insn/asm/inat_types.h create mode 100644 insn/asm/insn.h create mode 100644 insn/inat-tables.c create mode 100644 insn/inat.c create mode 100644 insn/insn.c create mode 100644 list.h create mode 100644 lookup.c create mode 100644 lookup.h create mode 100755 xsplice-build create mode 100755 xsplice-gcc diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a594867 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +create-diff-object +*.o +*.d diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..d159169 --- /dev/null +++ b/COPYING @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + 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. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..3741f6d --- /dev/null +++ b/Makefile @@ -0,0 +1,25 @@ +SHELL = /bin/sh +CC = gcc + +.PHONY: all install clean +.DEFAULT: all + +CFLAGS += -Iinsn -Wall -g +LDFLAGS = -lelf + +TARGETS = create-diff-object +OBJS = create-diff-object.o lookup.o insn/insn.o insn/inat.o +SOURCES = create-diff-object.c lookup.c insn/insn.c insn/inat.c + +all: $(TARGETS) + +-include $(SOURCES:.c=.d) + +%.o : %.c + $(CC) -MMD -MP $(CFLAGS) -c -o $@ $< + +create-diff-object: $(OBJS) + $(CC) $(CFLAGS) $^ -o $@ $(LDFLAGS) + +clean: + $(RM) $(TARGETS) $(OBJS) *.d insn/*.d diff --git a/README.md b/README.md new file mode 100644 index 0000000..c39b376 --- /dev/null +++ b/README.md @@ -0,0 +1,72 @@ +xsplice-build +============= + +xsplice-build is a tool for building xSplice patches from source code +patches. It takes as input, a Xen tree and a patch and outputs an +`.xsplice` module containing containing the live patch. + +Quick start +----------- +First checkout the code, and then run `make` to build it. + +Here is an example of building a patch for XSA-106: +``` +$ cd ~/src/xen +$ git reset --hard +$ git clean -x -f -d +$ git checkout 346d4545569928b652c40c7815c1732676f8587c^ +$ cd ~/src/xsplice-build +$ wget -q 'http://xenbits.xen.org/xsa/xsa106.patch' +$ ./xsplice-build --xen-debug -s ~/src/xen -p xsa106.patch -o out +Building xSplice patch: xsa106 + +Xen directory: /home/ross/src/xen +Patch file: /home/ross/src/xsplice-build/xsa106.patch +Output directory: /home/ross/src/xsplice-build/out +================================================ + +Testing patch file... +Perform full initial build with 4 CPU(s)... +Apply patch and build with 4 CPU(s)... +Unapply patch and build with 4 CPU(s)... +Extracting new and modified ELF sections... +Processing xen/arch/x86/x86_emulate.o +Creating patch module... +xsa106.xsplice created successfully + +$ ls -lh out/xsa106.xsplice +-rw-rw-r--. 1 ross ross 418K Oct 12 12:02 out/xsa106.xsplice +``` + +Project Status +-------------- +This is prototype code: + * There's no way to apply built patches + * Patches cannot be built for some source patches + * The output format does not correspond to the latest xSplice design + +With no source patch modifications, live patches can be built for every +XSA that applies to x86 back to XSA-90 except for XSA-97, XSA-111, +XSA-112, and XSA-114 (83% success rate). + +License +------- +xSplice is under the GPLv2 license. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +Credits +------- +Most of this code is copied from [kPatch](https://github.com/dynup/kpatch). +All credits to the kPatch authors. diff --git a/create-diff-object.c b/create-diff-object.c new file mode 100644 index 0000000..5b8fb09 --- /dev/null +++ b/create-diff-object.c @@ -0,0 +1,2589 @@ +/* + * Copyright (C) 2014 Seth Jennings + * Copyright (C) 2013-2014 Josh Poimboeuf + * Copyright (C) 2015 Ross Lagerwall + * + * 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 . + */ + +/* 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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; +} diff --git a/insn/asm/inat.h b/insn/asm/inat.h new file mode 100644 index 0000000..74a2e31 --- /dev/null +++ b/insn/asm/inat.h @@ -0,0 +1,221 @@ +#ifndef _ASM_X86_INAT_H +#define _ASM_X86_INAT_H +/* + * x86 instruction attributes + * + * Written by Masami Hiramatsu + * + * 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 + +/* + * Internal bits. Don't use bitmasks directly, because these bits are + * unstable. You should use checking functions. + */ + +#define INAT_OPCODE_TABLE_SIZE 256 +#define INAT_GROUP_TABLE_SIZE 8 + +/* Legacy last prefixes */ +#define INAT_PFX_OPNDSZ 1 /* 0x66 */ /* LPFX1 */ +#define INAT_PFX_REPE 2 /* 0xF3 */ /* LPFX2 */ +#define INAT_PFX_REPNE 3 /* 0xF2 */ /* LPFX3 */ +/* Other Legacy prefixes */ +#define INAT_PFX_LOCK 4 /* 0xF0 */ +#define INAT_PFX_CS 5 /* 0x2E */ +#define INAT_PFX_DS 6 /* 0x3E */ +#define INAT_PFX_ES 7 /* 0x26 */ +#define INAT_PFX_FS 8 /* 0x64 */ +#define INAT_PFX_GS 9 /* 0x65 */ +#define INAT_PFX_SS 10 /* 0x36 */ +#define INAT_PFX_ADDRSZ 11 /* 0x67 */ +/* x86-64 REX prefix */ +#define INAT_PFX_REX 12 /* 0x4X */ +/* AVX VEX prefixes */ +#define INAT_PFX_VEX2 13 /* 2-bytes VEX prefix */ +#define INAT_PFX_VEX3 14 /* 3-bytes VEX prefix */ + +#define INAT_LSTPFX_MAX 3 +#define INAT_LGCPFX_MAX 11 + +/* Immediate size */ +#define INAT_IMM_BYTE 1 +#define INAT_IMM_WORD 2 +#define INAT_IMM_DWORD 3 +#define INAT_IMM_QWORD 4 +#define INAT_IMM_PTR 5 +#define INAT_IMM_VWORD32 6 +#define INAT_IMM_VWORD 7 + +/* Legacy prefix */ +#define INAT_PFX_OFFS 0 +#define INAT_PFX_BITS 4 +#define INAT_PFX_MAX ((1 << INAT_PFX_BITS) - 1) +#define INAT_PFX_MASK (INAT_PFX_MAX << INAT_PFX_OFFS) +/* Escape opcodes */ +#define INAT_ESC_OFFS (INAT_PFX_OFFS + INAT_PFX_BITS) +#define INAT_ESC_BITS 2 +#define INAT_ESC_MAX ((1 << INAT_ESC_BITS) - 1) +#define INAT_ESC_MASK (INAT_ESC_MAX << INAT_ESC_OFFS) +/* Group opcodes (1-16) */ +#define INAT_GRP_OFFS (INAT_ESC_OFFS + INAT_ESC_BITS) +#define INAT_GRP_BITS 5 +#define INAT_GRP_MAX ((1 << INAT_GRP_BITS) - 1) +#define INAT_GRP_MASK (INAT_GRP_MAX << INAT_GRP_OFFS) +/* Immediates */ +#define INAT_IMM_OFFS (INAT_GRP_OFFS + INAT_GRP_BITS) +#define INAT_IMM_BITS 3 +#define INAT_IMM_MASK (((1 << INAT_IMM_BITS) - 1) << INAT_IMM_OFFS) +/* Flags */ +#define INAT_FLAG_OFFS (INAT_IMM_OFFS + INAT_IMM_BITS) +#define INAT_MODRM (1 << (INAT_FLAG_OFFS)) +#define INAT_FORCE64 (1 << (INAT_FLAG_OFFS + 1)) +#define INAT_SCNDIMM (1 << (INAT_FLAG_OFFS + 2)) +#define INAT_MOFFSET (1 << (INAT_FLAG_OFFS + 3)) +#define INAT_VARIANT (1 << (INAT_FLAG_OFFS + 4)) +#define INAT_VEXOK (1 << (INAT_FLAG_OFFS + 5)) +#define INAT_VEXONLY (1 << (INAT_FLAG_OFFS + 6)) +/* Attribute making macros for attribute tables */ +#define INAT_MAKE_PREFIX(pfx) (pfx << INAT_PFX_OFFS) +#define INAT_MAKE_ESCAPE(esc) (esc << INAT_ESC_OFFS) +#define INAT_MAKE_GROUP(grp) ((grp << INAT_GRP_OFFS) | INAT_MODRM) +#define INAT_MAKE_IMM(imm) (imm << INAT_IMM_OFFS) + +/* Attribute search APIs */ +extern insn_attr_t inat_get_opcode_attribute(insn_byte_t opcode); +extern int inat_get_last_prefix_id(insn_byte_t last_pfx); +extern insn_attr_t inat_get_escape_attribute(insn_byte_t opcode, + int lpfx_id, + insn_attr_t esc_attr); +extern insn_attr_t inat_get_group_attribute(insn_byte_t modrm, + int lpfx_id, + insn_attr_t esc_attr); +extern insn_attr_t inat_get_avx_attribute(insn_byte_t opcode, + insn_byte_t vex_m, + insn_byte_t vex_pp); + +/* Attribute checking functions */ +static inline int inat_is_legacy_prefix(insn_attr_t attr) +{ + attr &= INAT_PFX_MASK; + return attr && attr <= INAT_LGCPFX_MAX; +} + +static inline int inat_is_address_size_prefix(insn_attr_t attr) +{ + return (attr & INAT_PFX_MASK) == INAT_PFX_ADDRSZ; +} + +static inline int inat_is_operand_size_prefix(insn_attr_t attr) +{ + return (attr & INAT_PFX_MASK) == INAT_PFX_OPNDSZ; +} + +static inline int inat_is_rex_prefix(insn_attr_t attr) +{ + return (attr & INAT_PFX_MASK) == INAT_PFX_REX; +} + +static inline int inat_last_prefix_id(insn_attr_t attr) +{ + if ((attr & INAT_PFX_MASK) > INAT_LSTPFX_MAX) + return 0; + else + return attr & INAT_PFX_MASK; +} + +static inline int inat_is_vex_prefix(insn_attr_t attr) +{ + attr &= INAT_PFX_MASK; + return attr == INAT_PFX_VEX2 || attr == INAT_PFX_VEX3; +} + +static inline int inat_is_vex3_prefix(insn_attr_t attr) +{ + return (attr & INAT_PFX_MASK) == INAT_PFX_VEX3; +} + +static inline int inat_is_escape(insn_attr_t attr) +{ + return attr & INAT_ESC_MASK; +} + +static inline int inat_escape_id(insn_attr_t attr) +{ + return (attr & INAT_ESC_MASK) >> INAT_ESC_OFFS; +} + +static inline int inat_is_group(insn_attr_t attr) +{ + return attr & INAT_GRP_MASK; +} + +static inline int inat_group_id(insn_attr_t attr) +{ + return (attr & INAT_GRP_MASK) >> INAT_GRP_OFFS; +} + +static inline int inat_group_common_attribute(insn_attr_t attr) +{ + return attr & ~INAT_GRP_MASK; +} + +static inline int inat_has_immediate(insn_attr_t attr) +{ + return attr & INAT_IMM_MASK; +} + +static inline int inat_immediate_size(insn_attr_t attr) +{ + return (attr & INAT_IMM_MASK) >> INAT_IMM_OFFS; +} + +static inline int inat_has_modrm(insn_attr_t attr) +{ + return attr & INAT_MODRM; +} + +static inline int inat_is_force64(insn_attr_t attr) +{ + return attr & INAT_FORCE64; +} + +static inline int inat_has_second_immediate(insn_attr_t attr) +{ + return attr & INAT_SCNDIMM; +} + +static inline int inat_has_moffset(insn_attr_t attr) +{ + return attr & INAT_MOFFSET; +} + +static inline int inat_has_variant(insn_attr_t attr) +{ + return attr & INAT_VARIANT; +} + +static inline int inat_accept_vex(insn_attr_t attr) +{ + return attr & INAT_VEXOK; +} + +static inline int inat_must_vex(insn_attr_t attr) +{ + return attr & INAT_VEXONLY; +} +#endif diff --git a/insn/asm/inat_types.h b/insn/asm/inat_types.h new file mode 100644 index 0000000..cb3c20c --- /dev/null +++ b/insn/asm/inat_types.h @@ -0,0 +1,29 @@ +#ifndef _ASM_X86_INAT_TYPES_H +#define _ASM_X86_INAT_TYPES_H +/* + * x86 instruction attributes + * + * Written by Masami Hiramatsu + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +/* Instruction attributes */ +typedef unsigned int insn_attr_t; +typedef unsigned char insn_byte_t; +typedef signed int insn_value_t; + +#endif diff --git a/insn/asm/insn.h b/insn/asm/insn.h new file mode 100644 index 0000000..48eb30a --- /dev/null +++ b/insn/asm/insn.h @@ -0,0 +1,199 @@ +#ifndef _ASM_X86_INSN_H +#define _ASM_X86_INSN_H +/* + * x86 instruction analysis + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Copyright (C) IBM Corporation, 2009 + */ + +/* insn_attr_t is defined in inat.h */ +#include + +struct insn_field { + union { + insn_value_t value; + insn_byte_t bytes[4]; + }; + /* !0 if we've run insn_get_xxx() for this field */ + unsigned char got; + unsigned char nbytes; +}; + +struct insn { + struct insn_field prefixes; /* + * Prefixes + * prefixes.bytes[3]: last prefix + */ + struct insn_field rex_prefix; /* REX prefix */ + struct insn_field vex_prefix; /* VEX prefix */ + struct insn_field opcode; /* + * opcode.bytes[0]: opcode1 + * opcode.bytes[1]: opcode2 + * opcode.bytes[2]: opcode3 + */ + struct insn_field modrm; + struct insn_field sib; + struct insn_field displacement; + union { + struct insn_field immediate; + struct insn_field moffset1; /* for 64bit MOV */ + struct insn_field immediate1; /* for 64bit imm or off16/32 */ + }; + union { + struct insn_field moffset2; /* for 64bit MOV */ + struct insn_field immediate2; /* for 64bit imm or seg16 */ + }; + + insn_attr_t attr; + unsigned char opnd_bytes; + unsigned char addr_bytes; + unsigned char length; + unsigned char x86_64; + + const insn_byte_t *kaddr; /* kernel address of insn to analyze */ + const insn_byte_t *next_byte; +}; + +#define MAX_INSN_SIZE 16 + +#define X86_MODRM_MOD(modrm) (((modrm) & 0xc0) >> 6) +#define X86_MODRM_REG(modrm) (((modrm) & 0x38) >> 3) +#define X86_MODRM_RM(modrm) ((modrm) & 0x07) + +#define X86_SIB_SCALE(sib) (((sib) & 0xc0) >> 6) +#define X86_SIB_INDEX(sib) (((sib) & 0x38) >> 3) +#define X86_SIB_BASE(sib) ((sib) & 0x07) + +#define X86_REX_W(rex) ((rex) & 8) +#define X86_REX_R(rex) ((rex) & 4) +#define X86_REX_X(rex) ((rex) & 2) +#define X86_REX_B(rex) ((rex) & 1) + +/* VEX bit flags */ +#define X86_VEX_W(vex) ((vex) & 0x80) /* VEX3 Byte2 */ +#define X86_VEX_R(vex) ((vex) & 0x80) /* VEX2/3 Byte1 */ +#define X86_VEX_X(vex) ((vex) & 0x40) /* VEX3 Byte1 */ +#define X86_VEX_B(vex) ((vex) & 0x20) /* VEX3 Byte1 */ +#define X86_VEX_L(vex) ((vex) & 0x04) /* VEX3 Byte2, VEX2 Byte1 */ +/* VEX bit fields */ +#define X86_VEX3_M(vex) ((vex) & 0x1f) /* VEX3 Byte1 */ +#define X86_VEX2_M 1 /* VEX2.M always 1 */ +#define X86_VEX_V(vex) (((vex) & 0x78) >> 3) /* VEX3 Byte2, VEX2 Byte1 */ +#define X86_VEX_P(vex) ((vex) & 0x03) /* VEX3 Byte2, VEX2 Byte1 */ +#define X86_VEX_M_MAX 0x1f /* VEX3.M Maximum value */ + +extern void insn_init(struct insn *insn, const void *kaddr, int x86_64); +extern void insn_get_prefixes(struct insn *insn); +extern void insn_get_opcode(struct insn *insn); +extern void insn_get_modrm(struct insn *insn); +extern void insn_get_sib(struct insn *insn); +extern void insn_get_displacement(struct insn *insn); +extern void insn_get_immediate(struct insn *insn); +extern void insn_get_length(struct insn *insn); + +/* Attribute will be determined after getting ModRM (for opcode groups) */ +static inline void insn_get_attribute(struct insn *insn) +{ + insn_get_modrm(insn); +} + +/* Instruction uses RIP-relative addressing */ +extern int insn_rip_relative(struct insn *insn); + +/* Init insn for kernel text */ +static inline void kernel_insn_init(struct insn *insn, const void *kaddr) +{ +#ifdef CONFIG_X86_64 + insn_init(insn, kaddr, 1); +#else /* CONFIG_X86_32 */ + insn_init(insn, kaddr, 0); +#endif +} + +static inline int insn_is_avx(struct insn *insn) +{ + if (!insn->prefixes.got) + insn_get_prefixes(insn); + return (insn->vex_prefix.value != 0); +} + +/* Ensure this instruction is decoded completely */ +static inline int insn_complete(struct insn *insn) +{ + return insn->opcode.got && insn->modrm.got && insn->sib.got && + insn->displacement.got && insn->immediate.got; +} + +static inline insn_byte_t insn_vex_m_bits(struct insn *insn) +{ + if (insn->vex_prefix.nbytes == 2) /* 2 bytes VEX */ + return X86_VEX2_M; + else + return X86_VEX3_M(insn->vex_prefix.bytes[1]); +} + +static inline insn_byte_t insn_vex_p_bits(struct insn *insn) +{ + if (insn->vex_prefix.nbytes == 2) /* 2 bytes VEX */ + return X86_VEX_P(insn->vex_prefix.bytes[1]); + else + return X86_VEX_P(insn->vex_prefix.bytes[2]); +} + +/* Get the last prefix id from last prefix or VEX prefix */ +static inline int insn_last_prefix_id(struct insn *insn) +{ + if (insn_is_avx(insn)) + return insn_vex_p_bits(insn); /* VEX_p is a SIMD prefix id */ + + if (insn->prefixes.bytes[3]) + return inat_get_last_prefix_id(insn->prefixes.bytes[3]); + + return 0; +} + +/* Offset of each field from kaddr */ +static inline int insn_offset_rex_prefix(struct insn *insn) +{ + return insn->prefixes.nbytes; +} +static inline int insn_offset_vex_prefix(struct insn *insn) +{ + return insn_offset_rex_prefix(insn) + insn->rex_prefix.nbytes; +} +static inline int insn_offset_opcode(struct insn *insn) +{ + return insn_offset_vex_prefix(insn) + insn->vex_prefix.nbytes; +} +static inline int insn_offset_modrm(struct insn *insn) +{ + return insn_offset_opcode(insn) + insn->opcode.nbytes; +} +static inline int insn_offset_sib(struct insn *insn) +{ + return insn_offset_modrm(insn) + insn->modrm.nbytes; +} +static inline int insn_offset_displacement(struct insn *insn) +{ + return insn_offset_sib(insn) + insn->sib.nbytes; +} +static inline int insn_offset_immediate(struct insn *insn) +{ + return insn_offset_displacement(insn) + insn->displacement.nbytes; +} + +#endif /* _ASM_X86_INSN_H */ diff --git a/insn/inat-tables.c b/insn/inat-tables.c new file mode 100644 index 0000000..10de26c --- /dev/null +++ b/insn/inat-tables.c @@ -0,0 +1,1146 @@ +/* x86 opcode map generated from x86-opcode-map.txt */ +/* Do not change this code. */ + +/* Table: one byte opcode */ +const insn_attr_t inat_primary_table[INAT_OPCODE_TABLE_SIZE] = { + [0x00] = INAT_MODRM, + [0x01] = INAT_MODRM, + [0x02] = INAT_MODRM, + [0x03] = INAT_MODRM, + [0x04] = INAT_MAKE_IMM(INAT_IMM_BYTE), + [0x05] = INAT_MAKE_IMM(INAT_IMM_VWORD32), + [0x08] = INAT_MODRM, + [0x09] = INAT_MODRM, + [0x0a] = INAT_MODRM, + [0x0b] = INAT_MODRM, + [0x0c] = INAT_MAKE_IMM(INAT_IMM_BYTE), + [0x0d] = INAT_MAKE_IMM(INAT_IMM_VWORD32), + [0x0f] = INAT_MAKE_ESCAPE(1), + [0x10] = INAT_MODRM, + [0x11] = INAT_MODRM, + [0x12] = INAT_MODRM, + [0x13] = INAT_MODRM, + [0x14] = INAT_MAKE_IMM(INAT_IMM_BYTE), + [0x15] = INAT_MAKE_IMM(INAT_IMM_VWORD32), + [0x18] = INAT_MODRM, + [0x19] = INAT_MODRM, + [0x1a] = INAT_MODRM, + [0x1b] = INAT_MODRM, + [0x1c] = INAT_MAKE_IMM(INAT_IMM_BYTE), + [0x1d] = INAT_MAKE_IMM(INAT_IMM_VWORD32), + [0x20] = INAT_MODRM, + [0x21] = INAT_MODRM, + [0x22] = INAT_MODRM, + [0x23] = INAT_MODRM, + [0x24] = INAT_MAKE_IMM(INAT_IMM_BYTE), + [0x25] = INAT_MAKE_IMM(INAT_IMM_VWORD32), + [0x26] = INAT_MAKE_PREFIX(INAT_PFX_ES), + [0x28] = INAT_MODRM, + [0x29] = INAT_MODRM, + [0x2a] = INAT_MODRM, + [0x2b] = INAT_MODRM, + [0x2c] = INAT_MAKE_IMM(INAT_IMM_BYTE), + [0x2d] = INAT_MAKE_IMM(INAT_IMM_VWORD32), + [0x2e] = INAT_MAKE_PREFIX(INAT_PFX_CS), + [0x30] = INAT_MODRM, + [0x31] = INAT_MODRM, + [0x32] = INAT_MODRM, + [0x33] = INAT_MODRM, + [0x34] = INAT_MAKE_IMM(INAT_IMM_BYTE), + [0x35] = INAT_MAKE_IMM(INAT_IMM_VWORD32), + [0x36] = INAT_MAKE_PREFIX(INAT_PFX_SS), + [0x38] = INAT_MODRM, + [0x39] = INAT_MODRM, + [0x3a] = INAT_MODRM, + [0x3b] = INAT_MODRM, + [0x3c] = INAT_MAKE_IMM(INAT_IMM_BYTE), + [0x3d] = INAT_MAKE_IMM(INAT_IMM_VWORD32), + [0x3e] = INAT_MAKE_PREFIX(INAT_PFX_DS), + [0x40] = INAT_MAKE_PREFIX(INAT_PFX_REX), + [0x41] = INAT_MAKE_PREFIX(INAT_PFX_REX), + [0x42] = INAT_MAKE_PREFIX(INAT_PFX_REX), + [0x43] = INAT_MAKE_PREFIX(INAT_PFX_REX), + [0x44] = INAT_MAKE_PREFIX(INAT_PFX_REX), + [0x45] = INAT_MAKE_PREFIX(INAT_PFX_REX), + [0x46] = INAT_MAKE_PREFIX(INAT_PFX_REX), + [0x47] = INAT_MAKE_PREFIX(INAT_PFX_REX), + [0x48] = INAT_MAKE_PREFIX(INAT_PFX_REX), + [0x49] = INAT_MAKE_PREFIX(INAT_PFX_REX), + [0x4a] = INAT_MAKE_PREFIX(INAT_PFX_REX), + [0x4b] = INAT_MAKE_PREFIX(INAT_PFX_REX), + [0x4c] = INAT_MAKE_PREFIX(INAT_PFX_REX), + [0x4d] = INAT_MAKE_PREFIX(INAT_PFX_REX), + [0x4e] = INAT_MAKE_PREFIX(INAT_PFX_REX), + [0x4f] = INAT_MAKE_PREFIX(INAT_PFX_REX), + [0x50] = INAT_FORCE64, + [0x51] = INAT_FORCE64, + [0x52] = INAT_FORCE64, + [0x53] = INAT_FORCE64, + [0x54] = INAT_FORCE64, + [0x55] = INAT_FORCE64, + [0x56] = INAT_FORCE64, + [0x57] = INAT_FORCE64, + [0x58] = INAT_FORCE64, + [0x59] = INAT_FORCE64, + [0x5a] = INAT_FORCE64, + [0x5b] = INAT_FORCE64, + [0x5c] = INAT_FORCE64, + [0x5d] = INAT_FORCE64, + [0x5e] = INAT_FORCE64, + [0x5f] = INAT_FORCE64, + [0x62] = INAT_MODRM, + [0x63] = INAT_MODRM | INAT_MODRM, + [0x64] = INAT_MAKE_PREFIX(INAT_PFX_FS), + [0x65] = INAT_MAKE_PREFIX(INAT_PFX_GS), + [0x66] = INAT_MAKE_PREFIX(INAT_PFX_OPNDSZ), + [0x67] = INAT_MAKE_PREFIX(INAT_PFX_ADDRSZ), + [0x68] = INAT_MAKE_IMM(INAT_IMM_VWORD32) | INAT_FORCE64, + [0x69] = INAT_MAKE_IMM(INAT_IMM_VWORD32) | INAT_MODRM, + [0x6a] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_FORCE64, + [0x6b] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM, + [0x70] = INAT_MAKE_IMM(INAT_IMM_BYTE), + [0x71] = INAT_MAKE_IMM(INAT_IMM_BYTE), + [0x72] = INAT_MAKE_IMM(INAT_IMM_BYTE), + [0x73] = INAT_MAKE_IMM(INAT_IMM_BYTE), + [0x74] = INAT_MAKE_IMM(INAT_IMM_BYTE), + [0x75] = INAT_MAKE_IMM(INAT_IMM_BYTE), + [0x76] = INAT_MAKE_IMM(INAT_IMM_BYTE), + [0x77] = INAT_MAKE_IMM(INAT_IMM_BYTE), + [0x78] = INAT_MAKE_IMM(INAT_IMM_BYTE), + [0x79] = INAT_MAKE_IMM(INAT_IMM_BYTE), + [0x7a] = INAT_MAKE_IMM(INAT_IMM_BYTE), + [0x7b] = INAT_MAKE_IMM(INAT_IMM_BYTE), + [0x7c] = INAT_MAKE_IMM(INAT_IMM_BYTE), + [0x7d] = INAT_MAKE_IMM(INAT_IMM_BYTE), + [0x7e] = INAT_MAKE_IMM(INAT_IMM_BYTE), + [0x7f] = INAT_MAKE_IMM(INAT_IMM_BYTE), + [0x80] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_MAKE_GROUP(1), + [0x81] = INAT_MAKE_IMM(INAT_IMM_VWORD32) | INAT_MODRM | INAT_MAKE_GROUP(1), + [0x82] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_MAKE_GROUP(1), + [0x83] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_MAKE_GROUP(1), + [0x84] = INAT_MODRM, + [0x85] = INAT_MODRM, + [0x86] = INAT_MODRM, + [0x87] = INAT_MODRM, + [0x88] = INAT_MODRM, + [0x89] = INAT_MODRM, + [0x8a] = INAT_MODRM, + [0x8b] = INAT_MODRM, + [0x8c] = INAT_MODRM, + [0x8d] = INAT_MODRM, + [0x8e] = INAT_MODRM, + [0x8f] = INAT_MAKE_GROUP(2) | INAT_MODRM | INAT_FORCE64, + [0x9a] = INAT_MAKE_IMM(INAT_IMM_PTR), + [0x9c] = INAT_FORCE64, + [0x9d] = INAT_FORCE64, + [0xa0] = INAT_MOFFSET, + [0xa1] = INAT_MOFFSET, + [0xa2] = INAT_MOFFSET, + [0xa3] = INAT_MOFFSET, + [0xa8] = INAT_MAKE_IMM(INAT_IMM_BYTE), + [0xa9] = INAT_MAKE_IMM(INAT_IMM_VWORD32), + [0xb0] = INAT_MAKE_IMM(INAT_IMM_BYTE), + [0xb1] = INAT_MAKE_IMM(INAT_IMM_BYTE), + [0xb2] = INAT_MAKE_IMM(INAT_IMM_BYTE), + [0xb3] = INAT_MAKE_IMM(INAT_IMM_BYTE), + [0xb4] = INAT_MAKE_IMM(INAT_IMM_BYTE), + [0xb5] = INAT_MAKE_IMM(INAT_IMM_BYTE), + [0xb6] = INAT_MAKE_IMM(INAT_IMM_BYTE), + [0xb7] = INAT_MAKE_IMM(INAT_IMM_BYTE), + [0xb8] = INAT_MAKE_IMM(INAT_IMM_VWORD), + [0xb9] = INAT_MAKE_IMM(INAT_IMM_VWORD), + [0xba] = INAT_MAKE_IMM(INAT_IMM_VWORD), + [0xbb] = INAT_MAKE_IMM(INAT_IMM_VWORD), + [0xbc] = INAT_MAKE_IMM(INAT_IMM_VWORD), + [0xbd] = INAT_MAKE_IMM(INAT_IMM_VWORD), + [0xbe] = INAT_MAKE_IMM(INAT_IMM_VWORD), + [0xbf] = INAT_MAKE_IMM(INAT_IMM_VWORD), + [0xc0] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_MAKE_GROUP(3), + [0xc1] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_MAKE_GROUP(3), + [0xc2] = INAT_MAKE_IMM(INAT_IMM_WORD) | INAT_FORCE64, + [0xc4] = INAT_MODRM | INAT_MAKE_PREFIX(INAT_PFX_VEX3), + [0xc5] = INAT_MODRM | INAT_MAKE_PREFIX(INAT_PFX_VEX2), + [0xc6] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_MAKE_GROUP(4), + [0xc7] = INAT_MAKE_IMM(INAT_IMM_VWORD32) | INAT_MODRM | INAT_MAKE_GROUP(5), + [0xc8] = INAT_MAKE_IMM(INAT_IMM_WORD) | INAT_SCNDIMM, + [0xc9] = INAT_FORCE64, + [0xca] = INAT_MAKE_IMM(INAT_IMM_WORD), + [0xcd] = INAT_MAKE_IMM(INAT_IMM_BYTE), + [0xd0] = INAT_MODRM | INAT_MAKE_GROUP(3), + [0xd1] = INAT_MODRM | INAT_MAKE_GROUP(3), + [0xd2] = INAT_MODRM | INAT_MAKE_GROUP(3), + [0xd3] = INAT_MODRM | INAT_MAKE_GROUP(3), + [0xd4] = INAT_MAKE_IMM(INAT_IMM_BYTE), + [0xd5] = INAT_MAKE_IMM(INAT_IMM_BYTE), + [0xd8] = INAT_MODRM, + [0xd9] = INAT_MODRM, + [0xda] = INAT_MODRM, + [0xdb] = INAT_MODRM, + [0xdc] = INAT_MODRM, + [0xdd] = INAT_MODRM, + [0xde] = INAT_MODRM, + [0xdf] = INAT_MODRM, + [0xe0] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_FORCE64, + [0xe1] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_FORCE64, + [0xe2] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_FORCE64, + [0xe3] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_FORCE64, + [0xe4] = INAT_MAKE_IMM(INAT_IMM_BYTE), + [0xe5] = INAT_MAKE_IMM(INAT_IMM_BYTE), + [0xe6] = INAT_MAKE_IMM(INAT_IMM_BYTE), + [0xe7] = INAT_MAKE_IMM(INAT_IMM_BYTE), + [0xe8] = INAT_MAKE_IMM(INAT_IMM_VWORD32) | INAT_FORCE64, + [0xe9] = INAT_MAKE_IMM(INAT_IMM_VWORD32) | INAT_FORCE64, + [0xea] = INAT_MAKE_IMM(INAT_IMM_PTR), + [0xeb] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_FORCE64, + [0xf0] = INAT_MAKE_PREFIX(INAT_PFX_LOCK), + [0xf2] = INAT_MAKE_PREFIX(INAT_PFX_REPNE) | INAT_MAKE_PREFIX(INAT_PFX_REPNE), + [0xf3] = INAT_MAKE_PREFIX(INAT_PFX_REPE) | INAT_MAKE_PREFIX(INAT_PFX_REPE), + [0xf6] = INAT_MODRM | INAT_MAKE_GROUP(6), + [0xf7] = INAT_MODRM | INAT_MAKE_GROUP(7), + [0xfe] = INAT_MAKE_GROUP(8), + [0xff] = INAT_MAKE_GROUP(9), +}; + +/* Table: 2-byte opcode (0x0f) */ +const insn_attr_t inat_escape_table_1[INAT_OPCODE_TABLE_SIZE] = { + [0x00] = INAT_MAKE_GROUP(10), + [0x01] = INAT_MAKE_GROUP(11), + [0x02] = INAT_MODRM, + [0x03] = INAT_MODRM, + [0x0d] = INAT_MAKE_GROUP(12), + [0x0f] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM, + [0x10] = INAT_MODRM | INAT_VEXOK | INAT_VARIANT, + [0x11] = INAT_MODRM | INAT_VEXOK | INAT_VARIANT, + [0x12] = INAT_MODRM | INAT_VEXOK | INAT_MODRM | INAT_VEXOK | INAT_VARIANT, + [0x13] = INAT_MODRM | INAT_VEXOK | INAT_VARIANT, + [0x14] = INAT_MODRM | INAT_VEXOK | INAT_VARIANT, + [0x15] = INAT_MODRM | INAT_VEXOK | INAT_VARIANT, + [0x16] = INAT_MODRM | INAT_VEXOK | INAT_MODRM | INAT_VEXOK | INAT_VARIANT, + [0x17] = INAT_MODRM | INAT_VEXOK | INAT_VARIANT, + [0x18] = INAT_MAKE_GROUP(13), + [0x1a] = INAT_MODRM | INAT_MODRM | INAT_MODRM | INAT_MODRM, + [0x1b] = INAT_MODRM | INAT_MODRM | INAT_MODRM | INAT_MODRM, + [0x1f] = INAT_MODRM, + [0x20] = INAT_MODRM, + [0x21] = INAT_MODRM, + [0x22] = INAT_MODRM, + [0x23] = INAT_MODRM, + [0x28] = INAT_MODRM | INAT_VEXOK | INAT_VARIANT, + [0x29] = INAT_MODRM | INAT_VEXOK | INAT_VARIANT, + [0x2a] = INAT_MODRM | INAT_VARIANT, + [0x2b] = INAT_MODRM | INAT_VEXOK | INAT_VARIANT, + [0x2c] = INAT_MODRM | INAT_VARIANT, + [0x2d] = INAT_MODRM | INAT_VARIANT, + [0x2e] = INAT_MODRM | INAT_VEXOK | INAT_VARIANT, + [0x2f] = INAT_MODRM | INAT_VEXOK | INAT_VARIANT, + [0x38] = INAT_MAKE_ESCAPE(2), + [0x3a] = INAT_MAKE_ESCAPE(3), + [0x40] = INAT_MODRM, + [0x41] = INAT_MODRM, + [0x42] = INAT_MODRM, + [0x43] = INAT_MODRM, + [0x44] = INAT_MODRM, + [0x45] = INAT_MODRM, + [0x46] = INAT_MODRM, + [0x47] = INAT_MODRM, + [0x48] = INAT_MODRM, + [0x49] = INAT_MODRM, + [0x4a] = INAT_MODRM, + [0x4b] = INAT_MODRM, + [0x4c] = INAT_MODRM, + [0x4d] = INAT_MODRM, + [0x4e] = INAT_MODRM, + [0x4f] = INAT_MODRM, + [0x50] = INAT_MODRM | INAT_VEXOK | INAT_VARIANT, + [0x51] = INAT_MODRM | INAT_VEXOK | INAT_VARIANT, + [0x52] = INAT_MODRM | INAT_VEXOK | INAT_VARIANT, + [0x53] = INAT_MODRM | INAT_VEXOK | INAT_VARIANT, + [0x54] = INAT_MODRM | INAT_VEXOK | INAT_VARIANT, + [0x55] = INAT_MODRM | INAT_VEXOK | INAT_VARIANT, + [0x56] = INAT_MODRM | INAT_VEXOK | INAT_VARIANT, + [0x57] = INAT_MODRM | INAT_VEXOK | INAT_VARIANT, + [0x58] = INAT_MODRM | INAT_VEXOK | INAT_VARIANT, + [0x59] = INAT_MODRM | INAT_VEXOK | INAT_VARIANT, + [0x5a] = INAT_MODRM | INAT_VEXOK | INAT_VARIANT, + [0x5b] = INAT_MODRM | INAT_VEXOK | INAT_VARIANT, + [0x5c] = INAT_MODRM | INAT_VEXOK | INAT_VARIANT, + [0x5d] = INAT_MODRM | INAT_VEXOK | INAT_VARIANT, + [0x5e] = INAT_MODRM | INAT_VEXOK | INAT_VARIANT, + [0x5f] = INAT_MODRM | INAT_VEXOK | INAT_VARIANT, + [0x60] = INAT_MODRM | INAT_VARIANT, + [0x61] = INAT_MODRM | INAT_VARIANT, + [0x62] = INAT_MODRM | INAT_VARIANT, + [0x63] = INAT_MODRM | INAT_VARIANT, + [0x64] = INAT_MODRM | INAT_VARIANT, + [0x65] = INAT_MODRM | INAT_VARIANT, + [0x66] = INAT_MODRM | INAT_VARIANT, + [0x67] = INAT_MODRM | INAT_VARIANT, + [0x68] = INAT_MODRM | INAT_VARIANT, + [0x69] = INAT_MODRM | INAT_VARIANT, + [0x6a] = INAT_MODRM | INAT_VARIANT, + [0x6b] = INAT_MODRM | INAT_VARIANT, + [0x6c] = INAT_VARIANT, + [0x6d] = INAT_VARIANT, + [0x6e] = INAT_MODRM | INAT_VARIANT, + [0x6f] = INAT_MODRM | INAT_VARIANT, + [0x70] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VARIANT, + [0x71] = INAT_MAKE_GROUP(14), + [0x72] = INAT_MAKE_GROUP(15), + [0x73] = INAT_MAKE_GROUP(16), + [0x74] = INAT_MODRM | INAT_VARIANT, + [0x75] = INAT_MODRM | INAT_VARIANT, + [0x76] = INAT_MODRM | INAT_VARIANT, + [0x77] = INAT_VEXOK | INAT_VEXOK, + [0x78] = INAT_MODRM, + [0x79] = INAT_MODRM, + [0x7c] = INAT_VARIANT, + [0x7d] = INAT_VARIANT, + [0x7e] = INAT_MODRM | INAT_VARIANT, + [0x7f] = INAT_MODRM | INAT_VARIANT, + [0x80] = INAT_MAKE_IMM(INAT_IMM_VWORD32) | INAT_FORCE64, + [0x81] = INAT_MAKE_IMM(INAT_IMM_VWORD32) | INAT_FORCE64, + [0x82] = INAT_MAKE_IMM(INAT_IMM_VWORD32) | INAT_FORCE64, + [0x83] = INAT_MAKE_IMM(INAT_IMM_VWORD32) | INAT_FORCE64, + [0x84] = INAT_MAKE_IMM(INAT_IMM_VWORD32) | INAT_FORCE64, + [0x85] = INAT_MAKE_IMM(INAT_IMM_VWORD32) | INAT_FORCE64, + [0x86] = INAT_MAKE_IMM(INAT_IMM_VWORD32) | INAT_FORCE64, + [0x87] = INAT_MAKE_IMM(INAT_IMM_VWORD32) | INAT_FORCE64, + [0x88] = INAT_MAKE_IMM(INAT_IMM_VWORD32) | INAT_FORCE64, + [0x89] = INAT_MAKE_IMM(INAT_IMM_VWORD32) | INAT_FORCE64, + [0x8a] = INAT_MAKE_IMM(INAT_IMM_VWORD32) | INAT_FORCE64, + [0x8b] = INAT_MAKE_IMM(INAT_IMM_VWORD32) | INAT_FORCE64, + [0x8c] = INAT_MAKE_IMM(INAT_IMM_VWORD32) | INAT_FORCE64, + [0x8d] = INAT_MAKE_IMM(INAT_IMM_VWORD32) | INAT_FORCE64, + [0x8e] = INAT_MAKE_IMM(INAT_IMM_VWORD32) | INAT_FORCE64, + [0x8f] = INAT_MAKE_IMM(INAT_IMM_VWORD32) | INAT_FORCE64, + [0x90] = INAT_MODRM, + [0x91] = INAT_MODRM, + [0x92] = INAT_MODRM, + [0x93] = INAT_MODRM, + [0x94] = INAT_MODRM, + [0x95] = INAT_MODRM, + [0x96] = INAT_MODRM, + [0x97] = INAT_MODRM, + [0x98] = INAT_MODRM, + [0x99] = INAT_MODRM, + [0x9a] = INAT_MODRM, + [0x9b] = INAT_MODRM, + [0x9c] = INAT_MODRM, + [0x9d] = INAT_MODRM, + [0x9e] = INAT_MODRM, + [0x9f] = INAT_MODRM, + [0xa0] = INAT_FORCE64, + [0xa1] = INAT_FORCE64, + [0xa3] = INAT_MODRM, + [0xa4] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM, + [0xa5] = INAT_MODRM, + [0xa6] = INAT_MAKE_GROUP(17), + [0xa7] = INAT_MAKE_GROUP(18), + [0xa8] = INAT_FORCE64, + [0xa9] = INAT_FORCE64, + [0xab] = INAT_MODRM, + [0xac] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM, + [0xad] = INAT_MODRM, + [0xae] = INAT_MAKE_GROUP(19), + [0xaf] = INAT_MODRM, + [0xb0] = INAT_MODRM, + [0xb1] = INAT_MODRM, + [0xb2] = INAT_MODRM, + [0xb3] = INAT_MODRM, + [0xb4] = INAT_MODRM, + [0xb5] = INAT_MODRM, + [0xb6] = INAT_MODRM, + [0xb7] = INAT_MODRM, + [0xb8] = INAT_VARIANT, + [0xb9] = INAT_MAKE_GROUP(20), + [0xba] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_MAKE_GROUP(21), + [0xbb] = INAT_MODRM, + [0xbc] = INAT_MODRM | INAT_VARIANT, + [0xbd] = INAT_MODRM | INAT_VARIANT, + [0xbe] = INAT_MODRM, + [0xbf] = INAT_MODRM, + [0xc0] = INAT_MODRM, + [0xc1] = INAT_MODRM, + [0xc2] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK | INAT_VARIANT, + [0xc3] = INAT_MODRM, + [0xc4] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VARIANT, + [0xc5] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VARIANT, + [0xc6] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK | INAT_VARIANT, + [0xc7] = INAT_MAKE_GROUP(22), + [0xd0] = INAT_VARIANT, + [0xd1] = INAT_MODRM | INAT_VARIANT, + [0xd2] = INAT_MODRM | INAT_VARIANT, + [0xd3] = INAT_MODRM | INAT_VARIANT, + [0xd4] = INAT_MODRM | INAT_VARIANT, + [0xd5] = INAT_MODRM | INAT_VARIANT, + [0xd6] = INAT_VARIANT, + [0xd7] = INAT_MODRM | INAT_VARIANT, + [0xd8] = INAT_MODRM | INAT_VARIANT, + [0xd9] = INAT_MODRM | INAT_VARIANT, + [0xda] = INAT_MODRM | INAT_VARIANT, + [0xdb] = INAT_MODRM | INAT_VARIANT, + [0xdc] = INAT_MODRM | INAT_VARIANT, + [0xdd] = INAT_MODRM | INAT_VARIANT, + [0xde] = INAT_MODRM | INAT_VARIANT, + [0xdf] = INAT_MODRM | INAT_VARIANT, + [0xe0] = INAT_MODRM | INAT_VARIANT, + [0xe1] = INAT_MODRM | INAT_VARIANT, + [0xe2] = INAT_MODRM | INAT_VARIANT, + [0xe3] = INAT_MODRM | INAT_VARIANT, + [0xe4] = INAT_MODRM | INAT_VARIANT, + [0xe5] = INAT_MODRM | INAT_VARIANT, + [0xe6] = INAT_VARIANT, + [0xe7] = INAT_MODRM | INAT_VARIANT, + [0xe8] = INAT_MODRM | INAT_VARIANT, + [0xe9] = INAT_MODRM | INAT_VARIANT, + [0xea] = INAT_MODRM | INAT_VARIANT, + [0xeb] = INAT_MODRM | INAT_VARIANT, + [0xec] = INAT_MODRM | INAT_VARIANT, + [0xed] = INAT_MODRM | INAT_VARIANT, + [0xee] = INAT_MODRM | INAT_VARIANT, + [0xef] = INAT_MODRM | INAT_VARIANT, + [0xf0] = INAT_VARIANT, + [0xf1] = INAT_MODRM | INAT_VARIANT, + [0xf2] = INAT_MODRM | INAT_VARIANT, + [0xf3] = INAT_MODRM | INAT_VARIANT, + [0xf4] = INAT_MODRM | INAT_VARIANT, + [0xf5] = INAT_MODRM | INAT_VARIANT, + [0xf6] = INAT_MODRM | INAT_VARIANT, + [0xf7] = INAT_MODRM | INAT_VARIANT, + [0xf8] = INAT_MODRM | INAT_VARIANT, + [0xf9] = INAT_MODRM | INAT_VARIANT, + [0xfa] = INAT_MODRM | INAT_VARIANT, + [0xfb] = INAT_MODRM | INAT_VARIANT, + [0xfc] = INAT_MODRM | INAT_VARIANT, + [0xfd] = INAT_MODRM | INAT_VARIANT, + [0xfe] = INAT_MODRM | INAT_VARIANT, +}; +const insn_attr_t inat_escape_table_1_1[INAT_OPCODE_TABLE_SIZE] = { + [0x10] = INAT_MODRM | INAT_VEXOK, + [0x11] = INAT_MODRM | INAT_VEXOK, + [0x12] = INAT_MODRM | INAT_VEXOK, + [0x13] = INAT_MODRM | INAT_VEXOK, + [0x14] = INAT_MODRM | INAT_VEXOK, + [0x15] = INAT_MODRM | INAT_VEXOK, + [0x16] = INAT_MODRM | INAT_VEXOK, + [0x17] = INAT_MODRM | INAT_VEXOK, + [0x28] = INAT_MODRM | INAT_VEXOK, + [0x29] = INAT_MODRM | INAT_VEXOK, + [0x2a] = INAT_MODRM, + [0x2b] = INAT_MODRM | INAT_VEXOK, + [0x2c] = INAT_MODRM, + [0x2d] = INAT_MODRM, + [0x2e] = INAT_MODRM | INAT_VEXOK, + [0x2f] = INAT_MODRM | INAT_VEXOK, + [0x50] = INAT_MODRM | INAT_VEXOK, + [0x51] = INAT_MODRM | INAT_VEXOK, + [0x54] = INAT_MODRM | INAT_VEXOK, + [0x55] = INAT_MODRM | INAT_VEXOK, + [0x56] = INAT_MODRM | INAT_VEXOK, + [0x57] = INAT_MODRM | INAT_VEXOK, + [0x58] = INAT_MODRM | INAT_VEXOK, + [0x59] = INAT_MODRM | INAT_VEXOK, + [0x5a] = INAT_MODRM | INAT_VEXOK, + [0x5b] = INAT_MODRM | INAT_VEXOK, + [0x5c] = INAT_MODRM | INAT_VEXOK, + [0x5d] = INAT_MODRM | INAT_VEXOK, + [0x5e] = INAT_MODRM | INAT_VEXOK, + [0x5f] = INAT_MODRM | INAT_VEXOK, + [0x60] = INAT_MODRM | INAT_VEXOK, + [0x61] = INAT_MODRM | INAT_VEXOK, + [0x62] = INAT_MODRM | INAT_VEXOK, + [0x63] = INAT_MODRM | INAT_VEXOK, + [0x64] = INAT_MODRM | INAT_VEXOK, + [0x65] = INAT_MODRM | INAT_VEXOK, + [0x66] = INAT_MODRM | INAT_VEXOK, + [0x67] = INAT_MODRM | INAT_VEXOK, + [0x68] = INAT_MODRM | INAT_VEXOK, + [0x69] = INAT_MODRM | INAT_VEXOK, + [0x6a] = INAT_MODRM | INAT_VEXOK, + [0x6b] = INAT_MODRM | INAT_VEXOK, + [0x6c] = INAT_MODRM | INAT_VEXOK, + [0x6d] = INAT_MODRM | INAT_VEXOK, + [0x6e] = INAT_MODRM | INAT_VEXOK, + [0x6f] = INAT_MODRM | INAT_VEXOK, + [0x70] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, + [0x74] = INAT_MODRM | INAT_VEXOK, + [0x75] = INAT_MODRM | INAT_VEXOK, + [0x76] = INAT_MODRM | INAT_VEXOK, + [0x7c] = INAT_MODRM | INAT_VEXOK, + [0x7d] = INAT_MODRM | INAT_VEXOK, + [0x7e] = INAT_MODRM | INAT_VEXOK, + [0x7f] = INAT_MODRM | INAT_VEXOK, + [0xbc] = INAT_MODRM, + [0xbd] = INAT_MODRM, + [0xc2] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, + [0xc4] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, + [0xc5] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, + [0xc6] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, + [0xd0] = INAT_MODRM | INAT_VEXOK, + [0xd1] = INAT_MODRM | INAT_VEXOK, + [0xd2] = INAT_MODRM | INAT_VEXOK, + [0xd3] = INAT_MODRM | INAT_VEXOK, + [0xd4] = INAT_MODRM | INAT_VEXOK, + [0xd5] = INAT_MODRM | INAT_VEXOK, + [0xd6] = INAT_MODRM | INAT_VEXOK, + [0xd7] = INAT_MODRM | INAT_VEXOK, + [0xd8] = INAT_MODRM | INAT_VEXOK, + [0xd9] = INAT_MODRM | INAT_VEXOK, + [0xda] = INAT_MODRM | INAT_VEXOK, + [0xdb] = INAT_MODRM | INAT_VEXOK, + [0xdc] = INAT_MODRM | INAT_VEXOK, + [0xdd] = INAT_MODRM | INAT_VEXOK, + [0xde] = INAT_MODRM | INAT_VEXOK, + [0xdf] = INAT_MODRM | INAT_VEXOK, + [0xe0] = INAT_MODRM | INAT_VEXOK, + [0xe1] = INAT_MODRM | INAT_VEXOK, + [0xe2] = INAT_MODRM | INAT_VEXOK, + [0xe3] = INAT_MODRM | INAT_VEXOK, + [0xe4] = INAT_MODRM | INAT_VEXOK, + [0xe5] = INAT_MODRM | INAT_VEXOK, + [0xe6] = INAT_MODRM | INAT_VEXOK, + [0xe7] = INAT_MODRM | INAT_VEXOK, + [0xe8] = INAT_MODRM | INAT_VEXOK, + [0xe9] = INAT_MODRM | INAT_VEXOK, + [0xea] = INAT_MODRM | INAT_VEXOK, + [0xeb] = INAT_MODRM | INAT_VEXOK, + [0xec] = INAT_MODRM | INAT_VEXOK, + [0xed] = INAT_MODRM | INAT_VEXOK, + [0xee] = INAT_MODRM | INAT_VEXOK, + [0xef] = INAT_MODRM | INAT_VEXOK, + [0xf1] = INAT_MODRM | INAT_VEXOK, + [0xf2] = INAT_MODRM | INAT_VEXOK, + [0xf3] = INAT_MODRM | INAT_VEXOK, + [0xf4] = INAT_MODRM | INAT_VEXOK, + [0xf5] = INAT_MODRM | INAT_VEXOK, + [0xf6] = INAT_MODRM | INAT_VEXOK, + [0xf7] = INAT_MODRM | INAT_VEXOK, + [0xf8] = INAT_MODRM | INAT_VEXOK, + [0xf9] = INAT_MODRM | INAT_VEXOK, + [0xfa] = INAT_MODRM | INAT_VEXOK, + [0xfb] = INAT_MODRM | INAT_VEXOK, + [0xfc] = INAT_MODRM | INAT_VEXOK, + [0xfd] = INAT_MODRM | INAT_VEXOK, + [0xfe] = INAT_MODRM | INAT_VEXOK, +}; +const insn_attr_t inat_escape_table_1_2[INAT_OPCODE_TABLE_SIZE] = { + [0x10] = INAT_MODRM | INAT_VEXOK, + [0x11] = INAT_MODRM | INAT_VEXOK, + [0x12] = INAT_MODRM | INAT_VEXOK, + [0x16] = INAT_MODRM | INAT_VEXOK, + [0x2a] = INAT_MODRM | INAT_VEXOK, + [0x2c] = INAT_MODRM | INAT_VEXOK, + [0x2d] = INAT_MODRM | INAT_VEXOK, + [0x51] = INAT_MODRM | INAT_VEXOK, + [0x52] = INAT_MODRM | INAT_VEXOK, + [0x53] = INAT_MODRM | INAT_VEXOK, + [0x58] = INAT_MODRM | INAT_VEXOK, + [0x59] = INAT_MODRM | INAT_VEXOK, + [0x5a] = INAT_MODRM | INAT_VEXOK, + [0x5b] = INAT_MODRM | INAT_VEXOK, + [0x5c] = INAT_MODRM | INAT_VEXOK, + [0x5d] = INAT_MODRM | INAT_VEXOK, + [0x5e] = INAT_MODRM | INAT_VEXOK, + [0x5f] = INAT_MODRM | INAT_VEXOK, + [0x6f] = INAT_MODRM | INAT_VEXOK, + [0x70] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, + [0x7e] = INAT_MODRM | INAT_VEXOK, + [0x7f] = INAT_MODRM | INAT_VEXOK, + [0xb8] = INAT_MODRM, + [0xbc] = INAT_MODRM, + [0xbd] = INAT_MODRM, + [0xc2] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, + [0xd6] = INAT_MODRM, + [0xe6] = INAT_MODRM | INAT_VEXOK, +}; +const insn_attr_t inat_escape_table_1_3[INAT_OPCODE_TABLE_SIZE] = { + [0x10] = INAT_MODRM | INAT_VEXOK, + [0x11] = INAT_MODRM | INAT_VEXOK, + [0x12] = INAT_MODRM | INAT_VEXOK, + [0x2a] = INAT_MODRM | INAT_VEXOK, + [0x2c] = INAT_MODRM | INAT_VEXOK, + [0x2d] = INAT_MODRM | INAT_VEXOK, + [0x51] = INAT_MODRM | INAT_VEXOK, + [0x58] = INAT_MODRM | INAT_VEXOK, + [0x59] = INAT_MODRM | INAT_VEXOK, + [0x5a] = INAT_MODRM | INAT_VEXOK, + [0x5c] = INAT_MODRM | INAT_VEXOK, + [0x5d] = INAT_MODRM | INAT_VEXOK, + [0x5e] = INAT_MODRM | INAT_VEXOK, + [0x5f] = INAT_MODRM | INAT_VEXOK, + [0x70] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, + [0x7c] = INAT_MODRM | INAT_VEXOK, + [0x7d] = INAT_MODRM | INAT_VEXOK, + [0xbc] = INAT_MODRM, + [0xbd] = INAT_MODRM, + [0xc2] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, + [0xd0] = INAT_MODRM | INAT_VEXOK, + [0xd6] = INAT_MODRM, + [0xe6] = INAT_MODRM | INAT_VEXOK, + [0xf0] = INAT_MODRM | INAT_VEXOK, +}; + +/* Table: 3-byte opcode 1 (0x0f 0x38) */ +const insn_attr_t inat_escape_table_2[INAT_OPCODE_TABLE_SIZE] = { + [0x00] = INAT_MODRM | INAT_VARIANT, + [0x01] = INAT_MODRM | INAT_VARIANT, + [0x02] = INAT_MODRM | INAT_VARIANT, + [0x03] = INAT_MODRM | INAT_VARIANT, + [0x04] = INAT_MODRM | INAT_VARIANT, + [0x05] = INAT_MODRM | INAT_VARIANT, + [0x06] = INAT_MODRM | INAT_VARIANT, + [0x07] = INAT_MODRM | INAT_VARIANT, + [0x08] = INAT_MODRM | INAT_VARIANT, + [0x09] = INAT_MODRM | INAT_VARIANT, + [0x0a] = INAT_MODRM | INAT_VARIANT, + [0x0b] = INAT_MODRM | INAT_VARIANT, + [0x0c] = INAT_VARIANT, + [0x0d] = INAT_VARIANT, + [0x0e] = INAT_VARIANT, + [0x0f] = INAT_VARIANT, + [0x10] = INAT_VARIANT, + [0x13] = INAT_VARIANT, + [0x14] = INAT_VARIANT, + [0x15] = INAT_VARIANT, + [0x16] = INAT_VARIANT, + [0x17] = INAT_VARIANT, + [0x18] = INAT_VARIANT, + [0x19] = INAT_VARIANT, + [0x1a] = INAT_VARIANT, + [0x1c] = INAT_MODRM | INAT_VARIANT, + [0x1d] = INAT_MODRM | INAT_VARIANT, + [0x1e] = INAT_MODRM | INAT_VARIANT, + [0x20] = INAT_VARIANT, + [0x21] = INAT_VARIANT, + [0x22] = INAT_VARIANT, + [0x23] = INAT_VARIANT, + [0x24] = INAT_VARIANT, + [0x25] = INAT_VARIANT, + [0x28] = INAT_VARIANT, + [0x29] = INAT_VARIANT, + [0x2a] = INAT_VARIANT, + [0x2b] = INAT_VARIANT, + [0x2c] = INAT_VARIANT, + [0x2d] = INAT_VARIANT, + [0x2e] = INAT_VARIANT, + [0x2f] = INAT_VARIANT, + [0x30] = INAT_VARIANT, + [0x31] = INAT_VARIANT, + [0x32] = INAT_VARIANT, + [0x33] = INAT_VARIANT, + [0x34] = INAT_VARIANT, + [0x35] = INAT_VARIANT, + [0x36] = INAT_VARIANT, + [0x37] = INAT_VARIANT, + [0x38] = INAT_VARIANT, + [0x39] = INAT_VARIANT, + [0x3a] = INAT_VARIANT, + [0x3b] = INAT_VARIANT, + [0x3c] = INAT_VARIANT, + [0x3d] = INAT_VARIANT, + [0x3e] = INAT_VARIANT, + [0x3f] = INAT_VARIANT, + [0x40] = INAT_VARIANT, + [0x41] = INAT_VARIANT, + [0x45] = INAT_VARIANT, + [0x46] = INAT_VARIANT, + [0x47] = INAT_VARIANT, + [0x58] = INAT_VARIANT, + [0x59] = INAT_VARIANT, + [0x5a] = INAT_VARIANT, + [0x78] = INAT_VARIANT, + [0x79] = INAT_VARIANT, + [0x80] = INAT_VARIANT, + [0x81] = INAT_VARIANT, + [0x82] = INAT_VARIANT, + [0x8c] = INAT_VARIANT, + [0x8e] = INAT_VARIANT, + [0x90] = INAT_VARIANT, + [0x91] = INAT_VARIANT, + [0x92] = INAT_VARIANT, + [0x93] = INAT_VARIANT, + [0x96] = INAT_VARIANT, + [0x97] = INAT_VARIANT, + [0x98] = INAT_VARIANT, + [0x99] = INAT_VARIANT, + [0x9a] = INAT_VARIANT, + [0x9b] = INAT_VARIANT, + [0x9c] = INAT_VARIANT, + [0x9d] = INAT_VARIANT, + [0x9e] = INAT_VARIANT, + [0x9f] = INAT_VARIANT, + [0xa6] = INAT_VARIANT, + [0xa7] = INAT_VARIANT, + [0xa8] = INAT_VARIANT, + [0xa9] = INAT_VARIANT, + [0xaa] = INAT_VARIANT, + [0xab] = INAT_VARIANT, + [0xac] = INAT_VARIANT, + [0xad] = INAT_VARIANT, + [0xae] = INAT_VARIANT, + [0xaf] = INAT_VARIANT, + [0xb6] = INAT_VARIANT, + [0xb7] = INAT_VARIANT, + [0xb8] = INAT_VARIANT, + [0xb9] = INAT_VARIANT, + [0xba] = INAT_VARIANT, + [0xbb] = INAT_VARIANT, + [0xbc] = INAT_VARIANT, + [0xbd] = INAT_VARIANT, + [0xbe] = INAT_VARIANT, + [0xbf] = INAT_VARIANT, + [0xdb] = INAT_VARIANT, + [0xdc] = INAT_VARIANT, + [0xdd] = INAT_VARIANT, + [0xde] = INAT_VARIANT, + [0xdf] = INAT_VARIANT, + [0xf0] = INAT_MODRM | INAT_MODRM | INAT_VARIANT, + [0xf1] = INAT_MODRM | INAT_MODRM | INAT_VARIANT, + [0xf2] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0xf3] = INAT_MAKE_GROUP(23), + [0xf5] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY | INAT_VARIANT, + [0xf6] = INAT_VARIANT, + [0xf7] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY | INAT_VARIANT, +}; +const insn_attr_t inat_escape_table_2_1[INAT_OPCODE_TABLE_SIZE] = { + [0x00] = INAT_MODRM | INAT_VEXOK, + [0x01] = INAT_MODRM | INAT_VEXOK, + [0x02] = INAT_MODRM | INAT_VEXOK, + [0x03] = INAT_MODRM | INAT_VEXOK, + [0x04] = INAT_MODRM | INAT_VEXOK, + [0x05] = INAT_MODRM | INAT_VEXOK, + [0x06] = INAT_MODRM | INAT_VEXOK, + [0x07] = INAT_MODRM | INAT_VEXOK, + [0x08] = INAT_MODRM | INAT_VEXOK, + [0x09] = INAT_MODRM | INAT_VEXOK, + [0x0a] = INAT_MODRM | INAT_VEXOK, + [0x0b] = INAT_MODRM | INAT_VEXOK, + [0x0c] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0x0d] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0x0e] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0x0f] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0x10] = INAT_MODRM, + [0x13] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0x14] = INAT_MODRM, + [0x15] = INAT_MODRM, + [0x16] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0x17] = INAT_MODRM | INAT_VEXOK, + [0x18] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0x19] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0x1a] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0x1c] = INAT_MODRM | INAT_VEXOK, + [0x1d] = INAT_MODRM | INAT_VEXOK, + [0x1e] = INAT_MODRM | INAT_VEXOK, + [0x20] = INAT_MODRM | INAT_VEXOK, + [0x21] = INAT_MODRM | INAT_VEXOK, + [0x22] = INAT_MODRM | INAT_VEXOK, + [0x23] = INAT_MODRM | INAT_VEXOK, + [0x24] = INAT_MODRM | INAT_VEXOK, + [0x25] = INAT_MODRM | INAT_VEXOK, + [0x28] = INAT_MODRM | INAT_VEXOK, + [0x29] = INAT_MODRM | INAT_VEXOK, + [0x2a] = INAT_MODRM | INAT_VEXOK, + [0x2b] = INAT_MODRM | INAT_VEXOK, + [0x2c] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0x2d] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0x2e] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0x2f] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0x30] = INAT_MODRM | INAT_VEXOK, + [0x31] = INAT_MODRM | INAT_VEXOK, + [0x32] = INAT_MODRM | INAT_VEXOK, + [0x33] = INAT_MODRM | INAT_VEXOK, + [0x34] = INAT_MODRM | INAT_VEXOK, + [0x35] = INAT_MODRM | INAT_VEXOK, + [0x36] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0x37] = INAT_MODRM | INAT_VEXOK, + [0x38] = INAT_MODRM | INAT_VEXOK, + [0x39] = INAT_MODRM | INAT_VEXOK, + [0x3a] = INAT_MODRM | INAT_VEXOK, + [0x3b] = INAT_MODRM | INAT_VEXOK, + [0x3c] = INAT_MODRM | INAT_VEXOK, + [0x3d] = INAT_MODRM | INAT_VEXOK, + [0x3e] = INAT_MODRM | INAT_VEXOK, + [0x3f] = INAT_MODRM | INAT_VEXOK, + [0x40] = INAT_MODRM | INAT_VEXOK, + [0x41] = INAT_MODRM | INAT_VEXOK, + [0x45] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0x46] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0x47] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0x58] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0x59] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0x5a] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0x78] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0x79] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0x80] = INAT_MODRM, + [0x81] = INAT_MODRM, + [0x82] = INAT_MODRM, + [0x8c] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0x8e] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0x90] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0x91] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0x92] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0x93] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0x96] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0x97] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0x98] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0x99] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0x9a] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0x9b] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0x9c] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0x9d] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0x9e] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0x9f] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0xa6] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0xa7] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0xa8] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0xa9] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0xaa] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0xab] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0xac] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0xad] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0xae] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0xaf] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0xb6] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0xb7] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0xb8] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0xb9] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0xba] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0xbb] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0xbc] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0xbd] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0xbe] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0xbf] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0xdb] = INAT_MODRM | INAT_VEXOK, + [0xdc] = INAT_MODRM | INAT_VEXOK, + [0xdd] = INAT_MODRM | INAT_VEXOK, + [0xde] = INAT_MODRM | INAT_VEXOK, + [0xdf] = INAT_MODRM | INAT_VEXOK, + [0xf0] = INAT_MODRM, + [0xf1] = INAT_MODRM, + [0xf6] = INAT_MODRM, + [0xf7] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, +}; +const insn_attr_t inat_escape_table_2_2[INAT_OPCODE_TABLE_SIZE] = { + [0xf5] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0xf6] = INAT_MODRM, + [0xf7] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, +}; +const insn_attr_t inat_escape_table_2_3[INAT_OPCODE_TABLE_SIZE] = { + [0xf0] = INAT_MODRM | INAT_MODRM, + [0xf1] = INAT_MODRM | INAT_MODRM, + [0xf5] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0xf6] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0xf7] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, +}; + +/* Table: 3-byte opcode 2 (0x0f 0x3a) */ +const insn_attr_t inat_escape_table_3[INAT_OPCODE_TABLE_SIZE] = { + [0x00] = INAT_VARIANT, + [0x01] = INAT_VARIANT, + [0x02] = INAT_VARIANT, + [0x04] = INAT_VARIANT, + [0x05] = INAT_VARIANT, + [0x06] = INAT_VARIANT, + [0x08] = INAT_VARIANT, + [0x09] = INAT_VARIANT, + [0x0a] = INAT_VARIANT, + [0x0b] = INAT_VARIANT, + [0x0c] = INAT_VARIANT, + [0x0d] = INAT_VARIANT, + [0x0e] = INAT_VARIANT, + [0x0f] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VARIANT, + [0x14] = INAT_VARIANT, + [0x15] = INAT_VARIANT, + [0x16] = INAT_VARIANT, + [0x17] = INAT_VARIANT, + [0x18] = INAT_VARIANT, + [0x19] = INAT_VARIANT, + [0x1d] = INAT_VARIANT, + [0x20] = INAT_VARIANT, + [0x21] = INAT_VARIANT, + [0x22] = INAT_VARIANT, + [0x38] = INAT_VARIANT, + [0x39] = INAT_VARIANT, + [0x40] = INAT_VARIANT, + [0x41] = INAT_VARIANT, + [0x42] = INAT_VARIANT, + [0x44] = INAT_VARIANT, + [0x46] = INAT_VARIANT, + [0x4a] = INAT_VARIANT, + [0x4b] = INAT_VARIANT, + [0x4c] = INAT_VARIANT, + [0x60] = INAT_VARIANT, + [0x61] = INAT_VARIANT, + [0x62] = INAT_VARIANT, + [0x63] = INAT_VARIANT, + [0xdf] = INAT_VARIANT, + [0xf0] = INAT_VARIANT, +}; +const insn_attr_t inat_escape_table_3_1[INAT_OPCODE_TABLE_SIZE] = { + [0x00] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0x01] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0x02] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0x04] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0x05] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0x06] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0x08] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, + [0x09] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, + [0x0a] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, + [0x0b] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, + [0x0c] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, + [0x0d] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, + [0x0e] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, + [0x0f] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, + [0x14] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, + [0x15] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, + [0x16] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, + [0x17] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, + [0x18] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0x19] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0x1d] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0x20] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, + [0x21] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, + [0x22] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, + [0x38] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0x39] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0x40] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, + [0x41] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, + [0x42] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, + [0x44] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, + [0x46] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0x4a] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0x4b] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0x4c] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, + [0x60] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, + [0x61] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, + [0x62] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, + [0x63] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, + [0xdf] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, +}; +const insn_attr_t inat_escape_table_3_3[INAT_OPCODE_TABLE_SIZE] = { + [0xf0] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, +}; + +/* GrpTable: Grp1 */ + +/* GrpTable: Grp1A */ + +/* GrpTable: Grp2 */ + +/* GrpTable: Grp3_1 */ +const insn_attr_t inat_group_table_6[INAT_GROUP_TABLE_SIZE] = { + [0x0] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM, + [0x2] = INAT_MODRM, + [0x3] = INAT_MODRM, + [0x4] = INAT_MODRM, + [0x5] = INAT_MODRM, + [0x6] = INAT_MODRM, + [0x7] = INAT_MODRM, +}; + +/* GrpTable: Grp3_2 */ +const insn_attr_t inat_group_table_7[INAT_GROUP_TABLE_SIZE] = { + [0x0] = INAT_MAKE_IMM(INAT_IMM_VWORD32) | INAT_MODRM, + [0x2] = INAT_MODRM, + [0x3] = INAT_MODRM, + [0x4] = INAT_MODRM, + [0x5] = INAT_MODRM, + [0x6] = INAT_MODRM, + [0x7] = INAT_MODRM, +}; + +/* GrpTable: Grp4 */ +const insn_attr_t inat_group_table_8[INAT_GROUP_TABLE_SIZE] = { + [0x0] = INAT_MODRM, + [0x1] = INAT_MODRM, +}; + +/* GrpTable: Grp5 */ +const insn_attr_t inat_group_table_9[INAT_GROUP_TABLE_SIZE] = { + [0x0] = INAT_MODRM, + [0x1] = INAT_MODRM, + [0x2] = INAT_MODRM | INAT_FORCE64, + [0x3] = INAT_MODRM, + [0x4] = INAT_MODRM | INAT_FORCE64, + [0x5] = INAT_MODRM, + [0x6] = INAT_MODRM | INAT_FORCE64, +}; + +/* GrpTable: Grp6 */ +const insn_attr_t inat_group_table_10[INAT_GROUP_TABLE_SIZE] = { + [0x0] = INAT_MODRM, + [0x1] = INAT_MODRM, + [0x2] = INAT_MODRM, + [0x3] = INAT_MODRM, + [0x4] = INAT_MODRM, + [0x5] = INAT_MODRM, +}; + +/* GrpTable: Grp7 */ +const insn_attr_t inat_group_table_11[INAT_GROUP_TABLE_SIZE] = { + [0x0] = INAT_MODRM, + [0x1] = INAT_MODRM, + [0x2] = INAT_MODRM, + [0x3] = INAT_MODRM, + [0x4] = INAT_MODRM, + [0x6] = INAT_MODRM, + [0x7] = INAT_MODRM, +}; + +/* GrpTable: Grp8 */ + +/* GrpTable: Grp9 */ +const insn_attr_t inat_group_table_22[INAT_GROUP_TABLE_SIZE] = { + [0x1] = INAT_MODRM, + [0x6] = INAT_MODRM | INAT_MODRM | INAT_VARIANT, + [0x7] = INAT_MODRM | INAT_MODRM | INAT_VARIANT, +}; +const insn_attr_t inat_group_table_22_1[INAT_GROUP_TABLE_SIZE] = { + [0x6] = INAT_MODRM, +}; +const insn_attr_t inat_group_table_22_2[INAT_GROUP_TABLE_SIZE] = { + [0x6] = INAT_MODRM, + [0x7] = INAT_MODRM, +}; + +/* GrpTable: Grp10 */ + +/* GrpTable: Grp11A */ +const insn_attr_t inat_group_table_4[INAT_GROUP_TABLE_SIZE] = { + [0x0] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM, + [0x7] = INAT_MAKE_IMM(INAT_IMM_BYTE), +}; + +/* GrpTable: Grp11B */ +const insn_attr_t inat_group_table_5[INAT_GROUP_TABLE_SIZE] = { + [0x0] = INAT_MAKE_IMM(INAT_IMM_VWORD32) | INAT_MODRM, + [0x7] = INAT_MAKE_IMM(INAT_IMM_VWORD32), +}; + +/* GrpTable: Grp12 */ +const insn_attr_t inat_group_table_14[INAT_GROUP_TABLE_SIZE] = { + [0x2] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VARIANT, + [0x4] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VARIANT, + [0x6] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VARIANT, +}; +const insn_attr_t inat_group_table_14_1[INAT_GROUP_TABLE_SIZE] = { + [0x2] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, + [0x4] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, + [0x6] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, +}; + +/* GrpTable: Grp13 */ +const insn_attr_t inat_group_table_15[INAT_GROUP_TABLE_SIZE] = { + [0x2] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VARIANT, + [0x4] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VARIANT, + [0x6] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VARIANT, +}; +const insn_attr_t inat_group_table_15_1[INAT_GROUP_TABLE_SIZE] = { + [0x2] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, + [0x4] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, + [0x6] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, +}; + +/* GrpTable: Grp14 */ +const insn_attr_t inat_group_table_16[INAT_GROUP_TABLE_SIZE] = { + [0x2] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VARIANT, + [0x3] = INAT_VARIANT, + [0x6] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VARIANT, + [0x7] = INAT_VARIANT, +}; +const insn_attr_t inat_group_table_16_1[INAT_GROUP_TABLE_SIZE] = { + [0x2] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, + [0x3] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, + [0x6] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, + [0x7] = INAT_MAKE_IMM(INAT_IMM_BYTE) | INAT_MODRM | INAT_VEXOK, +}; + +/* GrpTable: Grp15 */ +const insn_attr_t inat_group_table_19[INAT_GROUP_TABLE_SIZE] = { + [0x0] = INAT_VARIANT, + [0x1] = INAT_VARIANT, + [0x2] = INAT_MODRM | INAT_VEXOK | INAT_VARIANT, + [0x3] = INAT_MODRM | INAT_VEXOK | INAT_VARIANT, +}; +const insn_attr_t inat_group_table_19_2[INAT_GROUP_TABLE_SIZE] = { + [0x0] = INAT_MODRM, + [0x1] = INAT_MODRM, + [0x2] = INAT_MODRM, + [0x3] = INAT_MODRM, +}; + +/* GrpTable: Grp16 */ +const insn_attr_t inat_group_table_13[INAT_GROUP_TABLE_SIZE] = { + [0x0] = INAT_MODRM, + [0x1] = INAT_MODRM, + [0x2] = INAT_MODRM, + [0x3] = INAT_MODRM, +}; + +/* GrpTable: Grp17 */ +const insn_attr_t inat_group_table_23[INAT_GROUP_TABLE_SIZE] = { + [0x1] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0x2] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, + [0x3] = INAT_MODRM | INAT_VEXOK | INAT_VEXONLY, +}; + +/* GrpTable: GrpP */ + +/* GrpTable: GrpPDLK */ + +/* GrpTable: GrpRNG */ + +/* Escape opcode map array */ +const insn_attr_t * const inat_escape_tables[INAT_ESC_MAX + 1][INAT_LSTPFX_MAX + 1] = { + [1][0] = inat_escape_table_1, + [1][1] = inat_escape_table_1_1, + [1][2] = inat_escape_table_1_2, + [1][3] = inat_escape_table_1_3, + [2][0] = inat_escape_table_2, + [2][1] = inat_escape_table_2_1, + [2][2] = inat_escape_table_2_2, + [2][3] = inat_escape_table_2_3, + [3][0] = inat_escape_table_3, + [3][1] = inat_escape_table_3_1, + [3][3] = inat_escape_table_3_3, +}; + +/* Group opcode map array */ +const insn_attr_t * const inat_group_tables[INAT_GRP_MAX + 1][INAT_LSTPFX_MAX + 1] = { + [4][0] = inat_group_table_4, + [5][0] = inat_group_table_5, + [6][0] = inat_group_table_6, + [7][0] = inat_group_table_7, + [8][0] = inat_group_table_8, + [9][0] = inat_group_table_9, + [10][0] = inat_group_table_10, + [11][0] = inat_group_table_11, + [13][0] = inat_group_table_13, + [14][0] = inat_group_table_14, + [14][1] = inat_group_table_14_1, + [15][0] = inat_group_table_15, + [15][1] = inat_group_table_15_1, + [16][0] = inat_group_table_16, + [16][1] = inat_group_table_16_1, + [19][0] = inat_group_table_19, + [19][2] = inat_group_table_19_2, + [22][0] = inat_group_table_22, + [22][1] = inat_group_table_22_1, + [22][2] = inat_group_table_22_2, + [23][0] = inat_group_table_23, +}; + +/* AVX opcode map array */ +const insn_attr_t * const inat_avx_tables[X86_VEX_M_MAX + 1][INAT_LSTPFX_MAX + 1] = { + [1][0] = inat_escape_table_1, + [1][1] = inat_escape_table_1_1, + [1][2] = inat_escape_table_1_2, + [1][3] = inat_escape_table_1_3, + [2][0] = inat_escape_table_2, + [2][1] = inat_escape_table_2_1, + [2][2] = inat_escape_table_2_2, + [2][3] = inat_escape_table_2_3, + [3][0] = inat_escape_table_3, + [3][1] = inat_escape_table_3_1, + [3][3] = inat_escape_table_3_3, +}; diff --git a/insn/inat.c b/insn/inat.c new file mode 100644 index 0000000..c1f01a8 --- /dev/null +++ b/insn/inat.c @@ -0,0 +1,97 @@ +/* + * x86 instruction attribute tables + * + * Written by Masami Hiramatsu + * + * 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 + +/* Attribute tables are generated from opcode map */ +#include "inat-tables.c" + +/* Attribute search APIs */ +insn_attr_t inat_get_opcode_attribute(insn_byte_t opcode) +{ + return inat_primary_table[opcode]; +} + +int inat_get_last_prefix_id(insn_byte_t last_pfx) +{ + insn_attr_t lpfx_attr; + + lpfx_attr = inat_get_opcode_attribute(last_pfx); + return inat_last_prefix_id(lpfx_attr); +} + +insn_attr_t inat_get_escape_attribute(insn_byte_t opcode, int lpfx_id, + insn_attr_t esc_attr) +{ + const insn_attr_t *table; + int n; + + n = inat_escape_id(esc_attr); + + table = inat_escape_tables[n][0]; + if (!table) + return 0; + if (inat_has_variant(table[opcode]) && lpfx_id) { + table = inat_escape_tables[n][lpfx_id]; + if (!table) + return 0; + } + return table[opcode]; +} + +insn_attr_t inat_get_group_attribute(insn_byte_t modrm, int lpfx_id, + insn_attr_t grp_attr) +{ + const insn_attr_t *table; + int n; + + n = inat_group_id(grp_attr); + + table = inat_group_tables[n][0]; + if (!table) + return inat_group_common_attribute(grp_attr); + if (inat_has_variant(table[X86_MODRM_REG(modrm)]) && lpfx_id) { + table = inat_group_tables[n][lpfx_id]; + if (!table) + return inat_group_common_attribute(grp_attr); + } + return table[X86_MODRM_REG(modrm)] | + inat_group_common_attribute(grp_attr); +} + +insn_attr_t inat_get_avx_attribute(insn_byte_t opcode, insn_byte_t vex_m, + insn_byte_t vex_p) +{ + const insn_attr_t *table; + if (vex_m > X86_VEX_M_MAX || vex_p > INAT_LSTPFX_MAX) + return 0; + /* At first, this checks the master table */ + table = inat_avx_tables[vex_m][0]; + if (!table) + return 0; + if (!inat_is_group(table[opcode]) && vex_p) { + /* If this is not a group, get attribute directly */ + table = inat_avx_tables[vex_m][vex_p]; + if (!table) + return 0; + } + return table[opcode]; +} + diff --git a/insn/insn.c b/insn/insn.c new file mode 100644 index 0000000..7acdc96 --- /dev/null +++ b/insn/insn.c @@ -0,0 +1,582 @@ +/* + * x86 instruction analysis + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Copyright (C) IBM Corporation, 2002, 2004, 2009 + */ + +#ifdef __KERNEL__ +#include +#else +#include +#endif +#include +#include + +#define unlikely(a) a + +/* Verify next sizeof(t) bytes can be on the same instruction */ +#define validate_next(t, insn, n) \ + ((insn)->next_byte + sizeof(t) + n - (insn)->kaddr <= MAX_INSN_SIZE) + +#define __get_next(t, insn) \ + ({ t r = *(t*)insn->next_byte; insn->next_byte += sizeof(t); r; }) + +#define __peek_nbyte_next(t, insn, n) \ + ({ t r = *(t*)((insn)->next_byte + n); r; }) + +#define get_next(t, insn) \ + ({ if (unlikely(!validate_next(t, insn, 0))) goto err_out; __get_next(t, insn); }) + +#define peek_nbyte_next(t, insn, n) \ + ({ if (unlikely(!validate_next(t, insn, n))) goto err_out; __peek_nbyte_next(t, insn, n); }) + +#define peek_next(t, insn) peek_nbyte_next(t, insn, 0) + +/** + * insn_init() - initialize struct insn + * @insn: &struct insn to be initialized + * @kaddr: address (in kernel memory) of instruction (or copy thereof) + * @x86_64: !0 for 64-bit kernel or 64-bit app + */ +void insn_init(struct insn *insn, const void *kaddr, int x86_64) +{ + memset(insn, 0, sizeof(*insn)); + insn->kaddr = kaddr; + insn->next_byte = kaddr; + insn->x86_64 = x86_64 ? 1 : 0; + insn->opnd_bytes = 4; + if (x86_64) + insn->addr_bytes = 8; + else + insn->addr_bytes = 4; +} + +/** + * insn_get_prefixes - scan x86 instruction prefix bytes + * @insn: &struct insn containing instruction + * + * Populates the @insn->prefixes bitmap, and updates @insn->next_byte + * to point to the (first) opcode. No effect if @insn->prefixes.got + * is already set. + */ +void insn_get_prefixes(struct insn *insn) +{ + struct insn_field *prefixes = &insn->prefixes; + insn_attr_t attr; + insn_byte_t b, lb; + int i, nb; + + if (prefixes->got) + return; + + nb = 0; + lb = 0; + b = peek_next(insn_byte_t, insn); + attr = inat_get_opcode_attribute(b); + while (inat_is_legacy_prefix(attr)) { + /* Skip if same prefix */ + for (i = 0; i < nb; i++) + if (prefixes->bytes[i] == b) + goto found; + if (nb == 4) + /* Invalid instruction */ + break; + prefixes->bytes[nb++] = b; + if (inat_is_address_size_prefix(attr)) { + /* address size switches 2/4 or 4/8 */ + if (insn->x86_64) + insn->addr_bytes ^= 12; + else + insn->addr_bytes ^= 6; + } else if (inat_is_operand_size_prefix(attr)) { + /* oprand size switches 2/4 */ + insn->opnd_bytes ^= 6; + } +found: + prefixes->nbytes++; + insn->next_byte++; + lb = b; + b = peek_next(insn_byte_t, insn); + attr = inat_get_opcode_attribute(b); + } + /* Set the last prefix */ + if (lb && lb != insn->prefixes.bytes[3]) { + if (unlikely(insn->prefixes.bytes[3])) { + /* Swap the last prefix */ + b = insn->prefixes.bytes[3]; + for (i = 0; i < nb; i++) + if (prefixes->bytes[i] == lb) + prefixes->bytes[i] = b; + } + insn->prefixes.bytes[3] = lb; + } + + /* Decode REX prefix */ + if (insn->x86_64) { + b = peek_next(insn_byte_t, insn); + attr = inat_get_opcode_attribute(b); + if (inat_is_rex_prefix(attr)) { + insn->rex_prefix.value = b; + insn->rex_prefix.nbytes = 1; + insn->next_byte++; + if (X86_REX_W(b)) + /* REX.W overrides opnd_size */ + insn->opnd_bytes = 8; + } + } + insn->rex_prefix.got = 1; + + /* Decode VEX prefix */ + b = peek_next(insn_byte_t, insn); + attr = inat_get_opcode_attribute(b); + if (inat_is_vex_prefix(attr)) { + insn_byte_t b2 = peek_nbyte_next(insn_byte_t, insn, 1); + if (!insn->x86_64) { + /* + * In 32-bits mode, if the [7:6] bits (mod bits of + * ModRM) on the second byte are not 11b, it is + * LDS or LES. + */ + if (X86_MODRM_MOD(b2) != 3) + goto vex_end; + } + insn->vex_prefix.bytes[0] = b; + insn->vex_prefix.bytes[1] = b2; + if (inat_is_vex3_prefix(attr)) { + b2 = peek_nbyte_next(insn_byte_t, insn, 2); + insn->vex_prefix.bytes[2] = b2; + insn->vex_prefix.nbytes = 3; + insn->next_byte += 3; + if (insn->x86_64 && X86_VEX_W(b2)) + /* VEX.W overrides opnd_size */ + insn->opnd_bytes = 8; + } else { + insn->vex_prefix.nbytes = 2; + insn->next_byte += 2; + } + } +vex_end: + insn->vex_prefix.got = 1; + + prefixes->got = 1; + +err_out: + return; +} + +/** + * insn_get_opcode - collect opcode(s) + * @insn: &struct insn containing instruction + * + * Populates @insn->opcode, updates @insn->next_byte to point past the + * opcode byte(s), and set @insn->attr (except for groups). + * If necessary, first collects any preceding (prefix) bytes. + * Sets @insn->opcode.value = opcode1. No effect if @insn->opcode.got + * is already 1. + */ +void insn_get_opcode(struct insn *insn) +{ + struct insn_field *opcode = &insn->opcode; + insn_byte_t op; + int pfx_id; + if (opcode->got) + return; + if (!insn->prefixes.got) + insn_get_prefixes(insn); + + /* Get first opcode */ + op = get_next(insn_byte_t, insn); + opcode->bytes[0] = op; + opcode->nbytes = 1; + + /* Check if there is VEX prefix or not */ + if (insn_is_avx(insn)) { + insn_byte_t m, p; + m = insn_vex_m_bits(insn); + p = insn_vex_p_bits(insn); + insn->attr = inat_get_avx_attribute(op, m, p); + if (!inat_accept_vex(insn->attr) && !inat_is_group(insn->attr)) + insn->attr = 0; /* This instruction is bad */ + goto end; /* VEX has only 1 byte for opcode */ + } + + insn->attr = inat_get_opcode_attribute(op); + while (inat_is_escape(insn->attr)) { + /* Get escaped opcode */ + op = get_next(insn_byte_t, insn); + opcode->bytes[opcode->nbytes++] = op; + pfx_id = insn_last_prefix_id(insn); + insn->attr = inat_get_escape_attribute(op, pfx_id, insn->attr); + } + if (inat_must_vex(insn->attr)) + insn->attr = 0; /* This instruction is bad */ +end: + opcode->got = 1; + +err_out: + return; +} + +/** + * insn_get_modrm - collect ModRM byte, if any + * @insn: &struct insn containing instruction + * + * Populates @insn->modrm and updates @insn->next_byte to point past the + * ModRM byte, if any. If necessary, first collects the preceding bytes + * (prefixes and opcode(s)). No effect if @insn->modrm.got is already 1. + */ +void insn_get_modrm(struct insn *insn) +{ + struct insn_field *modrm = &insn->modrm; + insn_byte_t pfx_id, mod; + if (modrm->got) + return; + if (!insn->opcode.got) + insn_get_opcode(insn); + + if (inat_has_modrm(insn->attr)) { + mod = get_next(insn_byte_t, insn); + modrm->value = mod; + modrm->nbytes = 1; + if (inat_is_group(insn->attr)) { + pfx_id = insn_last_prefix_id(insn); + insn->attr = inat_get_group_attribute(mod, pfx_id, + insn->attr); + if (insn_is_avx(insn) && !inat_accept_vex(insn->attr)) + insn->attr = 0; /* This is bad */ + } + } + + if (insn->x86_64 && inat_is_force64(insn->attr)) + insn->opnd_bytes = 8; + modrm->got = 1; + +err_out: + return; +} + + +/** + * insn_rip_relative() - Does instruction use RIP-relative addressing mode? + * @insn: &struct insn containing instruction + * + * If necessary, first collects the instruction up to and including the + * ModRM byte. No effect if @insn->x86_64 is 0. + */ +int insn_rip_relative(struct insn *insn) +{ + struct insn_field *modrm = &insn->modrm; + + if (!insn->x86_64) + return 0; + if (!modrm->got) + insn_get_modrm(insn); + /* + * For rip-relative instructions, the mod field (top 2 bits) + * is zero and the r/m field (bottom 3 bits) is 0x5. + */ + return (modrm->nbytes && (modrm->value & 0xc7) == 0x5); +} + +/** + * insn_get_sib() - Get the SIB byte of instruction + * @insn: &struct insn containing instruction + * + * If necessary, first collects the instruction up to and including the + * ModRM byte. + */ +void insn_get_sib(struct insn *insn) +{ + insn_byte_t modrm; + + if (insn->sib.got) + return; + if (!insn->modrm.got) + insn_get_modrm(insn); + if (insn->modrm.nbytes) { + modrm = (insn_byte_t)insn->modrm.value; + if (insn->addr_bytes != 2 && + X86_MODRM_MOD(modrm) != 3 && X86_MODRM_RM(modrm) == 4) { + insn->sib.value = get_next(insn_byte_t, insn); + insn->sib.nbytes = 1; + } + } + insn->sib.got = 1; + +err_out: + return; +} + + +/** + * insn_get_displacement() - Get the displacement of instruction + * @insn: &struct insn containing instruction + * + * If necessary, first collects the instruction up to and including the + * SIB byte. + * Displacement value is sign-expanded. + */ +void insn_get_displacement(struct insn *insn) +{ + insn_byte_t mod, rm, base; + + if (insn->displacement.got) + return; + if (!insn->sib.got) + insn_get_sib(insn); + if (insn->modrm.nbytes) { + /* + * Interpreting the modrm byte: + * mod = 00 - no displacement fields (exceptions below) + * mod = 01 - 1-byte displacement field + * mod = 10 - displacement field is 4 bytes, or 2 bytes if + * address size = 2 (0x67 prefix in 32-bit mode) + * mod = 11 - no memory operand + * + * If address size = 2... + * mod = 00, r/m = 110 - displacement field is 2 bytes + * + * If address size != 2... + * mod != 11, r/m = 100 - SIB byte exists + * mod = 00, SIB base = 101 - displacement field is 4 bytes + * mod = 00, r/m = 101 - rip-relative addressing, displacement + * field is 4 bytes + */ + mod = X86_MODRM_MOD(insn->modrm.value); + rm = X86_MODRM_RM(insn->modrm.value); + base = X86_SIB_BASE(insn->sib.value); + if (mod == 3) + goto out; + if (mod == 1) { + insn->displacement.value = get_next(char, insn); + insn->displacement.nbytes = 1; + } else if (insn->addr_bytes == 2) { + if ((mod == 0 && rm == 6) || mod == 2) { + insn->displacement.value = + get_next(short, insn); + insn->displacement.nbytes = 2; + } + } else { + if ((mod == 0 && rm == 5) || mod == 2 || + (mod == 0 && base == 5)) { + insn->displacement.value = get_next(int, insn); + insn->displacement.nbytes = 4; + } + } + } +out: + insn->displacement.got = 1; + +err_out: + return; +} + +/* Decode moffset16/32/64. Return 0 if failed */ +static int __get_moffset(struct insn *insn) +{ + switch (insn->addr_bytes) { + case 2: + insn->moffset1.value = get_next(short, insn); + insn->moffset1.nbytes = 2; + break; + case 4: + insn->moffset1.value = get_next(int, insn); + insn->moffset1.nbytes = 4; + break; + case 8: + insn->moffset1.value = get_next(int, insn); + insn->moffset1.nbytes = 4; + insn->moffset2.value = get_next(int, insn); + insn->moffset2.nbytes = 4; + break; + default: /* opnd_bytes must be modified manually */ + goto err_out; + } + insn->moffset1.got = insn->moffset2.got = 1; + + return 1; + +err_out: + return 0; +} + +/* Decode imm v32(Iz). Return 0 if failed */ +static int __get_immv32(struct insn *insn) +{ + switch (insn->opnd_bytes) { + case 2: + insn->immediate.value = get_next(short, insn); + insn->immediate.nbytes = 2; + break; + case 4: + case 8: + insn->immediate.value = get_next(int, insn); + insn->immediate.nbytes = 4; + break; + default: /* opnd_bytes must be modified manually */ + goto err_out; + } + + return 1; + +err_out: + return 0; +} + +/* Decode imm v64(Iv/Ov), Return 0 if failed */ +static int __get_immv(struct insn *insn) +{ + switch (insn->opnd_bytes) { + case 2: + insn->immediate1.value = get_next(short, insn); + insn->immediate1.nbytes = 2; + break; + case 4: + insn->immediate1.value = get_next(int, insn); + insn->immediate1.nbytes = 4; + break; + case 8: + insn->immediate1.value = get_next(int, insn); + insn->immediate1.nbytes = 4; + insn->immediate2.value = get_next(int, insn); + insn->immediate2.nbytes = 4; + break; + default: /* opnd_bytes must be modified manually */ + goto err_out; + } + insn->immediate1.got = insn->immediate2.got = 1; + + return 1; +err_out: + return 0; +} + +/* Decode ptr16:16/32(Ap) */ +static int __get_immptr(struct insn *insn) +{ + switch (insn->opnd_bytes) { + case 2: + insn->immediate1.value = get_next(short, insn); + insn->immediate1.nbytes = 2; + break; + case 4: + insn->immediate1.value = get_next(int, insn); + insn->immediate1.nbytes = 4; + break; + case 8: + /* ptr16:64 is not exist (no segment) */ + return 0; + default: /* opnd_bytes must be modified manually */ + goto err_out; + } + insn->immediate2.value = get_next(unsigned short, insn); + insn->immediate2.nbytes = 2; + insn->immediate1.got = insn->immediate2.got = 1; + + return 1; +err_out: + return 0; +} + +/** + * insn_get_immediate() - Get the immediates of instruction + * @insn: &struct insn containing instruction + * + * If necessary, first collects the instruction up to and including the + * displacement bytes. + * Basically, most of immediates are sign-expanded. Unsigned-value can be + * get by bit masking with ((1 << (nbytes * 8)) - 1) + */ +void insn_get_immediate(struct insn *insn) +{ + if (insn->immediate.got) + return; + if (!insn->displacement.got) + insn_get_displacement(insn); + + if (inat_has_moffset(insn->attr)) { + if (!__get_moffset(insn)) + goto err_out; + goto done; + } + + if (!inat_has_immediate(insn->attr)) + /* no immediates */ + goto done; + + switch (inat_immediate_size(insn->attr)) { + case INAT_IMM_BYTE: + insn->immediate.value = get_next(char, insn); + insn->immediate.nbytes = 1; + break; + case INAT_IMM_WORD: + insn->immediate.value = get_next(short, insn); + insn->immediate.nbytes = 2; + break; + case INAT_IMM_DWORD: + insn->immediate.value = get_next(int, insn); + insn->immediate.nbytes = 4; + break; + case INAT_IMM_QWORD: + insn->immediate1.value = get_next(int, insn); + insn->immediate1.nbytes = 4; + insn->immediate2.value = get_next(int, insn); + insn->immediate2.nbytes = 4; + break; + case INAT_IMM_PTR: + if (!__get_immptr(insn)) + goto err_out; + break; + case INAT_IMM_VWORD32: + if (!__get_immv32(insn)) + goto err_out; + break; + case INAT_IMM_VWORD: + if (!__get_immv(insn)) + goto err_out; + break; + default: + /* Here, insn must have an immediate, but failed */ + goto err_out; + } + if (inat_has_second_immediate(insn->attr)) { + insn->immediate2.value = get_next(char, insn); + insn->immediate2.nbytes = 1; + } +done: + insn->immediate.got = 1; + +err_out: + return; +} + +/** + * insn_get_length() - Get the length of instruction + * @insn: &struct insn containing instruction + * + * If necessary, first collects the instruction up to and including the + * immediates bytes. + */ +void insn_get_length(struct insn *insn) +{ + if (insn->length) + return; + if (!insn->immediate.got) + insn_get_immediate(insn); + insn->length = (unsigned char)((unsigned long)insn->next_byte + - (unsigned long)insn->kaddr); +} diff --git a/list.h b/list.h new file mode 100644 index 0000000..e95593c --- /dev/null +++ b/list.h @@ -0,0 +1,221 @@ +/* + * list.h + * + * Adapted from http://www.mcs.anl.gov/~kazutomo/list/list.h which is a + * userspace port of the Linux kernel implementation in include/linux/list.h + * + * Thus licensed as GPLv2. + * + * Copyright (C) 2014 Seth Jennings + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA, + * 02110-1301, USA. + */ + +#ifndef _LIST_H +#define _LIST_H + +/** + * Get offset of a member + */ +#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) + +/** + * Casts a member of a structure out to the containing structure + * @param ptr the pointer to the member. + * @param type the type of the container struct this is embedded in. + * @param member the name of the member within the struct. + * + */ +#define container_of(ptr, type, member) ({ \ + const typeof( ((type *)0)->member ) *__mptr = (ptr); \ + (type *)( (char *)__mptr - offsetof(type,member) );}) + +/* + * These are non-NULL pointers that will result in page faults + * under normal circumstances, used to verify that nobody uses + * non-initialized list entries. + */ +#define LIST_POISON1 ((void *) 0x00100100) +#define LIST_POISON2 ((void *) 0x00200200) + +/** + * Simple doubly linked list implementation. + * + * Some of the internal functions ("__xxx") are useful when + * manipulating whole lists rather than single entries, as + * sometimes we already know the next/prev entries and we can + * generate better code by using them directly rather than + * using the generic single-entry routines. + */ +struct list_head { + struct list_head *next, *prev; +}; + +#define LIST_HEAD_INIT(name) { &(name), &(name) } + +#define LIST_HEAD(name) \ + struct list_head name = LIST_HEAD_INIT(name) + +#define INIT_LIST_HEAD(ptr) do { \ + (ptr)->next = (ptr); (ptr)->prev = (ptr); \ +} while (0) + +/* + * Insert a new entry between two known consecutive entries. + * + * This is only for internal list manipulation where we know + * the prev/next entries already! + */ +static inline void __list_add(struct list_head *new, + struct list_head *prev, + struct list_head *next) +{ + next->prev = new; + new->next = next; + new->prev = prev; + prev->next = new; +} + +/** + * list_add - add a new entry + * @new: new entry to be added + * @head: list head to add it after + * + * Insert a new entry after the specified head. + * This is good for implementing stacks. + */ +static inline void list_add(struct list_head *new, struct list_head *head) +{ + __list_add(new, head, head->next); +} + +/** + * list_add_tail - add a new entry + * @new: new entry to be added + * @head: list head to add it before + * + * Insert a new entry before the specified head. + * This is useful for implementing queues. + */ +static inline void list_add_tail(struct list_head *new, struct list_head *head) +{ + __list_add(new, head->prev, head); +} + + +/* + * Delete a list entry by making the prev/next entries + * point to each other. + * + * This is only for internal list manipulation where we know + * the prev/next entries already! + */ +static inline void __list_del(struct list_head * prev, struct list_head * next) +{ + next->prev = prev; + prev->next = next; +} + +/** + * list_del - deletes entry from list. + * @entry: the element to delete from the list. + * Note: list_empty on entry does not return true after this, the entry is + * in an undefined state. + */ +static inline void list_del(struct list_head *entry) +{ + __list_del(entry->prev, entry->next); + entry->next = LIST_POISON1; + entry->prev = LIST_POISON2; +} + +/** + * list_replace - replace old entry by new one + * @old : the element to be replaced + * @new : the new element to insert + * + * If @old was empty, it will be overwritten. + */ +static inline void list_replace(struct list_head *old, + struct list_head *new) +{ + new->next = old->next; + new->next->prev = new; + new->prev = old->prev; + new->prev->next = new; +} + +#define list_entry(ptr, type, member) \ + container_of(ptr, type, member) + +/** + * list_first_entry - get the first element from a list + * @ptr: the list head to take the element from. + * @type: the type of the struct this is embedded in. + * @member: the name of the list_struct within the struct. + * + * Note, that list is expected to be not empty. + */ +#define list_first_entry(ptr, type, member) \ + list_entry((ptr)->next, type, member) + +/** + * list_next_entry - get the next element in list + * @pos: the type * to cursor + * @member: the name of the list_struct within the struct. + */ +#define list_next_entry(pos, member) \ + list_entry((pos)->member.next, typeof(*(pos)), member) + +/** + * list_for_each_entry - iterate over list of given type + * @pos: the type * to use as a loop counter. + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + */ +#define list_for_each_entry(pos, head, member) \ + for (pos = list_entry((head)->next, typeof(*pos), member); \ + &pos->member != (head); \ + pos = list_entry(pos->member.next, typeof(*pos), member)) + +/** + * list_for_each_entry_continue - continue iteration over list of given type + * @pos: the type * to use as a loop cursor. + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + * + * Continue to iterate over list of given type, continuing after + * the current position. + */ +#define list_for_each_entry_continue(pos, head, member) \ + for (pos = list_next_entry(pos, member); \ + &pos->member != (head); \ + pos = list_next_entry(pos, member)) + +/** + * list_for_each_entry_safe - iterate over list of given type safe against removal of list entry + * @pos: the type * to use as a loop counter. + * @n: another type * to use as temporary storage + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + */ +#define list_for_each_entry_safe(pos, n, head, member) \ + for (pos = list_entry((head)->next, typeof(*pos), member), \ + n = list_entry(pos->member.next, typeof(*pos), member); \ + &pos->member != (head); \ + pos = n, n = list_entry(n->member.next, typeof(*n), member)) + +#endif /* _LIST_H_ */ diff --git a/lookup.c b/lookup.c new file mode 100644 index 0000000..39125c6 --- /dev/null +++ b/lookup.c @@ -0,0 +1,251 @@ +/* + * lookup.c + * + * This file contains functions that assist in the reading and searching + * the symbol table of an ELF object. + * + * Copyright (C) 2014 Seth Jennings + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include "lookup.h" + +#define ERROR(format, ...) \ + error(1, 0, "%s: %d: " format, __FUNCTION__, __LINE__, ##__VA_ARGS__) + +struct symbol { + unsigned long value; + unsigned long size; + char *name; + int type, bind, skip; +}; + +struct lookup_table { + int fd, nr; + Elf *elf; + struct symbol *syms; +}; + +#define for_each_symbol(ndx, iter, table) \ + for (ndx = 0, iter = table->syms; ndx < table->nr; ndx++, iter++) + +struct lookup_table *lookup_open(char *path) +{ + Elf *elf; + int fd, i, len; + Elf_Scn *scn; + GElf_Shdr sh; + GElf_Sym sym; + Elf_Data *data; + char *name; + struct lookup_table *table; + struct symbol *mysym; + size_t shstrndx; + + if ((fd = open(path, O_RDONLY, 0)) < 0) + ERROR("open"); + + elf_version(EV_CURRENT); + + elf = elf_begin(fd, ELF_C_READ_MMAP, NULL); + if (!elf) { + printf("%s\n", elf_errmsg(-1)); + ERROR("elf_begin"); + } + + if (elf_getshdrstrndx(elf, &shstrndx)) + ERROR("elf_getshdrstrndx"); + + scn = NULL; + while ((scn = elf_nextscn(elf, scn))) { + if (!gelf_getshdr(scn, &sh)) + ERROR("gelf_getshdr"); + + name = elf_strptr(elf, shstrndx, sh.sh_name); + if (!name) + ERROR("elf_strptr scn"); + + if (!strcmp(name, ".symtab")) + break; + } + + if (!scn) + ERROR(".symtab section not found"); + + data = elf_getdata(scn, NULL); + if (!data) + ERROR("elf_getdata"); + + len = sh.sh_size / sh.sh_entsize; + + table = malloc(sizeof(*table)); + if (!table) + ERROR("malloc table"); + table->syms = malloc(len * sizeof(struct symbol)); + if (!table->syms) + ERROR("malloc table.syms"); + memset(table->syms, 0, len * sizeof(struct symbol)); + table->nr = len; + table->fd = fd; + table->elf = elf; + + for_each_symbol(i, mysym, table) { + if (!gelf_getsym(data, i, &sym)) + ERROR("gelf_getsym"); + + if (sym.st_shndx == SHN_UNDEF) { + mysym->skip = 1; + continue; + } + + name = elf_strptr(elf, sh.sh_link, sym.st_name); + if(!name) + ERROR("elf_strptr sym"); + + mysym->value = sym.st_value; + mysym->size = sym.st_size; + mysym->type = GELF_ST_TYPE(sym.st_info); + mysym->bind = GELF_ST_BIND(sym.st_info); + mysym->name = name; + } + + return table; +} + +void lookup_close(struct lookup_table *table) +{ + elf_end(table->elf); + close(table->fd); + free(table); +} + +int lookup_local_symbol(struct lookup_table *table, char *name, char *hint, + struct lookup_result *result) +{ + struct symbol *sym, *match = NULL; + int i; + char *curfile = NULL; + + memset(result, 0, sizeof(*result)); + for_each_symbol(i, sym, table) { + if (sym->type == STT_FILE) { + if (!strcmp(sym->name, hint)) { + curfile = sym->name; + continue; /* begin hint file symbols */ + } else if (curfile) + curfile = NULL; /* end hint file symbols */ + } + if (!curfile) + continue; + if (sym->bind == STB_LOCAL && !strcmp(sym->name, name)) { + if (match) + /* dup file+symbol, unresolvable ambiguity */ + return 1; + match = sym; + } + } + + if (!match) + return 1; + + result->value = match->value; + result->size = match->size; + return 0; +} + +int lookup_global_symbol(struct lookup_table *table, char *name, + struct lookup_result *result) +{ + struct symbol *sym; + int i; + + memset(result, 0, sizeof(*result)); + for_each_symbol(i, sym, table) + if (!sym->skip && sym->bind == STB_GLOBAL && + !strcmp(sym->name, name)) { + result->value = sym->value; + result->size = sym->size; + return 0; + } + + return 1; +} + +int lookup_is_exported_symbol(struct lookup_table *table, char *name) +{ + struct symbol *sym; + int i; + char export[255] = "__ksymtab_"; + + strncat(export, name, 254); + + for_each_symbol(i, sym, table) + if (!sym->skip && !strcmp(sym->name, export)) + return 1; + + return 0; +} + +#if 0 /* for local testing */ +static void find_this(struct lookup_table *table, char *sym, char *hint) +{ + struct lookup_result result; + + if (hint) + lookup_local_symbol(table, sym, hint, &result); + else + lookup_global_symbol(table, sym, &result); + + printf("%s %s w/ %s hint at 0x%016lx len %lu\n", + hint ? "local" : "global", sym, hint ? hint : "no", + result.value, result.size); +} + +int main(int argc, char **argv) +{ + struct lookup_table *vmlinux; + + if (argc != 2) + return 1; + + vmlinux = lookup_open(argv[1]); + + printf("printk is%s exported\n", + lookup_is_exported_symbol(vmlinux, "__fentry__") ? "" : " not"); + printf("meminfo_proc_show is%s exported\n", + lookup_is_exported_symbol(vmlinux, "meminfo_proc_show") ? "" : " not"); + + find_this(vmlinux, "printk", NULL); + find_this(vmlinux, "pages_to_scan_show", "ksm.c"); + find_this(vmlinux, "pages_to_scan_show", "huge_memory.c"); + find_this(vmlinux, "pages_to_scan_show", NULL); /* should fail */ + + lookup_close(vmlinux); + + return 0; +} +#endif diff --git a/lookup.h b/lookup.h new file mode 100644 index 0000000..cbb3dae --- /dev/null +++ b/lookup.h @@ -0,0 +1,19 @@ +#ifndef _LOOKUP_H_ +#define _LOOKUP_H_ + +struct lookup_table; + +struct lookup_result { + unsigned long value; + unsigned long size; +}; + +struct lookup_table *lookup_open(char *path); +void lookup_close(struct lookup_table *table); +int lookup_local_symbol(struct lookup_table *table, char *name, char *hint, + struct lookup_result *result); +int lookup_global_symbol(struct lookup_table *table, char *name, + struct lookup_result *result); +int lookup_is_exported_symbol(struct lookup_table *table, char *name); + +#endif /* _LOOKUP_H_ */ diff --git a/xsplice-build b/xsplice-build new file mode 100755 index 0000000..ca414b1 --- /dev/null +++ b/xsplice-build @@ -0,0 +1,252 @@ +#!/bin/bash +# +# xsplice build script +# +# Copyright (C) 2014 Seth Jennings +# Copyright (C) 2013,2014 Josh Poimboeuf +# Copyright (C) 2015 Ross Lagerwall +# +# 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 . +# +# This script takes a Xen tree, and a patch and outputs an xsplice +# module intended to patch Xen at runtime. +# Large amounts of this script are taken from kpatch's kpatch-build +# script. + +SCRIPTDIR="$(readlink -f $(dirname $(type -p $0)))" +CPUS="$(getconf _NPROCESSORS_ONLN)" +DEBUG=n +XEN_DEBUG=n +SKIP= + +warn() { + echo "ERROR: $1" >&2 +} + +die() { + if [[ -z $1 ]]; then + msg="xSplice build failed" + else + msg="$1" + fi + + warn "$msg." + + exit 1 +} + +function make_patch_name() +{ + PATCHNAME=$(basename "$1") + if [[ "$PATCHNAME" =~ \.patch ]] || [[ "$PATCHNAME" =~ \.diff ]]; then + PATCHNAME="${PATCHNAME%.*}" + fi + + # Only allow alphanumerics and '_' and '-' in the patch name. Everything + # else is replaced with '-'. Truncate to 48 chars. + echo ${PATCHNAME//[^a-zA-Z0-9_-]/-} |cut -c 1-48 +} + +# Do a full normal build +function build_full() +{ + cd "${SRCDIR}/xen" || die + make "-j$CPUS" clean &> "${OUTPUT}/build_full_clean.log" || die + make "-j$CPUS" debug="$XEN_DEBUG" &> "${OUTPUT}/build_full_compile.log" || die + cp xen-syms "$OUTPUT" +} + +# Build with special GCC flags +function build_special() +{ + name=$1 + + cd "${SRCDIR}" || die + + # Capture .o files from the patched build + export CROSS_COMPILE="${SCRIPTDIR}/xsplice-gcc " + export XSPLICE_BUILD_DIR="$(pwd)/" + export XSPLICE_CAPTURE_DIR="$OUTPUT/${name}" + mkdir -p "$XSPLICE_CAPTURE_DIR" + + # Build with special GCC flags + cd "${SRCDIR}/xen" || die + sed -i 's/CFLAGS += -nostdinc/CFLAGS += -nostdinc -ffunction-sections -fdata-sections/' Rules.mk + make "-j$CPUS" debug="$XEN_DEBUG" &> "${OUTPUT}/build_${name}_compile.log" || die + sed -i 's/CFLAGS += -nostdinc -ffunction-sections -fdata-sections/CFLAGS += -nostdinc/' Rules.mk + + unset XSPLICE_BUILD_DIR + unset XSPLICE_CAPTURE_DIR +} + +function create_patch() +{ + echo "Extracting new and modified ELF sections..." + + [[ -e "${OUTPUT}/original/changed_objs" ]] || die "no changed objects found" + [[ -e "${OUTPUT}/patched/changed_objs" ]] || die "no changed objects found" + + cd "${OUTPUT}/original" || die + FILES="$(find xen -type f -name "*.o")" + cd "${OUTPUT}" || die + CHANGED=0 + ERROR=0 + debugopt= + [[ $DEBUG -eq 1 ]] && debugopt=-d + + for i in $FILES; do + mkdir -p "output/$(dirname $i)" || die + echo "Processing ${i}" + echo "Run create-diff-object on $i" >> "${OUTPUT}/create-diff-object.log" + "${SCRIPTDIR}"/create-diff-object $debugopt "original/$i" "patched/$i" xen-syms "output/$i" &>> "${OUTPUT}/create-diff-object.log" + rc="${PIPESTATUS[0]}" + if [[ $rc = 139 ]]; then + warn "create-diff-object SIGSEGV" + if ls core* &> /dev/null; then + cp core* /tmp + die "core file at /tmp/$(ls core*)" + fi + die "no core file found, run 'ulimit -c unlimited' and try to recreate" + fi + # create-diff-object returns 3 if no functional change is found + [[ $rc -eq 0 ]] || [[ $rc -eq 3 ]] || ERROR=$(expr $ERROR "+" 1) + if [[ $rc -eq 0 ]]; then + CHANGED=1 + fi + done + + if [[ $ERROR -ne 0 ]]; then + die "$ERROR error(s) encountered" + fi + + if [[ $CHANGED -eq 0 ]]; then + die "no functional changes found" + fi + + echo "Creating patch module..." + ld -r -o "${PATCHNAME}.xsplice" $(find output -type f -name "*.o") || die +} + +usage() { + echo "usage: $(basename $0) [options]" >&2 + echo " -h, --help Show this help message" >&2 + echo " -s, --srcdir Xen source directory" >&2 + echo " -p, --patch Patch file" >&2 + echo " -o, --output Output directory" >&2 + echo " -j, --cpus Number of CPUs to use" >&2 + echo " -k, --skip Skip build or diff phase" >&2 + echo " -d, --debug Enable debug logging" >&2 + echo " --xen-debug Build debug Xen" >&2 +} + +options=$(getopt -o hs:p:o:j:k:d -l "help,srcdir:patch:output:cpus:,skip:,debug,xen-debug" -- "$@") || die "getopt failed" + +eval set -- "$options" + +while [[ $# -gt 0 ]]; do + case "$1" in + -h|--help) + usage + exit 0 + ;; + -j|--cpus) + shift + CPUS="$1" + shift + ;; + -k|--skip) + shift + SKIP="$1" + shift + ;; + -d|--debug) + DEBUG=1 + shift + ;; + --xen-debug) + XEN_DEBUG=y + shift + ;; + -s|--srcdir) + shift + srcarg="$1" + shift + ;; + -p|--patch) + shift + patcharg="$1" + shift + ;; + -o|--output) + shift + outputarg="$1" + shift + ;; + --) + shift + break + ;; + esac +done + +[ -z "$srcarg" ] && die "Xen directory not given" +[ -z "$patcharg" ] && die "Patchfile not given" +[ -z "$outputarg" ] && die "Output directory not given" + +SRCDIR="$(realpath -m -- "$srcarg")" +PATCHFILE="$(realpath -m -- "$patcharg")" +OUTPUT="$(realpath -m -- "$outputarg")" + +[ -d "${SRCDIR}" ] || die "Xen directory does not exist" +[ -f "${PATCHFILE}" ] || die "Patchfile does not exist" + +PATCHNAME=$(make_patch_name "${PATCHFILE}") + +echo "Building xSplice patch: ${PATCHNAME}" +echo +echo "Xen directory: ${SRCDIR}" +echo "Patch file: ${PATCHFILE}" +echo "Output directory: ${OUTPUT}" +echo "================================================" +echo + +if [ "${SKIP}" != "build" ]; then + [ -e "${OUTPUT}" ] && die "Output directory exists" + mkdir -p "${OUTPUT}" || die + + echo "Testing patch file..." + cd "$SRCDIR" || die + patch -s -N -p1 --dry-run < "$PATCHFILE" || die "source patch file failed to apply" + + echo "Perform full initial build with ${CPUS} CPU(s)..." + build_full + + echo "Apply patch and build with ${CPUS} CPU(s)..." + cd "$SRCDIR" || die + patch -s -N -p1 < "$PATCHFILE" || die + build_special original + + echo "Unapply patch and build with ${CPUS} CPU(s)..." + cd "$SRCDIR" || die + patch -s -R -p1 < "$PATCHFILE" || die + build_special patched +fi + +if [ "${SKIP}" != "diff" ]; then + [ -d "${OUTPUT}" ] || die "Output directory does not exist" + + cd "${OUTPUT}" || die + create_patch + echo "${PATCHNAME}.xsplice created successfully" +fi diff --git a/xsplice-gcc b/xsplice-gcc new file mode 100755 index 0000000..8b63e50 --- /dev/null +++ b/xsplice-gcc @@ -0,0 +1,66 @@ +#!/bin/bash +# +# xsplice build script +# +# Copyright (C) 2015 Ross Lagerwall +# +# 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 . +# +# 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" -- 2.39.5