diff --git a/.gitignore b/.gitignore
index bcd0e37..f0a50c3 100644
--- a/.gitignore
+++ b/.gitignore
@@ -13,4 +13,3 @@
.externalNativeBuild
.cxx
elf-cleaner.sh
-settings.gradle
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..30ace6a
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,674 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc.
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+ The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users. We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors. 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
+them 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 prevent others from denying you
+these rights or asking you to surrender the rights. Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received. 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.
+
+ Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+ For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software. For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+ Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so. This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software. The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable. Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products. If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+ Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary. To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ TERMS AND CONDITIONS
+
+ 0. Definitions.
+
+ "This License" refers to version 3 of the GNU General Public License.
+
+ "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+ "The Program" refers to any copyrightable work licensed under this
+License. Each licensee is addressed as "you". "Licensees" and
+"recipients" may be individuals or organizations.
+
+ To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy. The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+ A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+ To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+ To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies. Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+ An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License. If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+ 1. Source Code.
+
+ The "source code" for a work means the preferred form of the work
+for making modifications to it. "Object code" means any non-source
+form of a work.
+
+ A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+ The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form. A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+ The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities. However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work. For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+ The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+ The Corresponding Source for a work in source code form is that
+same work.
+
+ 2. Basic Permissions.
+
+ All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met. This License explicitly affirms your unlimited
+permission to run the unmodified Program. The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work. This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+ You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force. You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright. Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+ Conveying under any other circumstances is permitted solely under
+the conditions stated below. Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+ No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+ When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+ 4. Conveying Verbatim Copies.
+
+ You may convey 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;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+ You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+ 5. Conveying Modified Source Versions.
+
+ You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+ a) The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+
+ b) The work must carry prominent notices stating that it is
+ released under this License and any conditions added under section
+ 7. This requirement modifies the requirement in section 4 to
+ "keep intact all notices".
+
+ c) You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+
+ d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+
+ A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit. Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+ 6. Conveying Non-Source Forms.
+
+ You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+ a) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+
+ b) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either (1) a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or (2) access to copy the
+ Corresponding Source from a network server at no charge.
+
+ c) Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+
+ d) Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+
+ e) Convey the object code using peer-to-peer transmission, provided
+ you inform other peers where the object code and Corresponding
+ Source of the work are being offered to the general public at no
+ charge under subsection 6d.
+
+ A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+ A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling. In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage. For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product. A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+ "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source. The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+ If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information. But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+ The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed. Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+ Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+ 7. Additional Terms.
+
+ "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law. If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+ When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it. (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.) You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+ Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+ a) Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or
+
+ b) Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or
+
+ c) Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or
+
+ d) Limiting the use for publicity purposes of names of licensors or
+ authors of the material; or
+
+ e) Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or
+
+ f) Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions of
+ it) with contractual assumptions of liability to the recipient, for
+ any liability that these contractual assumptions directly impose on
+ those licensors and authors.
+
+ All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10. If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term. If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+ If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+ Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+ 8. Termination.
+
+ You may not propagate or modify a covered work except as expressly
+provided under this License. Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+ However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+ Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+ Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+ 9. Acceptance Not Required for Having Copies.
+
+ You are not required to accept this License in order to receive or
+run a copy of the Program. Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance. However,
+nothing other than this License grants you permission to propagate or
+modify any covered work. These actions infringe copyright if you do
+not accept this License. Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+ 10. Automatic Licensing of Downstream Recipients.
+
+ Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License. You are not responsible
+for enforcing compliance by third parties with this License.
+
+ An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+ You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License. For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+ 11. Patents.
+
+ A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based. The
+work thus licensed is called the contributor's "contributor version".
+
+ A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version. For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+ In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement). To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+ If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients. "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+ If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+ A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License. You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+ Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+ 12. No Surrender of Others' Freedom.
+
+ If 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 convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all. For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+ 13. Use with the GNU Affero General Public License.
+
+ Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work. The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+ 14. Revised Versions of this License.
+
+ The Free Software Foundation may publish revised and/or new versions of
+the GNU 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 that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation. If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+ If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+ Later license versions may give you additional or different
+permissions. However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+ 15. Disclaimer of Warranty.
+
+ 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.
+
+ 16. Limitation of Liability.
+
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+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.
+
+ 17. Interpretation of Sections 15 and 16.
+
+ If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+ 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
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ {one line to give the program's name and a brief idea of what it does.}
+ Copyright (C) {year} {name of author}
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 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 .
+
+Also add information on how to contact you by electronic and paper mail.
+
+ If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+ {project} Copyright (C) {year} {fullname}
+ This program 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, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+ You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+.
+
+ The GNU 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. But first, please read
+.
\ No newline at end of file
diff --git a/README.md b/README.md
index f4b71af..9ee9eba 100644
--- a/README.md
+++ b/README.md
@@ -1,24 +1,20 @@
-# Riru - Template
+# Riru - MiPushFake
-[Riru](https://github.com/RikkaApps/Riru) module template.
+
-## Build
+Fake as a Xiaomi device by hook system_property_get.
-1. Replace all "template" to your name
-
- * `module/build.gradle`: "template" in variable `moduleId`, `moduleProp`
- * `module/src/main/cpp/CMakeLists.txt`: "riru_template" in `set(MODULE_NAME )`
- * `template/magisk_module/post-fs-data.sh`: `MODULE_ID="template"`
- * `template/magisk_module/uninstall.sh`: `MODULE_ID="template"`
+Requires Riru - Core v19 or above installed.
-2. Write you codes
-3. Run gradle task `:module:assembleRelease` task from Android Studio or command line, zip will be saved in `out`.
+## What does this module do
-## Notes
+By default, `__system_property_get` (`android::base::GetProperty` on Pie+) will be hooked in packages selected in
+MiPushFramework with value map below
-* DO NOT overwrite `android.os.SystemProperties#native_set` in core, or your data may be wiped
- ([Detail info](https://github.com/RikkaApps/Riru/blob/v7/riru-core/jni/main/jni_native_method.cpp#L162-L176))
- (If you really need to hook this, remember to clear exception)
-* DO NO print log (`__android_log_print`) in `nativeForkAndSpecialize(Pre/Post)` `nativeForkSystemServer(Pre/Post)` when in zygote process, or it may cause zygote not work
- (magic not confirmed, [Detail info](https://github.com/RikkaApps/Riru/blob/77adfd6a4a6a81bfd20569c910bc4854f2f84f5e/riru-core/jni/main/jni_native_method.cpp#L55-L66))
-* Add `-ffixed-x18` to both compiler and linker parameter, or it will cause problems on Android Q (see template)
\ No newline at end of file
+* `ro.miui.ui.version.name` -> `V11`
+* `ro.miui.ui.version.code` -> `9`
+* `ro.miui.version.code_time` -> `1570636800`
+* `ro.miui.internal.storage` -> `/sdcard/`
+* `ro.product.manufacturer` -> `Xiaomi`
+* `ro.product.brand` -> `Xiaomi`
+* `ro.product.name` -> `Xiaomi`
\ No newline at end of file
diff --git a/gradlew b/gradlew
old mode 100644
new mode 100755
diff --git a/gradlew.bat b/gradlew.bat
index f955316..e95643d 100644
--- a/gradlew.bat
+++ b/gradlew.bat
@@ -1,84 +1,84 @@
-@if "%DEBUG%" == "" @echo off
-@rem ##########################################################################
-@rem
-@rem Gradle startup script for Windows
-@rem
-@rem ##########################################################################
-
-@rem Set local scope for the variables with windows NT shell
-if "%OS%"=="Windows_NT" setlocal
-
-set DIRNAME=%~dp0
-if "%DIRNAME%" == "" set DIRNAME=.
-set APP_BASE_NAME=%~n0
-set APP_HOME=%DIRNAME%
-
-@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
-set DEFAULT_JVM_OPTS=
-
-@rem Find java.exe
-if defined JAVA_HOME goto findJavaFromJavaHome
-
-set JAVA_EXE=java.exe
-%JAVA_EXE% -version >NUL 2>&1
-if "%ERRORLEVEL%" == "0" goto init
-
-echo.
-echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
-echo.
-echo Please set the JAVA_HOME variable in your environment to match the
-echo location of your Java installation.
-
-goto fail
-
-:findJavaFromJavaHome
-set JAVA_HOME=%JAVA_HOME:"=%
-set JAVA_EXE=%JAVA_HOME%/bin/java.exe
-
-if exist "%JAVA_EXE%" goto init
-
-echo.
-echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
-echo.
-echo Please set the JAVA_HOME variable in your environment to match the
-echo location of your Java installation.
-
-goto fail
-
-:init
-@rem Get command-line arguments, handling Windows variants
-
-if not "%OS%" == "Windows_NT" goto win9xME_args
-
-:win9xME_args
-@rem Slurp the command line arguments.
-set CMD_LINE_ARGS=
-set _SKIP=2
-
-:win9xME_args_slurp
-if "x%~1" == "x" goto execute
-
-set CMD_LINE_ARGS=%*
-
-:execute
-@rem Setup the command line
-
-set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
-
-@rem Execute Gradle
-"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
-
-:end
-@rem End local scope for the variables with windows NT shell
-if "%ERRORLEVEL%"=="0" goto mainEnd
-
-:fail
-rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
-rem the _cmd.exe /c_ return code!
-if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
-exit /b 1
-
-:mainEnd
-if "%OS%"=="Windows_NT" endlocal
-
-:omega
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windows variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/module.gradle b/module.gradle
new file mode 100644
index 0000000..381560f
--- /dev/null
+++ b/module.gradle
@@ -0,0 +1,22 @@
+ext {
+ moduleId = "mipush_fake"
+ riruApiVersion = 4
+
+ moduleProp = [
+ name : "Riru - MiPushFake",
+ version : "v13.0",
+ versionCode: "13",
+ author : "Timothy, MlgmXyysd",
+ description: "Fake as a Xiaomi device by hook system_property_get. Requires Riru - Core v19 or above installed.",
+ api : riruApiVersion
+ ]
+
+ magiskModuleProp = [
+ id : "riru-${moduleId.replace('_', '-')}",
+ name : "Riru - ${moduleProp['name']}",
+ version : moduleProp['version'],
+ versionCode: moduleProp['versionCode'],
+ author : moduleProp['author'],
+ description: moduleProp['description']
+ ]
+}
\ No newline at end of file
diff --git a/module/build.gradle b/module/build.gradle
index 695e0f1..8ebd753 100644
--- a/module/build.gradle
+++ b/module/build.gradle
@@ -1,10 +1,16 @@
apply plugin: 'com.android.library'
+apply from: file(rootProject.file('module.gradle'))
android {
compileSdkVersion rootProject.ext.targetSdkVersion
defaultConfig {
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
+ externalNativeBuild {
+ cmake {
+ arguments "-DMODULE_NAME:STRING=riru_$moduleId"
+ }
+ }
}
externalNativeBuild {
cmake {
@@ -14,29 +20,10 @@ android {
}
}
-def moduleId = "template"
-def moduleProp = [
- name : "Template",
- version : "v1.0",
- versionCode: "1",
- author : "Template",
- description: "Riru module template.",
- api : 4
-]
-
-def magiskModuleProp = [
- id : "riru-${moduleId.replace('_', '-')}",
- name : "Riru - ${moduleProp['name']}",
- version : moduleProp['version'],
- versionCode: moduleProp['versionCode'],
- author : moduleProp['author'],
- description: moduleProp['description']
-]
-
def outDir = file("$rootDir/out")
def magiskDir = file("$outDir/magisk_module")
-def zipName = "magisk-${magiskModuleProp['id']}-${magiskModuleProp['version']}.zip"
-def riruDir = "$magiskDir/data/misc/riru/modules/${moduleId}"
+def zipName = "magisk-${magiskModuleProp['id'].replace('_', '-')}-${magiskModuleProp['version']}.zip"
+def riruDir = "$magiskDir/riru"
import java.nio.file.Files
import java.security.MessageDigest
@@ -55,6 +42,8 @@ static def renameOrFail(File from, File to) {
}
}
+import org.apache.tools.ant.filters.FixCrLfFilter
+
android.libraryVariants.all { variant ->
def task = variant.assembleProvider.get()
task.doLast {
@@ -65,6 +54,19 @@ android.libraryVariants.all { variant ->
copy {
from "$rootDir/template/magisk_module"
into magiskDir.path
+ exclude 'riru.sh'
+ }
+ // copy riru.sh
+ copy {
+ from "$rootDir/template/magisk_module"
+ into magiskDir.path
+ include 'riru.sh'
+ filter { line ->
+ line.replaceAll('%%%RIRU_MODULE_ID%%%', moduleId)
+ .replaceAll('%%%RIRU_MIN_API_VERSION%%%', riruApiVersion.toString())
+ }
+ filter(FixCrLfFilter.class,
+ eol: FixCrLfFilter.CrLf.newInstance("lf"))
}
// copy .git files manually since gradle exclude it by default
Files.copy(file("$rootDir/template/magisk_module/.gitattributes").toPath(), file("${magiskDir.path}/.gitattributes").toPath())
diff --git a/module/src/main/AndroidManifest.xml b/module/src/main/AndroidManifest.xml
index 235c657..c814c07 100644
--- a/module/src/main/AndroidManifest.xml
+++ b/module/src/main/AndroidManifest.xml
@@ -1 +1 @@
-
+
diff --git a/module/src/main/cpp/CMakeLists.txt b/module/src/main/cpp/CMakeLists.txt
index 7d9c393..1bbf47d 100644
--- a/module/src/main/cpp/CMakeLists.txt
+++ b/module/src/main/cpp/CMakeLists.txt
@@ -1,6 +1,18 @@
cmake_minimum_required(VERSION 3.4.1)
-set(MODULE_NAME "riru_template")
+set(MODULE_NAME "riru_mipush_fake")
+
+SET(XHOOK_PATH ${CMAKE_CURRENT_SOURCE_DIR}/external/xhook/)
+
+
+INCLUDE_DIRECTORIES(
+ ${CMAKE_CURRENT_SOURCE_DIR}/include
+ ${XHOOK_PATH}/include
+)
+
+LINK_DIRECTORIES(
+ ${XHOOK_PATH}/lib/
+)
message("Build type: ${CMAKE_BUILD_TYPE}")
@@ -22,6 +34,22 @@ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${C_FLAGS}")
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} ${LINKER_FLAGS}")
set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} ${LINKER_FLAGS}")
-add_library(${MODULE_NAME} SHARED main.cpp)
-target_link_libraries(${MODULE_NAME} log)
+
+SET(XHOOK_OBJS
+ external/xhook/xhook.c
+ external/xhook/xh_core.c
+ external/xhook/xh_elf.c
+ external/xhook/xh_jni.c
+ external/xhook/xh_log.c
+ external/xhook/xh_util.c
+ external/xhook/xh_version.c
+ )
+ADD_LIBRARY(libxhook STATIC ${XHOOK_OBJS})
+set_target_properties(libxhook PROPERTIES LINKER_LANGUAGE CXX C_STANDARD 11 OUTPUT_NAME "xhook")
+
+
+add_library(${MODULE_NAME} SHARED hook.cpp riru.c main.cpp)
+target_link_libraries(${MODULE_NAME} log libxhook)
set_target_properties(${MODULE_NAME} PROPERTIES LINK_FLAGS_RELEASE -s)
+
+
diff --git a/module/src/main/cpp/external/Android.mk b/module/src/main/cpp/external/Android.mk
new file mode 100644
index 0000000..04d3e48
--- /dev/null
+++ b/module/src/main/cpp/external/Android.mk
@@ -0,0 +1,15 @@
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := xhook
+LOCAL_SRC_FILES := xhook/xhook.c \
+ xhook/xh_core.c \
+ xhook/xh_elf.c \
+ xhook/xh_jni.c \
+ xhook/xh_log.c \
+ xhook/xh_util.c \
+ xhook/xh_version.c
+LOCAL_C_INCLUDES := $(LOCAL_PATH)
+LOCAL_CFLAGS := -Wall -Wextra -Werror -fvisibility=hidden
+LOCAL_CONLYFLAGS := -std=c11
+include $(BUILD_STATIC_LIBRARY)
\ No newline at end of file
diff --git a/module/src/main/cpp/external/xhook/include/xhook.h b/module/src/main/cpp/external/xhook/include/xhook.h
new file mode 100644
index 0000000..93dd5b4
--- /dev/null
+++ b/module/src/main/cpp/external/xhook/include/xhook.h
@@ -0,0 +1,50 @@
+// Copyright (c) 2018-present, iQIYI, Inc. All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+//
+
+// Created by caikelun on 2018-04-11.
+
+#ifndef XHOOK_H
+#define XHOOK_H 1
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define XHOOK_EXPORT __attribute__((visibility("default")))
+
+int xhook_register(const char *pathname_regex_str, const char *symbol,
+ void *new_func, void **old_func) XHOOK_EXPORT;
+
+int xhook_ignore(const char *pathname_regex_str, const char *symbol) XHOOK_EXPORT;
+
+int xhook_refresh(int async) XHOOK_EXPORT;
+
+void xhook_clear() XHOOK_EXPORT;
+
+void xhook_enable_debug(int flag) XHOOK_EXPORT;
+
+void xhook_enable_sigsegv_protection(int flag) XHOOK_EXPORT;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/module/src/main/cpp/external/xhook/queue.h b/module/src/main/cpp/external/xhook/queue.h
new file mode 100644
index 0000000..c2443be
--- /dev/null
+++ b/module/src/main/cpp/external/xhook/queue.h
@@ -0,0 +1,554 @@
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)queue.h 8.5 (Berkeley) 8/20/94
+ * $FreeBSD: stable/9/sys/sys/queue.h 252365 2013-06-29 04:25:40Z lstewart $
+ */
+
+#ifndef QUEUE_H
+#define QUEUE_H
+
+/* #include */
+#define __containerof(ptr, type, field) ((type *)((char *)(ptr) - ((char *)&((type *)0)->field)))
+
+/*
+ * This file defines four types of data structures: singly-linked lists,
+ * singly-linked tail queues, lists and tail queues.
+ *
+ * A singly-linked list is headed by a single forward pointer. The elements
+ * are singly linked for minimum space and pointer manipulation overhead at
+ * the expense of O(n) removal for arbitrary elements. New elements can be
+ * added to the list after an existing element or at the head of the list.
+ * Elements being removed from the head of the list should use the explicit
+ * macro for this purpose for optimum efficiency. A singly-linked list may
+ * only be traversed in the forward direction. Singly-linked lists are ideal
+ * for applications with large datasets and few or no removals or for
+ * implementing a LIFO queue.
+ *
+ * A singly-linked tail queue is headed by a pair of pointers, one to the
+ * head of the list and the other to the tail of the list. The elements are
+ * singly linked for minimum space and pointer manipulation overhead at the
+ * expense of O(n) removal for arbitrary elements. New elements can be added
+ * to the list after an existing element, at the head of the list, or at the
+ * end of the list. Elements being removed from the head of the tail queue
+ * should use the explicit macro for this purpose for optimum efficiency.
+ * A singly-linked tail queue may only be traversed in the forward direction.
+ * Singly-linked tail queues are ideal for applications with large datasets
+ * and few or no removals or for implementing a FIFO queue.
+ *
+ * A list is headed by a single forward pointer (or an array of forward
+ * pointers for a hash table header). The elements are doubly linked
+ * so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list before
+ * or after an existing element or at the head of the list. A list
+ * may be traversed in either direction.
+ *
+ * A tail queue is headed by a pair of pointers, one to the head of the
+ * list and the other to the tail of the list. The elements are doubly
+ * linked so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list before or
+ * after an existing element, at the head of the list, or at the end of
+ * the list. A tail queue may be traversed in either direction.
+ *
+ * For details on the use of these macros, see the queue(3) manual page.
+ *
+ * SLIST LIST STAILQ TAILQ
+ * _HEAD + + + +
+ * _HEAD_INITIALIZER + + + +
+ * _ENTRY + + + +
+ * _INIT + + + +
+ * _EMPTY + + + +
+ * _FIRST + + + +
+ * _NEXT + + + +
+ * _PREV - + - +
+ * _LAST - - + +
+ * _FOREACH + + + +
+ * _FOREACH_FROM + + + +
+ * _FOREACH_SAFE + + + +
+ * _FOREACH_FROM_SAFE + + + +
+ * _FOREACH_REVERSE - - - +
+ * _FOREACH_REVERSE_FROM - - - +
+ * _FOREACH_REVERSE_SAFE - - - +
+ * _FOREACH_REVERSE_FROM_SAFE - - - +
+ * _INSERT_HEAD + + + +
+ * _INSERT_BEFORE - + - +
+ * _INSERT_AFTER + + + +
+ * _INSERT_TAIL - - + +
+ * _CONCAT - - + +
+ * _REMOVE_AFTER + - + -
+ * _REMOVE_HEAD + - + -
+ * _REMOVE + + + +
+ * _SWAP + + + +
+ *
+ */
+
+/*
+ * Singly-linked List declarations.
+ */
+#define SLIST_HEAD(name, type, qual) \
+ struct name { \
+ struct type *qual slh_first; /* first element */ \
+ }
+
+#define SLIST_HEAD_INITIALIZER(head) \
+ { NULL }
+
+#define SLIST_ENTRY(type, qual) \
+ struct { \
+ struct type *qual sle_next; /* next element */ \
+ }
+
+/*
+ * Singly-linked List functions.
+ */
+#define SLIST_INIT(head) do { \
+ SLIST_FIRST((head)) = NULL; \
+ } while (0)
+
+#define SLIST_EMPTY(head) ((head)->slh_first == NULL)
+
+#define SLIST_FIRST(head) ((head)->slh_first)
+
+#define SLIST_NEXT(elm, field) ((elm)->field.sle_next)
+
+#define SLIST_FOREACH(var, head, field) \
+ for ((var) = SLIST_FIRST((head)); \
+ (var); \
+ (var) = SLIST_NEXT((var), field))
+
+#define SLIST_FOREACH_FROM(var, head, field) \
+ for ((var) = ((var) ? (var) : SLIST_FIRST((head))); \
+ (var); \
+ (var) = SLIST_NEXT((var), field))
+
+#define SLIST_FOREACH_SAFE(var, head, field, tvar) \
+ for ((var) = SLIST_FIRST((head)); \
+ (var) && ((tvar) = SLIST_NEXT((var), field), 1); \
+ (var) = (tvar))
+
+#define SLIST_FOREACH_FROM_SAFE(var, head, field, tvar) \
+ for ((var) = ((var) ? (var) : SLIST_FIRST((head))); \
+ (var) && ((tvar) = SLIST_NEXT((var), field), 1); \
+ (var) = (tvar))
+
+#define SLIST_INSERT_HEAD(head, elm, field) do { \
+ SLIST_NEXT((elm), field) = SLIST_FIRST((head)); \
+ SLIST_FIRST((head)) = (elm); \
+ } while (0)
+
+#define SLIST_INSERT_AFTER(slistelm, elm, field) do { \
+ SLIST_NEXT((elm), field) = SLIST_NEXT((slistelm), field); \
+ SLIST_NEXT((slistelm), field) = (elm); \
+ } while (0)
+
+#define SLIST_REMOVE_AFTER(elm, field) do { \
+ SLIST_NEXT(elm, field) = \
+ SLIST_NEXT(SLIST_NEXT(elm, field), field); \
+ } while (0)
+
+#define SLIST_REMOVE_HEAD(head, field) do { \
+ SLIST_FIRST((head)) = SLIST_NEXT(SLIST_FIRST((head)), field); \
+ } while (0)
+
+#define SLIST_REMOVE(head, elm, type, field) do { \
+ if (SLIST_FIRST((head)) == (elm)) { \
+ SLIST_REMOVE_HEAD((head), field); \
+ } \
+ else { \
+ struct type *curelm = SLIST_FIRST((head)); \
+ while (SLIST_NEXT(curelm, field) != (elm)) \
+ curelm = SLIST_NEXT(curelm, field); \
+ SLIST_REMOVE_AFTER(curelm, field); \
+ } \
+ } while (0)
+
+#define SLIST_SWAP(head1, head2, type) do { \
+ struct type *swap_first = SLIST_FIRST(head1); \
+ SLIST_FIRST(head1) = SLIST_FIRST(head2); \
+ SLIST_FIRST(head2) = swap_first; \
+ } while (0)
+
+/*
+ * List declarations.
+ */
+#define LIST_HEAD(name, type, qual) \
+ struct name { \
+ struct type *qual lh_first; /* first element */ \
+ }
+
+#define LIST_HEAD_INITIALIZER(head) \
+ { NULL }
+
+#define LIST_ENTRY(type, qual) \
+ struct { \
+ struct type *qual le_next; /* next element */ \
+ struct type *qual *le_prev; /* address of previous next element */ \
+ }
+
+/*
+ * List functions.
+ */
+#define LIST_INIT(head) do { \
+ LIST_FIRST((head)) = NULL; \
+ } while (0)
+
+#define LIST_EMPTY(head) ((head)->lh_first == NULL)
+
+#define LIST_FIRST(head) ((head)->lh_first)
+
+#define LIST_NEXT(elm, field) ((elm)->field.le_next)
+
+#define LIST_PREV(elm, head, type, field) \
+ ((elm)->field.le_prev == &LIST_FIRST((head)) ? NULL : \
+ __containerof((elm)->field.le_prev, struct type, field.le_next))
+
+#define LIST_FOREACH(var, head, field) \
+ for ((var) = LIST_FIRST((head)); \
+ (var); \
+ (var) = LIST_NEXT((var), field))
+
+#define LIST_FOREACH_FROM(var, head, field) \
+ for ((var) = ((var) ? (var) : LIST_FIRST((head))); \
+ (var); \
+ (var) = LIST_NEXT((var), field))
+
+#define LIST_FOREACH_SAFE(var, head, field, tvar) \
+ for ((var) = LIST_FIRST((head)); \
+ (var) && ((tvar) = LIST_NEXT((var), field), 1); \
+ (var) = (tvar))
+
+#define LIST_FOREACH_FROM_SAFE(var, head, field, tvar) \
+ for ((var) = ((var) ? (var) : LIST_FIRST((head))); \
+ (var) && ((tvar) = LIST_NEXT((var), field), 1); \
+ (var) = (tvar))
+
+#define LIST_INSERT_HEAD(head, elm, field) do { \
+ if ((LIST_NEXT((elm), field) = LIST_FIRST((head))) != NULL) \
+ LIST_FIRST((head))->field.le_prev = &LIST_NEXT((elm), field); \
+ LIST_FIRST((head)) = (elm); \
+ (elm)->field.le_prev = &LIST_FIRST((head)); \
+ } while (0)
+
+#define LIST_INSERT_BEFORE(listelm, elm, field) do { \
+ (elm)->field.le_prev = (listelm)->field.le_prev; \
+ LIST_NEXT((elm), field) = (listelm); \
+ *(listelm)->field.le_prev = (elm); \
+ (listelm)->field.le_prev = &LIST_NEXT((elm), field); \
+ } while (0)
+
+#define LIST_INSERT_AFTER(listelm, elm, field) do { \
+ if ((LIST_NEXT((elm), field) = LIST_NEXT((listelm), field)) != NULL) \
+ LIST_NEXT((listelm), field)->field.le_prev = \
+ &LIST_NEXT((elm), field); \
+ LIST_NEXT((listelm), field) = (elm); \
+ (elm)->field.le_prev = &LIST_NEXT((listelm), field); \
+ } while (0)
+
+#define LIST_REMOVE(elm, field) do { \
+ if (LIST_NEXT((elm), field) != NULL) \
+ LIST_NEXT((elm), field)->field.le_prev = \
+ (elm)->field.le_prev; \
+ *(elm)->field.le_prev = LIST_NEXT((elm), field); \
+ } while (0)
+
+#define LIST_SWAP(head1, head2, type, field) do { \
+ struct type *swap_tmp = LIST_FIRST((head1)); \
+ LIST_FIRST((head1)) = LIST_FIRST((head2)); \
+ LIST_FIRST((head2)) = swap_tmp; \
+ if ((swap_tmp = LIST_FIRST((head1))) != NULL) \
+ swap_tmp->field.le_prev = &LIST_FIRST((head1)); \
+ if ((swap_tmp = LIST_FIRST((head2))) != NULL) \
+ swap_tmp->field.le_prev = &LIST_FIRST((head2)); \
+ } while (0)
+
+/*
+ * Singly-linked Tail queue declarations.
+ */
+#define STAILQ_HEAD(name, type, qual) \
+ struct name { \
+ struct type *qual stqh_first;/* first element */ \
+ struct type *qual *stqh_last;/* addr of last next element */ \
+ }
+
+#define STAILQ_HEAD_INITIALIZER(head) \
+ { NULL, &(head).stqh_first }
+
+#define STAILQ_ENTRY(type, qual) \
+ struct { \
+ struct type *qual stqe_next; /* next element */ \
+ }
+
+/*
+ * Singly-linked Tail queue functions.
+ */
+#define STAILQ_INIT(head) do { \
+ STAILQ_FIRST((head)) = NULL; \
+ (head)->stqh_last = &STAILQ_FIRST((head)); \
+ } while (0)
+
+#define STAILQ_EMPTY(head) ((head)->stqh_first == NULL)
+
+#define STAILQ_FIRST(head) ((head)->stqh_first)
+
+#define STAILQ_NEXT(elm, field) ((elm)->field.stqe_next)
+
+#define STAILQ_LAST(head, type, field) \
+ (STAILQ_EMPTY((head)) ? NULL : \
+ __containerof((head)->stqh_last, struct type, field.stqe_next))
+
+#define STAILQ_FOREACH(var, head, field) \
+ for((var) = STAILQ_FIRST((head)); \
+ (var); \
+ (var) = STAILQ_NEXT((var), field))
+
+#define STAILQ_FOREACH_FROM(var, head, field) \
+ for ((var) = ((var) ? (var) : STAILQ_FIRST((head))); \
+ (var); \
+ (var) = STAILQ_NEXT((var), field))
+
+#define STAILQ_FOREACH_SAFE(var, head, field, tvar) \
+ for ((var) = STAILQ_FIRST((head)); \
+ (var) && ((tvar) = STAILQ_NEXT((var), field), 1); \
+ (var) = (tvar))
+
+#define STAILQ_FOREACH_FROM_SAFE(var, head, field, tvar) \
+ for ((var) = ((var) ? (var) : STAILQ_FIRST((head))); \
+ (var) && ((tvar) = STAILQ_NEXT((var), field), 1); \
+ (var) = (tvar))
+
+#define STAILQ_INSERT_HEAD(head, elm, field) do { \
+ if ((STAILQ_NEXT((elm), field) = STAILQ_FIRST((head))) == NULL) \
+ (head)->stqh_last = &STAILQ_NEXT((elm), field); \
+ STAILQ_FIRST((head)) = (elm); \
+ } while (0)
+
+#define STAILQ_INSERT_AFTER(head, tqelm, elm, field) do { \
+ if ((STAILQ_NEXT((elm), field) = STAILQ_NEXT((tqelm), field)) == NULL) \
+ (head)->stqh_last = &STAILQ_NEXT((elm), field); \
+ STAILQ_NEXT((tqelm), field) = (elm); \
+ } while (0)
+
+#define STAILQ_INSERT_TAIL(head, elm, field) do { \
+ STAILQ_NEXT((elm), field) = NULL; \
+ *(head)->stqh_last = (elm); \
+ (head)->stqh_last = &STAILQ_NEXT((elm), field); \
+ } while (0)
+
+#define STAILQ_CONCAT(head1, head2) do { \
+ if (!STAILQ_EMPTY((head2))) { \
+ *(head1)->stqh_last = (head2)->stqh_first; \
+ (head1)->stqh_last = (head2)->stqh_last; \
+ STAILQ_INIT((head2)); \
+ } \
+ } while (0)
+
+#define STAILQ_REMOVE_AFTER(head, elm, field) do { \
+ if ((STAILQ_NEXT(elm, field) = \
+ STAILQ_NEXT(STAILQ_NEXT(elm, field), field)) == NULL) \
+ (head)->stqh_last = &STAILQ_NEXT((elm), field); \
+ } while (0)
+
+#define STAILQ_REMOVE_HEAD(head, field) do { \
+ if ((STAILQ_FIRST((head)) = \
+ STAILQ_NEXT(STAILQ_FIRST((head)), field)) == NULL) \
+ (head)->stqh_last = &STAILQ_FIRST((head)); \
+ } while (0)
+
+#define STAILQ_REMOVE(head, elm, type, field) do { \
+ if (STAILQ_FIRST((head)) == (elm)) { \
+ STAILQ_REMOVE_HEAD((head), field); \
+ } \
+ else { \
+ struct type *curelm = STAILQ_FIRST((head)); \
+ while (STAILQ_NEXT(curelm, field) != (elm)) \
+ curelm = STAILQ_NEXT(curelm, field); \
+ STAILQ_REMOVE_AFTER(head, curelm, field); \
+ } \
+ } while (0)
+
+#define STAILQ_SWAP(head1, head2, type) do { \
+ struct type *swap_first = STAILQ_FIRST(head1); \
+ struct type **swap_last = (head1)->stqh_last; \
+ STAILQ_FIRST(head1) = STAILQ_FIRST(head2); \
+ (head1)->stqh_last = (head2)->stqh_last; \
+ STAILQ_FIRST(head2) = swap_first; \
+ (head2)->stqh_last = swap_last; \
+ if (STAILQ_EMPTY(head1)) \
+ (head1)->stqh_last = &STAILQ_FIRST(head1); \
+ if (STAILQ_EMPTY(head2)) \
+ (head2)->stqh_last = &STAILQ_FIRST(head2); \
+ } while (0)
+
+/*
+ * Tail queue declarations.
+ */
+#define TAILQ_HEAD(name, type, qual) \
+ struct name { \
+ struct type *qual tqh_first; /* first element */ \
+ struct type *qual *tqh_last; /* addr of last next element */ \
+}
+
+#define TAILQ_HEAD_INITIALIZER(head) \
+ { NULL, &(head).tqh_first }
+
+#define TAILQ_ENTRY(type, qual) \
+ struct { \
+ struct type *qual tqe_next; /* next element */ \
+ struct type *qual *tqe_prev; /* address of previous next element */ \
+ }
+
+/*
+ * Tail queue functions.
+ */
+#define TAILQ_INIT(head) do { \
+ TAILQ_FIRST((head)) = NULL; \
+ (head)->tqh_last = &TAILQ_FIRST((head)); \
+ } while (0)
+
+#define TAILQ_EMPTY(head) ((head)->tqh_first == NULL)
+
+#define TAILQ_FIRST(head) ((head)->tqh_first)
+
+#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next)
+
+#define TAILQ_PREV(elm, headname, field) \
+ (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last))
+
+#define TAILQ_LAST(head, headname) \
+ (*(((struct headname *)((head)->tqh_last))->tqh_last))
+
+#define TAILQ_FOREACH(var, head, field) \
+ for ((var) = TAILQ_FIRST((head)); \
+ (var); \
+ (var) = TAILQ_NEXT((var), field))
+
+#define TAILQ_FOREACH_FROM(var, head, field) \
+ for ((var) = ((var) ? (var) : TAILQ_FIRST((head))); \
+ (var); \
+ (var) = TAILQ_NEXT((var), field))
+
+#define TAILQ_FOREACH_SAFE(var, head, field, tvar) \
+ for ((var) = TAILQ_FIRST((head)); \
+ (var) && ((tvar) = TAILQ_NEXT((var), field), 1); \
+ (var) = (tvar))
+
+#define TAILQ_FOREACH_FROM_SAFE(var, head, field, tvar) \
+ for ((var) = ((var) ? (var) : TAILQ_FIRST((head))); \
+ (var) && ((tvar) = TAILQ_NEXT((var), field), 1); \
+ (var) = (tvar))
+
+#define TAILQ_FOREACH_REVERSE(var, head, headname, field) \
+ for ((var) = TAILQ_LAST((head), headname); \
+ (var); \
+ (var) = TAILQ_PREV((var), headname, field))
+
+#define TAILQ_FOREACH_REVERSE_FROM(var, head, headname, field) \
+ for ((var) = ((var) ? (var) : TAILQ_LAST((head), headname)); \
+ (var); \
+ (var) = TAILQ_PREV((var), headname, field))
+
+#define TAILQ_FOREACH_REVERSE_SAFE(var, head, headname, field, tvar) \
+ for ((var) = TAILQ_LAST((head), headname); \
+ (var) && ((tvar) = TAILQ_PREV((var), headname, field), 1); \
+ (var) = (tvar))
+
+#define TAILQ_FOREACH_REVERSE_FROM_SAFE(var, head, headname, field, tvar) \
+ for ((var) = ((var) ? (var) : TAILQ_LAST((head), headname)); \
+ (var) && ((tvar) = TAILQ_PREV((var), headname, field), 1); \
+ (var) = (tvar))
+
+#define TAILQ_INSERT_HEAD(head, elm, field) do { \
+ if ((TAILQ_NEXT((elm), field) = TAILQ_FIRST((head))) != NULL) \
+ TAILQ_FIRST((head))->field.tqe_prev = \
+ &TAILQ_NEXT((elm), field); \
+ else \
+ (head)->tqh_last = &TAILQ_NEXT((elm), field); \
+ TAILQ_FIRST((head)) = (elm); \
+ (elm)->field.tqe_prev = &TAILQ_FIRST((head)); \
+ } while (0)
+
+#define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \
+ (elm)->field.tqe_prev = (listelm)->field.tqe_prev; \
+ TAILQ_NEXT((elm), field) = (listelm); \
+ *(listelm)->field.tqe_prev = (elm); \
+ (listelm)->field.tqe_prev = &TAILQ_NEXT((elm), field); \
+ } while (0)
+
+#define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \
+ if ((TAILQ_NEXT((elm), field) = TAILQ_NEXT((listelm), field)) != NULL) \
+ TAILQ_NEXT((elm), field)->field.tqe_prev = \
+ &TAILQ_NEXT((elm), field); \
+ else \
+ (head)->tqh_last = &TAILQ_NEXT((elm), field); \
+ TAILQ_NEXT((listelm), field) = (elm); \
+ (elm)->field.tqe_prev = &TAILQ_NEXT((listelm), field); \
+ } while (0)
+
+#define TAILQ_INSERT_TAIL(head, elm, field) do { \
+ TAILQ_NEXT((elm), field) = NULL; \
+ (elm)->field.tqe_prev = (head)->tqh_last; \
+ *(head)->tqh_last = (elm); \
+ (head)->tqh_last = &TAILQ_NEXT((elm), field); \
+ } while (0)
+
+#define TAILQ_CONCAT(head1, head2, field) do { \
+ if (!TAILQ_EMPTY(head2)) { \
+ *(head1)->tqh_last = (head2)->tqh_first; \
+ (head2)->tqh_first->field.tqe_prev = (head1)->tqh_last; \
+ (head1)->tqh_last = (head2)->tqh_last; \
+ TAILQ_INIT((head2)); \
+ } \
+ } while (0)
+
+#define TAILQ_REMOVE(head, elm, field) do { \
+ if ((TAILQ_NEXT((elm), field)) != NULL) \
+ TAILQ_NEXT((elm), field)->field.tqe_prev = \
+ (elm)->field.tqe_prev; \
+ else \
+ (head)->tqh_last = (elm)->field.tqe_prev; \
+ *(elm)->field.tqe_prev = TAILQ_NEXT((elm), field); \
+ } while (0)
+
+#define TAILQ_SWAP(head1, head2, type, field) do { \
+ struct type *swap_first = (head1)->tqh_first; \
+ struct type **swap_last = (head1)->tqh_last; \
+ (head1)->tqh_first = (head2)->tqh_first; \
+ (head1)->tqh_last = (head2)->tqh_last; \
+ (head2)->tqh_first = swap_first; \
+ (head2)->tqh_last = swap_last; \
+ if ((swap_first = (head1)->tqh_first) != NULL) \
+ swap_first->field.tqe_prev = &(head1)->tqh_first; \
+ else \
+ (head1)->tqh_last = &(head1)->tqh_first; \
+ if ((swap_first = (head2)->tqh_first) != NULL) \
+ swap_first->field.tqe_prev = &(head2)->tqh_first; \
+ else \
+ (head2)->tqh_last = &(head2)->tqh_first; \
+ } while (0)
+
+#endif
diff --git a/module/src/main/cpp/external/xhook/tree.h b/module/src/main/cpp/external/xhook/tree.h
new file mode 100644
index 0000000..dc938ae
--- /dev/null
+++ b/module/src/main/cpp/external/xhook/tree.h
@@ -0,0 +1,768 @@
+/* $NetBSD: tree.h,v 1.8 2004/03/28 19:38:30 provos Exp $ */
+/* $OpenBSD: tree.h,v 1.7 2002/10/17 21:51:54 art Exp $ */
+/* $FreeBSD: stable/9/sys/sys/tree.h 189204 2009-03-01 04:57:23Z bms $ */
+
+/*-
+ * Copyright 2002 Niels Provos
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef TREE_H
+#define TREE_H
+
+/* #include */
+#ifndef __unused
+#define __unused __attribute__((__unused__))
+#endif
+
+/*
+ * This file defines data structures for different types of trees:
+ * splay trees and red-black trees.
+ *
+ * A splay tree is a self-organizing data structure. Every operation
+ * on the tree causes a splay to happen. The splay moves the requested
+ * node to the root of the tree and partly rebalances it.
+ *
+ * This has the benefit that request locality causes faster lookups as
+ * the requested nodes move to the top of the tree. On the other hand,
+ * every lookup causes memory writes.
+ *
+ * The Balance Theorem bounds the total access time for m operations
+ * and n inserts on an initially empty tree as O((m + n)lg n). The
+ * amortized cost for a sequence of m accesses to a splay tree is O(lg n);
+ *
+ * A red-black tree is a binary search tree with the node color as an
+ * extra attribute. It fulfills a set of conditions:
+ * - every search path from the root to a leaf consists of the
+ * same number of black nodes,
+ * - each red node (except for the root) has a black parent,
+ * - each leaf node is black.
+ *
+ * Every operation on a red-black tree is bounded as O(lg n).
+ * The maximum height of a red-black tree is 2lg (n+1).
+ */
+
+#define SPLAY_HEAD(name, type) \
+struct name { \
+ struct type *sph_root; /* root of the tree */ \
+}
+
+#define SPLAY_INITIALIZER(root) \
+ { NULL }
+
+#define SPLAY_INIT(root) do { \
+ (root)->sph_root = NULL; \
+} while (/*CONSTCOND*/ 0)
+
+#define SPLAY_ENTRY(type) \
+struct { \
+ struct type *spe_left; /* left element */ \
+ struct type *spe_right; /* right element */ \
+}
+
+#define SPLAY_LEFT(elm, field) (elm)->field.spe_left
+#define SPLAY_RIGHT(elm, field) (elm)->field.spe_right
+#define SPLAY_ROOT(head) (head)->sph_root
+#define SPLAY_EMPTY(head) (SPLAY_ROOT(head) == NULL)
+
+/* SPLAY_ROTATE_{LEFT,RIGHT} expect that tmp hold SPLAY_{RIGHT,LEFT} */
+#define SPLAY_ROTATE_RIGHT(head, tmp, field) do { \
+ SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(tmp, field); \
+ SPLAY_RIGHT(tmp, field) = (head)->sph_root; \
+ (head)->sph_root = tmp; \
+} while (/*CONSTCOND*/ 0)
+
+#define SPLAY_ROTATE_LEFT(head, tmp, field) do { \
+ SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(tmp, field); \
+ SPLAY_LEFT(tmp, field) = (head)->sph_root; \
+ (head)->sph_root = tmp; \
+} while (/*CONSTCOND*/ 0)
+
+#define SPLAY_LINKLEFT(head, tmp, field) do { \
+ SPLAY_LEFT(tmp, field) = (head)->sph_root; \
+ tmp = (head)->sph_root; \
+ (head)->sph_root = SPLAY_LEFT((head)->sph_root, field); \
+} while (/*CONSTCOND*/ 0)
+
+#define SPLAY_LINKRIGHT(head, tmp, field) do { \
+ SPLAY_RIGHT(tmp, field) = (head)->sph_root; \
+ tmp = (head)->sph_root; \
+ (head)->sph_root = SPLAY_RIGHT((head)->sph_root, field); \
+} while (/*CONSTCOND*/ 0)
+
+#define SPLAY_ASSEMBLE(head, node, left, right, field) do { \
+ SPLAY_RIGHT(left, field) = SPLAY_LEFT((head)->sph_root, field); \
+ SPLAY_LEFT(right, field) = SPLAY_RIGHT((head)->sph_root, field);\
+ SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(node, field); \
+ SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(node, field); \
+} while (/*CONSTCOND*/ 0)
+
+/* Generates prototypes and inline functions */
+
+#define SPLAY_PROTOTYPE(name, type, field, cmp) \
+void name##_SPLAY(struct name *, struct type *); \
+void name##_SPLAY_MINMAX(struct name *, int); \
+struct type *name##_SPLAY_INSERT(struct name *, struct type *); \
+struct type *name##_SPLAY_REMOVE(struct name *, struct type *); \
+ \
+/* Finds the node with the same key as elm */ \
+static __inline struct type * \
+name##_SPLAY_FIND(struct name *head, struct type *elm) \
+{ \
+ if (SPLAY_EMPTY(head)) \
+ return(NULL); \
+ name##_SPLAY(head, elm); \
+ if ((cmp)(elm, (head)->sph_root) == 0) \
+ return (head->sph_root); \
+ return (NULL); \
+} \
+ \
+static __inline struct type * \
+name##_SPLAY_NEXT(struct name *head, struct type *elm) \
+{ \
+ name##_SPLAY(head, elm); \
+ if (SPLAY_RIGHT(elm, field) != NULL) { \
+ elm = SPLAY_RIGHT(elm, field); \
+ while (SPLAY_LEFT(elm, field) != NULL) { \
+ elm = SPLAY_LEFT(elm, field); \
+ } \
+ } else \
+ elm = NULL; \
+ return (elm); \
+} \
+ \
+static __inline struct type * \
+name##_SPLAY_MIN_MAX(struct name *head, int val) \
+{ \
+ name##_SPLAY_MINMAX(head, val); \
+ return (SPLAY_ROOT(head)); \
+}
+
+/* Main splay operation.
+ * Moves node close to the key of elm to top
+ */
+#define SPLAY_GENERATE(name, type, field, cmp) \
+struct type * \
+name##_SPLAY_INSERT(struct name *head, struct type *elm) \
+{ \
+ if (SPLAY_EMPTY(head)) { \
+ SPLAY_LEFT(elm, field) = SPLAY_RIGHT(elm, field) = NULL; \
+ } else { \
+ int __comp; \
+ name##_SPLAY(head, elm); \
+ __comp = (cmp)(elm, (head)->sph_root); \
+ if(__comp < 0) { \
+ SPLAY_LEFT(elm, field) = SPLAY_LEFT((head)->sph_root, field);\
+ SPLAY_RIGHT(elm, field) = (head)->sph_root; \
+ SPLAY_LEFT((head)->sph_root, field) = NULL; \
+ } else if (__comp > 0) { \
+ SPLAY_RIGHT(elm, field) = SPLAY_RIGHT((head)->sph_root, field);\
+ SPLAY_LEFT(elm, field) = (head)->sph_root; \
+ SPLAY_RIGHT((head)->sph_root, field) = NULL; \
+ } else \
+ return ((head)->sph_root); \
+ } \
+ (head)->sph_root = (elm); \
+ return (NULL); \
+} \
+ \
+struct type * \
+name##_SPLAY_REMOVE(struct name *head, struct type *elm) \
+{ \
+ struct type *__tmp; \
+ if (SPLAY_EMPTY(head)) \
+ return (NULL); \
+ name##_SPLAY(head, elm); \
+ if ((cmp)(elm, (head)->sph_root) == 0) { \
+ if (SPLAY_LEFT((head)->sph_root, field) == NULL) { \
+ (head)->sph_root = SPLAY_RIGHT((head)->sph_root, field);\
+ } else { \
+ __tmp = SPLAY_RIGHT((head)->sph_root, field); \
+ (head)->sph_root = SPLAY_LEFT((head)->sph_root, field);\
+ name##_SPLAY(head, elm); \
+ SPLAY_RIGHT((head)->sph_root, field) = __tmp; \
+ } \
+ return (elm); \
+ } \
+ return (NULL); \
+} \
+ \
+void \
+name##_SPLAY(struct name *head, struct type *elm) \
+{ \
+ struct type __node, *__left, *__right, *__tmp; \
+ int __comp; \
+\
+ SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL;\
+ __left = __right = &__node; \
+\
+ while ((__comp = (cmp)(elm, (head)->sph_root)) != 0) { \
+ if (__comp < 0) { \
+ __tmp = SPLAY_LEFT((head)->sph_root, field); \
+ if (__tmp == NULL) \
+ break; \
+ if ((cmp)(elm, __tmp) < 0){ \
+ SPLAY_ROTATE_RIGHT(head, __tmp, field); \
+ if (SPLAY_LEFT((head)->sph_root, field) == NULL)\
+ break; \
+ } \
+ SPLAY_LINKLEFT(head, __right, field); \
+ } else if (__comp > 0) { \
+ __tmp = SPLAY_RIGHT((head)->sph_root, field); \
+ if (__tmp == NULL) \
+ break; \
+ if ((cmp)(elm, __tmp) > 0){ \
+ SPLAY_ROTATE_LEFT(head, __tmp, field); \
+ if (SPLAY_RIGHT((head)->sph_root, field) == NULL)\
+ break; \
+ } \
+ SPLAY_LINKRIGHT(head, __left, field); \
+ } \
+ } \
+ SPLAY_ASSEMBLE(head, &__node, __left, __right, field); \
+} \
+ \
+/* Splay with either the minimum or the maximum element \
+ * Used to find minimum or maximum element in tree. \
+ */ \
+void name##_SPLAY_MINMAX(struct name *head, int __comp) \
+{ \
+ struct type __node, *__left, *__right, *__tmp; \
+\
+ SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL;\
+ __left = __right = &__node; \
+\
+ while (1) { \
+ if (__comp < 0) { \
+ __tmp = SPLAY_LEFT((head)->sph_root, field); \
+ if (__tmp == NULL) \
+ break; \
+ if (__comp < 0){ \
+ SPLAY_ROTATE_RIGHT(head, __tmp, field); \
+ if (SPLAY_LEFT((head)->sph_root, field) == NULL)\
+ break; \
+ } \
+ SPLAY_LINKLEFT(head, __right, field); \
+ } else if (__comp > 0) { \
+ __tmp = SPLAY_RIGHT((head)->sph_root, field); \
+ if (__tmp == NULL) \
+ break; \
+ if (__comp > 0) { \
+ SPLAY_ROTATE_LEFT(head, __tmp, field); \
+ if (SPLAY_RIGHT((head)->sph_root, field) == NULL)\
+ break; \
+ } \
+ SPLAY_LINKRIGHT(head, __left, field); \
+ } \
+ } \
+ SPLAY_ASSEMBLE(head, &__node, __left, __right, field); \
+}
+
+#define SPLAY_NEGINF -1
+#define SPLAY_INF 1
+
+#define SPLAY_INSERT(name, x, y) name##_SPLAY_INSERT(x, y)
+#define SPLAY_REMOVE(name, x, y) name##_SPLAY_REMOVE(x, y)
+#define SPLAY_FIND(name, x, y) name##_SPLAY_FIND(x, y)
+#define SPLAY_NEXT(name, x, y) name##_SPLAY_NEXT(x, y)
+#define SPLAY_MIN(name, x) (SPLAY_EMPTY(x) ? NULL \
+ : name##_SPLAY_MIN_MAX(x, SPLAY_NEGINF))
+#define SPLAY_MAX(name, x) (SPLAY_EMPTY(x) ? NULL \
+ : name##_SPLAY_MIN_MAX(x, SPLAY_INF))
+
+#define SPLAY_FOREACH(x, name, head) \
+ for ((x) = SPLAY_MIN(name, head); \
+ (x) != NULL; \
+ (x) = SPLAY_NEXT(name, head, x))
+
+/* Macros that define a red-black tree */
+#define RB_HEAD(name, type) \
+struct name { \
+ struct type *rbh_root; /* root of the tree */ \
+}
+
+#define RB_INITIALIZER(root) \
+ { NULL }
+
+#define RB_INIT(root) do { \
+ (root)->rbh_root = NULL; \
+} while (/*CONSTCOND*/ 0)
+
+#define RB_BLACK 0
+#define RB_RED 1
+#define RB_ENTRY(type) \
+struct { \
+ struct type *rbe_left; /* left element */ \
+ struct type *rbe_right; /* right element */ \
+ struct type *rbe_parent; /* parent element */ \
+ int rbe_color; /* node color */ \
+}
+
+#define RB_LEFT(elm, field) (elm)->field.rbe_left
+#define RB_RIGHT(elm, field) (elm)->field.rbe_right
+#define RB_PARENT(elm, field) (elm)->field.rbe_parent
+#define RB_COLOR(elm, field) (elm)->field.rbe_color
+#define RB_ROOT(head) (head)->rbh_root
+#define RB_EMPTY(head) (RB_ROOT(head) == NULL)
+
+#define RB_SET(elm, parent, field) do { \
+ RB_PARENT(elm, field) = parent; \
+ RB_LEFT(elm, field) = RB_RIGHT(elm, field) = NULL; \
+ RB_COLOR(elm, field) = RB_RED; \
+} while (/*CONSTCOND*/ 0)
+
+#define RB_SET_BLACKRED(black, red, field) do { \
+ RB_COLOR(black, field) = RB_BLACK; \
+ RB_COLOR(red, field) = RB_RED; \
+} while (/*CONSTCOND*/ 0)
+
+#ifndef RB_AUGMENT
+#define RB_AUGMENT(x) do {} while (0)
+#endif
+
+#define RB_ROTATE_LEFT(head, elm, tmp, field) do { \
+ (tmp) = RB_RIGHT(elm, field); \
+ if ((RB_RIGHT(elm, field) = RB_LEFT(tmp, field)) != NULL) { \
+ RB_PARENT(RB_LEFT(tmp, field), field) = (elm); \
+ } \
+ RB_AUGMENT(elm); \
+ if ((RB_PARENT(tmp, field) = RB_PARENT(elm, field)) != NULL) { \
+ if ((elm) == RB_LEFT(RB_PARENT(elm, field), field)) \
+ RB_LEFT(RB_PARENT(elm, field), field) = (tmp); \
+ else \
+ RB_RIGHT(RB_PARENT(elm, field), field) = (tmp); \
+ } else \
+ (head)->rbh_root = (tmp); \
+ RB_LEFT(tmp, field) = (elm); \
+ RB_PARENT(elm, field) = (tmp); \
+ RB_AUGMENT(tmp); \
+ if ((RB_PARENT(tmp, field))) \
+ RB_AUGMENT(RB_PARENT(tmp, field)); \
+} while (/*CONSTCOND*/ 0)
+
+#define RB_ROTATE_RIGHT(head, elm, tmp, field) do { \
+ (tmp) = RB_LEFT(elm, field); \
+ if ((RB_LEFT(elm, field) = RB_RIGHT(tmp, field)) != NULL) { \
+ RB_PARENT(RB_RIGHT(tmp, field), field) = (elm); \
+ } \
+ RB_AUGMENT(elm); \
+ if ((RB_PARENT(tmp, field) = RB_PARENT(elm, field)) != NULL) { \
+ if ((elm) == RB_LEFT(RB_PARENT(elm, field), field)) \
+ RB_LEFT(RB_PARENT(elm, field), field) = (tmp); \
+ else \
+ RB_RIGHT(RB_PARENT(elm, field), field) = (tmp); \
+ } else \
+ (head)->rbh_root = (tmp); \
+ RB_RIGHT(tmp, field) = (elm); \
+ RB_PARENT(elm, field) = (tmp); \
+ RB_AUGMENT(tmp); \
+ if ((RB_PARENT(tmp, field))) \
+ RB_AUGMENT(RB_PARENT(tmp, field)); \
+} while (/*CONSTCOND*/ 0)
+
+/* Generates prototypes and inline functions */
+#define RB_PROTOTYPE(name, type, field, cmp) \
+ RB_PROTOTYPE_INTERNAL(name, type, field, cmp,)
+#define RB_PROTOTYPE_STATIC(name, type, field, cmp) \
+ RB_PROTOTYPE_INTERNAL(name, type, field, cmp, __unused static)
+#define RB_PROTOTYPE_INTERNAL(name, type, field, cmp, attr) \
+attr void name##_RB_INSERT_COLOR(struct name *, struct type *); \
+attr void name##_RB_REMOVE_COLOR(struct name *, struct type *, struct type *);\
+attr struct type *name##_RB_REMOVE(struct name *, struct type *); \
+attr struct type *name##_RB_INSERT(struct name *, struct type *); \
+attr struct type *name##_RB_FIND(struct name *, struct type *); \
+attr struct type *name##_RB_NFIND(struct name *, struct type *); \
+attr struct type *name##_RB_NEXT(struct type *); \
+attr struct type *name##_RB_PREV(struct type *); \
+attr struct type *name##_RB_MINMAX(struct name *, int); \
+ \
+
+/* Main rb operation.
+ * Moves node close to the key of elm to top
+ */
+#define RB_GENERATE(name, type, field, cmp) \
+ RB_GENERATE_INTERNAL(name, type, field, cmp,)
+#define RB_GENERATE_STATIC(name, type, field, cmp) \
+ RB_GENERATE_INTERNAL(name, type, field, cmp, __unused static)
+#define RB_GENERATE_INTERNAL(name, type, field, cmp, attr) \
+attr void \
+name##_RB_INSERT_COLOR(struct name *head, struct type *elm) \
+{ \
+ struct type *parent, *gparent, *tmp; \
+ while ((parent = RB_PARENT(elm, field)) != NULL && \
+ RB_COLOR(parent, field) == RB_RED) { \
+ gparent = RB_PARENT(parent, field); \
+ if (parent == RB_LEFT(gparent, field)) { \
+ tmp = RB_RIGHT(gparent, field); \
+ if (tmp && RB_COLOR(tmp, field) == RB_RED) { \
+ RB_COLOR(tmp, field) = RB_BLACK; \
+ RB_SET_BLACKRED(parent, gparent, field);\
+ elm = gparent; \
+ continue; \
+ } \
+ if (RB_RIGHT(parent, field) == elm) { \
+ RB_ROTATE_LEFT(head, parent, tmp, field);\
+ tmp = parent; \
+ parent = elm; \
+ elm = tmp; \
+ } \
+ RB_SET_BLACKRED(parent, gparent, field); \
+ RB_ROTATE_RIGHT(head, gparent, tmp, field); \
+ } else { \
+ tmp = RB_LEFT(gparent, field); \
+ if (tmp && RB_COLOR(tmp, field) == RB_RED) { \
+ RB_COLOR(tmp, field) = RB_BLACK; \
+ RB_SET_BLACKRED(parent, gparent, field);\
+ elm = gparent; \
+ continue; \
+ } \
+ if (RB_LEFT(parent, field) == elm) { \
+ RB_ROTATE_RIGHT(head, parent, tmp, field);\
+ tmp = parent; \
+ parent = elm; \
+ elm = tmp; \
+ } \
+ RB_SET_BLACKRED(parent, gparent, field); \
+ RB_ROTATE_LEFT(head, gparent, tmp, field); \
+ } \
+ } \
+ RB_COLOR(head->rbh_root, field) = RB_BLACK; \
+} \
+ \
+attr void \
+name##_RB_REMOVE_COLOR(struct name *head, struct type *parent, struct type *elm) \
+{ \
+ struct type *tmp; \
+ while ((elm == NULL || RB_COLOR(elm, field) == RB_BLACK) && \
+ elm != RB_ROOT(head)) { \
+ if (RB_LEFT(parent, field) == elm) { \
+ tmp = RB_RIGHT(parent, field); \
+ if (RB_COLOR(tmp, field) == RB_RED) { \
+ RB_SET_BLACKRED(tmp, parent, field); \
+ RB_ROTATE_LEFT(head, parent, tmp, field);\
+ tmp = RB_RIGHT(parent, field); \
+ } \
+ if ((RB_LEFT(tmp, field) == NULL || \
+ RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) &&\
+ (RB_RIGHT(tmp, field) == NULL || \
+ RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK)) {\
+ RB_COLOR(tmp, field) = RB_RED; \
+ elm = parent; \
+ parent = RB_PARENT(elm, field); \
+ } else { \
+ if (RB_RIGHT(tmp, field) == NULL || \
+ RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK) {\
+ struct type *oleft; \
+ if ((oleft = RB_LEFT(tmp, field)) \
+ != NULL) \
+ RB_COLOR(oleft, field) = RB_BLACK;\
+ RB_COLOR(tmp, field) = RB_RED; \
+ RB_ROTATE_RIGHT(head, tmp, oleft, field);\
+ tmp = RB_RIGHT(parent, field); \
+ } \
+ RB_COLOR(tmp, field) = RB_COLOR(parent, field);\
+ RB_COLOR(parent, field) = RB_BLACK; \
+ if (RB_RIGHT(tmp, field)) \
+ RB_COLOR(RB_RIGHT(tmp, field), field) = RB_BLACK;\
+ RB_ROTATE_LEFT(head, parent, tmp, field);\
+ elm = RB_ROOT(head); \
+ break; \
+ } \
+ } else { \
+ tmp = RB_LEFT(parent, field); \
+ if (RB_COLOR(tmp, field) == RB_RED) { \
+ RB_SET_BLACKRED(tmp, parent, field); \
+ RB_ROTATE_RIGHT(head, parent, tmp, field);\
+ tmp = RB_LEFT(parent, field); \
+ } \
+ if ((RB_LEFT(tmp, field) == NULL || \
+ RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) &&\
+ (RB_RIGHT(tmp, field) == NULL || \
+ RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK)) {\
+ RB_COLOR(tmp, field) = RB_RED; \
+ elm = parent; \
+ parent = RB_PARENT(elm, field); \
+ } else { \
+ if (RB_LEFT(tmp, field) == NULL || \
+ RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) {\
+ struct type *oright; \
+ if ((oright = RB_RIGHT(tmp, field)) \
+ != NULL) \
+ RB_COLOR(oright, field) = RB_BLACK;\
+ RB_COLOR(tmp, field) = RB_RED; \
+ RB_ROTATE_LEFT(head, tmp, oright, field);\
+ tmp = RB_LEFT(parent, field); \
+ } \
+ RB_COLOR(tmp, field) = RB_COLOR(parent, field);\
+ RB_COLOR(parent, field) = RB_BLACK; \
+ if (RB_LEFT(tmp, field)) \
+ RB_COLOR(RB_LEFT(tmp, field), field) = RB_BLACK;\
+ RB_ROTATE_RIGHT(head, parent, tmp, field);\
+ elm = RB_ROOT(head); \
+ break; \
+ } \
+ } \
+ } \
+ if (elm) \
+ RB_COLOR(elm, field) = RB_BLACK; \
+} \
+ \
+attr struct type * \
+name##_RB_REMOVE(struct name *head, struct type *elm) \
+{ \
+ struct type *child, *parent, *old = elm; \
+ int color; \
+ if (RB_LEFT(elm, field) == NULL) \
+ child = RB_RIGHT(elm, field); \
+ else if (RB_RIGHT(elm, field) == NULL) \
+ child = RB_LEFT(elm, field); \
+ else { \
+ struct type *left; \
+ elm = RB_RIGHT(elm, field); \
+ while ((left = RB_LEFT(elm, field)) != NULL) \
+ elm = left; \
+ child = RB_RIGHT(elm, field); \
+ parent = RB_PARENT(elm, field); \
+ color = RB_COLOR(elm, field); \
+ if (child) \
+ RB_PARENT(child, field) = parent; \
+ if (parent) { \
+ if (RB_LEFT(parent, field) == elm) \
+ RB_LEFT(parent, field) = child; \
+ else \
+ RB_RIGHT(parent, field) = child; \
+ RB_AUGMENT(parent); \
+ } else \
+ RB_ROOT(head) = child; \
+ if (RB_PARENT(elm, field) == old) \
+ parent = elm; \
+ (elm)->field = (old)->field; \
+ if (RB_PARENT(old, field)) { \
+ if (RB_LEFT(RB_PARENT(old, field), field) == old)\
+ RB_LEFT(RB_PARENT(old, field), field) = elm;\
+ else \
+ RB_RIGHT(RB_PARENT(old, field), field) = elm;\
+ RB_AUGMENT(RB_PARENT(old, field)); \
+ } else \
+ RB_ROOT(head) = elm; \
+ RB_PARENT(RB_LEFT(old, field), field) = elm; \
+ if (RB_RIGHT(old, field)) \
+ RB_PARENT(RB_RIGHT(old, field), field) = elm; \
+ if (parent) { \
+ left = parent; \
+ do { \
+ RB_AUGMENT(left); \
+ } while ((left = RB_PARENT(left, field)) != NULL); \
+ } \
+ goto color; \
+ } \
+ parent = RB_PARENT(elm, field); \
+ color = RB_COLOR(elm, field); \
+ if (child) \
+ RB_PARENT(child, field) = parent; \
+ if (parent) { \
+ if (RB_LEFT(parent, field) == elm) \
+ RB_LEFT(parent, field) = child; \
+ else \
+ RB_RIGHT(parent, field) = child; \
+ RB_AUGMENT(parent); \
+ } else \
+ RB_ROOT(head) = child; \
+color: \
+ if (color == RB_BLACK) \
+ name##_RB_REMOVE_COLOR(head, parent, child); \
+ return (old); \
+} \
+ \
+/* Inserts a node into the RB tree */ \
+attr struct type * \
+name##_RB_INSERT(struct name *head, struct type *elm) \
+{ \
+ struct type *tmp; \
+ struct type *parent = NULL; \
+ int comp = 0; \
+ tmp = RB_ROOT(head); \
+ while (tmp) { \
+ parent = tmp; \
+ comp = (cmp)(elm, parent); \
+ if (comp < 0) \
+ tmp = RB_LEFT(tmp, field); \
+ else if (comp > 0) \
+ tmp = RB_RIGHT(tmp, field); \
+ else \
+ return (tmp); \
+ } \
+ RB_SET(elm, parent, field); \
+ if (parent != NULL) { \
+ if (comp < 0) \
+ RB_LEFT(parent, field) = elm; \
+ else \
+ RB_RIGHT(parent, field) = elm; \
+ RB_AUGMENT(parent); \
+ } else \
+ RB_ROOT(head) = elm; \
+ name##_RB_INSERT_COLOR(head, elm); \
+ return (NULL); \
+} \
+ \
+/* Finds the node with the same key as elm */ \
+attr struct type * \
+name##_RB_FIND(struct name *head, struct type *elm) \
+{ \
+ struct type *tmp = RB_ROOT(head); \
+ int comp; \
+ while (tmp) { \
+ comp = cmp(elm, tmp); \
+ if (comp < 0) \
+ tmp = RB_LEFT(tmp, field); \
+ else if (comp > 0) \
+ tmp = RB_RIGHT(tmp, field); \
+ else \
+ return (tmp); \
+ } \
+ return (NULL); \
+} \
+ \
+/* Finds the first node greater than or equal to the search key */ \
+attr struct type * \
+name##_RB_NFIND(struct name *head, struct type *elm) \
+{ \
+ struct type *tmp = RB_ROOT(head); \
+ struct type *res = NULL; \
+ int comp; \
+ while (tmp) { \
+ comp = cmp(elm, tmp); \
+ if (comp < 0) { \
+ res = tmp; \
+ tmp = RB_LEFT(tmp, field); \
+ } \
+ else if (comp > 0) \
+ tmp = RB_RIGHT(tmp, field); \
+ else \
+ return (tmp); \
+ } \
+ return (res); \
+} \
+ \
+/* ARGSUSED */ \
+attr struct type * \
+name##_RB_NEXT(struct type *elm) \
+{ \
+ if (RB_RIGHT(elm, field)) { \
+ elm = RB_RIGHT(elm, field); \
+ while (RB_LEFT(elm, field)) \
+ elm = RB_LEFT(elm, field); \
+ } else { \
+ if (RB_PARENT(elm, field) && \
+ (elm == RB_LEFT(RB_PARENT(elm, field), field))) \
+ elm = RB_PARENT(elm, field); \
+ else { \
+ while (RB_PARENT(elm, field) && \
+ (elm == RB_RIGHT(RB_PARENT(elm, field), field)))\
+ elm = RB_PARENT(elm, field); \
+ elm = RB_PARENT(elm, field); \
+ } \
+ } \
+ return (elm); \
+} \
+ \
+/* ARGSUSED */ \
+attr struct type * \
+name##_RB_PREV(struct type *elm) \
+{ \
+ if (RB_LEFT(elm, field)) { \
+ elm = RB_LEFT(elm, field); \
+ while (RB_RIGHT(elm, field)) \
+ elm = RB_RIGHT(elm, field); \
+ } else { \
+ if (RB_PARENT(elm, field) && \
+ (elm == RB_RIGHT(RB_PARENT(elm, field), field))) \
+ elm = RB_PARENT(elm, field); \
+ else { \
+ while (RB_PARENT(elm, field) && \
+ (elm == RB_LEFT(RB_PARENT(elm, field), field)))\
+ elm = RB_PARENT(elm, field); \
+ elm = RB_PARENT(elm, field); \
+ } \
+ } \
+ return (elm); \
+} \
+ \
+attr struct type * \
+name##_RB_MINMAX(struct name *head, int val) \
+{ \
+ struct type *tmp = RB_ROOT(head); \
+ struct type *parent = NULL; \
+ while (tmp) { \
+ parent = tmp; \
+ if (val < 0) \
+ tmp = RB_LEFT(tmp, field); \
+ else \
+ tmp = RB_RIGHT(tmp, field); \
+ } \
+ return (parent); \
+}
+
+#define RB_NEGINF -1
+#define RB_INF 1
+
+#define RB_INSERT(name, x, y) name##_RB_INSERT(x, y)
+#define RB_REMOVE(name, x, y) name##_RB_REMOVE(x, y)
+#define RB_FIND(name, x, y) name##_RB_FIND(x, y)
+#define RB_NFIND(name, x, y) name##_RB_NFIND(x, y)
+#define RB_NEXT(name, x, y) name##_RB_NEXT(y)
+#define RB_PREV(name, x, y) name##_RB_PREV(y)
+#define RB_MIN(name, x) name##_RB_MINMAX(x, RB_NEGINF)
+#define RB_MAX(name, x) name##_RB_MINMAX(x, RB_INF)
+
+#define RB_FOREACH(x, name, head) \
+ for ((x) = RB_MIN(name, head); \
+ (x) != NULL; \
+ (x) = name##_RB_NEXT(x))
+
+#define RB_FOREACH_FROM(x, name, y) \
+ for ((x) = (y); \
+ ((x) != NULL) && ((y) = name##_RB_NEXT(x), (x) != NULL); \
+ (x) = (y))
+
+#define RB_FOREACH_SAFE(x, name, head, y) \
+ for ((x) = RB_MIN(name, head); \
+ ((x) != NULL) && ((y) = name##_RB_NEXT(x), (x) != NULL); \
+ (x) = (y))
+
+#define RB_FOREACH_REVERSE(x, name, head) \
+ for ((x) = RB_MAX(name, head); \
+ (x) != NULL; \
+ (x) = name##_RB_PREV(x))
+
+#define RB_FOREACH_REVERSE_FROM(x, name, y) \
+ for ((x) = (y); \
+ ((x) != NULL) && ((y) = name##_RB_PREV(x), (x) != NULL); \
+ (x) = (y))
+
+#define RB_FOREACH_REVERSE_SAFE(x, name, head, y) \
+ for ((x) = RB_MAX(name, head); \
+ ((x) != NULL) && ((y) = name##_RB_PREV(x), (x) != NULL); \
+ (x) = (y))
+
+#endif
diff --git a/module/src/main/cpp/external/xhook/xh_core.c b/module/src/main/cpp/external/xhook/xh_core.c
new file mode 100644
index 0000000..4d14ba4
--- /dev/null
+++ b/module/src/main/cpp/external/xhook/xh_core.c
@@ -0,0 +1,656 @@
+// Copyright (c) 2018-present, iQIYI, Inc. All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+//
+
+// Created by caikelun on 2018-04-11.
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include "queue.h"
+#include "tree.h"
+#include "xh_errno.h"
+#include "xh_log.h"
+#include "xh_elf.h"
+#include "xh_version.h"
+#include "xh_core.h"
+
+#define XH_CORE_DEBUG 0
+
+//registered hook info collection
+typedef struct xh_core_hook_info
+{
+#if XH_CORE_DEBUG
+ char *pathname_regex_str;
+#endif
+ regex_t pathname_regex;
+ char *symbol;
+ void *new_func;
+ void **old_func;
+ TAILQ_ENTRY(xh_core_hook_info,) link;
+} xh_core_hook_info_t;
+typedef TAILQ_HEAD(xh_core_hook_info_queue, xh_core_hook_info,) xh_core_hook_info_queue_t;
+
+//ignored hook info collection
+typedef struct xh_core_ignore_info
+{
+#if XH_CORE_DEBUG
+ char *pathname_regex_str;
+#endif
+ regex_t pathname_regex;
+ char *symbol; //NULL meaning for all symbols
+ TAILQ_ENTRY(xh_core_ignore_info,) link;
+} xh_core_ignore_info_t;
+typedef TAILQ_HEAD(xh_core_ignore_info_queue, xh_core_ignore_info,) xh_core_ignore_info_queue_t;
+
+//required info from /proc/self/maps
+typedef struct xh_core_map_info
+{
+ char *pathname;
+ uintptr_t base_addr;
+ xh_elf_t elf;
+ RB_ENTRY(xh_core_map_info) link;
+} xh_core_map_info_t;
+static __inline__ int xh_core_map_info_cmp(xh_core_map_info_t *a, xh_core_map_info_t *b)
+{
+ return strcmp(a->pathname, b->pathname);
+}
+typedef RB_HEAD(xh_core_map_info_tree, xh_core_map_info) xh_core_map_info_tree_t;
+RB_GENERATE_STATIC(xh_core_map_info_tree, xh_core_map_info, link, xh_core_map_info_cmp)
+
+//signal handler for SIGSEGV
+//for xh_elf_init(), xh_elf_hook(), xh_elf_check_elfheader()
+static int xh_core_sigsegv_enable = 1; //enable by default
+static struct sigaction xh_core_sigsegv_act_old;
+static volatile int xh_core_sigsegv_flag = 0;
+static sigjmp_buf xh_core_sigsegv_env;
+static void xh_core_sigsegv_handler(int sig)
+{
+ (void)sig;
+
+ if(xh_core_sigsegv_flag)
+ siglongjmp(xh_core_sigsegv_env, 1);
+ else
+ sigaction(SIGSEGV, &xh_core_sigsegv_act_old, NULL);
+}
+static int xh_core_add_sigsegv_handler()
+{
+ struct sigaction act;
+
+ if(!xh_core_sigsegv_enable) return 0;
+
+ if(0 != sigemptyset(&act.sa_mask)) return (0 == errno ? XH_ERRNO_UNKNOWN : errno);
+ act.sa_handler = xh_core_sigsegv_handler;
+
+ if(0 != sigaction(SIGSEGV, &act, &xh_core_sigsegv_act_old))
+ return (0 == errno ? XH_ERRNO_UNKNOWN : errno);
+
+ return 0;
+}
+static void xh_core_del_sigsegv_handler()
+{
+ if(!xh_core_sigsegv_enable) return;
+
+ sigaction(SIGSEGV, &xh_core_sigsegv_act_old, NULL);
+}
+
+
+static xh_core_hook_info_queue_t xh_core_hook_info = TAILQ_HEAD_INITIALIZER(xh_core_hook_info);
+static xh_core_ignore_info_queue_t xh_core_ignore_info = TAILQ_HEAD_INITIALIZER(xh_core_ignore_info);
+static xh_core_map_info_tree_t xh_core_map_info = RB_INITIALIZER(&xh_core_map_info);
+static pthread_mutex_t xh_core_mutex = PTHREAD_MUTEX_INITIALIZER;
+static pthread_cond_t xh_core_cond = PTHREAD_COND_INITIALIZER;
+static volatile int xh_core_inited = 0;
+static volatile int xh_core_init_ok = 0;
+static volatile int xh_core_async_inited = 0;
+static volatile int xh_core_async_init_ok = 0;
+static pthread_mutex_t xh_core_refresh_mutex = PTHREAD_MUTEX_INITIALIZER;
+static pthread_t xh_core_refresh_thread_tid;
+static volatile int xh_core_refresh_thread_running = 0;
+static volatile int xh_core_refresh_thread_do = 0;
+
+
+int xh_core_register(const char *pathname_regex_str, const char *symbol,
+ void *new_func, void **old_func)
+{
+ xh_core_hook_info_t *hi;
+ regex_t regex;
+
+ if(NULL == pathname_regex_str || NULL == symbol || NULL == new_func) return XH_ERRNO_INVAL;
+
+ if(xh_core_inited)
+ {
+ XH_LOG_ERROR("do not register hook after refresh(): %s, %s", pathname_regex_str, symbol);
+ return XH_ERRNO_INVAL;
+ }
+
+ if(0 != regcomp(®ex, pathname_regex_str, REG_NOSUB)) return XH_ERRNO_INVAL;
+
+ if(NULL == (hi = malloc(sizeof(xh_core_hook_info_t)))) return XH_ERRNO_NOMEM;
+ if(NULL == (hi->symbol = strdup(symbol)))
+ {
+ free(hi);
+ return XH_ERRNO_NOMEM;
+ }
+#if XH_CORE_DEBUG
+ if(NULL == (hi->pathname_regex_str = strdup(pathname_regex_str)))
+ {
+ free(hi->symbol);
+ free(hi);
+ return XH_ERRNO_NOMEM;
+ }
+#endif
+ hi->pathname_regex = regex;
+ hi->new_func = new_func;
+ hi->old_func = old_func;
+
+ pthread_mutex_lock(&xh_core_mutex);
+ TAILQ_INSERT_TAIL(&xh_core_hook_info, hi, link);
+ pthread_mutex_unlock(&xh_core_mutex);
+
+ return 0;
+}
+
+int xh_core_ignore(const char *pathname_regex_str, const char *symbol)
+{
+ xh_core_ignore_info_t *ii;
+ regex_t regex;
+
+ if(NULL == pathname_regex_str) return XH_ERRNO_INVAL;
+
+ if(xh_core_inited)
+ {
+ XH_LOG_ERROR("do not ignore hook after refresh(): %s, %s", pathname_regex_str, symbol ? symbol : "ALL");
+ return XH_ERRNO_INVAL;
+ }
+
+ if(0 != regcomp(®ex, pathname_regex_str, REG_NOSUB)) return XH_ERRNO_INVAL;
+
+ if(NULL == (ii = malloc(sizeof(xh_core_ignore_info_t)))) return XH_ERRNO_NOMEM;
+ if(NULL != symbol)
+ {
+ if(NULL == (ii->symbol = strdup(symbol)))
+ {
+ free(ii);
+ return XH_ERRNO_NOMEM;
+ }
+ }
+ else
+ {
+ ii->symbol = NULL; //ignore all symbols
+ }
+#if XH_CORE_DEBUG
+ if(NULL == (ii->pathname_regex_str = strdup(pathname_regex_str)))
+ {
+ free(ii->symbol);
+ free(ii);
+ return XH_ERRNO_NOMEM;
+ }
+#endif
+ ii->pathname_regex = regex;
+
+ pthread_mutex_lock(&xh_core_mutex);
+ TAILQ_INSERT_TAIL(&xh_core_ignore_info, ii, link);
+ pthread_mutex_unlock(&xh_core_mutex);
+
+ return 0;
+}
+
+static int xh_core_check_elf_header(uintptr_t base_addr, const char *pathname)
+{
+ if(!xh_core_sigsegv_enable)
+ {
+ return xh_elf_check_elfheader(base_addr);
+ }
+ else
+ {
+ int ret = XH_ERRNO_UNKNOWN;
+
+ xh_core_sigsegv_flag = 1;
+ if(0 == sigsetjmp(xh_core_sigsegv_env, 1))
+ {
+ ret = xh_elf_check_elfheader(base_addr);
+ }
+ else
+ {
+ ret = XH_ERRNO_SEGVERR;
+ XH_LOG_WARN("catch SIGSEGV when check_elfheader: %s", pathname);
+ }
+ xh_core_sigsegv_flag = 0;
+ return ret;
+ }
+}
+
+static void xh_core_hook_impl(xh_core_map_info_t *mi)
+{
+ //init
+ if(0 != xh_elf_init(&(mi->elf), mi->base_addr, mi->pathname)) return;
+
+ //hook
+ xh_core_hook_info_t *hi;
+ xh_core_ignore_info_t *ii;
+ int ignore;
+ TAILQ_FOREACH(hi, &xh_core_hook_info, link) //find hook info
+ {
+ if(0 == regexec(&(hi->pathname_regex), mi->pathname, 0, NULL, 0))
+ {
+ ignore = 0;
+ TAILQ_FOREACH(ii, &xh_core_ignore_info, link) //find ignore info
+ {
+ if(0 == regexec(&(ii->pathname_regex), mi->pathname, 0, NULL, 0))
+ {
+ if(NULL == ii->symbol) //ignore all symbols
+ return;
+
+ if(0 == strcmp(ii->symbol, hi->symbol)) //ignore the current symbol
+ {
+ ignore = 1;
+ break;
+ }
+ }
+ }
+
+ if(0 == ignore)
+ xh_elf_hook(&(mi->elf), hi->symbol, hi->new_func, hi->old_func);
+ }
+ }
+}
+
+static void xh_core_hook(xh_core_map_info_t *mi)
+{
+ if(!xh_core_sigsegv_enable)
+ {
+ xh_core_hook_impl(mi);
+ }
+ else
+ {
+ xh_core_sigsegv_flag = 1;
+ if(0 == sigsetjmp(xh_core_sigsegv_env, 1))
+ {
+ xh_core_hook_impl(mi);
+ }
+ else
+ {
+ XH_LOG_WARN("catch SIGSEGV when init or hook: %s", mi->pathname);
+ }
+ xh_core_sigsegv_flag = 0;
+ }
+}
+
+static void xh_core_refresh_impl()
+{
+ char line[512];
+ FILE *fp;
+ uintptr_t base_addr;
+ char perm[5];
+ unsigned long offset;
+ int pathname_pos;
+ char *pathname;
+ size_t pathname_len;
+ xh_core_map_info_t *mi, *mi_tmp;
+ xh_core_map_info_t mi_key;
+ xh_core_hook_info_t *hi;
+ xh_core_ignore_info_t *ii;
+ int match;
+ xh_core_map_info_tree_t map_info_refreshed = RB_INITIALIZER(&map_info_refreshed);
+
+ if(NULL == (fp = fopen("/proc/self/maps", "r")))
+ {
+ XH_LOG_ERROR("fopen /proc/self/maps failed");
+ return;
+ }
+
+ while(fgets(line, sizeof(line), fp))
+ {
+ if(sscanf(line, "%"PRIxPTR"-%*lx %4s %lx %*x:%*x %*d%n", &base_addr, perm, &offset, &pathname_pos) != 3) continue;
+
+ //check permission
+ if(perm[0] != 'r') continue;
+ if(perm[3] != 'p') continue; //do not touch the shared memory
+
+ //check offset
+ //
+ //We are trying to find ELF header in memory.
+ //It can only be found at the beginning of a mapped memory regions
+ //whose offset is 0.
+ if(0 != offset) continue;
+
+ //get pathname
+ while(isspace(line[pathname_pos]) && pathname_pos < (int)(sizeof(line) - 1))
+ pathname_pos += 1;
+ if(pathname_pos >= (int)(sizeof(line) - 1)) continue;
+ pathname = line + pathname_pos;
+ pathname_len = strlen(pathname);
+ if(0 == pathname_len) continue;
+ if(pathname[pathname_len - 1] == '\n')
+ {
+ pathname[pathname_len - 1] = '\0';
+ pathname_len -= 1;
+ }
+ if(0 == pathname_len) continue;
+ if('[' == pathname[0]) continue;
+
+ //check pathname
+ //if we need to hook this elf?
+ match = 0;
+ TAILQ_FOREACH(hi, &xh_core_hook_info, link) //find hook info
+ {
+ if(0 == regexec(&(hi->pathname_regex), pathname, 0, NULL, 0))
+ {
+ TAILQ_FOREACH(ii, &xh_core_ignore_info, link) //find ignore info
+ {
+ if(0 == regexec(&(ii->pathname_regex), pathname, 0, NULL, 0))
+ {
+ if(NULL == ii->symbol)
+ goto check_finished;
+
+ if(0 == strcmp(ii->symbol, hi->symbol))
+ goto check_continue;
+ }
+ }
+
+ match = 1;
+ check_continue:
+ break;
+ }
+ }
+ check_finished:
+ if(0 == match) continue;
+
+ //check elf header format
+ //We are trying to do ELF header checking as late as possible.
+ if(0 != xh_core_check_elf_header(base_addr, pathname)) continue;
+
+ //check existed map item
+ mi_key.pathname = pathname;
+ if(NULL != (mi = RB_FIND(xh_core_map_info_tree, &xh_core_map_info, &mi_key)))
+ {
+ //exist
+ RB_REMOVE(xh_core_map_info_tree, &xh_core_map_info, mi);
+
+ //repeated?
+ //We only keep the first one, that is the real base address
+ if(NULL != RB_INSERT(xh_core_map_info_tree, &map_info_refreshed, mi))
+ {
+#if XH_CORE_DEBUG
+ XH_LOG_DEBUG("repeated map info when update: %s", line);
+#endif
+ free(mi->pathname);
+ free(mi);
+ continue;
+ }
+
+ //re-hook if base_addr changed
+ if(mi->base_addr != base_addr)
+ {
+ mi->base_addr = base_addr;
+ xh_core_hook(mi);
+ }
+ }
+ else
+ {
+ //not exist, create a new map info
+ if(NULL == (mi = (xh_core_map_info_t *)malloc(sizeof(xh_core_map_info_t)))) continue;
+ if(NULL == (mi->pathname = strdup(pathname)))
+ {
+ free(mi);
+ continue;
+ }
+ mi->base_addr = base_addr;
+
+ //repeated?
+ //We only keep the first one, that is the real base address
+ if(NULL != RB_INSERT(xh_core_map_info_tree, &map_info_refreshed, mi))
+ {
+#if XH_CORE_DEBUG
+ XH_LOG_DEBUG("repeated map info when create: %s", line);
+#endif
+ free(mi->pathname);
+ free(mi);
+ continue;
+ }
+
+ //hook
+ xh_core_hook(mi); //hook
+ }
+ }
+ fclose(fp);
+
+ //free all missing map item, maybe dlclosed?
+ RB_FOREACH_SAFE(mi, xh_core_map_info_tree, &xh_core_map_info, mi_tmp)
+ {
+#if XH_CORE_DEBUG
+ XH_LOG_DEBUG("remove missing map info: %s", mi->pathname);
+#endif
+ RB_REMOVE(xh_core_map_info_tree, &xh_core_map_info, mi);
+ if(mi->pathname) free(mi->pathname);
+ free(mi);
+ }
+
+ //save the new refreshed map info tree
+ xh_core_map_info = map_info_refreshed;
+
+ XH_LOG_INFO("map refreshed");
+
+#if XH_CORE_DEBUG
+ RB_FOREACH(mi, xh_core_map_info_tree, &xh_core_map_info)
+ XH_LOG_DEBUG(" %"PRIxPTR" %s\n", mi->base_addr, mi->pathname);
+#endif
+}
+
+static void *xh_core_refresh_thread_func(void *arg)
+{
+ (void)arg;
+
+ pthread_setname_np(pthread_self(), "xh_refresh_loop");
+
+ while(xh_core_refresh_thread_running)
+ {
+ //waiting for a refresh task or exit
+ pthread_mutex_lock(&xh_core_mutex);
+ while(!xh_core_refresh_thread_do && xh_core_refresh_thread_running)
+ {
+ pthread_cond_wait(&xh_core_cond, &xh_core_mutex);
+ }
+ if(!xh_core_refresh_thread_running)
+ {
+ pthread_mutex_unlock(&xh_core_mutex);
+ break;
+ }
+ xh_core_refresh_thread_do = 0;
+ pthread_mutex_unlock(&xh_core_mutex);
+
+ //refresh
+ pthread_mutex_lock(&xh_core_refresh_mutex);
+ xh_core_refresh_impl();
+ pthread_mutex_unlock(&xh_core_refresh_mutex);
+ }
+
+ return NULL;
+}
+
+static void xh_core_init_once()
+{
+ if(xh_core_inited) return;
+
+ pthread_mutex_lock(&xh_core_mutex);
+
+ if(xh_core_inited) goto end;
+
+ xh_core_inited = 1;
+
+ //dump debug info
+ XH_LOG_INFO("%s\n", xh_version_str_full());
+#if XH_CORE_DEBUG
+ xh_core_hook_info_t *hi;
+ TAILQ_FOREACH(hi, &xh_core_hook_info, link)
+ XH_LOG_INFO(" hook: %s @ %s, (%p, %p)\n", hi->symbol, hi->pathname_regex_str,
+ hi->new_func, hi->old_func);
+ xh_core_ignore_info_t *ii;
+ TAILQ_FOREACH(ii, &xh_core_ignore_info, link)
+ XH_LOG_INFO(" ignore: %s @ %s\n", ii->symbol ? ii->symbol : "ALL ",
+ ii->pathname_regex_str);
+#endif
+
+ //register signal handler
+ if(0 != xh_core_add_sigsegv_handler()) goto end;
+
+ //OK
+ xh_core_init_ok = 1;
+
+ end:
+ pthread_mutex_unlock(&xh_core_mutex);
+}
+
+static void xh_core_init_async_once()
+{
+ if(xh_core_async_inited) return;
+
+ pthread_mutex_lock(&xh_core_mutex);
+
+ if(xh_core_async_inited) goto end;
+
+ xh_core_async_inited = 1;
+
+ //create async refresh thread
+ xh_core_refresh_thread_running = 1;
+ if(0 != pthread_create(&xh_core_refresh_thread_tid, NULL, &xh_core_refresh_thread_func, NULL))
+ {
+ xh_core_refresh_thread_running = 0;
+ goto end;
+ }
+
+ //OK
+ xh_core_async_init_ok = 1;
+
+ end:
+ pthread_mutex_unlock(&xh_core_mutex);
+}
+
+int xh_core_refresh(int async)
+{
+ //init
+ xh_core_init_once();
+ if(!xh_core_init_ok) return XH_ERRNO_UNKNOWN;
+
+ if(async)
+ {
+ //init for async
+ xh_core_init_async_once();
+ if(!xh_core_async_init_ok) return XH_ERRNO_UNKNOWN;
+
+ //refresh async
+ pthread_mutex_lock(&xh_core_mutex);
+ xh_core_refresh_thread_do = 1;
+ pthread_cond_signal(&xh_core_cond);
+ pthread_mutex_unlock(&xh_core_mutex);
+ }
+ else
+ {
+ //refresh sync
+ pthread_mutex_lock(&xh_core_refresh_mutex);
+ xh_core_refresh_impl();
+ pthread_mutex_unlock(&xh_core_refresh_mutex);
+ }
+
+ return 0;
+}
+
+void xh_core_clear()
+{
+ //stop the async refresh thread
+ if(xh_core_async_init_ok)
+ {
+ pthread_mutex_lock(&xh_core_mutex);
+ xh_core_refresh_thread_running = 0;
+ pthread_cond_signal(&xh_core_cond);
+ pthread_mutex_unlock(&xh_core_mutex);
+
+ pthread_join(xh_core_refresh_thread_tid, NULL);
+ xh_core_async_init_ok = 0;
+ }
+ xh_core_async_inited = 0;
+
+ //unregister the sig handler
+ if(xh_core_init_ok)
+ {
+ xh_core_del_sigsegv_handler();
+ xh_core_init_ok = 0;
+ }
+ xh_core_inited = 0;
+
+ pthread_mutex_lock(&xh_core_mutex);
+ pthread_mutex_lock(&xh_core_refresh_mutex);
+
+ //free all map info
+ xh_core_map_info_t *mi, *mi_tmp;
+ RB_FOREACH_SAFE(mi, xh_core_map_info_tree, &xh_core_map_info, mi_tmp)
+ {
+ RB_REMOVE(xh_core_map_info_tree, &xh_core_map_info, mi);
+ if(mi->pathname) free(mi->pathname);
+ free(mi);
+ }
+
+ //free all hook info
+ xh_core_hook_info_t *hi, *hi_tmp;
+ TAILQ_FOREACH_SAFE(hi, &xh_core_hook_info, link, hi_tmp)
+ {
+ TAILQ_REMOVE(&xh_core_hook_info, hi, link);
+#if XH_CORE_DEBUG
+ free(hi->pathname_regex_str);
+#endif
+ regfree(&(hi->pathname_regex));
+ free(hi->symbol);
+ free(hi);
+ }
+
+ //free all ignore info
+ xh_core_ignore_info_t *ii, *ii_tmp;
+ TAILQ_FOREACH_SAFE(ii, &xh_core_ignore_info, link, ii_tmp)
+ {
+ TAILQ_REMOVE(&xh_core_ignore_info, ii, link);
+#if XH_CORE_DEBUG
+ free(ii->pathname_regex_str);
+#endif
+ regfree(&(ii->pathname_regex));
+ free(ii->symbol);
+ free(ii);
+ }
+
+ pthread_mutex_unlock(&xh_core_refresh_mutex);
+ pthread_mutex_unlock(&xh_core_mutex);
+}
+
+void xh_core_enable_debug(int flag)
+{
+ xh_log_priority = (flag ? ANDROID_LOG_DEBUG : ANDROID_LOG_WARN);
+}
+
+void xh_core_enable_sigsegv_protection(int flag)
+{
+ xh_core_sigsegv_enable = (flag ? 1 : 0);
+}
diff --git a/module/src/main/cpp/external/xhook/xh_core.h b/module/src/main/cpp/external/xhook/xh_core.h
new file mode 100644
index 0000000..3508794
--- /dev/null
+++ b/module/src/main/cpp/external/xhook/xh_core.h
@@ -0,0 +1,48 @@
+// Copyright (c) 2018-present, iQIYI, Inc. All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+//
+
+// Created by caikelun on 2018-04-11.
+
+#ifndef XH_CORE_H
+#define XH_CORE_H 1
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int xh_core_register(const char *pathname_regex_str, const char *symbol,
+ void *new_func, void **old_func);
+
+int xh_core_ignore(const char *pathname_regex_str, const char *symbol);
+
+int xh_core_refresh(int async);
+
+void xh_core_clear();
+
+void xh_core_enable_debug(int flag);
+
+void xh_core_enable_sigsegv_protection(int flag);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/module/src/main/cpp/external/xhook/xh_elf.c b/module/src/main/cpp/external/xhook/xh_elf.c
new file mode 100644
index 0000000..286ed87
--- /dev/null
+++ b/module/src/main/cpp/external/xhook/xh_elf.c
@@ -0,0 +1,1042 @@
+// Copyright (c) 2018-present, iQIYI, Inc. All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+//
+
+// Created by caikelun on 2018-04-11.
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include "xh_errno.h"
+#include "xh_log.h"
+#include "xh_util.h"
+#include "xh_elf.h"
+
+#define XH_ELF_DEBUG 0
+
+#ifndef EI_ABIVERSION
+#define EI_ABIVERSION 8
+#endif
+
+#if defined(__arm__)
+#define XH_ELF_R_GENERIC_JUMP_SLOT R_ARM_JUMP_SLOT //.rel.plt
+#define XH_ELF_R_GENERIC_GLOB_DAT R_ARM_GLOB_DAT //.rel.dyn
+#define XH_ELF_R_GENERIC_ABS R_ARM_ABS32 //.rel.dyn
+#elif defined(__aarch64__)
+#define XH_ELF_R_GENERIC_JUMP_SLOT R_AARCH64_JUMP_SLOT
+#define XH_ELF_R_GENERIC_GLOB_DAT R_AARCH64_GLOB_DAT
+#define XH_ELF_R_GENERIC_ABS R_AARCH64_ABS64
+#elif defined(__i386__)
+#define XH_ELF_R_GENERIC_JUMP_SLOT R_386_JMP_SLOT
+#define XH_ELF_R_GENERIC_GLOB_DAT R_386_GLOB_DAT
+#define XH_ELF_R_GENERIC_ABS R_386_32
+#elif defined(__x86_64__)
+#define XH_ELF_R_GENERIC_JUMP_SLOT R_X86_64_JUMP_SLOT
+#define XH_ELF_R_GENERIC_GLOB_DAT R_X86_64_GLOB_DAT
+#define XH_ELF_R_GENERIC_ABS R_X86_64_64
+#endif
+
+#if defined(__LP64__)
+#define XH_ELF_R_SYM(info) ELF64_R_SYM(info)
+#define XH_ELF_R_TYPE(info) ELF64_R_TYPE(info)
+#else
+#define XH_ELF_R_SYM(info) ELF32_R_SYM(info)
+#define XH_ELF_R_TYPE(info) ELF32_R_TYPE(info)
+#endif
+
+//iterator for plain PLT
+typedef struct
+{
+ uint8_t *cur;
+ uint8_t *end;
+ int is_use_rela;
+} xh_elf_plain_reloc_iterator_t;
+
+static void xh_elf_plain_reloc_iterator_init(xh_elf_plain_reloc_iterator_t *self,
+ ElfW(Addr) rel, ElfW(Word) rel_sz, int is_use_rela)
+{
+ self->cur = (uint8_t *)rel;
+ self->end = self->cur + rel_sz;
+ self->is_use_rela = is_use_rela;
+}
+
+static void *xh_elf_plain_reloc_iterator_next(xh_elf_plain_reloc_iterator_t *self)
+{
+ if(self->cur >= self->end) return NULL;
+
+ self->cur += (self->is_use_rela ? sizeof(ElfW(Rela)) : sizeof(ElfW(Rel)));
+ return (void *)(self->cur);
+}
+
+//sleb128 decoder
+typedef struct
+{
+ uint8_t *cur;
+ uint8_t *end;
+} xh_elf_sleb128_decoder_t;
+
+static void xh_elf_sleb128_decoder_init(xh_elf_sleb128_decoder_t *self,
+ ElfW(Addr) rel, ElfW(Word) rel_sz)
+{
+ self->cur = (uint8_t *)rel;
+ self->end = self->cur + rel_sz;
+}
+
+static int xh_elf_sleb128_decoder_next(xh_elf_sleb128_decoder_t *self, size_t *ret)
+{
+ size_t value = 0;
+ static const size_t size = 8 * sizeof(value);
+ size_t shift = 0;
+ uint8_t byte;
+
+ do
+ {
+ if(self->cur >= self->end)
+ return XH_ERRNO_FORMAT;
+
+ byte = *(self->cur)++;
+ value |= ((size_t)(byte & 127) << shift);
+ shift += 7;
+ } while(byte & 128);
+
+ if(shift < size && (byte & 64))
+ {
+ value |= -((size_t)(1) << shift);
+ }
+
+ *ret = value;
+ return 0;
+}
+
+//iterator for sleb128 decoded packed PLT
+typedef struct
+{
+ xh_elf_sleb128_decoder_t decoder;
+ size_t relocation_count;
+ size_t group_size;
+ size_t group_flags;
+ size_t group_r_offset_delta;
+ size_t relocation_index;
+ size_t relocation_group_index;
+ ElfW(Rela) rela;
+ ElfW(Rel) rel;
+ ElfW(Addr) r_offset;
+ size_t r_info;
+ ssize_t r_addend;
+ int is_use_rela;
+} xh_elf_packed_reloc_iterator_t;
+
+const size_t RELOCATION_GROUPED_BY_INFO_FLAG = 1;
+const size_t RELOCATION_GROUPED_BY_OFFSET_DELTA_FLAG = 2;
+const size_t RELOCATION_GROUPED_BY_ADDEND_FLAG = 4;
+const size_t RELOCATION_GROUP_HAS_ADDEND_FLAG = 8;
+
+static int xh_elf_packed_reloc_iterator_init(xh_elf_packed_reloc_iterator_t *self,
+ ElfW(Addr) rel, ElfW(Word) rel_sz, int is_use_rela)
+{
+ int r;
+
+ memset(self, 0, sizeof(xh_elf_packed_reloc_iterator_t));
+ xh_elf_sleb128_decoder_init(&(self->decoder), rel, rel_sz);
+ self->is_use_rela = is_use_rela;
+
+ if(0 != (r = xh_elf_sleb128_decoder_next(&(self->decoder), &(self->relocation_count)))) return r;
+ if(0 != (r = xh_elf_sleb128_decoder_next(&(self->decoder), (size_t *)&(self->r_offset)))) return r;
+ return 0;
+}
+
+static int xh_elf_packed_reloc_iterator_read_group_fields(xh_elf_packed_reloc_iterator_t *self)
+{
+ int r;
+ size_t val;
+
+ if(0 != (r = xh_elf_sleb128_decoder_next(&(self->decoder), &(self->group_size)))) return r;
+ if(0 != (r = xh_elf_sleb128_decoder_next(&(self->decoder), &(self->group_flags)))) return r;
+
+ if(self->group_flags & RELOCATION_GROUPED_BY_OFFSET_DELTA_FLAG)
+ if(0 != (r = xh_elf_sleb128_decoder_next(&(self->decoder), &(self->group_r_offset_delta)))) return r;
+
+ if(self->group_flags & RELOCATION_GROUPED_BY_INFO_FLAG)
+ if(0 != (r = xh_elf_sleb128_decoder_next(&(self->decoder), (size_t *)&(self->r_info)))) return r;
+
+ if((self->group_flags & RELOCATION_GROUP_HAS_ADDEND_FLAG) &&
+ (self->group_flags & RELOCATION_GROUPED_BY_ADDEND_FLAG))
+ {
+ if(0 == self->is_use_rela)
+ {
+ XH_LOG_ERROR("unexpected r_addend in android.rel section");
+ return XH_ERRNO_FORMAT;
+ }
+ if(0 != (r = xh_elf_sleb128_decoder_next(&(self->decoder), &val))) return r;
+ self->r_addend += (ssize_t)val;
+ }
+ else if(0 == (self->group_flags & RELOCATION_GROUP_HAS_ADDEND_FLAG))
+ {
+ self->r_addend = 0;
+ }
+
+ self->relocation_group_index = 0;
+ return 0;
+}
+
+static void *xh_elf_packed_reloc_iterator_next(xh_elf_packed_reloc_iterator_t *self)
+{
+ size_t val;
+
+ if(self->relocation_index >= self->relocation_count) return NULL;
+
+ if(self->relocation_group_index == self->group_size)
+ {
+ if(0 != xh_elf_packed_reloc_iterator_read_group_fields(self)) return NULL;
+ }
+
+ if(self->group_flags & RELOCATION_GROUPED_BY_OFFSET_DELTA_FLAG)
+ {
+ self->r_offset += self->group_r_offset_delta;
+ }
+ else
+ {
+ if(0 != xh_elf_sleb128_decoder_next(&(self->decoder), &val)) return NULL;
+ self->r_offset += val;
+ }
+
+ if(0 == (self->group_flags & RELOCATION_GROUPED_BY_INFO_FLAG))
+ if(0 != xh_elf_sleb128_decoder_next(&(self->decoder), &(self->r_info))) return NULL;
+
+ if(self->is_use_rela &&
+ (self->group_flags & RELOCATION_GROUP_HAS_ADDEND_FLAG) &&
+ (0 == (self->group_flags & RELOCATION_GROUPED_BY_ADDEND_FLAG)))
+ {
+ if(0 != xh_elf_sleb128_decoder_next(&(self->decoder), &val)) return NULL;
+ self->r_addend += (ssize_t)val;
+ }
+
+ self->relocation_index++;
+ self->relocation_group_index++;
+
+ if(self->is_use_rela)
+ {
+ self->rela.r_offset = self->r_offset;
+ self->rela.r_info = self->r_info;
+ self->rela.r_addend = self->r_addend;
+ return (void *)(&(self->rela));
+ }
+ else
+ {
+ self->rel.r_offset = self->r_offset;
+ self->rel.r_info = self->r_info;
+ return (void *)(&(self->rel));
+ }
+}
+
+//ELF header checker
+int xh_elf_check_elfheader(uintptr_t base_addr)
+{
+ ElfW(Ehdr) *ehdr = (ElfW(Ehdr) *)base_addr;
+
+ //check magic
+ if(0 != memcmp(ehdr->e_ident, ELFMAG, SELFMAG)) return XH_ERRNO_FORMAT;
+
+ //check class (64/32)
+#if defined(__LP64__)
+ if(ELFCLASS64 != ehdr->e_ident[EI_CLASS]) return XH_ERRNO_FORMAT;
+#else
+ if(ELFCLASS32 != ehdr->e_ident[EI_CLASS]) return XH_ERRNO_FORMAT;
+#endif
+
+ //check endian (little/big)
+ if(ELFDATA2LSB != ehdr->e_ident[EI_DATA]) return XH_ERRNO_FORMAT;
+
+ //check version
+ if(EV_CURRENT != ehdr->e_ident[EI_VERSION]) return XH_ERRNO_FORMAT;
+
+ //check type
+ if(ET_EXEC != ehdr->e_type && ET_DYN != ehdr->e_type) return XH_ERRNO_FORMAT;
+
+ //check machine
+#if defined(__arm__)
+ if(EM_ARM != ehdr->e_machine) return XH_ERRNO_FORMAT;
+#elif defined(__aarch64__)
+ if(EM_AARCH64 != ehdr->e_machine) return XH_ERRNO_FORMAT;
+#elif defined(__i386__)
+ if(EM_386 != ehdr->e_machine) return XH_ERRNO_FORMAT;
+#elif defined(__x86_64__)
+ if(EM_X86_64 != ehdr->e_machine) return XH_ERRNO_FORMAT;
+#else
+ return XH_ERRNO_FORMAT;
+#endif
+
+ //check version
+ if(EV_CURRENT != ehdr->e_version) return XH_ERRNO_FORMAT;
+
+ return 0;
+}
+
+//ELF hash func
+static uint32_t xh_elf_hash(const uint8_t *name)
+{
+ uint32_t h = 0, g;
+
+ while (*name) {
+ h = (h << 4) + *name++;
+ g = h & 0xf0000000;
+ h ^= g;
+ h ^= g >> 24;
+ }
+
+ return h;
+}
+
+//GNU hash func
+static uint32_t xh_elf_gnu_hash(const uint8_t *name)
+{
+ uint32_t h = 5381;
+
+ while(*name != 0)
+ {
+ h += (h << 5) + *name++;
+ }
+ return h;
+}
+
+static ElfW(Phdr) *xh_elf_get_first_segment_by_type(xh_elf_t *self, ElfW(Word) type)
+{
+ ElfW(Phdr) *phdr;
+
+ for(phdr = self->phdr; phdr < self->phdr + self->ehdr->e_phnum; phdr++)
+ {
+ if(phdr->p_type == type)
+ {
+ return phdr;
+ }
+ }
+ return NULL;
+}
+
+static ElfW(Phdr) *xh_elf_get_first_segment_by_type_offset(xh_elf_t *self, ElfW(Word) type, ElfW(Off) offset)
+{
+ ElfW(Phdr) *phdr;
+
+ for(phdr = self->phdr; phdr < self->phdr + self->ehdr->e_phnum; phdr++)
+ {
+ if(phdr->p_type == type && phdr->p_offset == offset)
+ {
+ return phdr;
+ }
+ }
+ return NULL;
+}
+
+static int xh_elf_hash_lookup(xh_elf_t *self, const char *symbol, uint32_t *symidx)
+{
+ uint32_t hash = xh_elf_hash((uint8_t *)symbol);
+ const char *symbol_cur;
+ uint32_t i;
+
+ for(i = self->bucket[hash % self->bucket_cnt]; 0 != i; i = self->chain[i])
+ {
+ symbol_cur = self->strtab + self->symtab[i].st_name;
+
+ if(0 == strcmp(symbol, symbol_cur))
+ {
+ *symidx = i;
+ XH_LOG_INFO("found %s at symidx: %u (ELF_HASH)\n", symbol, *symidx);
+ return 0;
+ }
+ }
+
+ return XH_ERRNO_NOTFND;
+}
+
+static int xh_elf_gnu_hash_lookup_def(xh_elf_t *self, const char *symbol, uint32_t *symidx)
+{
+ uint32_t hash = xh_elf_gnu_hash((uint8_t *)symbol);
+
+ static uint32_t elfclass_bits = sizeof(ElfW(Addr)) * 8;
+ size_t word = self->bloom[(hash / elfclass_bits) % self->bloom_sz];
+ size_t mask = 0
+ | (size_t)1 << (hash % elfclass_bits)
+ | (size_t)1 << ((hash >> self->bloom_shift) % elfclass_bits);
+
+ //if at least one bit is not set, this symbol is surely missing
+ if((word & mask) != mask) return XH_ERRNO_NOTFND;
+
+ //ignore STN_UNDEF
+ uint32_t i = self->bucket[hash % self->bucket_cnt];
+ if(i < self->symoffset) return XH_ERRNO_NOTFND;
+
+ //loop through the chain
+ while(1)
+ {
+ const char *symname = self->strtab + self->symtab[i].st_name;
+ const uint32_t symhash = self->chain[i - self->symoffset];
+
+ if((hash | (uint32_t)1) == (symhash | (uint32_t)1) && 0 == strcmp(symbol, symname))
+ {
+ *symidx = i;
+ XH_LOG_INFO("found %s at symidx: %u (GNU_HASH DEF)\n", symbol, *symidx);
+ return 0;
+ }
+
+ //chain ends with an element with the lowest bit set to 1
+ if(symhash & (uint32_t)1) break;
+
+ i++;
+ }
+
+ return XH_ERRNO_NOTFND;
+}
+
+static int xh_elf_gnu_hash_lookup_undef(xh_elf_t *self, const char *symbol, uint32_t *symidx)
+{
+ uint32_t i;
+
+ for(i = 0; i < self->symoffset; i++)
+ {
+ const char *symname = self->strtab + self->symtab[i].st_name;
+ if(0 == strcmp(symname, symbol))
+ {
+ *symidx = i;
+ XH_LOG_INFO("found %s at symidx: %u (GNU_HASH UNDEF)\n", symbol, *symidx);
+ return 0;
+ }
+ }
+ return XH_ERRNO_NOTFND;
+}
+
+static int xh_elf_gnu_hash_lookup(xh_elf_t *self, const char *symbol, uint32_t *symidx)
+{
+ if(0 == xh_elf_gnu_hash_lookup_def(self, symbol, symidx)) return 0;
+ if(0 == xh_elf_gnu_hash_lookup_undef(self, symbol, symidx)) return 0;
+ return XH_ERRNO_NOTFND;
+}
+
+static int xh_elf_find_symidx_by_name(xh_elf_t *self, const char *symbol, uint32_t *symidx)
+{
+ if(self->is_use_gnu_hash)
+ return xh_elf_gnu_hash_lookup(self, symbol, symidx);
+ else
+ return xh_elf_hash_lookup(self, symbol, symidx);
+}
+
+static int xh_elf_replace_function(xh_elf_t *self, const char *symbol, ElfW(Addr) addr, void *new_func, void **old_func)
+{
+ void *old_addr;
+ unsigned int old_prot = 0;
+ unsigned int need_prot = PROT_READ | PROT_WRITE;
+ int r;
+
+ //already replaced?
+ //here we assume that we always have read permission, is this a problem?
+ if(*(void **)addr == new_func) return 0;
+
+ //get old prot
+ if(0 != (r = xh_util_get_addr_protect(addr, self->pathname, &old_prot)))
+ {
+ XH_LOG_ERROR("get addr prot failed. ret: %d", r);
+ return r;
+ }
+
+ if(old_prot != need_prot)
+ {
+ //set new prot
+ if(0 != (r = xh_util_set_addr_protect(addr, need_prot)))
+ {
+ XH_LOG_ERROR("set addr prot failed. ret: %d", r);
+ return r;
+ }
+ }
+
+ //save old func
+ old_addr = *(void **)addr;
+ if(NULL != old_func) *old_func = old_addr;
+
+ //replace func
+ *(void **)addr = new_func; //segmentation fault sometimes
+
+ if(old_prot != need_prot)
+ {
+ //restore the old prot
+ if(0 != (r = xh_util_set_addr_protect(addr, old_prot)))
+ {
+ XH_LOG_WARN("restore addr prot failed. ret: %d", r);
+ }
+ }
+
+ //clear cache
+ xh_util_flush_instruction_cache(addr);
+
+ XH_LOG_INFO("XH_HK_OK %p: %p -> %p %s %s\n", (void *)addr, old_addr, new_func, symbol, self->pathname);
+ return 0;
+}
+
+static int xh_elf_check(xh_elf_t *self)
+{
+ if(0 == self->base_addr)
+ {
+ XH_LOG_ERROR("base_addr == 0\n");
+ return 1;
+ }
+ if(0 == self->bias_addr)
+ {
+ XH_LOG_ERROR("bias_addr == 0\n");
+ return 1;
+ }
+ if(NULL == self->ehdr)
+ {
+ XH_LOG_ERROR("ehdr == NULL\n");
+ return 1;
+ }
+ if(NULL == self->phdr)
+ {
+ XH_LOG_ERROR("phdr == NULL\n");
+ return 1;
+ }
+ if(NULL == self->strtab)
+ {
+ XH_LOG_ERROR("strtab == NULL\n");
+ return 1;
+ }
+ if(NULL == self->symtab)
+ {
+ XH_LOG_ERROR("symtab == NULL\n");
+ return 1;
+ }
+ if(NULL == self->bucket)
+ {
+ XH_LOG_ERROR("bucket == NULL\n");
+ return 1;
+ }
+ if(NULL == self->chain)
+ {
+ XH_LOG_ERROR("chain == NULL\n");
+ return 1;
+ }
+ if(1 == self->is_use_gnu_hash && NULL == self->bloom)
+ {
+ XH_LOG_ERROR("bloom == NULL\n");
+ return 1;
+ }
+
+ return 0;
+}
+
+#if XH_ELF_DEBUG
+
+static void xh_elf_dump_elfheader(xh_elf_t *self)
+{
+ static char alpha_tab[17] = "0123456789ABCDEF";
+ int i;
+ uint8_t ch;
+ char buff[EI_NIDENT * 3 + 1];
+
+ for(i = 0; i < EI_NIDENT; i++)
+ {
+ ch = self->ehdr->e_ident[i];
+ buff[i * 3 + 0] = alpha_tab[(int)((ch >> 4) & 0x0F)];
+ buff[i * 3 + 1] = alpha_tab[(int)(ch & 0x0F)];
+ buff[i * 3 + 2] = ' ';
+ }
+ buff[EI_NIDENT * 3] = '\0';
+
+ XH_LOG_DEBUG("Elf Header:\n");
+ XH_LOG_DEBUG(" Magic: %s\n", buff);
+ XH_LOG_DEBUG(" Class: %#x\n", self->ehdr->e_ident[EI_CLASS]);
+ XH_LOG_DEBUG(" Data: %#x\n", self->ehdr->e_ident[EI_DATA]);
+ XH_LOG_DEBUG(" Version: %#x\n", self->ehdr->e_ident[EI_VERSION]);
+ XH_LOG_DEBUG(" OS/ABI: %#x\n", self->ehdr->e_ident[EI_OSABI]);
+ XH_LOG_DEBUG(" ABI Version: %#x\n", self->ehdr->e_ident[EI_ABIVERSION]);
+ XH_LOG_DEBUG(" Type: %#x\n", self->ehdr->e_type);
+ XH_LOG_DEBUG(" Machine: %#x\n", self->ehdr->e_machine);
+ XH_LOG_DEBUG(" Version: %#x\n", self->ehdr->e_version);
+ XH_LOG_DEBUG(" Entry point address: %"XH_UTIL_FMT_X"\n", self->ehdr->e_entry);
+ XH_LOG_DEBUG(" Start of program headers: %"XH_UTIL_FMT_X" (bytes into file)\n", self->ehdr->e_phoff);
+ XH_LOG_DEBUG(" Start of section headers: %"XH_UTIL_FMT_X" (bytes into file)\n", self->ehdr->e_shoff);
+ XH_LOG_DEBUG(" Flags: %#x\n", self->ehdr->e_flags);
+ XH_LOG_DEBUG(" Size of this header: %u (bytes)\n", self->ehdr->e_ehsize);
+ XH_LOG_DEBUG(" Size of program headers: %u (bytes)\n", self->ehdr->e_phentsize);
+ XH_LOG_DEBUG(" Number of program headers: %u\n", self->ehdr->e_phnum);
+ XH_LOG_DEBUG(" Size of section headers: %u (bytes)\n", self->ehdr->e_shentsize);
+ XH_LOG_DEBUG(" Number of section headers: %u\n", self->ehdr->e_shnum);
+ XH_LOG_DEBUG(" Section header string table index: %u\n", self->ehdr->e_shstrndx);
+}
+
+static void xh_elf_dump_programheader(xh_elf_t *self)
+{
+ ElfW(Phdr) *phdr = self->phdr;
+ size_t i;
+
+ XH_LOG_DEBUG("Program Headers:\n");
+ XH_LOG_DEBUG(" %-8s " \
+ "%-"XH_UTIL_FMT_FIXED_S" " \
+ "%-"XH_UTIL_FMT_FIXED_S" " \
+ "%-"XH_UTIL_FMT_FIXED_S" " \
+ "%-"XH_UTIL_FMT_FIXED_S" " \
+ "%-"XH_UTIL_FMT_FIXED_S" " \
+ "%-8s " \
+ "%-s\n",
+ "Type",
+ "Offset",
+ "VirtAddr",
+ "PhysAddr",
+ "FileSiz",
+ "MemSiz",
+ "Flg",
+ "Align");
+ for(i = 0; i < self->ehdr->e_phnum; i++, phdr++)
+ {
+ XH_LOG_DEBUG(" %-8x " \
+ "%."XH_UTIL_FMT_FIXED_X" " \
+ "%."XH_UTIL_FMT_FIXED_X" " \
+ "%."XH_UTIL_FMT_FIXED_X" " \
+ "%."XH_UTIL_FMT_FIXED_X" " \
+ "%."XH_UTIL_FMT_FIXED_X" " \
+ "%-8x " \
+ "%"XH_UTIL_FMT_X"\n",
+ phdr->p_type,
+ phdr->p_offset,
+ phdr->p_vaddr,
+ phdr->p_paddr,
+ phdr->p_filesz,
+ phdr->p_memsz,
+ phdr->p_flags,
+ phdr->p_align);
+ }
+}
+
+static void xh_elf_dump_dynamic(xh_elf_t *self)
+{
+ ElfW(Dyn) *dyn = self->dyn;
+ size_t dyn_cnt = (self->dyn_sz / sizeof(ElfW(Dyn)));
+ size_t i;
+
+ XH_LOG_DEBUG("Dynamic section contains %zu entries:\n", dyn_cnt);
+ XH_LOG_DEBUG(" %-"XH_UTIL_FMT_FIXED_S" " \
+ "%s\n",
+ "Tag",
+ "Val");
+ for(i = 0; i < dyn_cnt; i++, dyn++)
+ {
+ XH_LOG_DEBUG(" %-"XH_UTIL_FMT_FIXED_X" " \
+ "%-"XH_UTIL_FMT_X"\n",
+ dyn->d_tag,
+ dyn->d_un.d_val);
+ }
+}
+
+static void xh_elf_dump_rel(xh_elf_t *self, const char *type, ElfW(Addr) rel_addr, ElfW(Word) rel_sz)
+{
+ ElfW(Rela) *rela;
+ ElfW(Rel) *rel;
+ ElfW(Word) cnt;
+ ElfW(Word) i;
+ ElfW(Sym) *sym;
+
+ if(self->is_use_rela)
+ {
+ rela = (ElfW(Rela) *)(rel_addr);
+ cnt = rel_sz / sizeof(ElfW(Rela));
+ }
+ else
+ {
+ rel = (ElfW(Rel) *)(rel_addr);
+ cnt = rel_sz / sizeof(ElfW(Rel));
+ }
+
+ XH_LOG_DEBUG("Relocation section '.rel%s%s' contains %u entries:\n",
+ (self->is_use_rela ? "a" : ""), type, cnt);
+ XH_LOG_DEBUG(" %-"XH_UTIL_FMT_FIXED_S" " \
+ "%-"XH_UTIL_FMT_FIXED_S" " \
+ "%-8s " \
+ "%-8s " \
+ "%-8s " \
+ "%s\n",
+ "Offset",
+ "Info",
+ "Type",
+ "Sym.Idx",
+ "Sym.Val",
+ "Sym.Name");
+ const char *fmt = " %."XH_UTIL_FMT_FIXED_X" " \
+ "%."XH_UTIL_FMT_FIXED_X" " \
+ "%.8x " \
+ "%.8u " \
+ "%.8x " \
+ "%s\n";
+ for(i = 0; i < cnt; i++)
+ {
+ if(self->is_use_rela)
+ {
+ sym = &(self->symtab[XH_ELF_R_SYM(rela[i].r_info)]);
+ XH_LOG_DEBUG(fmt,
+ rela[i].r_offset,
+ rela[i].r_info,
+ XH_ELF_R_TYPE(rela[i].r_info),
+ XH_ELF_R_SYM(rela[i].r_info),
+ sym->st_value,
+ self->strtab + sym->st_name);
+ }
+ else
+ {
+ sym = &(self->symtab[XH_ELF_R_SYM(rel[i].r_info)]);
+ XH_LOG_DEBUG(fmt,
+ rel[i].r_offset,
+ rel[i].r_info,
+ XH_ELF_R_TYPE(rel[i].r_info),
+ XH_ELF_R_SYM(rel[i].r_info),
+ sym->st_value,
+ self->strtab + sym->st_name);
+ }
+ }
+}
+
+static void xh_elf_dump_symtab(xh_elf_t *self)
+{
+ if(self->is_use_gnu_hash) return;
+
+ ElfW(Word) symtab_cnt = self->chain_cnt;
+ ElfW(Word) i;
+
+ XH_LOG_DEBUG("Symbol table '.dynsym' contains %u entries:\n", symtab_cnt);
+ XH_LOG_DEBUG(" %-8s " \
+ "%-"XH_UTIL_FMT_FIXED_S" " \
+ "%s\n",
+ "Idx",
+ "Value",
+ "Name");
+ for(i = 0; i < symtab_cnt; i++)
+ {
+ XH_LOG_DEBUG(" %-8u " \
+ "%."XH_UTIL_FMT_FIXED_X" " \
+ "%s\n",
+ i,
+ self->symtab[i].st_value,
+ self->strtab + self->symtab[i].st_name);
+ }
+}
+
+static void xh_elf_dump(xh_elf_t *self)
+{
+ if(xh_log_priority < ANDROID_LOG_DEBUG) return;
+
+ XH_LOG_DEBUG("Elf Pathname: %s\n", self->pathname);
+ XH_LOG_DEBUG("Elf bias addr: %p\n", (void *)self->bias_addr);
+ xh_elf_dump_elfheader(self);
+ xh_elf_dump_programheader(self);
+ xh_elf_dump_dynamic(self);
+ xh_elf_dump_rel(self, ".plt", self->relplt, self->relplt_sz);
+ xh_elf_dump_rel(self, ".dyn", self->reldyn, self->reldyn_sz);
+ xh_elf_dump_symtab(self);
+}
+
+#endif
+
+int xh_elf_init(xh_elf_t *self, uintptr_t base_addr, const char *pathname)
+{
+ if(0 == base_addr || NULL == pathname) return XH_ERRNO_INVAL;
+
+ //always reset
+ memset(self, 0, sizeof(xh_elf_t));
+
+ self->pathname = pathname;
+ self->base_addr = (ElfW(Addr))base_addr;
+ self->ehdr = (ElfW(Ehdr) *)base_addr;
+ self->phdr = (ElfW(Phdr) *)(base_addr + self->ehdr->e_phoff); //segmentation fault sometimes
+
+ //find the first load-segment with offset 0
+ ElfW(Phdr) *phdr0 = xh_elf_get_first_segment_by_type_offset(self, PT_LOAD, 0);
+ if(NULL == phdr0)
+ {
+ XH_LOG_ERROR("Can NOT found the first load segment. %s", pathname);
+ return XH_ERRNO_FORMAT;
+ }
+
+#if XH_ELF_DEBUG
+ if(0 != phdr0->p_vaddr)
+ XH_LOG_DEBUG("first load-segment vaddr NOT 0 (vaddr: %p). %s",
+ (void *)(phdr0->p_vaddr), pathname);
+#endif
+
+ //save load bias addr
+ if(self->base_addr < phdr0->p_vaddr) return XH_ERRNO_FORMAT;
+ self->bias_addr = self->base_addr - phdr0->p_vaddr;
+
+ //find dynamic-segment
+ ElfW(Phdr) *dhdr = xh_elf_get_first_segment_by_type(self, PT_DYNAMIC);
+ if(NULL == dhdr)
+ {
+ XH_LOG_ERROR("Can NOT found dynamic segment. %s", pathname);
+ return XH_ERRNO_FORMAT;
+ }
+
+ //parse dynamic-segment
+ self->dyn = (ElfW(Dyn) *)(self->bias_addr + dhdr->p_vaddr);
+ self->dyn_sz = dhdr->p_memsz;
+ ElfW(Dyn) *dyn = self->dyn;
+ ElfW(Dyn) *dyn_end = self->dyn + (self->dyn_sz / sizeof(ElfW(Dyn)));
+ uint32_t *raw;
+ for(; dyn < dyn_end; dyn++)
+ {
+ switch(dyn->d_tag) //segmentation fault sometimes
+ {
+ case DT_NULL:
+ //the end of the dynamic-section
+ dyn = dyn_end;
+ break;
+ case DT_STRTAB:
+ {
+ self->strtab = (const char *)(self->bias_addr + dyn->d_un.d_ptr);
+ if((ElfW(Addr))(self->strtab) < self->base_addr) return XH_ERRNO_FORMAT;
+ break;
+ }
+ case DT_SYMTAB:
+ {
+ self->symtab = (ElfW(Sym) *)(self->bias_addr + dyn->d_un.d_ptr);
+ if((ElfW(Addr))(self->symtab) < self->base_addr) return XH_ERRNO_FORMAT;
+ break;
+ }
+ case DT_PLTREL:
+ //use rel or rela?
+ self->is_use_rela = (dyn->d_un.d_val == DT_RELA ? 1 : 0);
+ break;
+ case DT_JMPREL:
+ {
+ self->relplt = (ElfW(Addr))(self->bias_addr + dyn->d_un.d_ptr);
+ if((ElfW(Addr))(self->relplt) < self->base_addr) return XH_ERRNO_FORMAT;
+ break;
+ }
+ case DT_PLTRELSZ:
+ self->relplt_sz = dyn->d_un.d_val;
+ break;
+ case DT_REL:
+ case DT_RELA:
+ {
+ self->reldyn = (ElfW(Addr))(self->bias_addr + dyn->d_un.d_ptr);
+ if((ElfW(Addr))(self->reldyn) < self->base_addr) return XH_ERRNO_FORMAT;
+ break;
+ }
+ case DT_RELSZ:
+ case DT_RELASZ:
+ self->reldyn_sz = dyn->d_un.d_val;
+ break;
+ case DT_ANDROID_REL:
+ case DT_ANDROID_RELA:
+ {
+ self->relandroid = (ElfW(Addr))(self->bias_addr + dyn->d_un.d_ptr);
+ if((ElfW(Addr))(self->relandroid) < self->base_addr) return XH_ERRNO_FORMAT;
+ break;
+ }
+ case DT_ANDROID_RELSZ:
+ case DT_ANDROID_RELASZ:
+ self->relandroid_sz = dyn->d_un.d_val;
+ break;
+ case DT_HASH:
+ {
+ raw = (uint32_t *)(self->bias_addr + dyn->d_un.d_ptr);
+ if((ElfW(Addr))raw < self->base_addr) return XH_ERRNO_FORMAT;
+ self->bucket_cnt = raw[0];
+ self->chain_cnt = raw[1];
+ self->bucket = &raw[2];
+ self->chain = &(self->bucket[self->bucket_cnt]);
+ break;
+ }
+ case DT_GNU_HASH:
+ {
+ raw = (uint32_t *)(self->bias_addr + dyn->d_un.d_ptr);
+ if((ElfW(Addr))raw < self->base_addr) return XH_ERRNO_FORMAT;
+ self->bucket_cnt = raw[0];
+ self->symoffset = raw[1];
+ self->bloom_sz = raw[2];
+ self->bloom_shift = raw[3];
+ self->bloom = (ElfW(Addr) *)(&raw[4]);
+ self->bucket = (uint32_t *)(&(self->bloom[self->bloom_sz]));
+ self->chain = (uint32_t *)(&(self->bucket[self->bucket_cnt]));
+ self->is_use_gnu_hash = 1;
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ //check android rel/rela
+ if(0 != self->relandroid)
+ {
+ const char *rel = (const char *)self->relandroid;
+ if(self->relandroid_sz < 4 ||
+ rel[0] != 'A' ||
+ rel[1] != 'P' ||
+ rel[2] != 'S' ||
+ rel[3] != '2')
+ {
+ XH_LOG_ERROR("android rel/rela format error\n");
+ return XH_ERRNO_FORMAT;
+ }
+
+ self->relandroid += 4;
+ self->relandroid_sz -= 4;
+ }
+
+ //check elf info
+ if(0 != xh_elf_check(self))
+ {
+ XH_LOG_ERROR("elf init check failed. %s", pathname);
+ return XH_ERRNO_FORMAT;
+ }
+
+#if XH_ELF_DEBUG
+ xh_elf_dump(self);
+#endif
+
+ XH_LOG_INFO("init OK: %s (%s %s PLT:%u DYN:%u ANDROID:%u)\n", self->pathname,
+ self->is_use_rela ? "RELA" : "REL",
+ self->is_use_gnu_hash ? "GNU_HASH" : "ELF_HASH",
+ self->relplt_sz, self->reldyn_sz, self->relandroid_sz);
+
+ return 0;
+}
+
+static int xh_elf_find_and_replace_func(xh_elf_t *self, const char *section,
+ int is_plt, const char *symbol,
+ void *new_func, void **old_func,
+ uint32_t symidx, void *rel_common,
+ int *found)
+{
+ ElfW(Rela) *rela;
+ ElfW(Rel) *rel;
+ ElfW(Addr) r_offset;
+ size_t r_info;
+ size_t r_sym;
+ size_t r_type;
+ ElfW(Addr) addr;
+ int r;
+
+ if(NULL != found) *found = 0;
+
+ if(self->is_use_rela)
+ {
+ rela = (ElfW(Rela) *)rel_common;
+ r_info = rela->r_info;
+ r_offset = rela->r_offset;
+ }
+ else
+ {
+ rel = (ElfW(Rel) *)rel_common;
+ r_info = rel->r_info;
+ r_offset = rel->r_offset;
+ }
+
+ //check sym
+ r_sym = XH_ELF_R_SYM(r_info);
+ if(r_sym != symidx) return 0;
+
+ //check type
+ r_type = XH_ELF_R_TYPE(r_info);
+ if(is_plt && r_type != XH_ELF_R_GENERIC_JUMP_SLOT) return 0;
+ if(!is_plt && (r_type != XH_ELF_R_GENERIC_GLOB_DAT && r_type != XH_ELF_R_GENERIC_ABS)) return 0;
+
+ //we found it
+ XH_LOG_INFO("found %s at %s offset: %p\n", symbol, section, (void *)r_offset);
+ if(NULL != found) *found = 1;
+
+ //do replace
+ addr = self->bias_addr + r_offset;
+ if(addr < self->base_addr) return XH_ERRNO_FORMAT;
+ if(0 != (r = xh_elf_replace_function(self, symbol, addr, new_func, old_func)))
+ {
+ XH_LOG_ERROR("replace function failed: %s at %s\n", symbol, section);
+ return r;
+ }
+
+ return 0;
+}
+
+int xh_elf_hook(xh_elf_t *self, const char *symbol, void *new_func, void **old_func)
+{
+ uint32_t symidx;
+ void *rel_common;
+ xh_elf_plain_reloc_iterator_t plain_iter;
+ xh_elf_packed_reloc_iterator_t packed_iter;
+ int found;
+ int r;
+
+ if(NULL == self->pathname)
+ {
+ XH_LOG_ERROR("not inited\n");
+ return XH_ERRNO_ELFINIT; //not inited?
+ }
+
+ if(NULL == symbol || NULL == new_func) return XH_ERRNO_INVAL;
+
+ XH_LOG_INFO("hooking %s in %s\n", symbol, self->pathname);
+
+ //find symbol index by symbol name
+ if(0 != (r = xh_elf_find_symidx_by_name(self, symbol, &symidx))) return 0;
+
+ //replace for .rel(a).plt
+ if(0 != self->relplt)
+ {
+ xh_elf_plain_reloc_iterator_init(&plain_iter, self->relplt, self->relplt_sz, self->is_use_rela);
+ while(NULL != (rel_common = xh_elf_plain_reloc_iterator_next(&plain_iter)))
+ {
+ if(0 != (r = xh_elf_find_and_replace_func(self,
+ (self->is_use_rela ? ".rela.plt" : ".rel.plt"), 1,
+ symbol, new_func, old_func,
+ symidx, rel_common, &found))) return r;
+ if(found) break;
+ }
+ }
+
+ //replace for .rel(a).dyn
+ if(0 != self->reldyn)
+ {
+ xh_elf_plain_reloc_iterator_init(&plain_iter, self->reldyn, self->reldyn_sz, self->is_use_rela);
+ while(NULL != (rel_common = xh_elf_plain_reloc_iterator_next(&plain_iter)))
+ {
+ if(0 != (r = xh_elf_find_and_replace_func(self,
+ (self->is_use_rela ? ".rela.dyn" : ".rel.dyn"), 0,
+ symbol, new_func, old_func,
+ symidx, rel_common, NULL))) return r;
+ }
+ }
+
+ //replace for .rel(a).android
+ if(0 != self->relandroid)
+ {
+ xh_elf_packed_reloc_iterator_init(&packed_iter, self->relandroid, self->relandroid_sz, self->is_use_rela);
+ while(NULL != (rel_common = xh_elf_packed_reloc_iterator_next(&packed_iter)))
+ {
+ if(0 != (r = xh_elf_find_and_replace_func(self,
+ (self->is_use_rela ? ".rela.android" : ".rel.android"), 0,
+ symbol, new_func, old_func,
+ symidx, rel_common, NULL))) return r;
+ }
+ }
+
+ return 0;
+}
diff --git a/module/src/main/cpp/external/xhook/xh_elf.h b/module/src/main/cpp/external/xhook/xh_elf.h
new file mode 100644
index 0000000..1697dc4
--- /dev/null
+++ b/module/src/main/cpp/external/xhook/xh_elf.h
@@ -0,0 +1,85 @@
+// Copyright (c) 2018-present, iQIYI, Inc. All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+//
+
+// Created by caikelun on 2018-04-11.
+
+#ifndef XH_ELF_H
+#define XH_ELF_H 1
+
+#include
+#include
+#include
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct
+{
+ const char *pathname;
+
+ ElfW(Addr) base_addr;
+ ElfW(Addr) bias_addr;
+
+ ElfW(Ehdr) *ehdr;
+ ElfW(Phdr) *phdr;
+
+ ElfW(Dyn) *dyn; //.dynamic
+ ElfW(Word) dyn_sz;
+
+ const char *strtab; //.dynstr (string-table)
+ ElfW(Sym) *symtab; //.dynsym (symbol-index to string-table's offset)
+
+ ElfW(Addr) relplt; //.rel.plt or .rela.plt
+ ElfW(Word) relplt_sz;
+
+ ElfW(Addr) reldyn; //.rel.dyn or .rela.dyn
+ ElfW(Word) reldyn_sz;
+
+ ElfW(Addr) relandroid; //android compressed rel or rela
+ ElfW(Word) relandroid_sz;
+
+ //for ELF hash
+ uint32_t *bucket;
+ uint32_t bucket_cnt;
+ uint32_t *chain;
+ uint32_t chain_cnt; //invalid for GNU hash
+
+ //append for GNU hash
+ uint32_t symoffset;
+ ElfW(Addr) *bloom;
+ uint32_t bloom_sz;
+ uint32_t bloom_shift;
+
+ int is_use_rela;
+ int is_use_gnu_hash;
+} xh_elf_t;
+
+int xh_elf_init(xh_elf_t *self, uintptr_t base_addr, const char *pathname);
+int xh_elf_hook(xh_elf_t *self, const char *symbol, void *new_func, void **old_func);
+
+int xh_elf_check_elfheader(uintptr_t base_addr);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/module/src/main/cpp/external/xhook/xh_errno.h b/module/src/main/cpp/external/xhook/xh_errno.h
new file mode 100644
index 0000000..e628cd7
--- /dev/null
+++ b/module/src/main/cpp/external/xhook/xh_errno.h
@@ -0,0 +1,37 @@
+// Copyright (c) 2018-present, iQIYI, Inc. All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+//
+
+// Created by caikelun on 2018-04-11.
+
+#ifndef XH_ERRNO_H
+#define XH_ERRNO_H 1
+
+#define XH_ERRNO_UNKNOWN 1001
+#define XH_ERRNO_INVAL 1002
+#define XH_ERRNO_NOMEM 1003
+#define XH_ERRNO_REPEAT 1004
+#define XH_ERRNO_NOTFND 1005
+#define XH_ERRNO_BADMAPS 1006
+#define XH_ERRNO_FORMAT 1007
+#define XH_ERRNO_ELFINIT 1008
+#define XH_ERRNO_SEGVERR 1009
+
+#endif
diff --git a/module/src/main/cpp/external/xhook/xh_jni.c b/module/src/main/cpp/external/xhook/xh_jni.c
new file mode 100644
index 0000000..f8ae223
--- /dev/null
+++ b/module/src/main/cpp/external/xhook/xh_jni.c
@@ -0,0 +1,59 @@
+// Copyright (c) 2018-present, iQIYI, Inc. All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+//
+
+// Created by caikelun on 2018-04-11.
+
+#include
+#include "xhook.h"
+
+#define JNI_API_DEF(f) Java_com_qiyi_xhook_NativeHandler_##f
+
+JNIEXPORT jint JNI_API_DEF(refresh)(JNIEnv *env, jobject obj, jboolean async)
+{
+ (void)env;
+ (void)obj;
+
+ return xhook_refresh(async ? 1 : 0);
+}
+
+JNIEXPORT void JNI_API_DEF(clear)(JNIEnv *env, jobject obj)
+{
+ (void)env;
+ (void)obj;
+
+ xhook_clear();
+}
+
+JNIEXPORT void JNI_API_DEF(enableDebug)(JNIEnv *env, jobject obj, jboolean flag)
+{
+ (void)env;
+ (void)obj;
+
+ xhook_enable_debug(flag ? 1 : 0);
+}
+
+JNIEXPORT void JNI_API_DEF(enableSigSegvProtection)(JNIEnv *env, jobject obj, jboolean flag)
+{
+ (void)env;
+ (void)obj;
+
+ xhook_enable_sigsegv_protection(flag ? 1 : 0);
+}
diff --git a/module/src/main/cpp/external/xhook/xh_log.c b/module/src/main/cpp/external/xhook/xh_log.c
new file mode 100644
index 0000000..6cba947
--- /dev/null
+++ b/module/src/main/cpp/external/xhook/xh_log.c
@@ -0,0 +1,27 @@
+// Copyright (c) 2018-present, iQIYI, Inc. All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+//
+
+// Created by caikelun on 2018-04-11.
+
+#include
+#include "xh_log.h"
+
+android_LogPriority xh_log_priority = ANDROID_LOG_WARN;
diff --git a/module/src/main/cpp/external/xhook/xh_log.h b/module/src/main/cpp/external/xhook/xh_log.h
new file mode 100644
index 0000000..e108c4b
--- /dev/null
+++ b/module/src/main/cpp/external/xhook/xh_log.h
@@ -0,0 +1,45 @@
+// Copyright (c) 2018-present, iQIYI, Inc. All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+//
+
+// Created by caikelun on 2018-04-11.
+
+#ifndef XH_LOG_H
+#define XH_LOG_H 1
+
+#include
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern android_LogPriority xh_log_priority;
+
+#define XH_LOG_TAG "xhook"
+#define XH_LOG_DEBUG(fmt, ...) do{if(xh_log_priority <= ANDROID_LOG_DEBUG) __android_log_print(ANDROID_LOG_DEBUG, XH_LOG_TAG, fmt, ##__VA_ARGS__);}while(0)
+#define XH_LOG_INFO(fmt, ...) do{if(xh_log_priority <= ANDROID_LOG_INFO) __android_log_print(ANDROID_LOG_INFO, XH_LOG_TAG, fmt, ##__VA_ARGS__);}while(0)
+#define XH_LOG_WARN(fmt, ...) do{if(xh_log_priority <= ANDROID_LOG_WARN) __android_log_print(ANDROID_LOG_WARN, XH_LOG_TAG, fmt, ##__VA_ARGS__);}while(0)
+#define XH_LOG_ERROR(fmt, ...) do{if(xh_log_priority <= ANDROID_LOG_ERROR) __android_log_print(ANDROID_LOG_ERROR, XH_LOG_TAG, fmt, ##__VA_ARGS__);}while(0)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/module/src/main/cpp/external/xhook/xh_util.c b/module/src/main/cpp/external/xhook/xh_util.c
new file mode 100644
index 0000000..0e2dca2
--- /dev/null
+++ b/module/src/main/cpp/external/xhook/xh_util.c
@@ -0,0 +1,121 @@
+// Copyright (c) 2018-present, iQIYI, Inc. All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+//
+
+// Created by caikelun on 2018-04-11.
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include "xh_util.h"
+#include "xh_errno.h"
+#include "xh_log.h"
+
+#define PAGE_START(addr) ((addr) & PAGE_MASK)
+#define PAGE_END(addr) (PAGE_START(addr + sizeof(uintptr_t) - 1) + PAGE_SIZE)
+#define PAGE_COVER(addr) (PAGE_END(addr) - PAGE_START(addr))
+
+int xh_util_get_mem_protect(uintptr_t addr, size_t len, const char *pathname, unsigned int *prot)
+{
+ uintptr_t start_addr = addr;
+ uintptr_t end_addr = addr + len;
+ FILE *fp;
+ char line[512];
+ uintptr_t start, end;
+ char perm[5];
+ int load0 = 1;
+ int found_all = 0;
+
+ *prot = 0;
+
+ if(NULL == (fp = fopen("/proc/self/maps", "r"))) return XH_ERRNO_BADMAPS;
+
+ while(fgets(line, sizeof(line), fp))
+ {
+ if(NULL != pathname)
+ if(NULL == strstr(line, pathname)) continue;
+
+ if(sscanf(line, "%"PRIxPTR"-%"PRIxPTR" %4s ", &start, &end, perm) != 3) continue;
+
+ if(perm[3] != 'p') continue;
+
+ if(start_addr >= start && start_addr < end)
+ {
+ if(load0)
+ {
+ //first load segment
+ if(perm[0] == 'r') *prot |= PROT_READ;
+ if(perm[1] == 'w') *prot |= PROT_WRITE;
+ if(perm[2] == 'x') *prot |= PROT_EXEC;
+ load0 = 0;
+ }
+ else
+ {
+ //others
+ if(perm[0] != 'r') *prot &= ~PROT_READ;
+ if(perm[1] != 'w') *prot &= ~PROT_WRITE;
+ if(perm[2] != 'x') *prot &= ~PROT_EXEC;
+ }
+
+ if(end_addr <= end)
+ {
+ found_all = 1;
+ break; //finished
+ }
+ else
+ {
+ start_addr = end; //try to find the next load segment
+ }
+ }
+ }
+
+ fclose(fp);
+
+ if(!found_all) return XH_ERRNO_SEGVERR;
+
+ return 0;
+}
+
+int xh_util_get_addr_protect(uintptr_t addr, const char *pathname, unsigned int *prot)
+{
+ return xh_util_get_mem_protect(addr, sizeof(addr), pathname, prot);
+}
+
+int xh_util_set_addr_protect(uintptr_t addr, unsigned int prot)
+{
+ if(0 != mprotect((void *)PAGE_START(addr), PAGE_COVER(addr), (int)prot))
+ return 0 == errno ? XH_ERRNO_UNKNOWN : errno;
+
+ return 0;
+}
+
+void xh_util_flush_instruction_cache(uintptr_t addr)
+{
+ __builtin___clear_cache((void *)PAGE_START(addr), (void *)PAGE_END(addr));
+}
diff --git a/module/src/main/cpp/external/xhook/xh_util.h b/module/src/main/cpp/external/xhook/xh_util.h
new file mode 100644
index 0000000..b57f8dc
--- /dev/null
+++ b/module/src/main/cpp/external/xhook/xh_util.h
@@ -0,0 +1,51 @@
+// Copyright (c) 2018-present, iQIYI, Inc. All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+//
+
+// Created by caikelun on 2018-04-11.
+
+#ifndef XH_UTILS_H
+#define XH_UTILS_H 1
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if defined(__LP64__)
+#define XH_UTIL_FMT_LEN "16"
+#define XH_UTIL_FMT_X "llx"
+#else
+#define XH_UTIL_FMT_LEN "8"
+#define XH_UTIL_FMT_X "x"
+#endif
+
+#define XH_UTIL_FMT_FIXED_X XH_UTIL_FMT_LEN XH_UTIL_FMT_X
+#define XH_UTIL_FMT_FIXED_S XH_UTIL_FMT_LEN "s"
+
+int xh_util_get_mem_protect(uintptr_t addr, size_t len, const char *pathname, unsigned int *prot);
+int xh_util_get_addr_protect(uintptr_t addr, const char *pathname, unsigned int *prot);
+int xh_util_set_addr_protect(uintptr_t addr, unsigned int prot);
+void xh_util_flush_instruction_cache(uintptr_t addr);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/module/src/main/cpp/external/xhook/xh_version.c b/module/src/main/cpp/external/xhook/xh_version.c
new file mode 100644
index 0000000..b237f16
--- /dev/null
+++ b/module/src/main/cpp/external/xhook/xh_version.c
@@ -0,0 +1,66 @@
+// Copyright (c) 2018-present, iQIYI, Inc. All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+//
+
+// Created by caikelun on 2018-04-11.
+
+#include "xh_version.h"
+
+#define XH_VERSION_MAJOR 1
+#define XH_VERSION_MINOR 1
+#define XH_VERSION_EXTRA 9
+
+#define XH_VERSION ((XH_VERSION_MAJOR << 16) | (XH_VERSION_MINOR << 8) | (XH_VERSION_EXTRA))
+
+#define XH_VERSION_TO_STR_HELPER(x) #x
+#define XH_VERSION_TO_STR(x) XH_VERSION_TO_STR_HELPER(x)
+
+#define XH_VERSION_STR XH_VERSION_TO_STR(XH_VERSION_MAJOR) "." \
+ XH_VERSION_TO_STR(XH_VERSION_MINOR) "." \
+ XH_VERSION_TO_STR(XH_VERSION_EXTRA)
+
+#if defined(__arm__)
+#define XH_VERSION_ARCH "arm"
+#elif defined(__aarch64__)
+#define XH_VERSION_ARCH "aarch64"
+#elif defined(__i386__)
+#define XH_VERSION_ARCH "x86"
+#elif defined(__x86_64__)
+#define XH_VERSION_ARCH "x86_64"
+#else
+#define XH_VERSION_ARCH "unknown"
+#endif
+
+#define XH_VERSION_STR_FULL "libxhook "XH_VERSION_STR" ("XH_VERSION_ARCH")"
+
+unsigned int xh_version()
+{
+ return XH_VERSION;
+}
+
+const char *xh_version_str()
+{
+ return XH_VERSION_STR;
+}
+
+const char *xh_version_str_full()
+{
+ return XH_VERSION_STR_FULL;
+}
diff --git a/module/src/main/cpp/external/xhook/xh_version.h b/module/src/main/cpp/external/xhook/xh_version.h
new file mode 100644
index 0000000..b70b4f2
--- /dev/null
+++ b/module/src/main/cpp/external/xhook/xh_version.h
@@ -0,0 +1,41 @@
+// Copyright (c) 2018-present, iQIYI, Inc. All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+//
+
+// Created by caikelun on 2018-04-11.
+
+#ifndef XH_VERSION_H
+#define XH_VERSION_H 1
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+unsigned int xh_version();
+
+const char *xh_version_str();
+
+const char *xh_version_str_full();
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/module/src/main/cpp/external/xhook/xhook.c b/module/src/main/cpp/external/xhook/xhook.c
new file mode 100644
index 0000000..49dfc59
--- /dev/null
+++ b/module/src/main/cpp/external/xhook/xhook.c
@@ -0,0 +1,56 @@
+// Copyright (c) 2018-present, iQIYI, Inc. All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+//
+
+// Created by caikelun on 2018-04-11.
+
+#include "xh_core.h"
+#include "xhook.h"
+
+int xhook_register(const char *pathname_regex_str, const char *symbol,
+ void *new_func, void **old_func)
+{
+ return xh_core_register(pathname_regex_str, symbol, new_func, old_func);
+}
+
+int xhook_ignore(const char *pathname_regex_str, const char *symbol)
+{
+ return xh_core_ignore(pathname_regex_str, symbol);
+}
+
+int xhook_refresh(int async)
+{
+ return xh_core_refresh(async);
+}
+
+void xhook_clear()
+{
+ return xh_core_clear();
+}
+
+void xhook_enable_debug(int flag)
+{
+ return xh_core_enable_debug(flag);
+}
+
+void xhook_enable_sigsegv_protection(int flag)
+{
+ return xh_core_enable_sigsegv_protection(flag);
+}
diff --git a/module/src/main/cpp/external/xhook/xhook.h b/module/src/main/cpp/external/xhook/xhook.h
new file mode 100644
index 0000000..93dd5b4
--- /dev/null
+++ b/module/src/main/cpp/external/xhook/xhook.h
@@ -0,0 +1,50 @@
+// Copyright (c) 2018-present, iQIYI, Inc. All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+//
+
+// Created by caikelun on 2018-04-11.
+
+#ifndef XHOOK_H
+#define XHOOK_H 1
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define XHOOK_EXPORT __attribute__((visibility("default")))
+
+int xhook_register(const char *pathname_regex_str, const char *symbol,
+ void *new_func, void **old_func) XHOOK_EXPORT;
+
+int xhook_ignore(const char *pathname_regex_str, const char *symbol) XHOOK_EXPORT;
+
+int xhook_refresh(int async) XHOOK_EXPORT;
+
+void xhook_clear() XHOOK_EXPORT;
+
+void xhook_enable_debug(int flag) XHOOK_EXPORT;
+
+void xhook_enable_sigsegv_protection(int flag) XHOOK_EXPORT;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/module/src/main/cpp/hook.cpp b/module/src/main/cpp/hook.cpp
new file mode 100644
index 0000000..6666e92
--- /dev/null
+++ b/module/src/main/cpp/hook.cpp
@@ -0,0 +1,106 @@
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "include/riru.h"
+#include "logging.h"
+
+#define XHOOK_REGISTER(NAME) \
+ if (xhook_register(".*", #NAME, (void*) new_##NAME, (void **) &old_##NAME) == 0) { \
+ if (riru_get_version() >= 8) { \
+ void *f = riru_get_func(#NAME); \
+ if (f != nullptr) \
+ memcpy(&old_##NAME, &f, sizeof(void *)); \
+ riru_set_func(#NAME, (void *) new_##NAME); \
+ } \
+ } else { \
+ LOGE("failed to register hook " #NAME "."); \
+ }
+
+#define NEW_FUNC_DEF(ret, func, ...) \
+ static ret (*old_##func)(__VA_ARGS__); \
+ static ret new_##func(__VA_ARGS__)
+
+NEW_FUNC_DEF(int, __system_property_get, const char *key, char *value) {
+ int res = old___system_property_get(key, value);
+ if (key) {
+ if (strcmp("ro.miui.ui.version.name", key) == 0) {
+ strcpy(value, "V11");
+ //LOGI("system_property_get: %s -> %s", key, value);
+ } else if (strcmp("ro.miui.ui.version.code", key) == 0) {
+ strcpy(value, "9");
+ //LOGI("system_property_get: %s -> %s", key, value);
+ } else if (strcmp("ro.miui.version.code_time", key) == 0) {
+ strcpy(value, "1570636800");
+ //LOGI("system_property_get: %s -> %s", key, value);
+ } else if (strcmp("ro.miui.internal.storage", key) == 0) {
+ strcpy(value, "/sdcard/");
+ //LOGI("system_property_get: %s -> %s", key, value);
+ } else if (strcmp("ro.product.manufacturer", key) == 0) {
+ strcpy(value, "Xiaomi");
+ //LOGI("system_property_get: %s -> %s", key, value);
+ } else if (strcmp("ro.product.brand", key) == 0) {
+ strcpy(value, "Xiaomi");
+ //LOGI("system_property_get: %s -> %s", key, value);
+ } else if (strcmp("ro.product.name", key) == 0) {
+ strcpy(value, "Xiaomi");
+ //LOGI("system_property_get: %s -> %s", key, value);
+ }
+
+ }
+ return res;
+}
+
+NEW_FUNC_DEF(std::string, _ZN7android4base11GetPropertyERKNSt3__112basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEES9_, const std::string &key, const std::string &default_value) {
+ std::string res = old__ZN7android4base11GetPropertyERKNSt3__112basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEES9_(key, default_value);
+
+ if (strcmp("ro.miui.ui.version.name", key.c_str()) == 0) {
+ res = "V11";
+ //LOGI("android::base::GetProperty: %s -> %s", key.c_str(), res.c_str());
+ } else if (strcmp("ro.miui.ui.version.code", key.c_str()) == 0) {
+ res = "9";
+ //LOGI("android::base::GetProperty: %s -> %s", key.c_str(), res.c_str());
+ } else if (strcmp("ro.miui.version.code_time", key.c_str()) == 0) {
+ res = "1570636800";
+ //LOGI("android::base::GetProperty: %s -> %s", key.c_str(), res.c_str());
+ } else if (strcmp("ro.miui.internal.storage", key.c_str()) == 0) {
+ res = "/sdcard/";
+ //LOGI("android::base::GetProperty: %s -> %s", key.c_str(), res.c_str());
+ } else if (strcmp("ro.product.manufacturer", key.c_str()) == 0) {
+ res = "Xiaomi";
+ //LOGI("android::base::GetProperty: %s -> %s", key.c_str(), res.c_str());
+ } else if (strcmp("ro.product.brand", key.c_str()) == 0) {
+ res = "Xiaomi";
+ //LOGI("android::base::GetProperty: %s -> %s", key.c_str(), res.c_str());
+ } else if (strcmp("ro.product.name", key.c_str()) == 0) {
+ res = "Xiaomi";
+ //LOGI("android::base::GetProperty: %s -> %s", key.c_str(), res.c_str());
+ }
+ return res;
+}
+
+void install_hook(const char *package_name, int user) {
+ LOGI("install hook for %d:%s", user, package_name);
+
+ XHOOK_REGISTER(__system_property_get);
+
+ char sdk[PROP_VALUE_MAX + 1];
+ if (__system_property_get("ro.build.version.sdk", sdk) > 0 && atoi(sdk) >= 28) {
+ XHOOK_REGISTER(_ZN7android4base11GetPropertyERKNSt3__112basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEES9_);
+ }
+
+ if (xhook_refresh(0) == 0)
+ xhook_clear();
+ else
+ LOGE("failed to refresh hook");
+}
\ No newline at end of file
diff --git a/module/src/main/cpp/include/hook.h b/module/src/main/cpp/include/hook.h
new file mode 100644
index 0000000..c922d85
--- /dev/null
+++ b/module/src/main/cpp/include/hook.h
@@ -0,0 +1,7 @@
+#ifndef HOOK_H
+#define HOOK_H
+
+void install_hook(const char* package_name, int user);
+
+
+#endif // HOOK_H
diff --git a/module/src/main/cpp/include/logging.h b/module/src/main/cpp/include/logging.h
new file mode 100644
index 0000000..d594a48
--- /dev/null
+++ b/module/src/main/cpp/include/logging.h
@@ -0,0 +1,22 @@
+#ifndef _LOGGING_H
+#define _LOGGING_H
+
+#include "errno.h"
+#include "android/log.h"
+
+#ifndef LOG_TAG
+#define LOG_TAG "MiPushFakeForRiru"
+#endif
+
+#ifdef DEBUG
+#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
+#else
+#define LOGD(...)
+#endif
+#define LOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__)
+#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
+#define LOGW(...) __android_log_print(ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__)
+#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
+#define PLOGE(fmt, args...) LOGE(fmt " failed with %d: %s", ##args, errno, strerror(errno))
+
+#endif // _LOGGING_H
diff --git a/module/src/main/cpp/include/riru.h b/module/src/main/cpp/include/riru.h
new file mode 100644
index 0000000..4c0ab3a
--- /dev/null
+++ b/module/src/main/cpp/include/riru.h
@@ -0,0 +1,57 @@
+#ifndef RIRU_H
+#define RIRU_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+__attribute__((visibility("default"))) void riru_set_module_name(const char *name);
+
+/**
+ * Get Riru version.
+ *
+ * @return Riru version
+ */
+int riru_get_version();
+
+/*
+ * Get new_func address from last module which hook func.
+ * Use this as your old_func if you want to hook func.
+ *
+ * @param name a unique name
+ * @return new_func from last module or null
+ */
+void *riru_get_func(const char *name);
+
+/*
+ * Java native version of riru_get_func.
+ *
+ * @param className class name
+ * @param name method name
+ * @param signature method signature
+ * @return new_func address from last module or original address
+ */
+void *riru_get_native_method_func(const char *className, const char *name, const char *signature);
+
+/*
+ * Set new_func address for next module which wants to hook func.
+ *
+ * @param name a unique name
+ * @param func your new_func address
+ */
+void riru_set_func(const char *name, void *func);
+
+/*
+ * Java native method version of riru_set_func.
+ *
+ * @param className class name
+ * @param name method name
+ * @param signature method signature
+ * @param func your new_func address
+ */
+void riru_set_native_method_func(const char *className, const char *name, const char *signature,
+ void *func);
+#ifdef __cplusplus
+}
+#endif
+
+#endif
\ No newline at end of file
diff --git a/module/src/main/cpp/main.cpp b/module/src/main/cpp/main.cpp
index e075b1f..cb26901 100644
--- a/module/src/main/cpp/main.cpp
+++ b/module/src/main/cpp/main.cpp
@@ -1,7 +1,62 @@
#include
#include
+#include
+#include
+#include
+#include
+#include "logging.h"
+#include "hook.h"
+
+#if PLATFORM_SDK_VERSION >= 24
+#define CONFIG_PATH "/data/user_de/0/top.trumeet.mipush"
+#else
+#define CONFIG_PATH "/data/data/top.trumeet.mipush"
+#endif
// You can remove functions you don't need
+static char package_name[256];
+static bool enable_hook;
+static int uid;
+
+static std::vector globalPkgBlackList = {"com.xiaomi.xmsf",
+ "top.trumeet.mipush",
+ // Android
+ "com.google.android",
+ "android",
+ "com.android",
+ // Samsung
+ "com.bst",
+ "com.sec",
+ "com.sem",
+ "com.sgmc",
+ "com.dsi.ant",
+ "com.wsomacp",
+ "com.samsung",
+ "com.diotek.sec",
+ "com.enhance.gameservice",
+ // XDA
+ "com.xda",
+ // Wechat
+ "com.tencent.mm",
+ // Quickpay
+ "com.example",
+ // Magisk
+ "com.topjohnwu.magisk",
+ // Xposed
+ "org.meowcat.edxposed.manager",
+ "de.robv.android.xposed.installer",
+ // Rikka apps
+ "rikka.appops",
+ "moe.shizuku.redirectstorage"};
+
+
+bool isAppNeedHook(JNIEnv *pEnv, jstring pJstring);
+
+void injectBuild(JNIEnv *pEnv);
+
+static void appProcessPre(JNIEnv *env, jint _uid, jstring appDataDir, jstring packageName);
+
+static void appProcessPost(JNIEnv *env);
extern "C" {
#define EXPORT __attribute__((visibility("default"))) __attribute__((used))
@@ -12,11 +67,14 @@ EXPORT void nativeForkAndSpecializePre(
jstring *instructionSet, jstring *appDataDir, jstring *packageName,
jobjectArray *packagesForUID, jstring *sandboxId) {
// packageName, packagesForUID, sandboxId are added from Android Q beta 2, removed from beta 5
+
+ appProcessPre(env, *_uid, *appDataDir, *packageName);
}
EXPORT int nativeForkAndSpecializePost(JNIEnv *env, jclass clazz, jint res) {
if (res == 0) {
// in app process
+ appProcessPost(env);
} else {
// in zygote process, res is child pid
// don't print log here, see https://github.com/RikkaApps/Riru/blob/77adfd6a4a6a81bfd20569c910bc4854f2f84f5e/riru-core/jni/main/jni_native_method.cpp#L55-L66
@@ -32,11 +90,15 @@ EXPORT __attribute__((visibility("default"))) void specializeAppProcessPre(
// this is added from Android Q beta, but seems Google disabled this in following updates
// packageName, packagesForUID, sandboxId are added from Android Q beta 2, removed from beta 5
+
+ appProcessPre(env, *_uid, *appDataDir, *packageName);
}
EXPORT __attribute__((visibility("default"))) int specializeAppProcessPost(
JNIEnv *env, jclass clazz) {
// this is added from Android Q beta, but seems Google disabled this in following updates
+
+ appProcessPost(env);
return 0;
}
@@ -65,4 +127,105 @@ EXPORT int shouldSkipUid(int uid) {
EXPORT void onModuleLoaded() {
// called when the shared library of Riru core is loaded
}
+}
+
+void injectBuild(JNIEnv *env) {
+ if (env == nullptr) {
+ LOGW("failed to inject android.os.Build for %s due to env is null", package_name);
+ return;
+ }
+ LOGI("inject android.os.Build for %s ", package_name);
+
+ jclass build_class = env->FindClass("android/os/Build");
+ if (build_class == nullptr) {
+ LOGW("failed to inject android.os.Build for %s due to build is null", package_name);
+ return;
+ }
+
+ jstring new_str = env->NewStringUTF("Xiaomi");
+
+ jfieldID brand_id = env->GetStaticFieldID(build_class, "BRAND", "Ljava/lang/String;");
+ if (brand_id != nullptr) {
+ env->SetStaticObjectField(build_class, brand_id, new_str);
+ }
+
+ jfieldID manufacturer_id = env->GetStaticFieldID(build_class, "MANUFACTURER", "Ljava/lang/String;");
+ if (manufacturer_id != nullptr) {
+ env->SetStaticObjectField(build_class, manufacturer_id, new_str);
+ }
+
+ jfieldID product_id = env->GetStaticFieldID(build_class, "PRODUCT", "Ljava/lang/String;");
+ if (product_id != nullptr) {
+ env->SetStaticObjectField(build_class, product_id, new_str);
+ }
+
+ if(env->ExceptionCheck())
+ {
+ env->ExceptionClear();
+ }
+
+ env->DeleteLocalRef(new_str);
+}
+
+bool isAppNeedHook(JNIEnv *env, jstring jAppDataDir) {
+ if (jAppDataDir == nullptr) {
+ return false;
+ }
+
+ const char *appDataDir = env->GetStringUTFChars(jAppDataDir, nullptr);
+
+ int user = 0;
+ while (true) {
+ // /data/user//
+ if (sscanf(appDataDir, "/data/%*[^/]/%d/%s", &user, package_name) == 2)
+ break;
+
+ // /mnt/expand//user//
+ if (sscanf(appDataDir, "/mnt/expand/%*[^/]/%*[^/]/%d/%s", &user, package_name) == 2)
+ break;
+
+ // /data/data/
+ if (sscanf(appDataDir, "/data/%*[^/]/%s", package_name) == 1)
+ break;
+
+ package_name[0] = '\0';
+ return false;
+ }
+ env->ReleaseStringUTFChars(jAppDataDir, appDataDir);
+
+ std::string pkgName = package_name;
+ for (auto &s : globalPkgBlackList) {
+ if (pkgName.find(s) != std::string::npos) {
+ return false;
+ }
+ }
+
+ if (access(CONFIG_PATH "/packages/ALL", F_OK) == 0) {
+ return true;
+ }
+
+ if (access(CONFIG_PATH "/packages", R_OK) == 0) {
+ char path[PATH_MAX];
+ snprintf(path, PATH_MAX, CONFIG_PATH "/packages/%d.%s", user, package_name);
+ return access(path, F_OK) == 0;
+ }
+
+ return false;
+}
+
+
+
+static void appProcessPre(JNIEnv *env, jint _uid, jstring appDataDir, jstring packageName) {
+
+ uid = _uid;
+ enable_hook = isAppNeedHook(env, appDataDir);
+}
+
+
+static void appProcessPost(JNIEnv *env) {
+
+ if (enable_hook) {
+ injectBuild(env);
+ install_hook(package_name, uid / 100000);
+ }
}
\ No newline at end of file
diff --git a/module/src/main/cpp/riru.c b/module/src/main/cpp/riru.c
new file mode 100644
index 0000000..3f6079d
--- /dev/null
+++ b/module/src/main/cpp/riru.c
@@ -0,0 +1,76 @@
+#include
+#include
+#include
+
+#ifdef __LP64__
+#define LIB "/system/lib64/libmemtrack.so"
+#else
+#define LIB "/system/lib/libmemtrack.so"
+#endif
+
+static void *riru_handle;
+static char *riru_module_name;
+
+static void *get_handle() {
+ if (riru_handle == NULL)
+ riru_handle = dlopen(LIB, RTLD_NOW | RTLD_GLOBAL);
+
+ return riru_handle;
+}
+
+const char *riru_get_module_name() {
+ return riru_module_name;
+}
+
+void riru_set_module_name(const char *name) {
+ riru_module_name = strdup(name);
+}
+
+int riru_get_version() {
+ static void **sym;
+ void *handle;
+ if ((handle = get_handle()) == NULL) return -1;
+ if (sym == NULL) sym = dlsym(handle, "riru_get_version");
+ if (sym) return ((int (*)()) sym)();
+ return -1;
+}
+
+void *riru_get_func(const char *name) {
+ static void **sym;
+ void *handle;
+ if ((handle = get_handle()) == NULL) return NULL;
+ if (sym == NULL) sym = dlsym(handle, "riru_get_func");
+ if (sym) return ((void *(*)(const char *, const char *)) sym)(riru_get_module_name(), name);
+ return NULL;
+}
+
+void *riru_get_native_method_func(const char *className, const char *name, const char *signature) {
+ static void **sym;
+ void *handle;
+ if ((handle = get_handle()) == NULL) return NULL;
+ if (sym == NULL) sym = dlsym(handle, "riru_get_native_method_func");
+ if (sym)
+ return ((void *(*)(const char *, const char *, const char *, const char *)) sym)(
+ riru_get_module_name(), className, name, signature);
+ return NULL;
+}
+
+void riru_set_func(const char *name, void *func) {
+ static void **sym;
+ void *handle;
+ if ((handle = get_handle()) == NULL) return;
+ if (sym == NULL) sym = dlsym(handle, "riru_set_func");
+ if (sym)
+ ((void *(*)(const char *, const char *, void *)) sym)(riru_get_module_name(), name, func);
+}
+
+void riru_set_native_method_func(const char *className, const char *name, const char *signature,
+ void *func) {
+ static void **sym;
+ void *handle;
+ if ((handle = get_handle()) == NULL) return;
+ if (sym == NULL) sym = dlsym(handle, "riru_set_native_method_func");
+ if (sym)
+ ((void *(*)(const char *, const char *, const char *, const char *, void *)) sym)(
+ riru_get_module_name(), className, name, signature, func);
+}
\ No newline at end of file
diff --git a/settings.gradle b/settings.gradle
new file mode 100644
index 0000000..1d0cab1
--- /dev/null
+++ b/settings.gradle
@@ -0,0 +1 @@
+include ':module'
diff --git a/template/magisk_module/.gitattributes b/template/magisk_module/.gitattributes
index cba5bea..11e33e9 100644
--- a/template/magisk_module/.gitattributes
+++ b/template/magisk_module/.gitattributes
@@ -3,7 +3,7 @@ META-INF/** text eol=lf
*.prop text eol=lf
*.sh text eol=lf
*.md text eol=lf
-sepolicy.rule eol=lf
+sepolicy.rule text eol=lf
# Denote all files that are truly binary and should not be modified.
system/** binary
diff --git a/template/magisk_module/README.md b/template/magisk_module/README.md
index c316e8b..9ee9eba 100644
--- a/template/magisk_module/README.md
+++ b/template/magisk_module/README.md
@@ -1 +1,20 @@
-# Riru - Template
\ No newline at end of file
+# Riru - MiPushFake
+
+
+
+Fake as a Xiaomi device by hook system_property_get.
+
+Requires Riru - Core v19 or above installed.
+
+## What does this module do
+
+By default, `__system_property_get` (`android::base::GetProperty` on Pie+) will be hooked in packages selected in
+MiPushFramework with value map below
+
+* `ro.miui.ui.version.name` -> `V11`
+* `ro.miui.ui.version.code` -> `9`
+* `ro.miui.version.code_time` -> `1570636800`
+* `ro.miui.internal.storage` -> `/sdcard/`
+* `ro.product.manufacturer` -> `Xiaomi`
+* `ro.product.brand` -> `Xiaomi`
+* `ro.product.name` -> `Xiaomi`
\ No newline at end of file
diff --git a/template/magisk_module/customize.sh b/template/magisk_module/customize.sh
index 665eabe..a49e5a6 100644
--- a/template/magisk_module/customize.sh
+++ b/template/magisk_module/customize.sh
@@ -1,64 +1,64 @@
SKIPUNZIP=1
-RIRU_PATH="/data/misc/riru"
-RIRU_MODULES_PATH="$RIRU_PATH/modules"
-
-# check architecture
-if [[ "$ARCH" != "arm" && "$ARCH" != "arm64" && "$ARCH" != "x86" && "$ARCH" != "x64" ]]; then
- abort "! Unsupported platform: $ARCH"
-else
- ui_print "- Device platform: $ARCH"
-fi
-
-# check Riru version
-if [[ ! -f "$RIRU_PATH/api_version" ]]; then
- ui_print "*********************************************************"
- ui_print "! 'Riru - Core' is not installed"
- ui_print "! You can download from 'Magisk Manager' or https://github.com/RikkaApps/Riru/releases"
- abort "*********************************************************"
-fi
-RIRU_API_VERSION=$(cat "$RIRU_PATH/api_version")
-ui_print "- Riru API version: $RIRU_API_VERSION"
-if [[ "$RIRU_API_VERSION" -lt 4 ]]; then
- ui_print "*********************************************************"
- ui_print "! The latest version of 'Riru - Core' is required"
- ui_print "! You can download from 'Magisk Manager' or https://github.com/RikkaApps/Riru/releases"
- abort "*********************************************************"
-fi
# extract verify.sh
+ui_print "- Extracting verify.sh"
unzip -o "$ZIPFILE" 'verify.sh' -d "$TMPDIR" >&2
-if [[ ! -f "$TMPDIR/verify.sh" ]]; then
- ui_print "*********************************************************"
- ui_print "! Unable to extract verify.sh!"
- ui_print "! This zip may be corrupted, please try downloading again"
- abort "*********************************************************"
+if [ ! -f "$TMPDIR/verify.sh" ]; then
+ ui_print "*********************************************************"
+ ui_print "! Unable to extract verify.sh!"
+ ui_print "! This zip may be corrupted, please try downloading again"
+ abort_clean "*********************************************************"
fi
. $TMPDIR/verify.sh
+# extract riru.sh
+extract "$ZIPFILE" 'riru.sh' "$MODPATH"
+. $MODPATH/riru.sh
+
+check_riru_version
+check_architecture
+
# extract libs
ui_print "- Extracting module files"
-unzip -o "$ZIPFILE" 'module.prop' 'post-fs-data.sh' 'uninstall.sh' -d "$MODPATH"
-if [[ "$ARCH" == "x86" || "$ARCH" == "x64" ]]; then
- ui_print "- Extracting x86/64 libraries"
- unzip -o "$ZIPFILE" 'system_x86/*' -d "$MODPATH"
+extract "$ZIPFILE" 'module.prop' "$MODPATH"
+extract "$ZIPFILE" 'post-fs-data.sh' "$MODPATH"
+extract "$ZIPFILE" 'uninstall.sh' "$MODPATH"
+extract "$ZIPFILE" 'sepolicy.rule' "$MODPATH"
+
+if [ "$ARCH" = "x86" ] || [ "$ARCH" = "x64" ]; then
+ ui_print "- Extracting x86 libraries"
+ extract "$ZIPFILE" "system_x86/lib/libriru_$RIRU_MODULE_ID.so" "$MODPATH"
mv "$MODPATH/system_x86/lib" "$MODPATH/system/lib"
- mv "$MODPATH/system_x86/lib64" "$MODPATH/system/lib64"
+
+ if [ "$IS64BIT" = true ]; then
+ ui_print "- Extracting x64 libraries"
+ extract "$ZIPFILE" "system_x86/lib64/libriru_$RIRU_MODULE_ID.so" "$MODPATH"
+ mv "$MODPATH/system_x86/lib64" "$MODPATH/system/lib64"
+ fi
else
- ui_print "- Extracting arm/arm64 libraries"
- unzip -o "$ZIPFILE" 'system/*' -d "$MODPATH"
-fi
+ ui_print "- Extracting arm libraries"
+ extract "$ZIPFILE" "system/lib/libriru_$RIRU_MODULE_ID.so" "$MODPATH"
-if [[ "$IS64BIT" == "false" ]]; then
- ui_print "- Removing 64-bit libraries"
- rm -rf "$MODPATH/system/lib64"
+ if [ "$IS64BIT" = true ]; then
+ ui_print "- Extracting arm64 libraries"
+ extract "$ZIPFILE" "system/lib64/libriru_$RIRU_MODULE_ID.so" "$MODPATH"
+ fi
fi
# Riru files
ui_print "- Extracting extra files"
-unzip -o "$ZIPFILE" 'data/*' -d "$TMPDIR" >&2
-[[ -d "$RIRU_MODULES_PATH" ]] || mkdir -p "$RIRU_MODULES_PATH" || abort "! Can't mkdir -p $RIRU_MODULES_PATH"
-cp -af "$TMPDIR$RIRU_MODULES_PATH/." "$RIRU_MODULES_PATH" || abort "! Can't cp -af $TMPDIR$RIRU_MODULES_PATH/. $RIRU_MODULES_PATH"
+[ -d "$RIRU_MODULE_PATH" ] || mkdir -p "$RIRU_MODULE_PATH" || abort_clean "! Can't create $RIRU_MODULE_PATH"
+
+# set permission just in case
+set_perm "$RIRU_PATH" 0 0 0700
+set_perm "$RIRU_PATH/modules" 0 0 0700
+set_perm "$RIRU_MODULE_PATH" 0 0 0700
+set_perm "$RIRU_MODULE_PATH/bin" 0 0 0700
+
+rm -f "$RIRU_MODULE_PATH/module.prop.new"
+extract "$ZIPFILE" 'riru/module.prop.new' "$RIRU_MODULE_PATH" true
+set_perm "$RIRU_MODULE_PATH/module.prop.new" 0 0 0600
# set permissions
ui_print "- Setting permissions"
diff --git a/template/magisk_module/post-fs-data.sh b/template/magisk_module/post-fs-data.sh
index d326cae..e0eba33 100644
--- a/template/magisk_module/post-fs-data.sh
+++ b/template/magisk_module/post-fs-data.sh
@@ -1,12 +1,21 @@
#!/system/bin/sh
MODDIR=${0%/*}
-MODULE_ID="template"
+
+sepolicy() {
+ supolicy --live "allow coredomain coredomain process { execmem }"\
+ "allow coredomain app_data_file * *"
+}
+
+[[ -f "${MODDIR}/sepolicy.rule" ]] || sepolicy
+
+[ ! -f "$MODDIR/riru.sh" ] && exit 1
+. $MODDIR/riru.sh
# Reset context jsut in case
chcon -R u:object_r:system_file:s0 "$MODDIR"
# Rename module.prop.new
-if [ -f "/data/misc/riru/modules/$MODULE_ID/module.prop.new" ]; then
- rm "/data/misc/riru/modules/$MODULE_ID/module.prop"
- mv "/data/misc/riru/modules/$MODULE_ID/module.prop.new" "/data/misc/riru/modules/$MODULE_ID/module.prop"
+if [ -f "$RIRU_MODULE_PATH/module.prop.new" ]; then
+ rm "$RIRU_MODULE_PATH/module.prop"
+ mv "$RIRU_MODULE_PATH/module.prop.new" "$RIRU_MODULE_PATH/module.prop"
fi
\ No newline at end of file
diff --git a/template/magisk_module/riru.sh b/template/magisk_module/riru.sh
new file mode 100644
index 0000000..5cd62fc
--- /dev/null
+++ b/template/magisk_module/riru.sh
@@ -0,0 +1,36 @@
+#!/sbin/sh
+RIRU_PATH="/data/misc/riru"
+RIRU_MODULE_ID="%%%RIRU_MODULE_ID%%%"
+RIRU_MODULE_PATH="$RIRU_PATH/modules/$RIRU_MODULE_ID"
+RIRU_MIN_API_VERSION=%%%RIRU_MIN_API_VERSION%%%
+
+abort_clean() {
+ rm -rf "$MODPATH"
+ abort "$1"
+}
+
+check_riru_version() {
+ if [ ! -f "$RIRU_PATH/api_version" ] && [ ! -f "$RIRU_PATH/api_version.new" ]; then
+ ui_print "*********************************************************"
+ ui_print "! Riru is not installed"
+ ui_print "! Please install Riru (Riru - Core) from Magisk Manager or https://github.com/RikkaApps/Riru/releases"
+ abort_clean "*********************************************************"
+ fi
+ RIRU_API_VERSION=$(cat "$RIRU_PATH/api_version.new") || RIRU_API_VERSION=$(cat "$RIRU_PATH/api_version") || RIRU_API_VERSION=0
+ [ "$RIRU_API_VERSION" -eq "$RIRU_API_VERSION" ] || RIRU_API_VERSION=0
+ ui_print "- Riru API version: $RIRU_API_VERSION"
+ if [ "$RIRU_API_VERSION" -lt $RIRU_MIN_API_VERSION ]; then
+ ui_print "*********************************************************"
+ ui_print "! The latest version of Riru is required"
+ ui_print "! Please install Riru (Riru - Core) from Magisk Manager or https://github.com/RikkaApps/Riru/releases"
+ abort_clean "*********************************************************"
+ fi
+}
+
+check_architecture() {
+ if [ "$ARCH" != "arm" ] && [ "$ARCH" != "arm64" ] && [ "$ARCH" != "x86" ] && [ "$ARCH" != "x64" ]; then
+ abort_clean "! Unsupported platform: $ARCH"
+ else
+ ui_print "- Device platform: $ARCH"
+ fi
+}
\ No newline at end of file
diff --git a/template/magisk_module/sepolicy.rule b/template/magisk_module/sepolicy.rule
new file mode 100644
index 0000000..4b50d0d
--- /dev/null
+++ b/template/magisk_module/sepolicy.rule
@@ -0,0 +1,2 @@
+allow coredomain coredomain process { execmem }
+allow coredomain app_data_file * *
\ No newline at end of file
diff --git a/template/magisk_module/uninstall.sh b/template/magisk_module/uninstall.sh
index b02ad6a..3c83e2b 100644
--- a/template/magisk_module/uninstall.sh
+++ b/template/magisk_module/uninstall.sh
@@ -1,5 +1,6 @@
#!/sbin/sh
-RIRU_MODULE_PATH="/data/misc/riru/modules"
-MODULE_ID="template"
+MODDIR=${0%/*}
+[ ! -f "$MODDIR/riru.sh" ] && exit 1
+. $MODDIR/riru.sh
-rm -rf "$RIRU_MODULE_PATH/$MODULE_ID"
\ No newline at end of file
+rm -rf "$RIRU_MODULE_PATH"
\ No newline at end of file
diff --git a/template/magisk_module/verify.sh b/template/magisk_module/verify.sh
index 1baf687..b04a9ea 100644
--- a/template/magisk_module/verify.sh
+++ b/template/magisk_module/verify.sh
@@ -14,15 +14,13 @@ extract() {
file=$2
dir=$3
junk_paths=$4
+ [ -z "$junk_paths" ] && junk_paths=false
opts="-o"
- [[ $junk_paths == "true" ]] && opts="-oj"
-
- unzip $opts "$zip" "$file" -d "$dir" >&2
- [[ -f $file_path ]] || abort_verify "$file not exists"
+ [ $junk_paths = true ] && opts="-oj"
file_path=""
hash_path=""
- if [[ $junk_paths == "true" ]]; then
+ if [ $junk_paths = true ]; then
file_path="$dir/$(basename "$file")"
hash_path="$TMPDIR_FOR_VERIFY/$(basename "$file").sha256sum"
else
@@ -30,8 +28,11 @@ extract() {
hash_path="$TMPDIR_FOR_VERIFY/$file.sha256sum"
fi
+ unzip $opts "$zip" "$file" -d "$dir" >&2
+ [ -f "$file_path" ] || abort_verify "$file not exists"
+
unzip $opts "$zip" "$file.sha256sum" -d "$TMPDIR_FOR_VERIFY" >&2
- [[ -f $hash_path ]] || abort_verify "$file.sha256sum not exists"
+ [ -f "$hash_path" ] || abort_verify "$file.sha256sum not exists"
(echo "$(cat "$hash_path") $file_path" | sha256sum -c -s -) || abort_verify "Failed to verify $file"
ui_print "- Verified $file" >&1