diff --git a/subprojects/libcapsule/.gitignore b/subprojects/libcapsule/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..9a9dad7d0c94acd774a578830e83a784e3c306f2 --- /dev/null +++ b/subprojects/libcapsule/.gitignore @@ -0,0 +1,68 @@ +.deps +/.version +/INSTALL +/build-aux/* +/data/*-linux-gnu*-capsule-mkstublib +/m4/* +/test-suite.log +/tests/*.log +/tests/*.t +/tests/*.test +/tests/*.trs +/tests/notgl-dlopener +/tests/notgl-helper-user +/tests/notgl-user +/tests/shim/libnotgl.so.0.c +/tests/shim/libnotgles.so.1.c +Makefile.in +aclocal.m4 +autom4te.cache +config.status +config.log +configure +libtool +update +Makefile +.*stamp +.libs +*.la +*.lo +*.o +shim/*.so.c +shim/*.so.symbols +shim/*.so.map +/capsule-capture-libs +/capsule-elf-dump +print-libstubs +capsule-symbols +capsule-version +TAGS +tags +*~ +data/*.pc +data/dirconf.txt +*.swp +*.tmp +install.log +*.1 +*.3 +*-docs.xml +*.types +gtk-doc.make +*.tar.* +*.tgz +*.tar +tmpl/ +*.stamp +html/ +xml/ +libcapsule-*.txt +*.args +*.hierarchy +*.interfaces +*.prerequisites +*.signals + +!/build-aux/git-version-gen +!/m4/ax_is_release.m4 +!/m4/capsule_*.m4 diff --git a/subprojects/libcapsule/AUTHORS b/subprojects/libcapsule/AUTHORS new file mode 100644 index 0000000000000000000000000000000000000000..be5806f634a07d413e5de15db2dbc172451545bf --- /dev/null +++ b/subprojects/libcapsule/AUTHORS @@ -0,0 +1 @@ +Vivek Das Mohapatra <vivek@collabora.com>; <vivek@etla.org> diff --git a/subprojects/libcapsule/COPYING b/subprojects/libcapsule/COPYING new file mode 100644 index 0000000000000000000000000000000000000000..4362b49151d7b34ef83b3067a8f9c9f877d72a0e --- /dev/null +++ b/subprojects/libcapsule/COPYING @@ -0,0 +1,502 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser 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 Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "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 +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY 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 +LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + <one line to give the library's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + <signature of Ty Coon>, 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! diff --git a/subprojects/libcapsule/Makefile.am b/subprojects/libcapsule/Makefile.am new file mode 100644 index 0000000000000000000000000000000000000000..736f26923bf4c48a1f9b85389a6d3fa1cea38673 --- /dev/null +++ b/subprojects/libcapsule/Makefile.am @@ -0,0 +1,591 @@ +# Copyright © 2017 Collabora Ltd + +# This file is part of libcapsule. + +# libcapsule is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as +# published by the Free Software Foundation; either version 2.1 of the +# License, or (at your option) any later version. + +# libcapsule 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 Lesser General Public License for more details. + +# You should have received a copy of the GNU Lesser General Public +# License along with libcapsule. If not, see <http://www.gnu.org/licenses/>. + +AUTOMAKE_OPTIONS = subdir-objects +ACLOCAL_AMFLAGS = -I m4 --install +AM_CPPFLAGS = -DHOST_PREFIX=\"$(HOST_PREFIX)\" +common_cflags = -fvisibility=hidden \ + --std=c99 \ + $(WARN_CFLAGS) \ + $(LIBELF_CFLAGS) \ + $(GLIB_CFLAGS) +# Used for production code and tests, but not for test fixtures +AM_CFLAGS = $(common_cflags) $(SANITIZE_CFLAGS) +# Used for test fixtures only +test_fixture_cflags = $(common_cflags) +AM_LDFLAGS = $(WARN_LDFLAGS) +static_docs = Building Debugging Limitations Quick-Start Capsules + +# installable items: +bin_PROGRAMS = capsule-capture-libs capsule-symbols capsule-version +if ENABLE_LIBRARY +dist_bin_SCRIPTS = data/capsule-init-project +lib_LTLIBRARIES = libcapsule.la +pkgconfigdir = $(libdir)/pkgconfig +pkgconfig_DATA = data/libcapsule.pc data/libcapsule-tools.pc +nobase_include_HEADERS = capsule/capsule.h +dist_doc_DATA = $(foreach doc,$(static_docs),doc/$(doc).txt) +endif + +if ENABLE_TOOLS_RPATH +# Use -Wl to hide it from libtool +tools_ldflags = -Wl,-rpath,$(enable_tools_rpath) +endif + +if ENABLE_HOST_PREFIX +if ENABLE_LIBRARY +bin_SCRIPTS = data/$(HOST_PREFIX)capsule-mkstublib + +# Write out a version of capsule-mkstublib with different defaults +# for each architecture +data/$(HOST_PREFIX)capsule-mkstublib: data/capsule-mkstublib Makefile + $(AM_V_GEN)set -e; \ + ( \ + sed -e 1q $<; \ + echo '# Inserted by Makefile to make this architecture-specific'; \ + echo ': "$${CAPSULE_SYMBOLS_TOOL:="$(bindir)/$(HOST_PREFIX)capsule-symbols"}"';\ + sed -e 1d $<; \ + ) > $@.tmp; chmod +x $@.tmp; mv $@.tmp $@ +endif + +install-exec-hook: install-exec-hook-host-prefix +install-exec-hook-host-prefix: + cd $(DESTDIR)$(bindir) && ln -f capsule-symbols $(HOST_PREFIX)capsule-symbols + cd $(DESTDIR)$(bindir) && ln -f capsule-version $(HOST_PREFIX)capsule-version + cd $(DESTDIR)$(bindir) && ln -f capsule-elf-dump $(HOST_PREFIX)capsule-elf-dump + cd $(DESTDIR)$(bindir) && ln -f capsule-capture-libs $(HOST_PREFIX)capsule-capture-libs + +uninstall-hook: uninstall-hook-host-prefix +uninstall-hook-host-prefix: + cd $(DESTDIR)$(bindir) && rm -f $(HOST_PREFIX)capsule-capture-libs + cd $(DESTDIR)$(bindir) && rm -f $(HOST_PREFIX)capsule-elf-dump + cd $(DESTDIR)$(bindir) && rm -f $(HOST_PREFIX)capsule-symbols + cd $(DESTDIR)$(bindir) && rm -f $(HOST_PREFIX)capsule-version +else +if ENABLE_LIBRARY +dist_bin_SCRIPTS += data/capsule-mkstublib +endif +endif + +if ENABLE_LIBRARY +# Headers to be included by shim libraries +nobase_include_HEADERS += capsule/capsule-shim.h \ + capsule/_int_dlopen.h +endif + +noinst_LTLIBRARIES = utils/libutils.la \ + utils/libld.la + +utils_libutils_la_SOURCES = utils/utils.c utils/utils.h +utils_libld_la_SOURCES = utils/ld-cache.c utils/ld-cache.h +utils_libld_la_SOURCES += utils/ld-libs.c utils/ld-libs.h +utils_libld_la_LIBADD = utils/libutils.la -ldl $(LIBELF_LIBS) + +# Mostly just a demo program, but keep this hanging around as it's a useful PoC +bin_PROGRAMS += capsule-elf-dump + +capsule_elf_dump_SOURCES = utils/elf-dump.c utils/dump.c +capsule_elf_dump_SOURCES += utils/tools.c utils/tools.h +capsule_elf_dump_LDADD = utils/libutils.la -ldl +# This looks redundant, but it forces Automake to build separate +# instantiations of dump.o for elf-dump and libcapsule +capsule_elf_dump_CFLAGS = $(AM_CFLAGS) +capsule_elf_dump_LDFLAGS = $(AM_LDFLAGS) $(tools_ldflags) + +capsule_capture_libs_SOURCES = utils/capture-libs.c \ + utils/library-cmp.c \ + utils/library-cmp.h \ + utils/tools.c \ + utils/tools.h +capsule_capture_libs_LDADD = utils/libld.la +capsule_capture_libs_LDFLAGS = $(AM_LDFLAGS) $(tools_ldflags) + +# now the rules for building our installables: +capsule_symbols_SOURCES = utils/print-libstubs.c utils/tools.c utils/tools.h +capsule_symbols_LDADD = utils/libld.la +capsule_symbols_LDFLAGS = $(AM_LDFLAGS) $(tools_ldflags) + +capsule_version_SOURCES = utils/print-version.c utils/tools.c utils/tools.h +capsule_version_LDADD = utils/libld.la +capsule_version_LDFLAGS = $(AM_LDFLAGS) $(tools_ldflags) + +if ENABLE_LIBRARY +libcapsule_la_SOURCES = capsule/capsule-dlmopen.c \ + capsule/capsule-relocate.c \ + capsule/capsule-init.c \ + capsule/capsule-private.h \ + capsule/capsule-malloc.h \ + capsule/capsule-wrappers.c \ + utils/dump.c \ + utils/process-pt-dynamic.c \ + utils/mmap-info.c +libcapsule_la_LDFLAGS = -version-number 0:1 -Wl,-z,nodelete +libcapsule_la_LIBADD = utils/libld.la +libcapsule_la_SOURCES += utils/debug.h \ + utils/dump.h \ + utils/process-pt-dynamic.h \ + utils/mmap-info.h + +dist_pkgdata_DATA = data/capsule-shim.mk \ + data/disabled.mk \ + data/shim-.gitignore \ + data/shim-Makefile.am \ + data/shim-README.in \ + data/shim-configure.ac.in \ + data/shim-target-Makefile.am +endif + +BUILT_SOURCES = $(top_srcdir)/.version + +$(top_srcdir)/.version: + $(AM_V_GEN)echo $(VERSION) > $@-t && mv $@-t $@ + +include $(top_srcdir)/data/disabled.mk + +# ============================================================================ +# GNOME-style installed-tests + +insttestsdir = $(libexecdir)/installed-tests/$(PACKAGE_TARNAME) +nobase_insttests_LTLIBRARIES = + +if ENABLE_LIBRARY +# /!\ Order is important here: the helper library must come first +nobase_insttests_LTLIBRARIES += tests/red/libhelper.la \ + tests/green/libhelper.la \ + tests/helper/libhelper.la \ + tests/red/libnotgl.la \ + tests/red/libnotgles.la \ + tests/green/libnotgl.la \ + tests/green/libnotgles.la \ + tests/lib/libnotgl.la \ + tests/lib/libnotgles.la \ + tests/shim/libnotgl.la \ + tests/shim/libnotgles.la +nobase_dist_insttests_DATA = tests/CapsuleTest.pm \ + tests/CapsuleTestDpkg.pm \ + tests/notgl.h \ + tests/notgl-green.c \ + tests/notgl-red.c \ + tests/notgl-ref.c \ + tests/notgl-helper.h \ + tests/notgl-helper-green.c \ + tests/notgl-helper-red.c \ + tests/notgl-helper-ref.c \ + tests/notgles.h \ + tests/notgles-green.c \ + tests/notgles-red.c \ + tests/notgles-ref.c \ + tests/shim/libnotgl.so.0.excluded \ + tests/shim/libnotgl.so.0.shared \ + tests/shim/libnotgl.so.0.symbols \ + tests/shim/libnotgles.so.1.excluded \ + tests/shim/libnotgles.so.1.shared \ + tests/shim/libnotgles.so.1.symbols +# Install the source code so we can rebuild it inside a container if we want to +gl_shims = libEGL.so.1 \ + libGL.so.1 \ + libGLESv2.so.2 \ + libGLX.so.0 \ + libOpenGL.so.0 \ + libX11.so.6 \ + libXext.so.6 \ + libXi.so.6 \ + libgbm.so.1 \ + libxcb-dri2.so.0 \ + libxcb-glx.so.0 \ + libxcb-present.so.0 \ + libxcb-sync.so.1 \ + libxcb.so.1 +# We don't do this via nobase_dist_insttests_DATA because patsubst doesn't +# work for make dist, so we would have to write it out longhand +install-data-local-shim-sources: + $(AM_V_GEN)set -e; \ + $(mkinstalldirs) $(DESTDIR)$(insttestsdir)/examples/shim; \ + for lib in ${gl_shims}; do \ + $(INSTALL_DATA) \ + $(srcdir)/examples/shim/$$lib.excluded \ + $(srcdir)/examples/shim/$$lib.shared \ + $(srcdir)/examples/shim/$$lib.symbols \ + $(srcdir)/examples/shim/$$lib.symbols.updated-for \ + $(DESTDIR)$(insttestsdir)/examples/shim; \ + done +uninstall-hook: uninstall-hook-shim-sources +uninstall-hook-shim-sources: + $(AM_V_GEN)set -e; \ + for lib in ${gl_shims}; do \ + rm -fr \ + $(DESTDIR)$(insttestsdir)/examples/shim/$$lib.excluded \ + $(DESTDIR)$(insttestsdir)/examples/shim/$$lib.shared \ + $(DESTDIR)$(insttestsdir)/examples/shim/$$lib.symbols \ + $(DESTDIR)$(insttestsdir)/examples/shim/$$lib.symbols.updated-for \ + ; \ + done +dist-hook-shim-sources: + $(AM_V_GEN)set -e; \ + $(MKDIR_P) $(distdir)/examples/shim; \ + for lib in ${gl_shims}; do \ + cp \ + $(srcdir)/examples/shim/$$lib.excluded \ + $(srcdir)/examples/shim/$$lib.shared \ + $(srcdir)/examples/shim/$$lib.symbols \ + $(srcdir)/examples/shim/$$lib.symbols.updated-for \ + $(distdir)/examples/shim; \ + done +endif + +maintainer-update-gl-shims: + @if test -z "${GL_SHIMS_SOURCE_MACHINE}"; then \ + echo "usage: make -C $(CURDIR) maintainer-update-gl-shims GL_SHIMS_SOURCE_MACHINE=some-machine">&2; \ + exit 2; \ + fi + set -e; for lib in ${gl_shims}; do \ + ssh "${GL_SHIMS_SOURCE_MACHINE}" \ + x86_64-linux-gnu-capsule-symbols "$$lib" \ + > $(abs_top_srcdir)/examples/shim/"$$lib.symbols.tmp"; \ + LC_ALL=C sort -u \ + < $(abs_top_srcdir)/examples/shim/"$$lib.symbols.tmp" \ + > $(abs_top_srcdir)/examples/shim/"$$lib.symbols"; \ + rm -f $(abs_top_srcdir)/examples/shim/"$$lib.symbols.tmp"; \ + : > $(abs_top_srcdir)/examples/shim/"$$lib.excluded"; \ + : > $(abs_top_srcdir)/examples/shim/"$$lib.shared"; \ + : > $(abs_top_srcdir)/examples/shim/"$$lib.symbols.updated-for"; \ + done + +if ENABLE_LIBRARY +tests_lib_libnotgl_la_SOURCES = tests/notgl-ref.c \ + tests/notgl.h \ + tests/notgl-helper.h +tests_lib_libnotgl_la_CFLAGS = $(test_fixture_cflags) +tests_lib_libnotgl_la_LDFLAGS = -shared -version-number 0:42:23 +tests_lib_libnotgl_la_LIBADD = tests/helper/libhelper.la + +tests_lib_libnotgles_la_SOURCES = tests/notgles-ref.c \ + tests/notgles.h \ + tests/notgl-helper.h +tests_lib_libnotgles_la_CFLAGS = $(test_fixture_cflags) +tests_lib_libnotgles_la_LDFLAGS = -shared -version-number 1:42:23 +tests_lib_libnotgles_la_LIBADD = tests/helper/libhelper.la + +tests_red_libnotgl_la_SOURCES = tests/notgl-red.c \ + tests/notgl.h \ + tests/notgl-helper.h +tests_red_libnotgl_la_CFLAGS = $(test_fixture_cflags) +# Give it a gratuitously higher version number +tests_red_libnotgl_la_LDFLAGS = -shared -version-number 0:123:456 +# We want the RUNPATH or RPATH to point to tests/helper here, so we can +# demonstrate the functionality of loading two different libraries from the +# same path on host and container +tests_red_libnotgl_la_LIBADD = tests/helper/libhelper.la + +tests_red_libnotgles_la_SOURCES = tests/notgles-red.c \ + tests/notgles.h \ + tests/notgl-helper.h +tests_red_libnotgles_la_CFLAGS = $(test_fixture_cflags) +tests_red_libnotgles_la_LDFLAGS = -shared -version-number 1:123:456 +tests_red_libnotgles_la_LIBADD = tests/helper/libhelper.la + +tests_green_libnotgl_la_SOURCES = tests/notgl-green.c \ + tests/notgl.h \ + tests/notgl-helper.h +tests_green_libnotgl_la_CFLAGS = $(test_fixture_cflags) +# Give this one a gratuitously *lower* version number +tests_green_libnotgl_la_LDFLAGS = -shared -version-number 0:23:42 +# Again, we want the RUNPATH or RPATH to point to tests/helper here +tests_green_libnotgl_la_LIBADD = tests/helper/libhelper.la + +tests_green_libnotgles_la_SOURCES = tests/notgles-green.c \ + tests/notgles.h \ + tests/notgl-helper.h +tests_green_libnotgles_la_CFLAGS = $(test_fixture_cflags) +tests_green_libnotgles_la_LDFLAGS = -shared -version-number 1:23:42 +tests_green_libnotgles_la_LIBADD = tests/helper/libhelper.la + +tests_helper_libhelper_la_SOURCES = tests/notgl-helper-ref.c \ + tests/notgl-helper.h +tests_helper_libhelper_la_CFLAGS = $(test_fixture_cflags) +tests_helper_libhelper_la_LDFLAGS = -shared -version-number 0:42:23 + +tests_red_libhelper_la_SOURCES = tests/notgl-helper-red.c \ + tests/notgl-helper.h +tests_red_libhelper_la_CFLAGS = $(test_fixture_cflags) +tests_red_libhelper_la_LDFLAGS = -shared -version-number 0:123:456 + +tests_green_libhelper_la_SOURCES = tests/notgl-helper-green.c \ + tests/notgl-helper.h +tests_green_libhelper_la_CFLAGS = $(test_fixture_cflags) +tests_green_libhelper_la_LDFLAGS = -shared -version-number 0:23:42 + +nodist_tests_shim_libnotgl_la_SOURCES = tests/shim/libnotgl.so.0.c +tests_shim_libnotgl_la_LIBADD = libcapsule.la +tests_shim_libnotgl_la_CFLAGS = $(test_fixture_cflags) +tests_shim_libnotgl_la_LDFLAGS = -shared -version-number 0:0:0 + +nodist_tests_shim_libnotgles_la_SOURCES = tests/shim/libnotgles.so.1.c +tests_shim_libnotgles_la_LIBADD = libcapsule.la +tests_shim_libnotgles_la_CFLAGS = $(test_fixture_cflags) +tests_shim_libnotgles_la_LDFLAGS = -shared -version-number 1:0:0 + +tests/shim/%.c: tests/shim/%.excluded tests/shim/%.shared tests/shim/%.symbols data/capsule-mkstublib Makefile + $(AM_V_GEN)\ + PKG_CONFIG_PATH=$(abs_builddir)/data \ + V=$V \ + $(top_srcdir)/data/capsule-mkstublib \ + --symbols-from=$(srcdir)/tests/shim/$*.symbols \ + --no-update-symbols \ + --runtime-tree=/host \ + $* \ + $(srcdir)/tests/shim/$*.excluded \ + $(srcdir)/tests/shim/$*.shared \ + $@ +endif + +if ENABLE_SHARED +AM_CPPFLAGS += -DENABLE_SHARED +nobase_insttests_LTLIBRARIES += tests/version1/libunversionedabibreak.la \ + tests/version2/libunversionedabibreak.la \ + tests/version1/libunversionednumber.la \ + tests/version2/libunversionednumber.la \ + tests/version1/libunversionedsymbols.la \ + tests/version2/libunversionedsymbols.la \ + tests/version1/libversionedabibreak.la \ + tests/version2/libversionedabibreak.la \ + tests/version1/libversionedlikedbus.la \ + tests/version2/libversionedlikedbus.la \ + tests/version1/libversionedlikeglibc.la \ + tests/version2/libversionedlikeglibc.la \ + tests/version1/libversionednumber.la \ + tests/version2/libversionednumber.la \ + tests/version1/libversionedprivatenothidden.la \ + tests/version2/libversionedprivatenothidden.la \ + tests/version1/libversionedsymbols.la \ + tests/version2/libversionedsymbols.la \ + tests/version1/libversionedupgrade.la \ + tests/version2/libversionedupgrade.la +tests_version1_libunversionedabibreak_la_CFLAGS = $(test_fixture_cflags) +tests_version1_libunversionedabibreak_la_LDFLAGS = -shared -version-number 1:0:0 +tests_version2_libunversionedabibreak_la_CFLAGS = $(test_fixture_cflags) +tests_version2_libunversionedabibreak_la_LDFLAGS = -shared -version-number 1:0:0 +tests_version1_libunversionednumber_la_CFLAGS = $(test_fixture_cflags) +tests_version1_libunversionednumber_la_LDFLAGS = -shared -version-number 1:0:0 +tests_version2_libunversionednumber_la_CFLAGS = $(test_fixture_cflags) +tests_version2_libunversionednumber_la_LDFLAGS = -shared -version-number 1:2:3 +tests_version1_libunversionedsymbols_la_CFLAGS = $(test_fixture_cflags) +tests_version1_libunversionedsymbols_la_LDFLAGS = -shared -version-number 1:0:0 +tests_version2_libunversionedsymbols_la_CFLAGS = $(test_fixture_cflags) +tests_version2_libunversionedsymbols_la_LDFLAGS = -shared -version-number 1:0:0 +tests_version1_libversionedabibreak_la_CFLAGS = $(test_fixture_cflags) +tests_version1_libversionedabibreak_la_LDFLAGS = -shared -version-number 1:0:0 \ + -Wl,--version-script=$(srcdir)/tests/version1/libversionedabibreak.map +tests_version2_libversionedabibreak_la_CFLAGS = $(test_fixture_cflags) +tests_version2_libversionedabibreak_la_LDFLAGS = -shared -version-number 1:0:0 \ + -Wl,--version-script=$(srcdir)/tests/version2/libversionedabibreak.map +tests_version1_libversionedlikedbus_la_CFLAGS = $(test_fixture_cflags) +tests_version1_libversionedlikedbus_la_LDFLAGS = -shared -version-number 1:0:0 \ + -Wl,--version-script=$(srcdir)/tests/version1/libversionedlikedbus.map +tests_version2_libversionedlikedbus_la_CFLAGS = $(test_fixture_cflags) +tests_version2_libversionedlikedbus_la_LDFLAGS = -shared -version-number 1:2:0 \ + -Wl,--version-script=$(srcdir)/tests/version2/libversionedlikedbus.map +tests_version1_libversionedlikeglibc_la_CFLAGS = $(test_fixture_cflags) +tests_version1_libversionedlikeglibc_la_LDFLAGS = -shared -version-number 1:0:0 \ + -Wl,--version-script=$(srcdir)/tests/version1/libversionedlikeglibc.map +tests_version2_libversionedlikeglibc_la_CFLAGS = $(test_fixture_cflags) +tests_version2_libversionedlikeglibc_la_LDFLAGS = -shared -version-number 1:0:0 \ + -Wl,--version-script=$(srcdir)/tests/version2/libversionedlikeglibc.map +tests_version1_libversionednumber_la_CFLAGS = $(test_fixture_cflags) +tests_version1_libversionednumber_la_LDFLAGS = -shared -version-number 1:0:0 \ + -Wl,--version-script=$(srcdir)/tests/libversioned.map +tests_version2_libversionednumber_la_CFLAGS = $(test_fixture_cflags) +tests_version2_libversionednumber_la_LDFLAGS = -shared -version-number 1:2:3 \ + -Wl,--version-script=$(srcdir)/tests/libversioned.map +tests_version1_libversionedprivatenothidden_la_CFLAGS = $(test_fixture_cflags) +tests_version1_libversionedprivatenothidden_la_LDFLAGS = -shared -version-number 1:1:1 \ + -Wl,--version-script=$(srcdir)/tests/version1/libversionedprivatenothidden.map +tests_version2_libversionedprivatenothidden_la_CFLAGS = $(test_fixture_cflags) +tests_version2_libversionedprivatenothidden_la_LDFLAGS = -shared -version-number 1:1:1 \ + -Wl,--version-script=$(srcdir)/tests/version2/libversionedprivatenothidden.map +tests_version1_libversionedsymbols_la_CFLAGS = $(test_fixture_cflags) +tests_version1_libversionedsymbols_la_LDFLAGS = -shared -version-number 1:0:0 \ + -Wl,--version-script=$(srcdir)/tests/libversioned.map +tests_version2_libversionedsymbols_la_CFLAGS = $(test_fixture_cflags) +tests_version2_libversionedsymbols_la_LDFLAGS = -shared -version-number 1:0:0 \ + -Wl,--version-script=$(srcdir)/tests/libversioned.map +tests_version1_libversionedupgrade_la_CFLAGS = $(test_fixture_cflags) +tests_version1_libversionedupgrade_la_LDFLAGS = -shared -version-number 1:0:0 \ + -Wl,--version-script=$(srcdir)/tests/version1/libversionedupgrade.map +tests_version2_libversionedupgrade_la_CFLAGS = $(test_fixture_cflags) +tests_version2_libversionedupgrade_la_LDFLAGS = -shared -version-number 1:0:0 \ + -Wl,--version-script=$(srcdir)/tests/version2/libversionedupgrade.map +endif + +LOG_DRIVER = env AM_TAP_AWK='$(AWK)' \ + $(SHELL) $(top_srcdir)/build-aux/tap-driver.sh +LOG_COMPILER = +TEST_EXTENSIONS = .pl .t +PL_LOG_DRIVER = $(LOG_DRIVER) +PL_LOG_COMPILER = $(PERL) +T_LOG_DRIVER = $(LOG_DRIVER) +T_LOG_COMPILER = $(SHELL) $(top_srcdir)/tests/glib-tap.sh +AM_TESTS_ENVIRONMENT = export CAPSULE_CAPTURE_LIBS_TOOL="$(abs_builddir)/capsule-capture-libs"; \ + export CAPSULE_CFLAGS="-I$(abs_top_srcdir) -I$(abs_top_builddir)"; \ + export CAPSULE_LIBS="$(abs_top_builddir)/libcapsule.la"; \ + export CAPSULE_SYMBOLS_TOOL="$(abs_builddir)/capsule-symbols";\ + export CAPSULE_TESTS_GNU_BUILD="${build}"; \ + export CAPSULE_TESTS_GNU_HOST="${host}"; \ + export CAPSULE_TESTS_UNINSTALLED=1; \ + export CAPSULE_VERSION_TOOL="$(abs_builddir)/capsule-version";\ + export CC="${CC}"; \ + export G_TEST_SRCDIR="$(abs_srcdir)"; \ + export G_TEST_BUILDDIR="$(abs_builddir)";\ + export G_DEBUG=gc-friendly; \ + export MALLOC_CHECK_=2; \ + export NM="$(NM)"; \ + export PKG_CONFIG_PATH="$(abs_builddir)/data"; +if ENABLE_LIBRARY +test_extra_programs = tests/notgl-user \ + tests/notgl-helper-user \ + tests/notgl-dlopener +tests_notgl_user_LDADD = tests/lib/libnotgl.la +tests_notgl_helper_user_LDADD = tests/lib/libnotgl.la \ + tests/lib/libnotgles.la \ + tests/helper/libhelper.la +tests_notgl_dlopener_LDADD = -ldl +endif +test_extra_scripts = tests/manual/gl.pl + +test_programs = +if HAVE_GLIB +test_programs += tests/utils.t +endif + +tests_utils_t_SOURCES = tests/utils.c \ + tests/test-helpers.c \ + tests/test-helpers.h \ + utils/library-cmp.c \ + utils/library-cmp.h +tests_utils_t_LDADD = utils/libutils.la $(GLIB_LIBS) $(LIBELF_LIBS) + +test_scripts = tests/capture-libs.pl \ + tests/symbols.pl \ + tests/version.pl + +if ENABLE_LIBRARY +test_scripts += tests/gl-shim.pl \ + tests/init-project.pl \ + tests/notgl.pl +endif + +nobase_insttests_PROGRAMS = $(test_programs) \ + $(test_extra_programs) +nobase_dist_insttests_SCRIPTS = $(test_scripts) \ + $(test_extra_scripts) +TESTS = $(test_programs) \ + $(test_scripts) +testmetadir = $(datadir)/installed-tests/$(PACKAGE_TARNAME) +testmeta_DATA = $(patsubst %,%.test,$(test_programs) $(test_scripts)) +$(testmeta_DATA): tests/%.test: tests/% Makefile + @$(MKDIR_P) $(dir $@) + $(AM_V_GEN)set -e; \ + maybe_tap=; \ + case "$*" in \ + (*.t) \ + maybe_tap=" --tap"; \ + ;; \ + esac; \ + (\ + echo "[Test]"; \ + echo "Type=session"; \ + echo "Output=TAP"; \ + echo "Exec=env CAPSULE_TESTS_GNU_HOST=$(host) $(insttestsdir)/tests/$*$$maybe_tap"; \ + ) > $@.tmp && mv $@.tmp $@ + +if ENABLE_LIBRARY +# ============================================================================ +# gtk-doc configuration: see /usr/share/doc/gtk-doc-tools/examples/Makefile.am +DOC_MODULE = libcapsule +DOC_MAIN_SGML_FILE = $(DOC_MODULE)-docs.xml +DOC_SOURCE_DIR = $(top_srcdir)/capsule +SCAN_OPTIONS = --rebuild-sections --rebuild-types +MKDB_OPTIONS = --xml-mode --output-format=xml +MKTMPL_OPTIONS = +MKHTML_OPTIONS = +FIXXREF_OPTIONS = +HFILE_GLOB = $(top_srcdir)/capsule/capsule.h +CFILE_GLOB = $(top_srcdir)/capsule/*.c +EXTRA_HFILES = +IGNORE_HFILES = +HTML_IMAGES = +content_files = +expand_content_files = +GTKDOC_CFLAGS = +GTKDOC_LIBS = + +include $(top_srcdir)/gtk-doc.make +# standalone man pages +include $(top_srcdir)/documentation.mk + +# Workaround for https://bugzilla.gnome.org/show_bug.cgi?id=786174 +$(DOC_MAIN_SGML_FILE): sgml-build.stamp + @true +else +CLEANFILES = +EXTRA_DIST = +endif + +# These need to come after we include gtk-doc.make, which unconditionally +# defines these variables and targets +dist-hook: dist-hook-git-version-gen +.PHONY: dist-hook-git-version-gen + +dist-hook-git-version-gen: + $(AM_V_GEN)echo $(VERSION) > $(distdir)/.tarball-version + +dist-hook: dist-hook-shim-sources +.PHONY: dist-hook-shim-sources + +install-data-local: install-data-local-shim-sources +.PHONY: install-data-local-shim-sources + +CLEANFILES += $(testmeta_DATA) +if ENABLE_HOST_PREFIX +CLEANFILES += data/$(HOST_PREFIX)capsule-mkstublib +endif +CLEANFILES += $(nodist_tests_shim_libnotgl_la_SOURCES) +CLEANFILES += $(nodist_tests_shim_libnotgles_la_SOURCES) + +EXTRA_DIST += $(top_srcdir)/.version +EXTRA_DIST += autogen.sh +EXTRA_DIST += build-aux/git-version-gen +EXTRA_DIST += ci/Jenkinsfile +EXTRA_DIST += ci/gitlab-ci.yml +EXTRA_DIST += data/capsule-mkstublib +EXTRA_DIST += doc/Building-libcapsule.txt +EXTRA_DIST += doc/Strategy.txt +EXTRA_DIST += doc/capsule-init-project.xml +EXTRA_DIST += doc/capsule-mkstublib.xml +EXTRA_DIST += doc/devhelp2man.xslt +EXTRA_DIST += tests/glib-tap.sh +EXTRA_DIST += tests/libversioned.map +EXTRA_DIST += tests/version1/libversionedabibreak.map +EXTRA_DIST += tests/version1/libversionedlikedbus.map +EXTRA_DIST += tests/version1/libversionedlikeglibc.map +EXTRA_DIST += tests/version1/libversionedprivatenothidden.map +EXTRA_DIST += tests/version1/libversionedupgrade.map +EXTRA_DIST += tests/version2/libversionedabibreak.map +EXTRA_DIST += tests/version2/libversionedlikedbus.map +EXTRA_DIST += tests/version2/libversionedlikeglibc.map +EXTRA_DIST += tests/version2/libversionedprivatenothidden.map +EXTRA_DIST += tests/version2/libversionedupgrade.map diff --git a/subprojects/libcapsule/NEWS b/subprojects/libcapsule/NEWS new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/subprojects/libcapsule/autogen.sh b/subprojects/libcapsule/autogen.sh new file mode 100755 index 0000000000000000000000000000000000000000..7ed39708c90ec464ba5638767265886426e896b3 --- /dev/null +++ b/subprojects/libcapsule/autogen.sh @@ -0,0 +1,31 @@ +#!/bin/sh + +set -e + +# Run this to generate all the initial makefiles, etc. + +[ -n "$srcdir" ] || srcdir=$(dirname "$0"); +[ -n "$srcdir" ] || srcdir=.; + +( cd $srcdir + + if [ "$#" = 0 -a "x$NOCONFIGURE" = "x" ]; + then + (echo "*** WARNING: Running 'configure' with no arguments."; + echo "*** configure arguments should be passed as:" ; + echo "*** $0 ARGS..." ; + echo "" ) >&2; + fi; + aclocal -I m4 --install || exit $?; + gtkdocize || exit $?; + autoreconf -ivf || exit $?; ); + +if [ "$NOCONFIGURE" = "" ]; +then + $srcdir/configure "$@" || exit $?; + + if [ "$1" = "--help" ]; then exit 0; fi; + echo "Now type 'make' to compile"; +else + echo "Skipping configure process."; +fi diff --git a/subprojects/libcapsule/build-aux/git-version-gen b/subprojects/libcapsule/build-aux/git-version-gen new file mode 100755 index 0000000000000000000000000000000000000000..0e254f4bef10ae712755cbb0bee8211b720b1b1e --- /dev/null +++ b/subprojects/libcapsule/build-aux/git-version-gen @@ -0,0 +1,227 @@ +#!/bin/sh +# Print a version string. +scriptversion=2017-09-13.06; # UTC + +# Copyright (C) 2007-2017 Free Software Foundation, Inc. +# +# 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 <https://www.gnu.org/licenses/>. + +# This script is derived from GIT-VERSION-GEN from GIT: https://git-scm.com/. +# It may be run two ways: +# - from a git repository in which the "git describe" command below +# produces useful output (thus requiring at least one signed tag) +# - from a non-git-repo directory containing a .tarball-version file, which +# presumes this script is invoked like "./git-version-gen .tarball-version". + +# In order to use intra-version strings in your project, you will need two +# separate generated version string files: +# +# .tarball-version - present only in a distribution tarball, and not in +# a checked-out repository. Created with contents that were learned at +# the last time autoconf was run, and used by git-version-gen. Must not +# be present in either $(srcdir) or $(builddir) for git-version-gen to +# give accurate answers during normal development with a checked out tree, +# but must be present in a tarball when there is no version control system. +# Therefore, it cannot be used in any dependencies. GNUmakefile has +# hooks to force a reconfigure at distribution time to get the value +# correct, without penalizing normal development with extra reconfigures. +# +# .version - present in a checked-out repository and in a distribution +# tarball. Usable in dependencies, particularly for files that don't +# want to depend on config.h but do want to track version changes. +# Delete this file prior to any autoconf run where you want to rebuild +# files to pick up a version string change; and leave it stale to +# minimize rebuild time after unrelated changes to configure sources. +# +# As with any generated file in a VC'd directory, you should add +# /.version to .gitignore, so that you don't accidentally commit it. +# .tarball-version is never generated in a VC'd directory, so needn't +# be listed there. +# +# Use the following line in your configure.ac, so that $(VERSION) will +# automatically be up-to-date each time configure is run (and note that +# since configure.ac no longer includes a version string, Makefile rules +# should not depend on configure.ac for version updates). +# +# AC_INIT([GNU project], +# m4_esyscmd([build-aux/git-version-gen .tarball-version]), +# [bug-project@example]) +# +# Then use the following lines in your Makefile.am, so that .version +# will be present for dependencies, and so that .version and +# .tarball-version will exist in distribution tarballs. +# +# EXTRA_DIST = $(top_srcdir)/.version +# BUILT_SOURCES = $(top_srcdir)/.version +# $(top_srcdir)/.version: +# echo $(VERSION) > $@-t && mv $@-t $@ +# dist-hook: +# echo $(VERSION) > $(distdir)/.tarball-version + + +me=$0 + +version="git-version-gen $scriptversion + +Copyright 2011 Free Software Foundation, Inc. +There is NO warranty. You may redistribute this software +under the terms of the GNU General Public License. +For more information about these matters, see the files named COPYING." + +usage="\ +Usage: $me [OPTION]... \$srcdir/.tarball-version [TAG-NORMALIZATION-SED-SCRIPT] +Print a version string. + +Options: + + --prefix PREFIX prefix of git tags (default 'v') + --fallback VERSION + fallback version to use if \"git --version\" fails + + --help display this help and exit + --version output version information and exit + +Running without arguments will suffice in most cases." + +prefix=v +fallback= + +while test $# -gt 0; do + case $1 in + --help) echo "$usage"; exit 0;; + --version) echo "$version"; exit 0;; + --prefix) shift; prefix=${1?};; + --fallback) shift; fallback=${1?};; + -*) + echo "$0: Unknown option '$1'." >&2 + echo "$0: Try '--help' for more information." >&2 + exit 1;; + *) + if test "x$tarball_version_file" = x; then + tarball_version_file="$1" + elif test "x$tag_sed_script" = x; then + tag_sed_script="$1" + else + echo "$0: extra non-option argument '$1'." >&2 + exit 1 + fi;; + esac + shift +done + +if test "x$tarball_version_file" = x; then + echo "$usage" + exit 1 +fi + +tag_sed_script="${tag_sed_script:-s/x/x/}" + +nl=' +' + +# Avoid meddling by environment variable of the same name. +v= +v_from_git= + +# First see if there is a tarball-only version file. +# then try "git describe", then default. +if test -f $tarball_version_file +then + v=`cat $tarball_version_file` || v= + case $v in + *$nl*) v= ;; # reject multi-line output + [0-9]*) ;; + *) v= ;; + esac + test "x$v" = x \ + && echo "$0: WARNING: $tarball_version_file is missing or damaged" 1>&2 +fi + +if test "x$v" != x +then + : # use $v +# Otherwise, if there is at least one git commit involving the working +# directory, and "git describe" output looks sensible, use that to +# derive a version string. +elif test "`git log -1 --pretty=format:x . 2>&1`" = x \ + && v=`git describe --abbrev=4 --match="$prefix*" HEAD 2>/dev/null \ + || git describe --abbrev=4 HEAD 2>/dev/null` \ + && v=`printf '%s\n' "$v" | sed "$tag_sed_script"` \ + && case $v in + $prefix[0-9]*) ;; + *) (exit 1) ;; + esac +then + # Is this a new git that lists number of commits since the last + # tag or the previous older version that did not? + # Newer: v6.10-77-g0f8faeb + # Older: v6.10-g0f8faeb + vprefix=`expr "X$v" : 'X\(.*\)-g[^-]*$'` || vprefix=$v + case $vprefix in + *-*) : git describe is probably okay three part flavor ;; + *) + : git describe is older two part flavor + # Recreate the number of commits and rewrite such that the + # result is the same as if we were using the newer version + # of git describe. + vtag=`echo "$v" | sed 's/-.*//'` + commit_list=`git rev-list "$vtag"..HEAD 2>/dev/null` \ + || { commit_list=failed; + echo "$0: WARNING: git rev-list failed" 1>&2; } + numcommits=`echo "$commit_list" | wc -l` + v=`echo "$v" | sed "s/\(.*\)-\(.*\)/\1-$numcommits-\2/"`; + test "$commit_list" = failed && v=UNKNOWN + ;; + esac + + # Change the penultimate "-" to ".", for version-comparing tools. + # Remove the "g" to save a byte. + v=`echo "$v" | sed 's/-\([^-]*\)-g\([^-]*\)$/.\1-\2/'`; + v_from_git=1 +elif test "x$fallback" = x || git --version >/dev/null 2>&1; then + v=UNKNOWN +else + v=$fallback +fi + +v=`echo "$v" |sed "s/^$prefix//"` + +# Test whether to append the "-dirty" suffix only if the version +# string we're using came from git. I.e., skip the test if it's "UNKNOWN" +# or if it came from .tarball-version. +if test "x$v_from_git" != x; then + # Don't declare a version "dirty" merely because a timestamp has changed. + git update-index --refresh > /dev/null 2>&1 + + dirty=`exec 2>/dev/null;git diff-index --name-only HEAD` || dirty= + case "$dirty" in + '') ;; + *) # Append the suffix only if there isn't one already. + case $v in + *-dirty) ;; + *) v="$v-dirty" ;; + esac ;; + esac +fi + +# Omit the trailing newline, so that m4_esyscmd can use the result directly. +printf %s "$v" + +# Local variables: +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "scriptversion=" +# time-stamp-format: "%:y-%02m-%02d.%02H" +# time-stamp-time-zone: "UTC0" +# time-stamp-end: "; # UTC" +# End: diff --git a/subprojects/libcapsule/capsule/_int_dlopen.h b/subprojects/libcapsule/capsule/_int_dlopen.h new file mode 100644 index 0000000000000000000000000000000000000000..127b9dc985851f755a6c96d5d6f836379a058568 --- /dev/null +++ b/subprojects/libcapsule/capsule/_int_dlopen.h @@ -0,0 +1,50 @@ +// Copyright © 2017 Collabora Ltd +// +// This file is part of libcapsule. +// +// libcapsule is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as +// published by the Free Software Foundation; either version 2.1 of the +// License, or (at your option) any later version. +// +// libcapsule 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with libcapsule. If not, see <http://www.gnu.org/licenses/>. + +// Standard implementation of _int_dlopen(), the implementation used +// when a library inside the capsule calls dlopen(). To override this +// implementation, copy this file to shim/capsule/_int_dlopen.h in +// your shim library and modify as needed. +static void * +_int_dlopen (const char *filename, int flag) +{ + if( flag & RTLD_GLOBAL ) + { + fprintf( stderr, "Warning: libcapsule dlopen wrapper cannot pass " + "RTLD_GLOBAL to underlying dlmopen(%s...) call\n", + filename ); + flag = (flag & ~RTLD_GLOBAL) & 0xfffff; + } + return capsule_shim_dlopen( cap, filename, flag ); +} + +// if the libc instances aren't unified (ie > 1 libc) then +// we must try to dispatch the to-be-freed pointer to the one +// that actually allocated it. +// This is far from foolproof: +static void +_wrapped_free (void *ptr) +{ + if (ptr) + capsule_shim_free( cap, ptr ); +} + +static void * +_wrapped_realloc (void *ptr, size_t size) +{ + return capsule_shim_realloc( cap, ptr, size ); +} diff --git a/subprojects/libcapsule/capsule/capsule-dlmopen.c b/subprojects/libcapsule/capsule/capsule-dlmopen.c new file mode 100644 index 0000000000000000000000000000000000000000..d863c537f87cd6c3eba9290b344d8225e1c3529c --- /dev/null +++ b/subprojects/libcapsule/capsule/capsule-dlmopen.c @@ -0,0 +1,333 @@ +// Copyright © 2017 Collabora Ltd + +// This file is part of libcapsule. + +// libcapsule is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as +// published by the Free Software Foundation; either version 2.1 of the +// License, or (at your option) any later version. + +// libcapsule 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 Lesser General Public License for more details. + +// You should have received a copy of the GNU Lesser General Public +// License along with libcapsule. If not, see <http://www.gnu.org/licenses/>. + +#include <inttypes.h> +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> +#include <limits.h> +#include <errno.h> + +#include <sys/types.h> +#include <sys/param.h> +#include <sys/stat.h> +#include <sys/mman.h> + +#include <string.h> +#include <fcntl.h> + +#include <libelf.h> +#include <gelf.h> +#include <dlfcn.h> + +#include <capsule/capsule.h> +#include "capsule/capsule-private.h" + +#include "utils/utils.h" +#include "utils/dump.h" +#include "utils/mmap-info.h" +#include "utils/process-pt-dynamic.h" +#include "utils/ld-cache.h" +#include "utils/ld-libs.h" + +// ========================================================================== +// some pretty printers for debugging: + +// dump out the contents of the ld cache to stderr: +static void +dump_ld_cache (ld_libs *ldlibs) +{ + ld_cache_foreach( &ldlibs->ldcache, ld_entry_dump, stderr ); +} + +/* + * wrap: + * @name: + * @base: Starting address of the program header in memory. + * Addresses are normally relative to this, except for when they are + * absolute (see fix_addr()). + * @dyn: An array of ElfW(Dyn) structures, somewhere after @base + * @wrappers: Relocations to apply + */ +static void +wrap (const char *name, + ElfW(Addr) base, + ElfW(Dyn) *dyn, + capsule_item *wrappers) +{ + int mmap_errno = 0; + const char *mmap_error = NULL; + ElfW(Addr) start = (ElfW(Addr)) dyn - base; + // we don't know the size so we'll have to rely on the linker putting + // well formed entries into the mmap()ed DSO region. + // (tbf if the linker is putting duff entries here we're boned anyway) + // + // dyn is the address of the dynamic section + // base is the start of the program header in memory + // start should be the offset from the program header to its dyn section + // + // the utility functions expect an upper bound though so set that to + // something suitably large: + relocation_data rdata = { 0 }; + + DEBUG( DEBUG_WRAPPERS, + "\"%s\": base address %" PRIxPTR ", dynamic section at %p", + name, base, dyn ); + + rdata.debug = debug_flags; + rdata.error = NULL; + rdata.relocs = wrappers; + + // if RELRO linking has happened we'll need to tweak the mprotect flags + // before monkeypatching the symbol tables, for which we will need the + // sizes, locations and current protections of any mmap()ed regions: + rdata.mmap_info = load_mmap_info( &mmap_errno, &mmap_error ); + + if( mmap_errno || mmap_error ) + { + DEBUG( DEBUG_MPROTECT, + "mmap/mprotect flags information load error (errno: %d): %s", + mmap_errno, mmap_error ); + DEBUG( DEBUG_MPROTECT, + "relocation will be unable to handle RELRO linked libraries" ); + } + + // make all the mmap()s writable: + for( int i = 0; rdata.mmap_info[i].start != MAP_FAILED; i++ ) + if( mmap_entry_should_be_writable( &rdata.mmap_info[i] ) ) + add_mmap_protection( &rdata.mmap_info[i], PROT_WRITE ); + + // if we're debugging wrapper installation in detail we + // will end up in a path that's normally only DEBUG_ELF + // debugged: + if( debug_flags & DEBUG_WRAPPERS ) + debug_flags = debug_flags | DEBUG_RELOCS; + + // install any required wrappers inside the capsule: + process_pt_dynamic( start, // offset from phdr to dyn section + 0, // fake size value + (void *) base, // address of phdr in memory + process_dt_rela, + process_dt_rel, + &rdata ); + + // put the debug flags back in case we changed them + debug_flags = rdata.debug; + + // put the mmap()/mprotect() permissions back the way they were: + for( int i = 0; rdata.mmap_info[i].start != MAP_FAILED; i++ ) + if( mmap_entry_should_be_writable( &rdata.mmap_info[i] ) ) + reset_mmap_protection( &rdata.mmap_info[i] ); + + free_mmap_info( rdata.mmap_info ); + rdata.mmap_info = NULL; +} + +static inline int +excluded_from_wrap (const char *name, char **exclude) +{ + const char *dso = strrchr(name, '/'); + + // we can't ever subvert the runtime linker itself: + if( strncmp( "/ld-", dso, 4 ) == 0 ) + return 1; + + for( char **x = exclude; x && *x; x++ ) + if( strcmp ( *x, dso + 1 ) == 0 ) + return 1; + + return 0; +} + +/* + * @exclude: (array zero-terminated=1): same as for capsule_dlmopen() + * @errcode: (out): errno + * @error: (out) (transfer full) (optional): Error string + */ +// replace calls out to dlopen in the encapsulated DSO with a wrapper +// which should take care of preserving the /path-prefix and namespace +// wrapping of the original capsule_dlmopen() call. +// +// strictly speaking we can wrap things other than dlopen(), +// but that's currently all we use this for: +static int install_wrappers ( void *dl_handle, + capsule_item *wrappers, + const char **exclude, + int *errcode, + char **error) +{ + int replacements = 0; + struct link_map *map; + + if( dlinfo( dl_handle, RTLD_DI_LINKMAP, &map ) != 0 ) + { + const char *local_error = dlerror(); + + if( error ) + *error = xstrdup( local_error ); + + if( errcode ) + *errcode = EINVAL; + + DEBUG( DEBUG_WRAPPERS, "mangling capsule symbols: %s", local_error ); + + return -1; + } + + DEBUG( DEBUG_WRAPPERS, "link_map: %p <- %p -> %p", + map ? map->l_next : NULL , + map ? map : NULL , + map ? map->l_prev : NULL ); + + // no guarantee that we're at either end of the link map: + while( map->l_prev ) + map = map->l_prev; + + unsigned long df = debug_flags; + + if( debug_flags & DEBUG_WRAPPERS ) + debug_flags |= DEBUG_RELOCS; + + if (map->l_next) + { + for( struct link_map *m = map; m; m = m->l_next ) + { + if( excluded_from_wrap(m->l_name, (char **)exclude) ) + { + DEBUG( DEBUG_WRAPPERS, "%s excluded from wrapping", + m->l_name ); + } + else + { + wrap( m->l_name, m->l_addr, m->l_ld, wrappers ); + } + } + } + + debug_flags = df; + + return replacements; +} + +// dump the link map info for the given dl handle (NULL = default) +static void +dump_link_map( void *dl_handle ) +{ + struct link_map *map; + void *handle; + + if( !dl_handle ) + handle = dlopen( NULL, RTLD_LAZY|RTLD_NOLOAD ); + else + handle = dl_handle; + + if( dlinfo( handle, RTLD_DI_LINKMAP, &map ) != 0 ) + { + DEBUG( DEBUG_CAPSULE, "failed to access link_map for handle %p-%p: %s", + dl_handle, handle, dlerror() ); + return; + } + + // be kind, rewind the link map: + while( map->l_prev ) + map = map->l_prev; + + fprintf( stderr, "(dl-handle %s", dl_handle ? "CAPSULE" : "DEFAULT" ); + for( struct link_map *m = map; m; m = m->l_next ) + fprintf( stderr, "\n [prev: %p] %p: \"%s\" [next: %p]", + m->l_prev, m, m->l_name, m->l_next ); + fprintf( stderr, ")\n" ); +} + +// ========================================================================== +void * +_capsule_load (const capsule cap, + capsule_item *wrappers, + int *errcode, + char **error) +{ + void *ret = NULL; + ld_libs ldlibs = {}; + + if( !ld_libs_init( &ldlibs, + (const char **) cap->ns->combined_exclude, + cap->ns->prefix, debug_flags, errcode, error ) ) + return NULL; + + // ================================================================== + // read in the ldo.so.cache - this will contain all architectures + // currently installed (x86_64, i386, x32) in no particular order + if( ld_libs_load_cache( &ldlibs, errcode, error ) ) + { + if( debug_flags & DEBUG_LDCACHE ) + dump_ld_cache( &ldlibs ); + } + else + { + return NULL; + } + + // ================================================================== + // find the starting point of our capsule + if( !ld_libs_set_target( &ldlibs, cap->meta->soname, errcode, error ) ) + goto cleanup; + + // ================================================================== + // once we have the starting point recursively find all its DT_NEEDED + // entries, except for the linker itself and libc, which must not + // be different between the capsule and the "real" DSO environment: + if( !ld_libs_find_dependencies( &ldlibs, errcode, error ) ) + goto cleanup; + + // ================================================================== + // load the stack of DSOs we need: + ret = ld_libs_load( &ldlibs, &cap->ns->ns, 0, errcode, error ); + + if( debug_flags & DEBUG_CAPSULE ) + { + dump_link_map( ret ); + dump_link_map( NULL ); + } + + if( !ret ) + goto cleanup; + + // ===================================================================== + // stash any free/malloc/etc implementations _before_ we overwrite them: + // (only really need to deal with functions that deal with pre-alloc'd + // pointers, ie free and realloc) + if( cap->ns->mem->free == NULL ) + cap->ns->mem->free = dlsym( ret, "free" ); + + if( cap->ns->mem->realloc == NULL ) + cap->ns->mem->realloc = dlsym( ret, "realloc" ); + + // ===================================================================== + // TODO: failure in the dlopen fixup phase should probably be fatal: + if( ret != NULL && // no errors so far + wrappers != NULL ) // have a dlopen fixup function + install_wrappers( ret, wrappers, + (const char **)cap->ns->combined_exclude, + errcode, error ); + + cap->dl_handle = ret; + +cleanup: + ld_libs_finish( &ldlibs ); + return ret; +} + diff --git a/subprojects/libcapsule/capsule/capsule-init.c b/subprojects/libcapsule/capsule/capsule-init.c new file mode 100644 index 0000000000000000000000000000000000000000..4c9d1ed2fc1467e6832ac2d0edb2d23b3e15a28b --- /dev/null +++ b/subprojects/libcapsule/capsule/capsule-init.c @@ -0,0 +1,665 @@ +#include <capsule/capsule.h> +#include "capsule/capsule-private.h" +#include "utils/utils.h" + +#include <assert.h> +#include <stdlib.h> +#include <stdio.h> +#include <dlfcn.h> +#include <string.h> +#include <ctype.h> +#include <sys/param.h> + +#include <unistd.h> + +#define CAP_ENV_PREFIX "CAPSULE_" + +#define DUMP_STRV(what, css) \ + ({ int _i = 0; for( char **_c = css; _c && *_c; _c++, _i++ ) \ + DEBUG( DEBUG_CAPSULE, " ->%s[ %02d ]: %s", #what, _i, *_c ); }) + +#define DUMP_CAPSULE(x, cap) ({ \ + DEBUG( DEBUG_CAPSULE, "\nCAPSULE #%d\n", (int) x ); \ + DEBUG( DEBUG_CAPSULE, " \n" \ + "struct _capsule %p" "\n" \ + "{" "\n" \ + " void *dl_handle; %p" "\n" \ + " capsule_metadata *meta; %p" "\n" \ + " {" "\n" \ + " const char *soname; %s" "\n" \ + " const char *default_prefix; %s" "\n" \ + " const char **exclude; %p" "\n" \ + " const char **export; %p" "\n" \ + " };" "\n" \ + " capsule_namespace *ns; %p" "\n" \ + " {" "\n" \ + " Lmid_t ns; %ld" "\n" \ + " char *prefix; %s" "\n" \ + " char **combined_exclude; %p" "\n" \ + " char **combined_export; %p" "\n" \ + " };" "\n" \ + "};" "\n", \ + cap , \ + cap->dl_handle , \ + cap->meta , \ + cap->meta->soname , \ + cap->meta->default_prefix , \ + cap->meta->exclude , \ + cap->meta->export , \ + cap->ns , \ + cap->ns->ns , \ + cap->ns->prefix , \ + cap->ns->combined_exclude , \ + cap->ns->combined_export);}) + +/* + * never_encapsulated: + * + * Array of SONAMEs that never have a private copy inside a capsule. + * Only one copy of each of these will be loaded, and they will always + * be loaded without respecting the #capsule_namespace's @prefix, even + * if loaded from inside a capsule. + * + * This currently contains the libraries built by the glibc source package. + */ +static const char * const never_encapsulated[] = +{ + "libBrokenLocale.so.1", + "libanl.so.1", + "libc.so.6", + "libcidn.so.1", + "libcrypt.so.1", + "libdl.so.2", + "libm.so.6", + "libmvec.so.1", + "libnsl.so.1", + "libpthread.so.0", + "libresolv.so.2", + "librt.so.1", + "libthread_db.so.1", + "libutil.so.1", +}; + +static ptr_list *namespaces = NULL; + +ptr_list *_capsule_list = NULL; +dlsymfunc _capsule_original_dlsym = NULL; +dlopnfunc _capsule_original_dlopen = NULL; + +freefunc _capsule_original_free = NULL; +mallocfunc _capsule_original_malloc = NULL; +callocfunc _capsule_original_calloc = NULL; +rallocfunc _capsule_original_realloc = NULL; +palignfunc _capsule_original_pmalign = NULL; + +static int str_equal (const void *a, const void *b) +{ + if( a == b ) + return 1; + + if( !a || !b ) + return 0; + + return strcmp( (char *)a, (char *)b ) == 0; +} + +static const char *get_prefix_nocopy (const char *dflt, const char *soname); + +static capsule_namespace * +get_namespace (const char *default_prefix, const char *soname) +{ + const char *prefix = get_prefix_nocopy( default_prefix, soname ); + capsule_namespace *ns; + + // Normalize so that libraries with prefix NULL, "" or "/" are all + // treated as equivalent + if( prefix == NULL || prefix[0] == '\0' ) + prefix = "/"; + + if( namespaces == NULL ) + namespaces = ptr_list_alloc( 4 ); + + for( size_t x = 0; x < namespaces->next; x++ ) + { + ns = ptr_list_nth_ptr( namespaces, x ); + + if( !ns || strcmp( prefix, ns->prefix ) ) + continue; + + return ns; + } + + ns = calloc( 1, sizeof(capsule_namespace) ); + ns->ns = LM_ID_NEWLM; + ns->prefix = strdup( prefix ); + ns->exclusions = ptr_list_alloc( 4 ); + ns->exports = ptr_list_alloc( 4 ); + ns->mem = calloc( 1, sizeof(capsule_memory) ); + + ptr_list_push_ptr( namespaces, ns ); + + return ns; +} + +// items with names starting with '-' are initialised by hand +// and will be skipped in the automatic section below: +static capsule_item alloc_func[] = +{ + { "-dlopen" , (capsule_addr) NULL }, + { "-free" , (capsule_addr) NULL }, + { "-realloc" , (capsule_addr) NULL }, + { "malloc" , (capsule_addr) &_capsule_original_malloc }, + { "calloc" , (capsule_addr) &_capsule_original_calloc }, + { "posix_memalign", (capsule_addr) &_capsule_original_pmalign }, + { NULL } +}; + +static void +get_capsule_metadata (struct link_map *map, const char *only) +{ + ElfW(Addr) base = map->l_addr; + ElfW(Dyn) *dyn = map->l_ld; + ElfW(Dyn) *entry; + ElfW(Sym) *symbol; + + const void *strtab = NULL; + const void *symtab = NULL; + + if( map->l_name == NULL || *map->l_name == '\0' ) + return; + + for( entry = dyn; entry->d_tag != DT_NULL; entry++ ) + { + switch( entry->d_tag ) + { + case DT_SYMTAB: + symtab = (const void *) fix_addr( (void *)base, entry->d_un.d_ptr ); + break; + + case DT_STRTAB: + strtab = (const void *) fix_addr( (void *)base, entry->d_un.d_ptr ); + break; + + default: + break; + } + } + + if( !strtab || !symtab ) + return; + + for( symbol = (ElfW(Sym) *)symtab; + ( (ELFW_ST_TYPE(symbol->st_info) < STT_NUM) && + (ELFW_ST_BIND(symbol->st_info) < STB_NUM) ); + symbol++ ) + { + capsule_metadata *meta = NULL; + capsule cap = NULL; + char *name = (char *)strtab + symbol->st_name; + + if( !name || strcmp( "capsule_meta", name ) ) + continue; + + meta = (capsule_metadata *)fix_addr( (void *)base, symbol->st_value ); + + // if we were asked for a specific soname's meta data then ignore + // everything else: + if( only && strcmp( only, meta->soname ) ) + continue; + + // not a version of the ABI we understand? skip it. + if( meta->capsule_abi != 0 ) + continue; + + cap = meta->handle; + + if( cap == NULL ) + { + // Functions we must override in the DSOs + // inside the capsule (mostly to take account of the fact that + // they're pulled in from a tree with a filesystem prefix like /host) + // NOTE: the shim address here isn't used, but we give it the same + // value as the real function address so it's never accidentally + // a value the capsule code will care about: + int i = 0; + static const capsule_item no_wrapper = { NULL }; + const capsule_item int_dlopen_wrapper = + { + "dlopen", + (capsule_addr) meta->int_dlopen, + (capsule_addr) meta->int_dlopen, + }; + + const capsule_item int_free_wrapper = + { + "free", + (capsule_addr) meta->int_free, + (capsule_addr) meta->int_free, + }; + + const capsule_item int_realloc_wrapper = + { + "realloc", + (capsule_addr) meta->int_realloc, + (capsule_addr) meta->int_realloc, + }; + + cap = xcalloc( 1, sizeof(struct _capsule) ); + cap->ns = get_namespace( meta->default_prefix, meta->soname ); + DEBUG( DEBUG_CAPSULE, + "Creating new capsule %p for metadata %p (%s … %s)", + cap, meta, cap->ns->prefix, meta->soname ); + cap->meta = meta; + cap->seen.all = ptr_list_alloc( 32 ); + cap->seen.some = ptr_list_alloc( 32 ); + + cap->internal_wrappers[ 0 ] = int_dlopen_wrapper; + cap->internal_wrappers[ 1 ] = int_free_wrapper; + cap->internal_wrappers[ 2 ] = int_realloc_wrapper; + + for( i = 3; alloc_func[ i ].name != NULL; i++ ) + { + // pre-populated functions that we should skip are + // flagged with an invalid name starting with '-'. + // in a properly laid out file they'll all be at the start of + // alloc_func and we should never see them, but just in case: + if( alloc_func[ i ].name[ 0 ] == '-' ) + continue; + + capsule_item *capi = &cap->internal_wrappers[ i ]; + void **slot = (void **) alloc_func[ i ].real; + + // the addresses held in alloc_func are actually the + // addresses of pointers to the real implementations, + // which are populated during capsule_init: we deref + // those here: + capi->name = alloc_func[ i ].name; + capi->shim = capi->real = (capsule_addr) *slot; + } + + cap->internal_wrappers[ i ] = no_wrapper; + meta->handle = cap; + ptr_list_push_ptr( _capsule_list, cap ); + } + + DEBUG( DEBUG_CAPSULE, + "found metadata for %s … %s at %p (capsule: %p)", + cap->ns->prefix, meta->soname, meta, cap ); + break; + } +} + +/** + * cook_list: + * @list: a ptr_list containing statically-allocated strings + * @extras: (array length=n_extras): extra entries to be added + * @n_extras: number of extra entries + * + * Return @list + @extras as an array of strings. The strings are not + * copied, so the result is only valid as long as the strings are. + * + * Returns: (transfer container) (array zero-terminated=1) (element-type utf8): + * a shallow copy of everything in either @list or @extras + */ +static char ** +cook_list (ptr_list *list, + const char * const *extras, + size_t n_extras) +{ + char **cooked = calloc( list->next + n_extras + 1, sizeof(char *) ); + + for( size_t j = 0; j < list->next; j++ ) + cooked[j] = (char *)ptr_list_nth_ptr( list, j ); + + for( size_t j = 0; j < n_extras; j++ ) + cooked[j + list->next] = (char *)extras[j]; + + *(cooked + list->next + n_extras) = NULL; + + return cooked; +} + +/** + * add_new_strings_to_ptrlist: + * @list: a ptr_list + * @strings: (transfer none) (array zero-terminated=1) (nullable): + * + * Add each string in @strings to @list, unless a string with the same + * content is already present. The strings must continue to exist as long + * as @list does. + */ +static void +add_new_strings_to_ptrlist (ptr_list *list, const char * const *strings) +{ + for( const char * const *c = strings; c && *c; c++ ) + ptr_list_add_ptr( list, (char *) *c, str_equal ); +} + +/** + * free_strv: + * @strv: (transfer container) (array zero-terminated=1) (element-type utf8): + * + * Free @strv, but not its contents. + */ +static void +free_strv (char **strv) +{ + if( !strv ) + return; + + free( strv ); +} + +static void update_namespaces (void); + +static void +update_metadata (const char *match) +{ + struct link_map *map; + void *handle; + + handle = dlmopen( LM_ID_BASE, NULL, RTLD_LAZY|RTLD_NOLOAD ); + dlinfo( handle, RTLD_DI_LINKMAP, &map ); + + // we're not guaranteed to be at the start of the link map chain: + while( map->l_prev ) + map = map->l_prev; + + // pick up any capsule metadata we can see: + // if the soname (match) is NULL, this means _all_ metadata, + // otherwise just the metadata that is string-equal: + if (map->l_next) + for( struct link_map *m = map; m; m = m->l_next ) + get_capsule_metadata( m, match ); + + update_namespaces(); +} + +static void +update_namespaces (void) +{ + if( !namespaces ) + return; + + // wipe out the namespaces' merged lists of exclusions etc. - + // they contain strings that point into capsule metadata that might + // no longer be valid, if we dlclosed a shim library + for( size_t i = 0; i < namespaces->next; i++ ) + { + capsule_namespace *ns = ptr_list_nth_ptr( namespaces, i ); + + if( !ns ) + continue; + + DEBUG( DEBUG_CAPSULE, "Resetting namespace #%zu %p \"%s\"", + i, ns, ns->prefix ); + + // We don't free the actual strings because we don't own them; + // just truncate the list to 0 entries + ns->exclusions->next = 0; + ns->exports->next = 0; + } + + // merge the string lists for each active prefix: + // ie all excludes for /host should be in one exclude list, + // all export entries for /host should bein another list, all + // excludes for /badgerbadger should be in another, etc etc: + for( size_t i = 0; i < _capsule_list->next; i++ ) + { + capsule cap = ptr_list_nth_ptr( _capsule_list, i ); + + if( !cap ) + continue; + + DEBUG( DEBUG_CAPSULE, + "Collecting strings from capsule #%zu %p \"%s\" into namespace " + "%p \"%s\"", + i, cap, cap->meta->soname, cap->ns, cap->ns->prefix ); + + add_new_strings_to_ptrlist( cap->ns->exclusions, cap->meta->exclude ); + add_new_strings_to_ptrlist( cap->ns->exports, cap->meta->export ); + } + + // now squash the meta data ptr_lists into char** that + // the underlying infrastructure actually uses: + for( size_t i = 0; i < namespaces->next; i++ ) + { + capsule_namespace *ns = ptr_list_nth_ptr( namespaces, i ); + + if( !ns ) + continue; + + free_strv( ns->combined_exclude ); + free_strv( ns->combined_export ); + + ns->combined_exclude = cook_list( ns->exclusions, + never_encapsulated, + N_ELEMENTS( never_encapsulated ) ); + ns->combined_export = cook_list( ns->exports, NULL, 0 ); + } +} + +static void __attribute__ ((constructor)) _init_capsule (void) +{ + _capsule_list = ptr_list_alloc( 16 ); + +#ifdef HAVE_SECURE_GETENV + set_debug_flags( secure_getenv("CAPSULE_DEBUG") ); +#endif + + // these are needed if there is > 1 libc instance: + _capsule_original_free = dlsym( RTLD_DEFAULT, "free" ); + _capsule_original_malloc = dlsym( RTLD_DEFAULT, "malloc" ); + _capsule_original_calloc = dlsym( RTLD_DEFAULT, "calloc" ); + _capsule_original_realloc = dlsym( RTLD_DEFAULT, "realloc" ); + _capsule_original_pmalign = dlsym( RTLD_DEFAULT, "posix_memalign" ); + + update_metadata( NULL ); + + _capsule_original_dlsym = dlsym( RTLD_DEFAULT, "dlsym" ); + _capsule_original_dlopen = dlsym( RTLD_DEFAULT, "dlopen" ); + + if( !(debug_flags & DEBUG_CAPSULE) ) + return; + + for( unsigned int x = 0; x < _capsule_list->next; x++ ) + { + capsule cap = _capsule_list->loc[ x ].ptr; + + if( !cap ) + continue; + + const char **soname = cap->meta->exclude; + + DEBUG( DEBUG_CAPSULE, "[%02d] %s metadata:\n", x, cap->meta->soname ); + + while( soname && *soname ) + fprintf( stderr, " %s\n", *(soname++) ); + } +} + +char * +capsule_get_prefix (const char *dflt, const char *soname) +{ + const char *prefix = get_prefix_nocopy( dflt, soname ); + + if (prefix != NULL) + return xstrdup( prefix ); + + return NULL; +} + +static const char * +get_prefix_nocopy (const char *dflt, const char *soname) +{ +#ifdef HAVE_SECURE_GETENV + char env_var[PATH_MAX] = CAP_ENV_PREFIX; + const size_t offs = strlen( CAP_ENV_PREFIX ); + size_t x = 0; + const char *prefix = NULL; + + for( ; (x < strlen(soname)) && (x + offs < PATH_MAX); x++ ) + { + char c = *(soname + x); + + env_var[ x + offs ] = isalnum( c ) ? toupper( c ) : '_'; + } + + safe_strncpy( &env_var[ x + offs ], "_PREFIX", PATH_MAX - (x + offs + 1) ); + env_var[ PATH_MAX - 1 ] = '\0'; + + DEBUG( DEBUG_CAPSULE, "checking %s\n", &env_var[0] ); + if( (prefix = secure_getenv( &env_var[0] )) ) + { + DEBUG( DEBUG_SEARCH, "Capsule prefix is %s: %s", &env_var[0], prefix ); + return prefix; + } + + DEBUG( DEBUG_CAPSULE, "checking %s\n", CAP_ENV_PREFIX "PREFIX" ); + if( (prefix = secure_getenv( CAP_ENV_PREFIX "PREFIX" )) ) + { + DEBUG( DEBUG_SEARCH, "Capsule prefix is "CAP_ENV_PREFIX"PREFIX: %s", + prefix ); + return prefix; + } +#endif + + if( dflt ) + { + DEBUG( DEBUG_SEARCH, "Capsule prefix is built-in: %s", dflt ); + return dflt; + } + + DEBUG( DEBUG_SEARCH, "Capsule prefix is missing" ); + return NULL; +} + +static capsule +get_capsule_by_soname (const char *soname) +{ + for( size_t n = 0; n < _capsule_list->next; n++ ) + { + capsule cap = ptr_list_nth_ptr( _capsule_list, n ); + + if( !cap || strcmp( cap->meta->soname, soname ) ) + continue; + + DUMP_CAPSULE(n, cap); + return cap; + } + + return NULL; +} + +capsule +capsule_init (const char *soname) +{ + capsule cap; + void *dso; + int capsule_errno = 0; + char *capsule_error = NULL; + + DEBUG( DEBUG_CAPSULE, "Initializing shim library %s", soname ); + + cap = get_capsule_by_soname( soname ); + + if( !cap ) + { + DEBUG( DEBUG_CAPSULE, "no metadata for %s registered: " + "may be a dlopened capsule", soname ); + DEBUG( DEBUG_CAPSULE, "updating capsule metadata" ); + update_metadata( soname ); + cap = get_capsule_by_soname( soname ); + } + + if( !cap ) + { + fprintf( stderr, + "libcapsule: %s: Fatal error: cannot initialize shim " + "library (capsule_meta not found)\n", + soname ); + abort(); + } + + for( size_t i = 0; i < _capsule_list->next; i++ ) + { + capsule other = ptr_list_nth_ptr( _capsule_list, i ); + + if( !other ) + continue; + + DEBUG( DEBUG_CAPSULE, " "); + DUMP_CAPSULE( i, other ); + DUMP_STRV( excluded, other->ns->combined_exclude ); + DUMP_STRV( exported, other->ns->combined_export ); + } + + dso = _capsule_load( cap, cap->internal_wrappers, + &capsule_errno, &capsule_error ); + + if( !dso ) + { + fprintf( stderr, "libcapsule: fatal error: %s: %s\n", + soname, capsule_error ); + abort(); + } + + int rloc = _capsule_relocate( cap, &capsule_error ); + + if( rloc != 0 ) // relocation failed. we're dead. + { + fprintf( stderr, + "libcapsule: fatal error: %s could not install " + "relocations\n", + soname ); + abort(); + } + + rloc = _capsule_relocate_dlopen( cap, &capsule_error ); + if( rloc != 0 ) // we may survive this depending on how dlopen is used + fprintf( stderr, + "libcapsule: warning: %s could not install dlopen() " + "dlopen wrappers. This error may or may not be " + "fatal later.\n", + soname ); + + return cap; +} + +#define CLEAR(f,x) ({ f( x ); x = NULL; }) + +void +capsule_close (capsule cap) +{ + capsule_metadata *meta = cap->meta; + + DEBUG( DEBUG_CAPSULE, "Uninitializing shim library %s", meta->soname ); + + // scrub all entries in the manifest pointing to this metadata + for( size_t n = 0; n < _capsule_list->next; n++ ) + { + capsule other = ptr_list_nth_ptr( _capsule_list, n ); + + if( other == cap ) + { + _capsule_list->loc[ n ].ptr = NULL; + } + else if( other != NULL ) + { + // There should only be one capsule per capsule_metadata + assert( other->meta != meta ); + } + } + + // Remove any pointers from the namespaces into this capsule + update_namespaces(); + + meta->handle = NULL; + + CLEAR( ptr_list_free, cap->seen.all ); + CLEAR( ptr_list_free, cap->seen.some ); + + // poison the capsule struct and free it + memset( cap, 'X', sizeof(struct _capsule) ); + free( cap ); +} diff --git a/subprojects/libcapsule/capsule/capsule-malloc.h b/subprojects/libcapsule/capsule/capsule-malloc.h new file mode 100644 index 0000000000000000000000000000000000000000..9a760376b73fce547ea21a807863f69921a1e6fc --- /dev/null +++ b/subprojects/libcapsule/capsule/capsule-malloc.h @@ -0,0 +1,208 @@ +#pragma once + +#define CAPSULE_MALLOC_EXTRA_CHECKS + +#define DEBUG_MALLOC_VOODOO 0 + +#if DEBUG_MALLOC_VOODOO +#include <stdio.h> +#include <sys/param.h> +// can't printf inside malloc/free et al +// can't jump to an fputs from a different libc either +typedef int (*fputsfunc) (const char *buf, FILE *s); +static fputsfunc wf = NULL; + +#define CAN_DEBUG_ALLOCS (wf != NULL) + +static void dump_ptr (const char *label, const void *ptr) +{ + char ptrbuf[ (sizeof(void *) * 2) + 1 ]; + unsigned long x = (unsigned long) ptr; + static const char pattern[] = "0123456789abcdef"; + ssize_t start = sizeof(long) * 2; + + ptrbuf[ sizeof(ptrbuf) - 1 ] = '\0'; + + for( size_t i = 0; i < sizeof(long) * 2; i++ ) + { + size_t c = (x >> ((((sizeof(long) * 2) - 1) - i) * 4)) & 0b01111; + ptrbuf[ i ] = pattern[ c ]; + if( (start == (sizeof(long) * 2)) && (ptrbuf[i] != '0') ) + start = i; + } + + start = MAX( 0, start - 1 ); + + wf( label , stderr ); + wf( ": <0x", stderr ); + wf( ptrbuf + start, stderr ); + wf( ">\n" , stderr ); +} +#define FETCH_FPUTS() \ + if(UNLIKELY(!wf)) wf = (fputsfunc) dlsym(cap->dl_handle, "fputs") +#else +#define dump_ptr(a,b) +#define FETCH_FPUTS() +#define CAN_DEBUG_ALLOCS 0 +#endif + + +//////////////////////////////////////////////////////////////////////////// +// copy some vodoo out of libc. +// it is to be hoped that this is a temporary hack but, well… + +#define SIZE_SZ (sizeof(size_t)) + +struct malloc_chunk +{ + size_t prev_size; /* Size of previous chunk (if free). */ + size_t size; /* Size in bytes, including overhead. */ + + struct malloc_chunk* fd; /* double links -- used only if free. */ + struct malloc_chunk* bk; + + struct malloc_chunk* fd_nextsize; /* double links -- used only if free. */ + struct malloc_chunk* bk_nextsize; +}; + +typedef struct malloc_chunk* mchunkptr; + +#define chunk2mem(p) ((void*)((char*)(p) + 2*SIZE_SZ)) +#define mem2chunk(mem) ((mchunkptr)((char*)(mem) - 2*SIZE_SZ)) + +/* size field is or'ed with IS_MMAPPED if the chunk was obtained with mmap() */ +#define IS_MMAPPED 0x2 + +#define chunk_is_mmapped(p) ((p)->size & IS_MMAPPED) + +//////////////////////////////////////////////////////////////////////////// +// extra voodoo. not clear if we need this: +#ifdef CAPSULE_MALLOC_EXTRA_CHECKS + +#define PREV_INUSE 0x1 +/* similarly for non-main-arena chunks */ +#define NON_MAIN_ARENA 0x4 + +#define chunk_non_main_arena(p) ((p)->size & NON_MAIN_ARENA) + +typedef struct malloc_chunk *mfastbinptr; +typedef int mutex_t; + +#define fastbin(ar_ptr, idx) ((ar_ptr)->fastbinsY[idx]) +/* offset 2 to use otherwise unindexable first 2 bins */ +#define fastbin_index(sz) \ + ((((unsigned int) (sz)) >> (SIZE_SZ == 8 ? 4 : 3)) - 2) +/* The maximum fastbin request size we support */ +#define MAX_FAST_SIZE (80 * SIZE_SZ / 4) + +#define MALLOC_ALIGNMENT (2 *SIZE_SZ < __alignof__ (long double) ? \ + __alignof__ (long double) : 2 *SIZE_SZ) +#define MALLOC_ALIGN_MASK (MALLOC_ALIGNMENT - 1) +#define MIN_CHUNK_SIZE (offsetof(struct malloc_chunk, fd_nextsize)) +/* The smallest size we can malloc is an aligned minimal chunk */ +#define MINSIZE \ + (unsigned long)(((MIN_CHUNK_SIZE+MALLOC_ALIGN_MASK) & ~MALLOC_ALIGN_MASK)) + +#define request2size(req) \ + (((req) + SIZE_SZ + MALLOC_ALIGN_MASK < MINSIZE) ? \ + MINSIZE : \ + ((req) + SIZE_SZ + MALLOC_ALIGN_MASK) & ~MALLOC_ALIGN_MASK) + +#define NFASTBINS (fastbin_index (request2size (MAX_FAST_SIZE)) + 1) +#define NBINS 128 +#define BINMAPSHIFT 5 +#define BITSPERMAP (1U << BINMAPSHIFT) +#define BINMAPSIZE (NBINS / BITSPERMAP) + +struct malloc_state +{ + /* Serialize access. */ + mutex_t mutex; + + /* Flags (formerly in max_fast). */ + int flags; + + /* Fastbins */ + mfastbinptr fastbinsY[NFASTBINS]; + + /* Base of the topmost chunk -- not otherwise kept in a bin */ + mchunkptr top; + + /* The remainder from the most recent split of a small request */ + mchunkptr last_remainder; + + /* Normal bins packed as described above */ + mchunkptr bins[NBINS * 2 - 2]; + + /* Bitmap of bins */ + unsigned int binmap[BINMAPSIZE]; + + /* Linked list */ + struct malloc_state *next; + + /* Linked list for free arenas. Access to this field is serialized + by free_list_lock in arena.c. */ + struct malloc_state *next_free; + + /* Number of threads attached to this arena. 0 if the arena is on + the free list. Access to this field is serialized by + free_list_lock in arena.c. */ + size_t attached_threads; + + /* Memory allocated from the system in this arena. */ + size_t system_mem; + size_t max_system_mem; +}; + +typedef struct malloc_state *mstate; + +typedef struct _heap_info +{ + mstate ar_ptr; /* Arena for this heap. */ + struct _heap_info *prev; /* Previous heap. */ + size_t size; /* Current size in bytes. */ + size_t mprotect_size; /* Size in bytes that has been mprotected + PROT_READ|PROT_WRITE. */ + /* Make sure the following data is properly aligned, particularly + that sizeof (heap_info) + 2 * SIZE_SZ is a multiple of + MALLOC_ALIGNMENT. */ + char pad[-6 * SIZE_SZ & MALLOC_ALIGN_MASK]; +} heap_info; + +#ifndef DEFAULT_MMAP_THRESHOLD_MAX + /* For 32-bit platforms we cannot increase the maximum mmap + threshold much because it is also the minimum value for the + maximum heap size and its alignment. Going above 512k (i.e., 1M + for new heaps) wastes too much address space. */ +# if __WORDSIZE == 32 +# define DEFAULT_MMAP_THRESHOLD_MAX (512 * 1024) +# else +# define DEFAULT_MMAP_THRESHOLD_MAX (4 * 1024 * 1024 * sizeof(long)) +# endif +#endif + +#define HEAP_MIN_SIZE (32 * 1024) +#ifndef HEAP_MAX_SIZE +# ifdef DEFAULT_MMAP_THRESHOLD_MAX +# define HEAP_MAX_SIZE (2 * DEFAULT_MMAP_THRESHOLD_MAX) +# else +# define HEAP_MAX_SIZE (1024 * 1024) +# endif +#endif + +#define heap_for_ptr(ptr) \ + ((heap_info *) ((unsigned long) (ptr) & ~(HEAP_MAX_SIZE - 1))) +#define arena_for_chunk(ptr) \ + (chunk_non_main_arena (ptr) ? heap_for_ptr (ptr)->ar_ptr : NULL) + +#define chunk_at_offset(p, s) ((mchunkptr) (((char *) (p)) + (s))) + +#define NONCONTIGUOUS_BIT (2U) +#define contiguous(M) (((M)->flags & NONCONTIGUOUS_BIT) == 0) +#define noncontiguous(M) (((M)->flags & NONCONTIGUOUS_BIT) != 0) + +#define SIZE_BITS (PREV_INUSE | IS_MMAPPED | NON_MAIN_ARENA) +#define chunksize(p) ((p)->size & ~(SIZE_BITS)) + +#endif // CAPSULE_MALLOC_EXTRA_CHECKS +//////////////////////////////////////////////////////////////////////////// diff --git a/subprojects/libcapsule/capsule/capsule-private.h b/subprojects/libcapsule/capsule/capsule-private.h new file mode 100644 index 0000000000000000000000000000000000000000..f4cbda6b2c9512325fbd6a0b5025695c01d38ff3 --- /dev/null +++ b/subprojects/libcapsule/capsule/capsule-private.h @@ -0,0 +1,132 @@ +#pragma once + +#include <link.h> +#include "utils/utils.h" + +typedef void * (*dlsymfunc) (void *handle, const char *symbol); +typedef void * (*dlopnfunc) (const char *file, int flags); + +// we need these to transplant the *alloc/free cluster into +// the capsule so that the memory allocation implementation +// is unified (at least until we can force libc to be shared): +typedef void (*freefunc) (void *ptr); +typedef void * (*mallocfunc) (size_t size); +typedef void * (*callocfunc) (size_t nmem, size_t size); +typedef void * (*rallocfunc) (void *ptr, size_t size); +typedef int (*palignfunc) (void **memptr, size_t alignment, size_t size); + +typedef struct _capsule_memory +{ + freefunc free; + rallocfunc realloc; + mallocfunc malloc; // UNUSED + callocfunc calloc; // UNUSED + palignfunc posix_memalign; // UNUSED +} capsule_memory; + +typedef struct _capsule_namespace +{ + Lmid_t ns; // dlmopen namespace. LM_ID_NEWLM to create + const char *prefix; // default library tree prefix, eg /host + ptr_list *exclusions; // sonames to ignore + ptr_list *exports; // sonames to expose/export + char **combined_exclude; // combined exclude & export lists from all + char **combined_export; // capsules DSOs sharing the same namespace + capsule_memory *mem; +} capsule_namespace; + +struct _capsule +{ + void *dl_handle; + struct { ptr_list *all; ptr_list *some; } seen; + capsule_metadata *meta; + capsule_namespace *ns; + capsule_item internal_wrappers[7]; +}; + +extern ptr_list *_capsule_list; +extern dlsymfunc _capsule_original_dlsym; +extern dlopnfunc _capsule_original_dlopen; + +extern freefunc _capsule_original_free; +extern callocfunc _capsule_original_calloc; +extern mallocfunc _capsule_original_malloc; +extern rallocfunc _capsule_original_realloc; +extern palignfunc _capsule_original_pmalign; + +/* + * _capsule_load: + * @capsule: a #capsule handle as returned by capsule_init() + * @dso: The name of the DSO to open (cf dlopen()) - eg libGL.so.1 + * @namespace: (out) Address of an #Lmid_t value. + * @wrappers: Array of #capsule_item used to replace symbols in the namespace + * @errcode: (out): location in which to store the error code (errno) on failure + * @error: (out) (transfer full) (optional): location in which to store + * an error message on failure, or %NULL to ignore. + * Free with free(). + * + * Returns: A (void *) DSO handle, as per dlopen(), or %NULL on error + * + * Opens @dso (a library) from a filesystem mounted at @prefix into a + * symbol namespace specified by @namespace, using dlmopen(). + * + * Any symbols specified in @wrappers will be replaced with the + * corresponding address from @wrappers (allowing you to replace + * function definitions inside the namespace with your own). + * + * This is normally used to replace calls inside the namespace to + * certain functions (such as dlopen()) which need to be overridden + * to operate correctly inside a private namespace associated with + * a nonstandard filsystem tree. + * + * The #Lmid_t value stored in @namespace gives the symbol namespace + * into which the target DSO was loaded. It should normally be #LM_ID_NEWLM. + * + * In addition to a bare libFOO.so.X style name, @dso may be an + * absolute path (or even a relative one) and in those cases should + * have the same effect as passing those values to dlopen(). This is + * not a normal use case and has not been heavily tested. + * + */ +void *_capsule_load (const capsule capsule, + capsule_item *wrappers, + int *errcode, + char **error); + +/* + * _capsule_relocate: + * @capsule: a #capsule handle as returned by capsule_init() + * @error: (out) (transfer full) (optional): location in which to store + * an error message on failure, or %NULL to ignore. + * Free with free(). + * + * Returns: 0 on success, non-zero on failure. + * + * This function updates the GOT entries in all DSOs outside the capsule + * so that when they call any function listed in the @items of the + * #capsule_metadata, they invoke the copy of that function inside the capsule. + * + * In the unlikely event that an error message is returned in @error it is the + * caller's responsibility to free() it. + */ +int _capsule_relocate (const capsule capsule, + char **error); + +/* + * _capsule_relocate_dlopen: + * @capsule: a #capsule handle as returned by capsule_init() + * @error: (out) (transfer full) (optional): location in which to store + * an error message on failure, or %NULL to ignore. + * Free with free(). + * + * Returns: 0 on success, non-zero on failure. + * + * This function updates the GOT entries in all DSOs outside the capsule + * _except_ those listed in @except: When they call dlopen(), instead + * they invoke the copy of that function provided by libcapsule. + * + * In the unlikely event that an error message is returned in @error it is the + * caller's responsibility to free() it. + */ +int _capsule_relocate_dlopen (const capsule capsule, + char **error); diff --git a/subprojects/libcapsule/capsule/capsule-relocate.c b/subprojects/libcapsule/capsule/capsule-relocate.c new file mode 100644 index 0000000000000000000000000000000000000000..513178dda980154140a2d058833f9a09be279328 --- /dev/null +++ b/subprojects/libcapsule/capsule/capsule-relocate.c @@ -0,0 +1,252 @@ +// Copyright © 2017 Collabora Ltd + +// This file is part of libcapsule. + +// libcapsule is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as +// published by the Free Software Foundation; either version 2.1 of the +// License, or (at your option) any later version. + +// libcapsule 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 Lesser General Public License for more details. + +// You should have received a copy of the GNU Lesser General Public +// License along with libcapsule. If not, see <http://www.gnu.org/licenses/>. + +#include <dlfcn.h> +#include <stdlib.h> +#include <stdio.h> + +#include <string.h> + +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> + +#include <capsule/capsule.h> +#include "capsule/capsule-private.h" + +#include "utils/dump.h" +#include "utils/utils.h" +#include "utils/process-pt-dynamic.h" + +// ========================================================================== +// some entries require us to peer into others to make sense of them: +// can't make full sense of relocations without looking names up +// in the stringtab, which does not have to occur at any fixed point in +// in the PT_DYNAMIC entry. +// IOW PT_DYNAMIC contains both relocations (DT_RELA, DT_REL) and a stringtab +// (DT_STRTAB) in arbitrary order but the former do not make sense without +// the latter. + + +// ========================================================================= + +/* + * process_phdr: + * @info: struct dl_phdr_info describing the shared object + * @size: sizeof(info) + * @rdata: the closure that we passed to dl_iterate_phdr() + * + * Callback called for each shared object loaded into the program. + */ +static int +process_phdr (struct dl_phdr_info *info, + size_t size, + relocation_data *rdata) +{ + int ret = 0; + + for( int j = 0; !ret && (j < info->dlpi_phnum); j++ ) + { + if( info->dlpi_phdr[j].p_type == PT_DYNAMIC ) + { + ret = process_pt_dynamic( info->dlpi_phdr[j].p_vaddr, + info->dlpi_phdr[j].p_memsz, + (void *) info->dlpi_addr, + process_dt_rela, + process_dt_rel, + rdata ); + } + } + + if( ret == 0 && rdata->seen != NULL ) + ptr_list_push_addr( rdata->seen, info->dlpi_addr ); + + return ret; +} + +static int +dso_is_blacklisted (const char *path, relocation_flags flags) +{ + const char * const *soname; + const char * const libc[] = + { + "libc.so", + "libdl.so", + "libpthread.so", + NULL + }; + static const char *never = "libcapsule.so"; + + if( soname_matches_path( never, path ) ) + return 1; + + if( flags & RELOCATION_FLAGS_AVOID_LIBC ) + for( soname = libc; soname && *soname; soname++ ) + if( soname_matches_path( *soname, path ) ) + return 1; + + return 0; +} + +static int +dso_has_been_relocated (ptr_list *seen, ElfW(Addr) base) +{ + if( seen == NULL ) + return 0; + + if( ptr_list_contains( seen, base ) ) + return 1; + + return 0; +} + +// first level of the callback: all we're doing here is skipping over +// any program headers that (for whatever reason) we decide we're not +// interested in. +// In practice we have to handle all existing DSOs, as any of them may +// call into the library we are acting as a shim for. +static int +relocate_cb (struct dl_phdr_info *info, size_t size, void *data) +{ + relocation_data *rdata = data; + const char *dso_path = *info->dlpi_name ? info->dlpi_name : "-elf-"; + + if( dso_is_blacklisted( dso_path, rdata->flags ) ) + { + DEBUG( DEBUG_RELOCS, "skipping %s %p (blacklisted)", + dso_path, (void *) info->dlpi_addr ); + return 0; + } + + if( dso_has_been_relocated( rdata->seen, info->dlpi_addr ) ) + { + DEBUG( DEBUG_RELOCS, "skipping %s %p (already relocated)", + dso_path, (void *) info->dlpi_addr ); + return 0; + } + + DEBUG( DEBUG_RELOCS, "processing %s %p", + dso_path, (void *) info->dlpi_addr ); + + return process_phdr( info, size, rdata ); +} + +static int relocate (const capsule cap, + capsule_item *relocations, + relocation_flags flags, + ptr_list *seen, + char **error) +{ + relocation_data rdata = { 0 }; + capsule_item *map; + int mmap_errno = 0; + const char *mmap_error = NULL; + int rval = 0; + + // load the relevant metadata into the callback argument: + rdata.debug = debug_flags; + rdata.error = NULL; + rdata.flags = flags; + rdata.mmap_info = load_mmap_info( &mmap_errno, &mmap_error ); + rdata.relocs = relocations; + rdata.seen = seen; + + if( mmap_errno || mmap_error ) + { + DEBUG( DEBUG_RELOCS|DEBUG_MPROTECT, + "mmap/mprotect flags information load error (errno: %d): %s\n", + mmap_errno, mmap_error ); + DEBUG( DEBUG_RELOCS|DEBUG_MPROTECT, + "relocation will be unable to handle relro linked libraries" ); + } + + // no source dl handle means we must have a pre-populated + // map of shim-to-real function pointers in `relocations', + // otherwise populate the map using [the real] dlsym(): + if( cap->dl_handle ) + for( map = cap->meta->items; map->name; map++ ) + { + if( !map->shim ) + map->shim = (ElfW(Addr)) _capsule_original_dlsym( RTLD_DEFAULT, map->name ); + + if( !map->real ) + map->real = (ElfW(Addr)) _capsule_original_dlsym( cap->dl_handle, map->name ); + } + + // time to enter some sort of ... dangerous... zone: + // we need the mmap()ed DSO regions to have the PROT_WRITE + // flag set, so that if they've been RELRO linked we can still + // overwrite their GOT entries. + for( int i = 0; rdata.mmap_info[i].start != MAP_FAILED; i++ ) + if( mmap_entry_should_be_writable( &rdata.mmap_info[i] ) ) + add_mmap_protection( &rdata.mmap_info[i], PROT_WRITE ); + + dl_iterate_phdr( relocate_cb, &rdata ); + + // and now we put those PROT_WRITE permissions back the way they were: + for( int i = 0; rdata.mmap_info[i].start != MAP_FAILED; i++ ) + if( mmap_entry_should_be_writable( &rdata.mmap_info[i] ) ) + reset_mmap_protection( &rdata.mmap_info[i] ); + + if( rdata.error ) + { + if( error ) + *error = rdata.error; + else + free( rdata.error ); + + rval = (rdata.count.failure == 0) ? -1 : rdata.count.failure; + } + + free_mmap_info( rdata.mmap_info ); + rdata.mmap_info = NULL; + + return rval; +} + +int +_capsule_relocate (const capsule cap, char **error) +{ + DEBUG( DEBUG_RELOCS, "beginning global symbol relocation:" ); + return relocate( cap, cap->meta->items, RELOCATION_FLAGS_NONE, cap->seen.all, error ); +} + +static capsule_item capsule_external_dl_relocs[] = +{ + { "dlopen", + (capsule_addr) capsule_external_dlopen , + (capsule_addr) capsule_external_dlopen }, + { NULL } +}; + +int +_capsule_relocate_dlopen (const capsule cap, + char **error) +{ + unsigned long df = debug_flags; + + if( debug_flags & DEBUG_DLFUNC ) + debug_flags |= DEBUG_RELOCS; + + DEBUG( DEBUG_RELOCS, "beginning restricted symbol relocation:" ); + int rv = relocate( cap, capsule_external_dl_relocs, + RELOCATION_FLAGS_AVOID_LIBC, cap->seen.some, error ); + + debug_flags = df; + + return rv; +} diff --git a/subprojects/libcapsule/capsule/capsule-shim.h b/subprojects/libcapsule/capsule/capsule-shim.h new file mode 100644 index 0000000000000000000000000000000000000000..7b6eb7941f089544e33e970538154e565f54a11f --- /dev/null +++ b/subprojects/libcapsule/capsule/capsule-shim.h @@ -0,0 +1,59 @@ +// Copyright © 2017 Collabora Ltd + +// This file is part of libcapsule. + +// libcapsule is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as +// published by the Free Software Foundation; either version 2.1 of the +// License, or (at your option) any later version. + +// libcapsule 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 Lesser General Public License for more details. + +// You should have received a copy of the GNU Lesser General Public +// License along with libcapsule. If not, see <http://www.gnu.org/licenses/>. + +#include <stdio.h> +#include <stdlib.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <stdio.h> +#include <execinfo.h> + +#include <capsule/capsule.h> + +#define UNVERSIONED_STUB(name) \ + void name (void) __attribute__((noreturn,visibility("default"))); \ + void name (void) \ + { \ + fprintf(stderr, "! SHIM " #name " called\n" ); \ + backtrace_shim_call(); \ + abort(); \ + } + +#define VERSIONED_STUB(name,version) \ + UNVERSIONED_STUB(name); + +static void +backtrace_shim_call (void) +{ + void *trace[16] = { NULL }; + int traced = 0; + char **symbols = NULL; + + traced = backtrace( trace, sizeof(trace)/sizeof(void *) ); + symbols = backtrace_symbols( trace, traced ); + + for( int x = 1; x < traced; x++ ) + fprintf( stderr, " -> %s\n", symbols[x] ); + + free( symbols ); +} + +// We don't support versioned symbols properly yet, they need som +// asm magic that looks like this and I'm not clear on the details: +// __asm__(".symver _" #name "," #name #version) + diff --git a/subprojects/libcapsule/capsule/capsule-wrappers.c b/subprojects/libcapsule/capsule/capsule-wrappers.c new file mode 100644 index 0000000000000000000000000000000000000000..320829f70c5532fc684ed0c23d6713958baf3fb4 --- /dev/null +++ b/subprojects/libcapsule/capsule/capsule-wrappers.c @@ -0,0 +1,373 @@ +#include "capsule/capsule.h" +#include "capsule/capsule-private.h" +#include "capsule/capsule-malloc.h" +#include "utils/utils.h" +#include "utils/ld-libs.h" + +#include <stddef.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <unistd.h> +#include <malloc.h> + +static int +dso_is_exported (const char *dsopath, char **exported) +{ + for( char **ex = exported; ex && *ex; ex++ ) + if( soname_matches_path( *ex, dsopath ) ) + return 1; + + return 0; +} + +static void * +_dlsym_from_capsules (const char *symbol) +{ + void *addr = NULL; + + for( size_t n = 0; n < _capsule_list->next; n++ ) + { + capsule cap = ptr_list_nth_ptr( _capsule_list, n ); + + if( !cap ) + continue; + + // TODO: If handle != cap->dl_handle, should we skip it? + // TODO: RTLD_NEXT isn't implemented (is it implementable?) + addr = _capsule_original_dlsym ( cap->dl_handle, symbol ); + + if( addr ) + { + Dl_info dso = { 0 }; + + // only keep addr from the capsule if it's from an exported DSO: + // or if we are unable to determine where it came from (what?) + if( dladdr( addr, &dso ) ) + { + if( !dso_is_exported( dso.dli_fname, cap->ns->combined_export ) ) + addr = NULL; + + DEBUG( DEBUG_DLFUNC|DEBUG_WRAPPERS, + "symbol %s is from soname %s - %s", + symbol, dso.dli_fname, addr ? "OK" : "Ignored" ); + + if( addr ) + break; + } + } + } + + return addr; +} + +static int +_dlsymbol_is_encapsulated (const void *addr) +{ + Dl_info dso = { 0 }; + + // no info, symbol may not even be valid: + if( !dladdr( addr, &dso ) ) + return 0; + + // no file name, can't be a shim: + if( !dso.dli_fname || *dso.dli_fname == '\0' ) + return 0; + + // check to see if addr came from a registered capsule: + for( size_t n = 0; n < _capsule_list->next; n++ ) + { + capsule cap = ptr_list_nth_ptr( _capsule_list, n ); + + if( cap && soname_matches_path( cap->meta->soname, dso.dli_fname) ) + return 1; + } + + return 0; +} + +// TODO: Implement dlvsym()? +// TODO: RTLD_NEXT needs special handling + +// revised algorithm here: +// +// use the vanilla dlsym +// if nothing is found, peek into the whole capsule; return the result +// +// if a symbol is found, check to see if it came from a shim +// if it did (ie it is a dummy), peek into the capsule as above +// if it did not, return what was found +// +// The main weakness here is that if the caller expects to find a +// symbol XYZ via ‘handle’ which does _not_ come from the capsule +// but the capsule also has a symbol XYZ which is from an explicitly +// exported-from soname then the caller will get the capsule's +// XYZ symbol. +// +// We can't just check for RTLD_DEFAULT as the handle since +// dlopen( NULL, … ) and/or the RTLD_GLOBAL flag can be used to +// promote symbols that would otherwise not be visible from a given +// handle (libGL does this). +void * +capsule_external_dlsym (void *handle, const char *symbol) +{ + DEBUG( DEBUG_DLFUNC|DEBUG_WRAPPERS, "dlsym(%s)", symbol ); + void *addr = _capsule_original_dlsym ( handle, symbol ); + + // nothing found, must be from a capsule or nowhere at all: + if( !addr ) + { + DEBUG( DEBUG_DLFUNC|DEBUG_WRAPPERS, + "%s not found, searching capsule", symbol ); + addr = _dlsym_from_capsules( symbol ); + DEBUG( DEBUG_DLFUNC|DEBUG_WRAPPERS, + "capsule %s has address %p", symbol, addr ); + return addr; + } + + // found something. is it a dummy symbol from a shim? + if( _dlsymbol_is_encapsulated( addr ) ) + { + DEBUG( DEBUG_DLFUNC|DEBUG_WRAPPERS, + "dummy %s found, searching capsule", symbol ); + addr = _dlsym_from_capsules( symbol ); + DEBUG( DEBUG_DLFUNC|DEBUG_WRAPPERS, + "capsule %s has address %p", symbol, addr ); + return addr; + } + + DEBUG( DEBUG_DLFUNC|DEBUG_WRAPPERS, + "vanilla %s found at %p", symbol, addr ); + return addr; +} + +void * +capsule_external_dlopen(const char *file, int flag) +{ + void *handle = NULL; + char *error = NULL; + + if( _capsule_original_dlopen ) + { + handle = _capsule_original_dlopen ( file, flag ); + } + else + { + fprintf( stderr, + "capsule_external_dlopen() has no dlopen() implementation\n" ); + abort(); + } + + if( handle != NULL ) + { + unsigned long df = debug_flags; + + if( debug_flags & DEBUG_DLFUNC ) + debug_flags |= DEBUG_RELOCS; + // This may not even be necessary, so it should not be fatal. + // We do want to log it though as it might be an important clue: + for( size_t n = 0; n < _capsule_list->next; n++ ) + { + const capsule c = ptr_list_nth_ptr( _capsule_list, n ); + + if( !c ) + continue; + + if( _capsule_relocate( c, &error ) != 0 ) + { + fprintf( stderr, + "relocation from %s after dlopen(%s, …) failed: %s\n", + c->meta->soname, file, error ); + free( error ); + } + + if( _capsule_relocate_dlopen( c, &error ) != 0 ) + { + fprintf( stderr, + "dl-wrapper relocation from %s after " + "dlopen(%s, …) failed: %s\n", + c->meta->soname, file, error ); + free( error ); + } + } + + debug_flags = df; + } + + return handle; +} + +void * +capsule_shim_dlopen(const capsule cap, const char *file, int flag) +{ + void *res = NULL; + int code = 0; + char *errors = NULL; + ld_libs ldlibs = {}; + + DEBUG( DEBUG_WRAPPERS|DEBUG_DLFUNC, + "dlopen(%s, %x) wrapper: LMID: %ld; prefix: %s;", + file, flag, cap->ns->ns, cap->ns->prefix ); + + if( cap->ns->prefix && strcmp(cap->ns->prefix, "/") ) + { + if( !ld_libs_init( &ldlibs, (const char **)cap->ns->combined_exclude, + cap->ns->prefix, debug_flags, &code, &errors ) ) + { + DEBUG( DEBUG_LDCACHE|DEBUG_WRAPPERS|DEBUG_DLFUNC, + "Initialising ld_libs data failed: error %d: %s", + code, errors); + goto cleanup; + } + + if( !ld_libs_load_cache( &ldlibs, &code, &errors ) ) + { + DEBUG( DEBUG_LDCACHE|DEBUG_WRAPPERS|DEBUG_DLFUNC, + "Loading ld.so.cache from %s: error %d: %s", cap->ns->prefix, + code, errors ); + goto cleanup; + } + + // find the initial DSO (ie what the caller actually asked for): + if( !ld_libs_set_target( &ldlibs, file, &code, &errors ) ) + { + DEBUG( DEBUG_SEARCH|DEBUG_WRAPPERS|DEBUG_DLFUNC, + "Not found: %s under %s: error %d: %s", + file, cap->ns->prefix, code, errors ); + goto cleanup; + } + + // harvest all the requested DSO's dependencies: + if( !ld_libs_find_dependencies( &ldlibs, &code, &errors ) ) + { + DEBUG( DEBUG_WRAPPERS|DEBUG_DLFUNC, + "capsule dlopen error %d: %s", code, errors ); + goto cleanup; + } + + // load them up in reverse dependency order: + res = ld_libs_load( &ldlibs, &cap->ns->ns, flag, &code, &errors ); + + if( !res ) + DEBUG( DEBUG_WRAPPERS|DEBUG_DLFUNC, + "capsule dlopen error %d: %s", code, errors ); + + goto cleanup; + } + else // no prefix: straightforward dlmopen into our capsule namespace: + { + res = dlmopen( cap->ns->ns, file, flag ); + + if( !res ) + DEBUG( DEBUG_WRAPPERS|DEBUG_DLFUNC, + "capsule dlopen error %s: %s", file, dlerror() ); + } + + return res; + +cleanup: + ld_libs_finish( &ldlibs ); + free( errors ); + return res; +} + +#ifdef CAPSULE_MALLOC_EXTRA_CHECKS +static inline int chunk_is_vanilla (mchunkptr p, void *ptr) +{ + mstate av = arena_for_chunk (p); + + // arena_for_chunk can't find the main arena... but if this poiner + // is from the _main_ main arena then I think it would have been + // trapped by the heap check in capsule_shim_free already, so + // this did not come from the main instance of libc: + if( LIKELY(!av) ) + return 0; + + size_t size = chunksize (p); + mchunkptr nextchunk = chunk_at_offset(p, size); + + // invalid next size (fast) + if( UNLIKELY( nextchunk->size <= 2 * SIZE_SZ ) || + UNLIKELY( chunksize( nextchunk ) >= av->system_mem) ) + return 0; + + // double free or corruption (out) + if( UNLIKELY( contiguous(av) && + (char *)nextchunk >= (char *)av->top + chunksize( av->top )) ) + return 0; + + return 1; +} +#else +static inline chunk_is_vanilla (mchunkptr p, void *ptr) { return 0; } +#endif + +static int address_within_main_heap (ElfW(Addr) addr) +{ + static ElfW(Addr) base = (ElfW(Addr)) NULL; + ElfW(Addr) top = (ElfW(Addr)) sbrk( 0 ); + + // past the end of the heap: + if( top <= addr ) + return 0; + + if( base == (ElfW(Addr)) NULL ) + { + struct mallinfo mi = mallinfo(); + base = top - (ElfW(Addr)) mi.arena; + } + + // address is below heap base + // ∴ either a mmapped address, non-malloc'd memory + // or an address from a secondary arena + if( base > addr ) + return 0; + + return 1; +} + +void * +capsule_shim_realloc (const capsule cap, void *ptr, size_t size) +{ + if( !ptr || address_within_main_heap( (ElfW(Addr)) ptr ) ) + return realloc( ptr, size ); + + mchunkptr p = mem2chunk( ptr ); + + if( chunk_is_mmapped( p ) || chunk_is_vanilla( p, ptr ) ) + return realloc( ptr, size ); + + return cap->ns->mem->realloc( ptr, size ); +} + +void +capsule_shim_free (const capsule cap, void *ptr) +{ + if( !ptr ) + return; + + // from the main heap: ie from the vanilla libc outside the capsule + if( address_within_main_heap( (ElfW(Addr)) ptr ) ) + { + free( ptr ); + return; + } + + mchunkptr p = mem2chunk( ptr ); + // mmapped pointer/chunk: can't tell whose this is but since we + // override the malloc/free cluster as early as possible we're + // kind of hoping we don't have any of these from inside the capsule + // + // we'd only have such a pointer if the libraries we dlmopen() into + // the capsule allocated large chunks of memory in their initialiser(s): + if( chunk_is_mmapped( p ) || chunk_is_vanilla( p, ptr ) ) + { + free( ptr ); + return; + } + + // doesn't look like a valid pointer to the main libc, + // pass it to the capsule libc and hope for the best: + cap->ns->mem->free( ptr ); +} diff --git a/subprojects/libcapsule/capsule/capsule.h b/subprojects/libcapsule/capsule/capsule.h new file mode 100644 index 0000000000000000000000000000000000000000..1435514f5987d2005e38206ac4973f8bbedee4a3 --- /dev/null +++ b/subprojects/libcapsule/capsule/capsule.h @@ -0,0 +1,279 @@ +// Copyright © 2017 Collabora Ltd + +// This file is part of libcapsule. + +// libcapsule is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as +// published by the Free Software Foundation; either version 2.1 of the +// License, or (at your option) any later version. + +// libcapsule 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 Lesser General Public License for more details. + +// You should have received a copy of the GNU Lesser General Public +// License along with libcapsule. If not, see <http://www.gnu.org/licenses/>. + +#pragma once + +#include <link.h> + +#define _CAPSULE_PUBLIC __attribute__((visibility("default"))) extern + +/** + * capsule_addr: + * + * Identical to an ElfW(Addr) from libelf. You may treat this as + * equivalent to a void * when assigning to it. + */ +typedef ElfW(Addr) capsule_addr; + +/** + * capsule + * + * A handle returned by capsule_init: A required parameter for all + * other capsule calls. + */ +typedef struct _capsule *capsule; + +/** + * capsule_item: + * @name: The name of the symbol to be relocated + * @real: address of the ‘real’ symbol in the target library + * @shim: address of the ‘fake’ symbol in the proxy library + * + * @real and @shim may typically be left empty by the shim library. + * + * Both slots will typically hold the correct values after a successful + * capsule… call. While this is sometimes important internally it is + * not usually of interest to the caller (except maybe for debugging) + */ +typedef struct _capsule_item capsule_item; + +struct _capsule_item +{ + const char *name; + capsule_addr real; + capsule_addr shim; + + /*< private >*/ + void *unused0; + void *unused1; + void *unused2; + void *unused3; +}; + +/** + * capsule_metadata: + * @capsule_abi: Version of the libcapsule ABI implmented by this struct + * @soname: The soname of the encapsulated library + * @default_prefix: The default root location of the filesystem from which the + * encapsulated library should be loaded + * @exclude: (array zero-terminated=1): an array of char *, each + * specifying a DSO not to load, terminated by a %NULL entry + * @export: (array zero-terminated=1): an array of char *, each + * specifying a DSO whose symbols should be exported from this + * capsule + * @items: (array zero-terminated=1): Array of capsule_item + * specifying which symbols to export, terminated by a + * #capsule_item whose @name is %NULL + * @int_dlopen: Implementation of the same API as `dlopen` + * + * This struct allows libcapsule proxy libraries to statically declare + * metadata about themselves that libcapsule needs at link time in order + * to function properly. + * + * The #capsule_item entries in @items need only specify the symbol + * name: The shim and real fields will be populated automatically if they + * are not pre-filled (this is the normal use case, as it would be unusual + * to know these value in advance). + */ +typedef struct _capsule_metadata capsule_metadata; + +struct _capsule_metadata +{ + /*< public >*/ + const int capsule_abi; + const char *soname; + const char *default_prefix; + const char **exclude; + const char **export; + capsule_item *items; + void *(*int_dlopen) (const char *filename, int flag); + void (*int_free) (void *ptr); + void *(*int_realloc) (void *ptr, size_t size); + /*< private >*/ + capsule handle; +}; + +/** + * capsule_init: + * @soname: the soname of the target library + * + * Returns: a #capsule handle. + * + * Does any initialisation necessary to use libcapsule’s functions. + * + * Initialises internal accounting structures within the capsule + * and triggers the metadata setup if this caspsule has been + * acquired via dlopen(), and finishes registering the capsule + * proxy with libcapsule itself. + */ +_CAPSULE_PUBLIC +capsule capsule_init (const char *soname); + +//////////////////////////////////////////////////////////////////////////// +// do not move the Returns declaration in this next doc-comment: +// gtk-doc does not tolerate any non-whitespace text between the +// last parameter and the Returns line if the name of the function +// is capsule_shim_dlopen. This was discovered empirically. +// As this is clearly insane, it must be a bad interaction with something +// else which shares some element of that name, but I have no idea what. +//////////////////////////////////////////////////////////////////////////// + +/** + * capsule_shim_dlopen: + * @cap: The capsule from which `dlopen` was called + * @file: SONAME or filename to be opened + * @flag: `dlopen` flags to pass to the real `dlopen` call + * + * Returns: A handle as if for `dlopen` + * + * An implementation of dlopen suitable to be called from inside a + * namespace. Load @file into @cap namespace. + * + * If @cap has a non-trivial prefix, load @file and its recursive + * dependencies from @cap prefix instead of from the root filesystem. + * + * This helper function exists because dlopen() cannot safely be called + * by a DSO opened into a private namespace. It takes @file and @flag + * arguments cf dlopen() and a @cap handle, + * and performs a safe dlmopen() call instead. + * + * Typically this function is used to implement a safe wrapper for dlopen() + * which is assigned to the int_dlopen member of the #capsule_metadata. + * This * replaces calls to dlopen() by all DSOs in the capsule, + * allowing libraries which use dlopen() to work inside the capsule. + * + * Limitations: RTLD_GLOBAL is not supported in @flag. This is a glibc + * limitation in the dlmopen() implementation. + */ +_CAPSULE_PUBLIC +void *capsule_shim_dlopen (const capsule cap, const char *file, int flag); + +/** + * capsule_external_dlsym: + * @handle: A dl handle, as passed to dlsym() + * @symbol: A symbol name, as passed to dlsym() + * + * An implementation of `dlsym`, used when it is called by the executable + * or by a library outside the capsule. + * + * Some libraries have a use pattern in which their caller/user + * uses dlsym() to obtain symbols rather than using those symbols + * directly in its own code (libGL is an example of this). + * + * Since the target library may have a different symbol set than the + * one the libcapsule proxy shim was generated from we can’t rely on + * dlsym() finding those symbols in the shim’s symbol table. + * + * Instead we must intercept dlsym() calls made outside the capsule + * and attempt to look for the required symbol in the namespace defined + * by the active capsules first - If the required symbol is found there + * AND is from one of the DSO names present in the exported list then that + * symbol is returned. If either of those conditions is not met then + * a normal dlsym call with the passed handle is made. + * + * This function provides the functionality described above, and is + * normally used automatically by libcapsule. It is exposed as API in + * case a libcapsule proxy library needs to provide its own specialised + * symbol lookup mechanism. + * + * Returns: The address associated with @symbol (as if for `dlsym`), or %NULL + */ +_CAPSULE_PUBLIC +void *capsule_external_dlsym (void *handle, const char *symbol); + +/** + * capsule_external_dlopen: + * @file: A soname, filename or path as passed to dlopen() + * @flag: The dl flags, as per dlopen() + * + * An implementation of `dlopen`, used when it is called by the executable + * or by a library outside the capsule. + * + * This wrapper is meant to be replace normal calls to dlopen() made by the + * main program or a non-capsule library - it is necessary because en ELF + * object loaded by dlopen() may need us to trigger the _capsule_relocate() + * operation in order to make sure its GOT entries are correctly updated. + * + * This wrapper carries out a normal dlopen() and then re-triggers the + * initial _capsule_relocate() call immediately, before returning + * the same value that dlopen() would have, given the same @file and @flag + * arguments. + * + * Returns: The handle returned by `dlopen` + */ +_CAPSULE_PUBLIC +void *capsule_external_dlopen(const char *file, int flag); + +/** + * capsule_close: + * @cap: a #capsule handle as returned by capsule_init() + * + * This function should be called from a capsule proxy library’s destructor: + * Its job is to clean up capsule-specific allocated memory and metadata when + * a capsule proxy is discarded via dlclose(), and ensure that libproxy itself + * won’t try to access any related invalidated memory afterwards. + */ +_CAPSULE_PUBLIC +void capsule_close (capsule cap); + +/** + * capsule_get_prefix: + * @dflt: A default capsule prefix path + * @soname: The soname of the library we are encapsulating + * + * Returns: A newly allocated char * pointing to the prefix path + * + * libcapsule provides a proxy to a library, potentially from a foreign + * filesystem tree (found at, for example, ‘/host’). + * + * Since it is useful for this location to be overrideable at startup + * on a per-target-library basis this function standardises the prefix + * selection algorithm as follows: + * + * - An environment variable based on @soname: + * libGL.so.1 would map to CAPSULE_LIBGL_SO_1_PREFIX + * - If that is unset, the CAPSULE_PREFIX environment variable + * - Next: The default to the value passed in @dflt + * - And if all that failed, NULL (which is internally equivalent to "/") + * + * The environment variables are ignored if the process is privileged + * (setuid, setgid, given special capabilities, or marked as privileged + * by a LSM), or if libcapsule was compiled against a glibc version + * older than 2.17. + * + * Although the value is newly allocated it will typically be cached + * in a structure that needs to survive the entire lifespan of the + * running program, so freeing it is unlikely to be a concern. + **/ +_CAPSULE_PUBLIC +char *capsule_get_prefix(const char *dflt, const char *soname); + +/** + * capsule_shim_free: + * @cap: The capsule from which `free` was called + * @ptr: The pointer to be freed + * + * Tries to safely route an allocated pointer to the correct free() + * implementation. + * + */ +_CAPSULE_PUBLIC +void capsule_shim_free (const capsule cap, void *ptr); + +_CAPSULE_PUBLIC +void * capsule_shim_realloc (const capsule cap, void *ptr, size_t size); + diff --git a/subprojects/libcapsule/ci/Jenkinsfile b/subprojects/libcapsule/ci/Jenkinsfile new file mode 100644 index 0000000000000000000000000000000000000000..2b04fe5a29f0ae65f0461bafdaac369a2096d5dc --- /dev/null +++ b/subprojects/libcapsule/ci/Jenkinsfile @@ -0,0 +1,121 @@ +#!/usr/bin/env groovy +/* + * SPDX-License-Identifier: LGPL-2.1+ + * + * Copyright © 2017-2018 Collabora Ltd + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This package 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this package. If not, see + * <http://www.gnu.org/licenses/>. + */ +@Library('steamos-ci') _ + +pipeline { + agent none + + options { + timestamps() + skipDefaultCheckout() + } + + stages { + stage ("deb-build-snapshot") { + agent { + docker { + label 'docker-slave' + image "docker-registry.internal.collabora.com/steamos/package-builder:${env.CI_SUITE ?: 'brewmaster'}" + registryUrl 'https://docker-registry.internal.collabora.com' + registryCredentialsId 'ccu-docker-internal-collabora-com' + alwaysPull true + } + } + environment { + HOME="${env.WORKSPACE}" + TMPDIR="${env.WORKSPACE}" + PYTHONUNBUFFERED="1" + } + steps { + sh ''' + git config --global user.name Jenkins + git config --global user.email nobody@example.com + ''' + checkoutCollaboraGitlab('vivek/libcapsule', 'master', 'src') + sh ''' + mkdir -p debs + echo "jenkins::$(id -u):$(id -g):Jenkins:$(pwd):/bin/sh" > passwd + export NSS_WRAPPER_PASSWD=$(pwd)/passwd + export NSS_WRAPPER_GROUP=/dev/null + export LD_PRELOAD=libnss_wrapper.so + ( cd src && deb-build-snapshot -d ../debs -s -u localhost ) + ''' + stash name: 'debs', includes: 'debs/**' + archiveArtifacts 'debs/**' + } + post { + always { + deleteDir() + } + } + } + + stage ("autopkgtest") { + agent { + docker { + label 'docker-slave' + args '--device=/dev/kvm' + image 'docker-registry.internal.collabora.com/steamos/package-builder:stretch' + registryUrl 'https://docker-registry.internal.collabora.com' + registryCredentialsId 'ccu-docker-internal-collabora-com' + alwaysPull true + } + } + environment { + HOME="${env.WORKSPACE}" + TMPDIR="${env.WORKSPACE}" + PYTHONUNBUFFERED="1" + } + steps { + sh 'git config --global user.name Jenkins' + sh 'git config --global user.email nobody@example.com' + checkoutCollaboraGitlab('vivek/libcapsule', 'master', 'src') + unstash 'debs' + sh ''' + mkdir -p tmp + mkdir -p output + wget --progress=dot:giga -O autopkgtest.qcow2 https://images.collabora.co.uk/steam/autopkgtest-virt-qemu/${CI_SUITE:=brewmaster}_amd64.qcow2 + + case "${CI_SUITE}" in + (brewmaster) + set -- --add-apt-source='deb http://deb.debian.org/debian jessie main' + for x in /etc/apt/trusted.gpg.d/*.gpg; do + set -- --copy="$x:$x" "$@" + done + ;; + (*) + set -- + ;; + esac + + autopkgtest "$@" --apt-upgrade --no-built-binaries --output-dir ./output/ debs/*.deb ./src/ -- qemu --qemu-options="-drive format=raw,read-only,if=pflash,file=/usr/share/ovmf/OVMF.fd" ./autopkgtest.qcow2 + ''' + } + post { + always { + archiveArtifacts 'output/**' + deleteDir() + } + } + } + } +} +/* vim:set sts=2 sw=2 et: */ diff --git a/subprojects/libcapsule/ci/gitlab-ci.yml b/subprojects/libcapsule/ci/gitlab-ci.yml new file mode 100644 index 0000000000000000000000000000000000000000..5dcef84aadb0b23ed05f1eed01d6cb8ff76cf3f7 --- /dev/null +++ b/subprojects/libcapsule/ci/gitlab-ci.yml @@ -0,0 +1,163 @@ +# Copyright © 2019 Collabora Ltd. +# +# SPDX-License-Identifier: MIT +# +# 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. + +variables: + BUILD_IMAGE: 'debian:buster-slim' + CI_DISABLE_LIBRARY: '' + CI_DISTCHECK: '' + CI_MAKE_J: '-j -l2' + + # A known-good version of deb-build-snapshot + DEB_BUILD_SNAPSHOT_TAG: v0.20190828.0 + +before_script: + - | + set -eux + apt-get -y update + apt-get install -y --no-install-recommends \ + autoconf \ + autoconf-archive \ + automake \ + bubblewrap \ + build-essential \ + ca-certificates \ + chrpath \ + debhelper \ + devscripts \ + dh-autoreconf \ + git \ + gtk-doc-tools \ + libelf-dev \ + libipc-run-perl \ + libglib2.0-dev \ + libxml2 \ + perl \ + python3-debian \ + rsync \ + xsltproc \ + zlib1g \ + ${NULL+} + +.build: + image: ${BUILD_IMAGE} + script: | + set -eux + NOCONFIGURE=1 ./autogen.sh + ./configure \ + CC=${CC-cc} \ + ${CI_DISABLE_LIBRARY+--disable-library} \ + SANITIZE_CFLAGS="${SANITIZE_CFLAGS-}" \ + ${NULL+} + make ${CI_MAKE_J} + make ${CI_MAKE_J} check + if [ -n "$CI_DISTCHECK" ]; then + make ${CI_MAKE_J} distcheck + fi + artifacts: + paths: + - config.log + - test-suite.log + +build:ubsan: + extends: .build + variables: + SANITIZE_CFLAGS: "-fsanitize=undefined -fno-sanitize=alignment -static-libubsan" + +build:asan: + extends: .build + variables: + # The actual libcapsule library doesn't work with AddressSanitizer, + # but at least we can unit-test utility code and test the tools + CI_DISABLE_LIBRARY: "yes" + SANITIZE_CFLAGS: "-fsanitize-recover=all -fsanitize=address -fsanitize=undefined -fno-sanitize=alignment -fPIE -pie" + +.deb-build-snapshot: + image: ${BUILD_IMAGE} + script: | + set -eux + + set -- --upstream --source + + mkdir -p "debian/tmp/artifacts/${CI_JOB_NAME}" + set -- "$@" --download "$(pwd)/debian/tmp/artifacts/${CI_JOB_NAME}" + + if [ -n "${CI_DISPOSABLE_ENVIRONMENT-}" ]; then + set -- "$@" --install --force-local-install + fi + + tempdir="$(mktemp -d)" + git clone --branch ${DEB_BUILD_SNAPSHOT_TAG} https://gitlab.collabora.com/smcv/deb-build-snapshot.git "$tempdir/deb-build-snapshot" + "$tempdir/deb-build-snapshot/deb-build-snapshot" "$@" localhost + rm -fr "$tempdir" + artifacts: + paths: + - debian/tmp/artifacts/${CI_JOB_NAME} + +build:stretch: + extends: .deb-build-snapshot + variables: + BUILD_IMAGE: 'debian:stretch-slim' + +build:buster: + extends: .deb-build-snapshot + variables: + BUILD_IMAGE: 'debian:buster-slim' + +build:bionic: + extends: .deb-build-snapshot + variables: + BUILD_IMAGE: 'ubuntu:bionic' + +build:arch: + when: manual + extends: .build + variables: + BUILD_IMAGE: 'archlinux' + before_script: + # bubblewrap-suid instead of bubblewrap will be necessary on some + # kernels. + # + # Full test coverage is likely to require running with + # docker run --privileged, which Gitlab-CI doesn't normally do; + # as much as anything, this is here to document what's necessary + # to build libcapsule with full test coverage. + - | + pacman -Syu --needed --noconfirm --noprogressbar \ + autoconf \ + autoconf-archive \ + automake \ + bubblewrap \ + chrpath \ + gcc \ + glib2 \ + gtk-doc \ + libelf \ + libtool \ + libxml2 \ + libxslt \ + make \ + perl \ + perl-ipc-run \ + pkgconf \ + sudo \ + ${NULL+} diff --git a/subprojects/libcapsule/configure.ac b/subprojects/libcapsule/configure.ac new file mode 100644 index 0000000000000000000000000000000000000000..00b6911b5ecae6e84c8b97f4bdd621ec67804ba1 --- /dev/null +++ b/subprojects/libcapsule/configure.ac @@ -0,0 +1,138 @@ +dnl Copyright © 2017 Collabora Ltd + +dnl This file is part of libcapsule. + +dnl libcapsule is free software: you can redistribute it and/or modify +dnl it under the terms of the GNU Lesser General Public License as +dnl published by the Free Software Foundation; either version 2.1 of the +dnl License, or (at your option) any later version. + +dnl libcapsule is distributed in the hope that it will be useful, +dnl but WITHOUT ANY WARRANTY; without even the implied warranty of +dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +dnl GNU Lesser General Public License for more details. + +dnl You should have received a copy of the GNU Lesser General Public +dnl License along with libcapsule. If not, see <http://www.gnu.org/licenses/>. + +AC_PREREQ([2.60]) + +AC_INIT([libcapsule], + m4_esyscmd([build-aux/git-version-gen .tarball-version]), + [], [], [https://gitlab.collabora.com/vivek/libcapsule]) +AC_CONFIG_AUX_DIR([build-aux]) +AC_CONFIG_MACRO_DIR([m4]) +AC_REQUIRE_AUX_FILE([tap-driver.sh]) +AC_ARG_VAR([PERL]) +AC_PATH_PROG([PERL], [perl], [perl]) + +m4_pattern_forbid( + [^AX_(IS_RELEASE|COMPILER_FLAGS)], + [Please install GNU autoconf-archive 2016.09.16 or later]) + +dnl don't kvetch about gnu makefile syntax +dnl and don't require ChangeLog &co if they don't exist +AM_INIT_AUTOMAKE([-Wno-portability foreign]) +AM_SILENT_RULES([yes]) +AC_USE_SYSTEM_EXTENSIONS +AC_SYS_LARGEFILE +# So we can install x86_64-linux-gnu-capsule-symbols etc. +AC_CANONICAL_HOST +# Make sure we are not mixing up --host=i686-linux-gnu with a 64-bit compiler +# or vice versa +CAPSULE_MATCHING_ABI + +LT_INIT + +AX_IS_RELEASE([dash-version]) +dnl -Wno-pointer-arith: We do a lot of arithmetic on void *, relying on gcc +dnl making (void * + int) the same as (char * + int) +dnl -Wno-declaration-after-statement: Stylistic choice, this is C99 +AX_COMPILER_FLAGS([], [], [], [], [ \ + -Wno-pointer-arith \ + -Wno-declaration-after-statement \ +]) +WARN_CFLAGS=$(echo "$WARN_CFLAGS" | sed -Ee 's,-W(error=)?declaration-after-statement,,') +WARN_CFLAGS=$(echo "$WARN_CFLAGS" | sed -Ee 's,-W(error=)?pointer-arith,,') + +AC_CHECK_FUNCS_ONCE([secure_getenv]) +AC_CHECK_HEADERS_ONCE([stdalign.h]) + +AM_CONDITIONAL([ENABLE_SHARED], [test "x$enable_shared" = xyes]) + +AC_ARG_ENABLE([library], + [AS_HELP_STRING([--enable-library], + [build the libcapsule library (disable if you only want CLI tools)])], + [], + [enable_library="$enable_shared"]) +AS_IF([test "x$enable_shared" != xyes && test "x$enable_library" = xyes], + [AC_MSG_ERROR([libcapsule is only useful as a shared library])]) +AM_CONDITIONAL([ENABLE_LIBRARY], [test "x$enable_library" = xyes]) + +dnl make sure we've got the _right_ libelf (doesn't always ship a .pc) +PKG_CHECK_MODULES([LIBELF], [libelf >= 0.152], [have_libelf=yes], [ + AC_MSG_WARN([libelf.pc not found]) + AC_MSG_WARN([Set LIBELF_CFLAGS, LIBELF_LIBS if -lelf is insufficient]) + save_LIBS="$LIBS" + AC_CHECK_TYPE([Elf_Cmd], [], [], [#include <libelf.h>]) + AC_CHECK_LIB([elf], [elf_begin], [], + [AC_MSG_ERROR([libelf not found])]) + AC_MSG_CHECKING([Elf_Cmd ELF_C_READ_MMAP]) + AC_LINK_IFELSE([AC_LANG_PROGRAM([#include <libelf.h>], + [Elf_Cmd x = ELF_C_READ_MMAP;])], + [AC_MSG_RESULT([yes])], + [AC_MSG_RESULT([no])] + [AC_MSG_ERROR([ELF_C_READ_MMAP missing: libelfg vs libelf0?])]) + LIBS="$save_LIBS" + LIBELF_LIBS=-lelf +]) + +AC_ARG_WITH([glib], + [AS_HELP_STRING([--with-glib=yes/no], + [use GLib for better test coverage [default=yes]])], + [], + [with_glib=auto]) +AS_IF([test "$with_glib" != no], + [PKG_CHECK_MODULES([GLIB], [glib-2.0 >= 2.38], [have_glib=yes], [have_glib=no])], + [have_glib=no]) +AS_IF([test "$with_glib" = yes && test "$have_glib" = no], + [AC_MSG_ERROR([GLib required but not found])]) +AM_CONDITIONAL([HAVE_GLIB], [test "x$have_glib" = xyes]) + +AC_ARG_ENABLE([host-prefix], + [AS_HELP_STRING([--enable-host-prefix=PREFIX/yes/no], + [install link to tools with a prefix [default=$host-]])], + [], + [enable_host_prefix=yes]) + +AM_CONDITIONAL([ENABLE_HOST_PREFIX], [test "x$enable_host_prefix" != xno]) +AS_IF([test "x$enable_host_prefix" = xyes], + [enable_host_prefix="$host-"]) +AS_IF([test "x$enable_host_prefix" = xno], + [enable_host_prefix=""]) +AC_SUBST([HOST_PREFIX], [$enable_host_prefix]) + +AC_ARG_ENABLE([tools-rpath], + [AS_HELP_STRING([--enable-tools-rpath=RPATH/yes/no], + [install link to tools with a RPATH ready to override with chrpath])], + [], + [enable_tools_rpath=no]) + +AS_IF([test "x$enable_tools_rpath" = xyes], + [enable_tools_rpath="/_ORIGIN_/__/lib/$host"]) +AS_IF([test "x$enable_tools_rpath" = xno], + [enable_tools_rpath=""]) +AC_SUBST([enable_tools_rpath]) +AM_CONDITIONAL([ENABLE_TOOLS_RPATH], [test "x$enable_tools_rpath" != x]) + +AC_SEARCH_LIBS([dlmopen], [dl]) + +AC_PATH_PROG([XSLTPROC], [xsltproc]) +AM_CONDITIONAL([HAVE_XSLTPROC], [test "x$XSLTPROC" != x]) +GTK_DOC_CHECK + +AC_ARG_VAR([SANITIZE_CFLAGS], + [Extra CFLAGS for modules that are instrumented for error-checking]) + +AC_CONFIG_FILES([Makefile data/libcapsule.pc data/libcapsule-uninstalled.pc data/libcapsule-tools.pc data/libcapsule-tools-uninstalled.pc data/dirconf.txt]) +AC_OUTPUT diff --git a/subprojects/libcapsule/data/capsule-init-project b/subprojects/libcapsule/data/capsule-init-project new file mode 100755 index 0000000000000000000000000000000000000000..d044cd026f4615a0da1a9c8fbf5bd60e1f79c4df --- /dev/null +++ b/subprojects/libcapsule/data/capsule-init-project @@ -0,0 +1,297 @@ +#!/bin/bash + +set -e; +set -u; + +me=$(basename $0); + +usage () +{ +cat << EOF +Usage: $me [OPTIONS] TARGET... + Options: + --capsule-pkgdatadir=PATH + Use supporting files from PATH + [default: libcapsule's \${pkgdatadir}] + --capsule-symbols-tool=PATH + Use replacement capsule-symbols(1) + [default: ${CAPSULE_SYMBOLS_TOOL:-ask pkg-config}] + --capsule-version-tool=PATH + Use replacement capsule-version(1) + [default: ${CAPSULE_VERSION_TOOL:-ask pkg-config}] + --search-tree=PATH Find libraries to be proxied in this + chroot, sysroot or container now + [default: /] + --set-version=VERSION Use this as the library's version number + (major, major.minor or major.minor.micro) + [default: based on first TARGET] + --symbols-from-dir=PATH Use symbols from files in this directory + instead of running capsule-symbols(1) + --runtime-tree=PATH Generated code will find proxied + libraries in this chroot, sysroot or + container at runtime if CAPSULE_PREFIX + is unset [default: /host] + --package-name=PACKAGE Use this name for the generated package + [default: based on name of first TARGET] + --destination=PATH Create the project here + [default: ./PACKAGE] + TARGET... SONAMEs of the libraries to be proxied + (libfoo.so.2), or libfoo.so.2/2.1.3 + to force a version +EOF + exit "${1:-0}" +} + +getopt_temp="$(getopt -o 'h' \ + -l 'autoreconf,no-autoreconf,capsule-pkgdatadir:,capsule-symbols-tool:,capsule-version-tool:,destination:,package-name:,search-tree:,set-version:,symbols-from-dir:,runtime-tree:,help' \ + -n "$me" -- "$@")" +eval set -- "$getopt_temp" + +autoreconf=yes +dest= +package_name= +runtime_tree= +search_tree= +symbols_from= +ver= + +while true; +do + case "$1" in + (--help|-h) + usage 0; + ;; + + (--autoreconf) + autoreconf=yes; + shift; + continue; + ;; + + (--no-autoreconf) + autoreconf=; + shift; + continue; + ;; + + (--capsule-pkgdatadir) + CAPSULE_MKINC="$2" + shift 2; + continue; + ;; + + (--capsule-symbols-tool) + CAPSULE_SYMBOLS_TOOL="$2" + shift 2; + continue; + ;; + + (--capsule-version-tool) + CAPSULE_VERSION_TOOL="$2" + shift 2; + continue; + ;; + + (--destination) + dest="$2" + shift 2; + continue; + ;; + + (--package-name) + package_name="$2" + shift 2; + continue; + ;; + + (--runtime-tree) + runtime_tree="$2" + shift 2; + continue; + ;; + + (--search-tree) + search_tree="$2" + shift 2; + continue; + ;; + + (--set-version) + ver="$2" + shift 2; + continue; + ;; + + (--symbols-from-dir) + symbols_from="$2" + shift 2; + continue; + ;; + + (--) + shift; + break; + ;; + (*) + echo "$me: Internal error" >&2; + usage 2 >&2; + ;; + esac +done + +if [ "$#" -lt 1 ]; +then + usage 2 >&2; +fi; + +first_target_maybe_ver="$1"; +first_target="${first_target_maybe_ver%%/*}"; + +if [ -z "$package_name" ]; +then + package_name="${first_target%%.so.*}"; + package_name="${package_name%%.so}-proxy"; +fi; + +if [ -z "$dest" ]; +then + dest="$package_name"; +fi; + +: "${PKG_CONFIG:=pkg-config}" +: "${CAPSULE_MKINC:="$($PKG_CONFIG --variable=makeinc libcapsule-tools)"}" +: "${CAPSULE_SYMBOLS_TOOL:="$($PKG_CONFIG --variable=CAPSULE_SYMBOLS_TOOL libcapsule-tools)"}" +: "${CAPSULE_VERSION_TOOL:="$($PKG_CONFIG --variable=CAPSULE_VERSION_TOOL libcapsule-tools)"}" +: "${runtime_tree:="/host"}" +: "${search_tree:="/"}" + +if [ -z "$ver" ]; +then + if [ "x$first_target" != "x$first_target_maybe_ver" ]; + then + ver="${first_target_maybe_ver#*/}"; + else + read x x ver x < <("$CAPSULE_VERSION_TOOL" "$first_target" "$search_tree"); + fi; +fi; + +echo "Generating project for $package_name, shims for $runtime_tree libraries: $*"; + +echo "Creating project directory $dest"; + +mkdir -p "$dest"; +cd $dest; +mkdir -p m4; +mkdir -p shim; + +echo "Working in $PWD"; + +escaped_target="$(echo "$first_target" | sed -e 's/\\/\\\\/g' -e 's/,/\\,/g')" +escaped_search_tree="$(echo "$search_tree" | sed -e 's/\\/\\\\/g' -e 's/,/\\,/g')" +escaped_runtime_tree="$(echo "$runtime_tree" | sed -e 's/\\/\\\\/g' -e 's/,/\\,/g')" +sed \ + -e "1,/^\$/d" \ + -e "s,@TARGET@,$escaped_target," \ + < "$CAPSULE_MKINC/shim-README.in" \ + > README; + +echo Initialising configure.ac; + +sed \ + -e "1,/^\$/d" \ + -e "s,@RUNTIME_TREE@,$escaped_runtime_tree," \ + -e "s,@SEARCH_TREE@,$escaped_search_tree," \ + -e "s,@PACKAGE@,$package_name," \ + -e "s,@VER@,$ver," \ + < "$CAPSULE_MKINC/shim-configure.ac.in" \ + > configure.ac; + +echo Initialising Makefile.am; + +sed \ + -e "1,/^\$/d" \ + -e "s,@RUNTIME_TREE@,$escaped_runtime_tree," \ + -e "s,@SEARCH_TREE@,$escaped_search_tree," \ + -e "s,@PACKAGE@,$package_name," \ + -e "s,@VER@,$ver," \ + < "$CAPSULE_MKINC/shim-Makefile.am" \ + > Makefile.am; + +for target_maybe_ver in "$@"; do + target="${target_maybe_ver%%/*}"; + if [ "x$target" != "x$target_maybe_ver" ]; + then + target_ver="${target_maybe_ver#*/}"; + else + read x x target_ver x < <("$CAPSULE_VERSION_TOOL" "$target" "$search_tree"); + fi; + + base=${target%%.so.*}; + base=${base%.so}; + name=${base#lib}; + lcbase=${base,,}; + amname=${name//-/_}; + + escaped_target="$(echo "$target" | sed -e 's/\\/\\\\/g' -e 's/,/\\,/g')" + automake_target="${escaped_target//-/_}" + escaped_lib="$(echo "$name" | sed -e 's/\\/\\\\/g' -e 's/,/\\,/g')" + automake_lib="$(echo "$amname" | sed -e 's/\\/\\\\/g' -e 's/,/\\,/g')" + + maj=${ver%%.*}; + + echo "Preparing proxy source files: $target" + touch shim/$target.{shared,excluded}; + + if [ -z "$symbols_from" ]; then + echo Extracting dynamic symbols from $search_tree : $target; + + "$CAPSULE_SYMBOLS_TOOL" "$target" "$search_tree" > "shim/$target.symbols.tmp"; + LC_ALL=C sort -u "shim/$target.symbols.tmp" > "shim/$target.symbols"; + rm -f "shim/$target.symbols.tmp"; + else + LC_ALL=C sort -u "$symbols_from/$target.symbols" > "shim/$target.symbols"; + fi + touch "shim/$target.symbols.updated-for"; + + sed \ + -e "1,/^\$/d" \ + -e "s,@TARGET@,$escaped_target," \ + -e "s,@AMTARGET@,$automake_target," \ + -e "s,@LIB@,$escaped_lib," \ + -e "s,@AMLIB@,$automake_lib," \ + -e "s,@RUNTIME_TREE@,$escaped_runtime_tree," \ + -e "s,@SEARCH_TREE@,$escaped_search_tree," \ + -e "s,@PACKAGE@,$package_name," \ + -e "s,@TARGET_VER@,$target_ver," \ + < "$CAPSULE_MKINC/shim-target-Makefile.am" \ + >> Makefile.am; +done + +cp "$CAPSULE_MKINC/shim-.gitignore" .gitignore + +if [ -n "$autoreconf" ]; then + echo Running initial configuration; + autoreconf -ivf +fi + +cat - <<EOF +$PWD is ready for ./configure, see README for details. + +You will probably want to add the following files to your version +control system: + +Makefile.am +README +configure.ac +EOF + + +for target_maybe_ver in "$@"; do + target="${target_maybe_ver%%/*}"; + cat - <<EOF +shim/$target.excluded +shim/$target.shared +shim/$target.symbols +shim/$target.symbols.updated-for +EOF +done diff --git a/subprojects/libcapsule/data/capsule-mkstublib b/subprojects/libcapsule/data/capsule-mkstublib new file mode 100755 index 0000000000000000000000000000000000000000..8871f22f10c1eb68ec8392efe265f5c5aeaa4dff --- /dev/null +++ b/subprojects/libcapsule/data/capsule-mkstublib @@ -0,0 +1,342 @@ +#!/bin/bash + +# Copyright © 2017 Collabora Ltd + +# This file is part of libcapsule. + +# libcapsule is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as +# published by the Free Software Foundation; either version 2.1 of the +# License, or (at your option) any later version. + +# libcapsule 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 Lesser General Public License for more details. + +# You should have received a copy of the GNU Lesser General Public +# License along with libcapsule. If not, see <http://www.gnu.org/licenses/>. + +set -u +set -e + +declare -A NODE; +top=$(dirname $0); +top=${top:-.}; +me=$(basename $0); + +usage () +{ +cat << EOF +Usage: $me [OPTIONS] TARGET EXCLUDES EXPORTS OUTPUT [DSO-VERSION [TREE]]" + Options: + --capsule-pkgdatadir=PATH + Use supporting files from PATH + [default: libcapsule's \${pkgdatadir}] + --capsule-symbols-tool=PATH + Use replacement capsule-symbols(1) + --symbols-from=PATH Use symbols from this file + [default: \${OUTPUT%.c}.symbols] + --[no-]update-symbols Do or don't update symbols list if EXPORTS + is newer than it [default: do] + --search-tree=PATH Find libraries to be proxied in this + chroot, sysroot or container now + [default: TREE or /] + --runtime-tree=PATH Generated code will find proxied + libraries in this chroot, sysroot or + container at runtime if CAPSULE_PREFIX + is unset [default: TREE or /host] + Positional parameters: + TARGET SONAME of shared library to proxy, + for example libGL.so.1 + EXCLUDES List of SONAMEs never loaded from TREE + in addition to libdl.so.2 + EXPORTS List of extra SONAMEs whose symbols are + to be exported from the shim, in addition + to TARGET itself + OUTPUT C source code filename to output, + for example libGL.so.c + DSO-VERSION Major or MAJOR:MINOR:MICRO version (as for + libtool -version-number) to export, for + example 1 or 1:x:y for libGL.so.1 + [default: parse SONAME] + TREE Chroot or container to find libraries in, + both now and at runtime; overridden by + --runtime-tree and --search-tree +EOF + exit "${1:-0}" +} + +update_symbols= + +getopt_temp="$(getopt -o 'h' \ + -l 'capsule-symbols-tool:,search-tree:,runtime-tree:,symbols-from:,update-symbols,no-update-symbols,help' \ + -n "$me" -- "$@")" +eval set -- "$getopt_temp" + +while true; +do + case "$1" in + (--capsule-symbols-tool) + CAPSULE_SYMBOLS_TOOL="$2" + shift 2; + continue; + ;; + + (--runtime-tree) + runtime_tree="$2" + shift 2; + continue; + ;; + + (--search-tree) + search_tree="$2" + shift 2; + continue; + ;; + + (--symbols-from) + symbol_file="$2" + shift 2; + continue; + ;; + + (--update-symbols) + update_symbols=yes; + shift; + continue; + ;; + + (--no-update-symbols) + update_symbols=; + shift; + continue; + ;; + + (--help|-h) + usage 0; + ;; + + (--) + shift; + break; + ;; + (*) + echo "$me: Internal error" >&2; + usage 2 >&2; + ;; + esac +done + +: "${PKG_CONFIG:=pkg-config}" +: "${CAPSULE_SYMBOLS_TOOL:="$($PKG_CONFIG --variable=CAPSULE_SYMBOLS_TOOL libcapsule-tools)"}" + +if [ "$#" -lt 4 ] || [ "$#" -gt 6 ]; +then + usage 2 >&2; +fi; + +proxied_dso=$1; shift; +proxy_excluded=$1; shift; +proxy_extra=$1; shift; +proxy_src=$1; shift; + +if [ "$#" -gt 0 ]; +then + ltver=$1; shift; +fi + +if [ "$#" -gt 0 ]; +then + proxy_tree=$1; shift; +else + proxy_tree= +fi + +if [ -n "${ltver:-}" ]; +then + major_version="${ltver%%:*}" +elif [ "x${proxied_dso%.so.*}" != "x$proxied_dso" ]; then + major_version="${proxied_dso##*.so.}" +fi + +: "${runtime_tree:="$proxy_tree"}" +: "${runtime_tree:=/host}" +: "${search_tree:="$proxy_tree"}" +: "${search_tree:=/}" + +dso_base=${proxied_dso#lib} +dso_base=${dso_base%.so*} +proxied_dso=lib${dso_base}.so.${major_version}; + +map_file=${proxy_src%.c}.map; + +if [ -z "${symbol_file:-}" ]; +then + symbol_file=${proxy_src%.c}.symbols; +fi + +exec >$proxy_src.tmp; + +echo '#include "capsule/capsule-shim.h"' + +# generate the .symbols file if it doesn't exist, or if the proxy_extra +# control file has been updated: +if [ -n "$update_symbols" ] && [ $symbol_file -ot $proxy_extra ]; +then + echo -n > $symbol_file; + if (for pt in $proxied_dso $(cat $proxy_extra); + do + if [ x$V = x1 ]; + then + echo " $CAPSULE_SYMBOLS_TOOL $pt $search_tree" >&2; + else + echo " SYMBOLS $pt $search_tree" >&2; + fi; + $CAPSULE_SYMBOLS_TOOL $pt $search_tree || exit 1; + done) > $symbol_file.tmp; + then + LC_ALL=C sort -u "$symbol_file.tmp" > "$symbol_file.tmp2"; + rm -f "$symbol_file.tmp"; + mv "$symbol_file.tmp2" "$symbol_file"; + else + code=$?; + rm $symbol_file.tmp + exit $code; + fi; +fi; + + +while read symbol version dependency; +do + case $version in + @*) + echo "VERSIONED_STUB ( $symbol, $version );"; + node=${version##*@}; + NODE[$node]=${NODE[$node]:-}" "$symbol; + ;; + *) + echo "UNVERSIONED_STUB( $symbol );"; + ;; + esac; +done < $symbol_file; + +cat - <<EOF +static capsule cap; +static const char soname[] = "$proxied_dso"; + +// _int_dlopen() from libcapsule or a locally overridden version +#include "capsule/_int_dlopen.h" + +// Array of SONAMEs that will not have a private copy inside a capsule. +// Only one copy of each of these will be loaded, and they will +// be loaded without respecting the #capsule_namespace's @prefix. +static const char *exclude[] = +{ +EOF + +while read excluded x; +do + case $excluded in lib*) printf "%32s \"%s\",\n" "" $excluded; ;; esac; +done < $proxy_excluded; + +cat - <<EOF + NULL +}; + +// DSOs to restrict dlsym lookups to: +static const char *valid_dlsym_sources[] = +{ + soname, +EOF + +if [ -f "${proxy_extra}" ]; +then + for pt in $(cat $proxy_extra); + do + cat - <<EOF + "$pt", +EOF + done; +fi; + +cat - <<EOF + NULL +}; + +// ------------------------------------------------------------- +// this is an array of the functions we want to act as a shim for: +static capsule_item relocs[] = +{ +EOF + +while read sym x; +do + cat - <<EOF + { "$sym" }, +EOF +done < $symbol_file; + +cat - <<EOF + { "dlsym", (capsule_addr) capsule_external_dlsym, + (capsule_addr) capsule_external_dlsym, }, + { NULL } +}; + +// make sure this symbol has global visibility so +// that the libcapsule initialisation can find it +__attribute__ ((visibility("default"))) +capsule_metadata capsule_meta = +{ + .capsule_abi = 0, + .soname = soname, + .default_prefix = "/host", + .exclude = exclude, + .export = valid_dlsym_sources, + .items = relocs, + .int_dlopen = _int_dlopen, + .int_free = _wrapped_free, + .int_realloc = _wrapped_realloc, +}; + +static void __attribute__ ((constructor)) _capsule_init (void) +{ + // Don't use capsule_meta.soname here, because if we did, it would be + // a relocatable, interposable reference to capsule_meta (assuming the + // shim library wasn't linked with -Bsymbolic). At runtime, we'd get + // an arbitrarily chosen one of the capsule_meta symbols defined by + // the other shim libraries sharing our global symbol namespace, + // and in particular not necessarily our own, with predictably bad + // results. + cap = capsule_init( soname ); +} + +static void __attribute__ ((destructor)) _capsule_exit (void) +{ + capsule_close( cap ); + cap = NULL; +} +EOF + +echo -n > $map_file; +exec >& $map_file; + +for node in ${!NODE[@]}; +do + echo "$node {"; + echo " global:"; + for symbol in ${NODE[$node]}; + do + echo " $symbol;"; + done; + echo "};"; + echo; +done; + +# scrub the (symbol version) map file if it's empty: +if [ ! -s $map_file ]; +then + rm -f $map_file; +fi; + +mv "$proxy_src.tmp" "$proxy_src"; diff --git a/subprojects/libcapsule/data/capsule-shim.mk b/subprojects/libcapsule/data/capsule-shim.mk new file mode 100644 index 0000000000000000000000000000000000000000..bd9bf066384522e2c459aca8fed48a1dc33f7ba5 --- /dev/null +++ b/subprojects/libcapsule/data/capsule-shim.mk @@ -0,0 +1,65 @@ +# allow stub file generation to be quiet or verbose per the value of V +V ?= 0 +GENSTUB_V1 = +GENSTUB_V0 = @echo " GENSTUB " $@; +GENSTUB = $(GENSTUB_V$(V)) + +CAPSULE_TREE ?= /host +CAPSULE_RUNTIME_TREE ?= $(CAPSULE_TREE) +CAPSULE_SEARCH_TREE ?= $(CAPSULE_TREE) + +comma = , + +define foreach_shim = +shim_ldflags_$(subst -,_,$(1)) = -version-number $$(subst .,:,$$(CAPSULE_VERSION_$(subst -,_,$(1)))) \ + $$(patsubst %,-Wl$$(comma)--version-script=%,$$(wildcard shim/$(1).map)) +maintainer-update-capsule-symbols: maintainer-update-capsule-symbols/$(1) +endef + +$(foreach CAPSULE_SONAME,$(CAPSULE_SONAMES),$(eval $(call foreach_shim,$(CAPSULE_SONAME)))) + +# regenerate if any dependencies get updated: +shim/%.c: $(srcdir)/shim/%.excluded $(srcdir)/shim/%.shared $(srcdir)/shim/%.symbols + $(GENSTUB)V=$V \ + $(CAPSULE_MKSTUBLIB_TOOL) \ + --no-update-symbols \ + --symbols-from=$(srcdir)/shim/$*.symbols \ + $(AM_CAPSULE_MKSTUBLIB_FLAGS) \ + $(CAPSULE_MKSTUBLIB_FLAGS) \ + $* \ + $(srcdir)/shim/$*.excluded \ + $(srcdir)/shim/$*.shared \ + $@ \ + $(subst .,:,$(CAPSULE_VERSION_$(subst -,_,$*))) \ + $(CAPSULE_RUNTIME_TREE) + +# error out when it's time to regenerate the exportable symbols list +# (we do not do this automatically because it's part of our ABI) +$(srcdir)/shim/%.symbols: $(srcdir)/shim/%.shared + @if cmp -s $< $@.updated-for; then \ + touch $@; \ + else \ + echo "*** ERROR: List of shared libraries has changed" >&2; \ + echo "*** Run: make maintainer-update-capsule-symbols [CAPSULE_SEARCH_TREE=/some-sysroot]" >&2; \ + exit 1; \ + fi + +maintainer-update-capsule-symbols: always + @true +.PHONY: maintainer-update-capsule-symbols + +maintainer-update-capsule-symbols/%: always + $(AM_V_GEN)set -e; \ + shared=$(srcdir)/shim/$*.shared; \ + out=$(srcdir)/shim/$*.symbols; \ + rm -f $$out.updated-for; \ + for dso in $* $$(cat "$$shared"); \ + do \ + $(CAPSULE_SYMBOLS_TOOL) $$dso $(CAPSULE_SEARCH_TREE); \ + done > "$$out.tmp"; \ + LC_ALL=C sort -u "$$out.tmp" > "$$out.tmp2"; \ + rm -f "$$out.tmp"; \ + mv "$$out.tmp2" "$$out"; \ + cp $(srcdir)/shim/$*.shared $$out.updated-for + +.PHONY: always diff --git a/subprojects/libcapsule/data/dirconf.txt.in b/subprojects/libcapsule/data/dirconf.txt.in new file mode 100644 index 0000000000000000000000000000000000000000..b1bcd397c2465a418f69c5213136aa7495de8850 --- /dev/null +++ b/subprojects/libcapsule/data/dirconf.txt.in @@ -0,0 +1,22 @@ +datarootdir : @datarootdir@ +bindir : @bindir@ +datadir : @datadir@ +datarootdir : @datarootdir@ +docdir : @docdir@ +dvidir : @dvidir@ +exec_prefix : @exec_prefix@ +htmldir : @htmldir@ +includedir : @includedir@ +infodir : @infodir@ +libdir : @libdir@ +bindir : @bindir@ +localedir : @localedir@ +localstatedir : @localstatedir@ +mandir : @mandir@ +oldincludedir : @oldincludedir@ +pdfdir : @pdfdir@ +prefix : @prefix@ +psdir : @psdir@ +sbindir : @sbindir@ +sharedstatedir: @sharedstatedir@ +sysconfdir : @sysconfdir@ diff --git a/subprojects/libcapsule/data/disabled.mk b/subprojects/libcapsule/data/disabled.mk new file mode 100644 index 0000000000000000000000000000000000000000..a1d0bd339f4d7ce7b784b3f660c95fa813e709f7 --- /dev/null +++ b/subprojects/libcapsule/data/disabled.mk @@ -0,0 +1,6 @@ +# all these catch-all rules do is slow things down +%:: %,v +%:: RCS/% +%:: RCS/%,v +%:: SCCS/s.% +%:: s.% diff --git a/subprojects/libcapsule/data/libcapsule-tools-uninstalled.pc.in b/subprojects/libcapsule/data/libcapsule-tools-uninstalled.pc.in new file mode 100644 index 0000000000000000000000000000000000000000..3aff18ac82b3d82f1d477133aff0d900d7b17016 --- /dev/null +++ b/subprojects/libcapsule/data/libcapsule-tools-uninstalled.pc.in @@ -0,0 +1,10 @@ +makeinc=@abs_top_srcdir@/data +CAPSULE_CAPTURE_LIBS_TOOL=@abs_top_builddir@/capsule-capture-libs +CAPSULE_INIT_PROJECT_TOOL=@abs_top_srcdir@/data/capsule-init-project +CAPSULE_MKSTUBLIB_TOOL=@abs_top_srcdir@/data/capsule-mkstublib +CAPSULE_SYMBOLS_TOOL=@abs_top_builddir@/capsule-symbols +CAPSULE_VERSION_TOOL=@abs_top_builddir@/capsule-version + +Name: libcapsule-tools +Description: segregated dynamic linking library - development tools +Version: @VERSION@ diff --git a/subprojects/libcapsule/data/libcapsule-tools.pc.in b/subprojects/libcapsule/data/libcapsule-tools.pc.in new file mode 100644 index 0000000000000000000000000000000000000000..6e38223f6a43b5588ffcc5eb074708b2f76c4425 --- /dev/null +++ b/subprojects/libcapsule/data/libcapsule-tools.pc.in @@ -0,0 +1,17 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ +libexecdir=@libexecdir@ +datarootdir=@datarootdir@ +datadir=@datarootdir@ +makeinc=@datadir@/libcapsule +CAPSULE_CAPTURE_LIBS_TOOL=@bindir@/@HOST_PREFIX@capsule-capture-libs +CAPSULE_INIT_PROJECT_TOOL=@bindir@/capsule-init-project +CAPSULE_MKSTUBLIB_TOOL=@bindir@/@HOST_PREFIX@capsule-mkstublib +CAPSULE_SYMBOLS_TOOL=@bindir@/@HOST_PREFIX@capsule-symbols +CAPSULE_VERSION_TOOL=@bindir@/@HOST_PREFIX@capsule-version + +Name: libcapsule-tools +Description: segregated dynamic linking library - development tools +Version: @VERSION@ diff --git a/subprojects/libcapsule/data/libcapsule-uninstalled.pc.in b/subprojects/libcapsule/data/libcapsule-uninstalled.pc.in new file mode 100644 index 0000000000000000000000000000000000000000..eab9321c7858476e36f147770f7aa7d539c69568 --- /dev/null +++ b/subprojects/libcapsule/data/libcapsule-uninstalled.pc.in @@ -0,0 +1,10 @@ +libdir=@abs_top_builddir@/capsule +includedir=@abs_top_srcdir@/capsule + +Name: libcapsule +Description: segregated dynamic linking library +Version: @VERSION@ + +Requires: +Libs: -L${libdir} -lcapsule +Cflags: -I${includedir} diff --git a/subprojects/libcapsule/data/libcapsule.pc.in b/subprojects/libcapsule/data/libcapsule.pc.in new file mode 100644 index 0000000000000000000000000000000000000000..1c5300b5168e7ec26ffe8dc82a0619bc307bcaa4 --- /dev/null +++ b/subprojects/libcapsule/data/libcapsule.pc.in @@ -0,0 +1,15 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ +libexecdir=@libexecdir@ +datarootdir=@datarootdir@ +datadir=@datarootdir@ + +Name: libcapsule +Description: segregated dynamic linking library +Version: @VERSION@ + +Requires: +Libs: -L${libdir} -lcapsule +Cflags: -I${includedir} diff --git a/subprojects/libcapsule/data/shim-.gitignore b/subprojects/libcapsule/data/shim-.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..bca62216bdb700810ee6ec5cf67bfd115d53ea86 --- /dev/null +++ b/subprojects/libcapsule/data/shim-.gitignore @@ -0,0 +1,21 @@ +*.a +*.la +*.lo +*.o +.deps/ +.dirstamp +.libs/ +/Makefile +/Makefile.in +/aclocal.m4 +/autom4te.cache/ +/build-aux/ +/capsule-shim.mk +/config.log +/config.status +/configure +/libtool +/m4/ +/preconfigure.log +/shim/*.so.*.c +/shim/*.so.*.map diff --git a/subprojects/libcapsule/data/shim-Makefile.am b/subprojects/libcapsule/data/shim-Makefile.am new file mode 100644 index 0000000000000000000000000000000000000000..a25dacf1d3d58ed119e4b80c4ad940bc798ec863 --- /dev/null +++ b/subprojects/libcapsule/data/shim-Makefile.am @@ -0,0 +1,17 @@ +# vim:set ft=automake: +# Processed by capsule-init-project. Everything before the first blank +# line is removed during processing. + +AUTOMAKE_OPTIONS = subdir-objects +ACLOCAL_AMFLAGS = -I m4 +AM_CFLAGS = -std=c99 -D_GNU_SOURCE -Wall -Werror +AM_CFLAGS += $(CAPSULE_CFLAGS) +AM_CAPSULE_MKSTUBLIB_FLAGS := + +CAPSULE_SONAMES := +lib_LTLIBRARIES = +CLEANFILES = +BUILT_SOURCES = + +include @CAPSULE_MKINC@/disabled.mk +include @CAPSULE_MKINC@/capsule-shim.mk diff --git a/subprojects/libcapsule/data/shim-README.in b/subprojects/libcapsule/data/shim-README.in new file mode 100644 index 0000000000000000000000000000000000000000..d40105106a72dc0cef18961c97c6adb97e8d2acd --- /dev/null +++ b/subprojects/libcapsule/data/shim-README.in @@ -0,0 +1,27 @@ +# Processed by capsule-init-project. Everything before the first blank +# line is removed during processing. + +This is a libcapsule based proxy library. + +Its purpose is to allow a library (@TARGET@) to be loaded from a +foreign filesystem tree (usually /host, but configurable at compile +time) without exposing any other libraries from that tree to the +program using it. + +You can export the symbols of more than one library via the proxy: +To do so, add a line containing the SONAME (libFOO.so.X) +of each extra library to shim/@TARGET@.c.shared and rebuild. + +You can also prevent a library from the foreign tree from being loaded +at all (at least as a result of being a dependency of @TARGET@). +To achieve this, add a line containing the SONAME (libFOO.so.X) +of the excluded library to shim/@TARGET@.c.excluded and rebuild. + +The generated library will have the same name as the main proxy target +(@TARGET@). You can control the exact version number (cf libfoo.x.y.z) +by setting CAPSULE_VERSION_@AMTARGET@ in Makefile.am + +If libcapsule's standard implementation of dlopen() for callers inside +the capsule is not suitable, copy capsule/_int_dlopen.h from +libcapsule's development files into shim/capsule/, modify it as +necessary, and add it to EXTRA_DIST in Makefile.am. diff --git a/subprojects/libcapsule/data/shim-configure.ac.in b/subprojects/libcapsule/data/shim-configure.ac.in new file mode 100644 index 0000000000000000000000000000000000000000..ef685b3fe926c7f39e31e517c17338642b97b151 --- /dev/null +++ b/subprojects/libcapsule/data/shim-configure.ac.in @@ -0,0 +1,50 @@ +# vim:set ft=config: +# Processed by capsule-init-project. Everything before the first blank +# line is removed during processing. + +AC_PREREQ([2.60]) + +AC_INIT([@PACKAGE@], [@VER@]) +AC_CONFIG_AUX_DIR([build-aux]) +AC_CONFIG_MACRO_DIR([m4]) + +dnl don't kvetch about gnu makefile syntax +dnl and don't require ChangeLog &co if they don't exist +AM_INIT_AUTOMAKE([-Wno-portability foreign]) +AM_SILENT_RULES([yes]) +AC_USE_SYSTEM_EXTENSIONS +AC_SYS_LARGEFILE + +LT_INIT([disable-static]) + +PKG_CHECK_MODULES([CAPSULE], [libcapsule]) +m4_ifndef([PKG_CHECK_VAR], + [AC_DEFUN([PKG_CHECK_VAR], + [AC_ARG_VAR([$1]) + : ${$1:=$PKG_CONFIG --variable="$3" "$2"}])]) +PKG_CHECK_VAR([CAPSULE_MKINC], [libcapsule-tools], [makeinc]) +PKG_CHECK_VAR([CAPSULE_MKSTUBLIB_TOOL], [libcapsule-tools], [CAPSULE_MKSTUBLIB_TOOL]) +PKG_CHECK_VAR([CAPSULE_SYMBOLS_TOOL], [libcapsule-tools], [CAPSULE_SYMBOLS_TOOL]) + +AC_ARG_WITH([runtime-tree], + [AS_HELP_STRING([--with-runtime-tree=PATH], + [Where to find encapsulated libraries at runtime [@RUNTIME_TREE@]])], + [], [with_runtime_tree=${CAPSULE_RUNTIME_TREE:-${CAPSULE_TREE:-@RUNTIME_TREE@}}]) +AS_CASE([$with_runtime_tree], + [/*], + [], + [AC_MSG_ERROR([--with-runtime-tree requires an absolute path argument])]) +AC_SUBST([CAPSULE_RUNTIME_TREE], [$with_runtime_tree]) + +AC_ARG_WITH([search-tree], + [AS_HELP_STRING([--with-search-tree=PATH], + [Where to find encapsulated libraries when updating symbols list [@SEARCH_TREE@]])], + [], [with_search_tree=${CAPSULE_SEARCH_TREE:-${CAPSULE_TREE:-@SEARCH_TREE@}}]) +AS_CASE([$with_search_tree], + [/*], + [], + [AC_MSG_ERROR([--with-search-tree requires an absolute path argument])]) +AC_SUBST([CAPSULE_SEARCH_TREE], [$with_search_tree]) + +AC_CONFIG_FILES([Makefile]) +AC_OUTPUT diff --git a/subprojects/libcapsule/data/shim-target-Makefile.am b/subprojects/libcapsule/data/shim-target-Makefile.am new file mode 100644 index 0000000000000000000000000000000000000000..1c439897da4baae2f1bd914cde90e0fc91641052 --- /dev/null +++ b/subprojects/libcapsule/data/shim-target-Makefile.am @@ -0,0 +1,19 @@ +# vim:set ft=automake: +# Processed by capsule-init-project. Everything before the first blank +# line is removed during processing; the second blank line is to +# provide some spacing between target libraries. + + +# -------- @TARGET@ -------- + +CAPSULE_SONAMES += @TARGET@ +CAPSULE_VERSION_@AMTARGET@ := @TARGET_VER@ +lib_LTLIBRARIES += lib@LIB@.la + +nodist_lib@AMLIB@_la_SOURCES = shim/@TARGET@.c +lib@AMLIB@_la_LDFLAGS = $(shim_ldflags_@AMTARGET@) +lib@AMLIB@_la_LIBADD = $(CAPSULE_LIBS) + +CLEANFILES += $(nodist_lib@AMLIB@_la_SOURCES) \ + $(nodist_lib@AMLIB@_la_SOURCES:.c=.map) +BUILT_SOURCES += $(nodist_lib@AMLIB@_la_SOURCES) diff --git a/subprojects/libcapsule/debian/.gitignore b/subprojects/libcapsule/debian/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..642ca1661ba18469d15e5c5a924d12d33a003c60 --- /dev/null +++ b/subprojects/libcapsule/debian/.gitignore @@ -0,0 +1,18 @@ +/*.debhelper +/*.log +/*.substvars +/autoreconf* +/files +/libGL-proxy/ +/libGL-proxy-build/ +/libcapsule-dev/ +/libcapsule-dev-bin +/libcapsule-doc/ +/libcapsule-gl-shims/ +/libcapsule-tools/ +/libcapsule-tools-relocatable/ +/libcapsule-libgl1-shims/ +/libcapsule-tests/ +/libcapsule0/ +/*-stamp +/tmp/ diff --git a/subprojects/libcapsule/debian/changelog b/subprojects/libcapsule/debian/changelog new file mode 100644 index 0000000000000000000000000000000000000000..2c3eadea86e162033cd1d9a77404d86256efcf60 --- /dev/null +++ b/subprojects/libcapsule/debian/changelog @@ -0,0 +1,276 @@ +libcapsule (0.20210114.0-0co1) upstream; urgency=medium + + * ldlibs: Try loading Clear Linux ld.so cache (/var/cache/ldconfig). + Partially addresses ValveSoftware/steam-runtime#345 + * tests: Silence some Perl warnings + * tests: Ignore symbol-versions in nm output. + The Debian 11 toolchain produces them like this. + + -- Simon McVittie <smcv@collabora.com> Thu, 14 Jan 2021 14:57:29 +0000 + +libcapsule (0.20210104.0-0co1) upstream; urgency=medium + + * ld-libs: Try loading Exherbo architecture-specific ld.so cache + before generic path. + I'm using Exherbo as an example of a host OS with an unusual layout that + breaks libcapsule's normal assumptions. As well as fixing Exherbo + itself, this puts the framework in place to deal with other + distributions' architecture-specific ld.so caches. + - Developers of other distributions with unusual ld.so cache paths + can edit ld_cache_filenames in utils/ld-cache.c and bind_usr() in + tests/CapsuleTest.pm to teach libcapsule about their paths. + * tests: Use libxml2 instead of libjpeg to test versioned symbols + * tests: Make Exherbo architecture-specific ld.so stuff available too + * tests: Allow even more variations of what a $libdir looks like + + -- Simon McVittie <smcv@collabora.com> Mon, 04 Jan 2021 17:08:59 +0000 + +libcapsule (0.20201120.0-0co1) upstream; urgency=medium + + [ Ludovico de Nittis ] + * capture-libs: Add support for remapping link target prefix. + If you don't want to change the link target for all the symlinks that + will be created, instead of using `--link-target`, it is now possible to + use the new `--remap-link-prefix` and just list the directories that + should be located under a different target. + + [ Simon McVittie ] + * debian: Suppress automatic debug symbols for libcapsule-tools-relocatable. + The debug symbols for libcapsule-tools-relocatable would be the same as + for libcapsule-tools, making them non-co-installable. Use the environment + variable instead of --no-automatic-dbgsym for compatibility with + Steam Runtime 1 'scout', which is too old to have that option. + + -- Simon McVittie <smcv@collabora.com> Fri, 20 Nov 2020 14:09:54 +0000 + +libcapsule (0.20201022.0-0co1) upstream; urgency=medium + + [ Ludovico de Nittis ] + * capture-libs: Skip libs with unmet dependencies when "if exists" flag + is set. + If we have the CAPTURE_FLAG_IF_EXISTS option flag, we should not + propagate an error if we are not able to find some dependencies of a + library. Instead we should just skip it. + This fixes an error that some users reported because they had some + leftover unused libraries from the NVIDIA proprietary driver, + and libcapsule reported an error while trying to capture their + (missing) dependencies. + + -- Simon McVittie <smcv@collabora.com> Thu, 22 Oct 2020 10:05:30 +0100 + +libcapsule (0.20200921.0-0co1) upstream; urgency=medium + + * debian: Delete files that were generated by gtk-doc during dist. + This fixes failure to build from the tarball with gtk-doc >= 1.30, + when the tarball was generated with older gtk-doc. + + -- Simon McVittie <smcv@collabora.com> Mon, 21 Sep 2020 19:17:22 +0100 + +libcapsule (0.20200908.0-0co1) upstream; urgency=medium + + [ Ludovico de Nittis ] + * utils: Add public/private knowledge about symbol versions and symbols. + We are now able to list the symbol versions and/or symbols that are + known to be public/private. + This is especially useful for comparing libraries that removed symbol + versions, or symbols, from one release to another. As this already + happened multiple times in the past, like for example with + `libdrm_nouveau.so.2` or `libedit.so.2`. + + -- Simon McVittie <smcv@collabora.com> Tue, 08 Sep 2020 18:48:30 +0100 + +libcapsule (0.20200708.0-0co1) upstream; urgency=medium + + [ Ludovico de Nittis, Simon McVittie ] + * utils: Add functions to compare symbols and versions with libelf. + When two libraries have the same numeric tail we were not able to + reliably determine which one was the newer. In particular, many + distributions install libgcc_s.so.1 as a regular file, rather than + a symlink to a versioned name, so library_cmp_by_name() can't work. + + [ Simon McVittie ] + * ci: Run CI with UndefinedBehaviourSanitizer and AddressSanitizer + * tests: Work around a binutils behaviour change (see #964457) + * capture-libs: Make library comparison configurable on the command-line. + This is primarily useful for testing and experimenting. + * capture-libs: Add --library-knowledge option. + This gives capsule-capture-libs a source of library-specific knowledge, + which we can use to choose the correct library. (T16562) + + -- Simon McVittie <smcv@collabora.com> Wed, 08 Jul 2020 13:49:28 +0100 + +libcapsule (0.20200624.0-0co1) upstream; urgency=medium + + * ld-libs: Always clear ldlibs->needed entry if ld_lib_open() fails, + fixing mishandling of libraries that are newer in the container but + only for one architecture + * build: Check that the compiler and --host are consistent + * build: Silence unwanted warnings + * Add basic Gitlab-CI on Debian (with deb-build-snapshot) and Arch Linux + * capture-libs: Expand list of known ld.so names + * capture-libs: Allow patterns to be read from a file + * tests: Fix Debianisms + * tests/capture-libs: Tolerate path to ld.so not being fully resolved + * capsule-malloc: Remove duplicate typedef for mstate, for clang 8 + * Distinguish between enabling the library and enabling shared libraries. + Some tests for capsule-capture-libs will need to compile shared + libraries, even if libcapsule itself is not built. + * tests: Make older linkers happy with one of our test libraries + * tests: Cope with newer versions of GNU make + * debian: Tell git-version-gen that we are behaving as an upstream + + -- Simon McVittie <smcv@collabora.com> Wed, 24 Jun 2020 19:06:02 +0100 + +libcapsule (0.20190926.0-0co1) upstream; urgency=medium + + * utils: Add --help, --version and GNU argument parsing to all tools + * capture-libs: Clarify by introducing more temporaries + * capture-libs: Factor out library_cmp_by_name() + * capture-libs: Treat unversioned libraries as in indeterminate order. + This avoids always choosing the container version of libgcc_s.so.1 + when using a Debian-derived container on Fedora, even if the host + version is newer. + + -- Simon McVittie <smcv@collabora.com> Thu, 26 Sep 2019 16:45:42 +0100 + +libcapsule (0.20190724.0-0co1) upstream; urgency=medium + + * process_pt_dynamic: Add missing 'break' to DT_JMPREL case + * safe_strncpy: Work around -Werror=stringop-truncation + * Add some notes on building from git on older host systems + * capture-libs: Fix a misleading debug message + * capture-libs: Don't capture results of path-match if they are other ABIs + + -- Simon McVittie <smcv@collabora.com> Wed, 24 Jul 2019 17:13:32 +0100 + +libcapsule (0.20190402.0-0co1) upstream; urgency=medium + + * debian: Install tools with canonicalized multiarch prefix + * debian: Build relocatable versions of libcapsule binary tools + * debian: Use dh_missing --fail-missing + * Build *.1 man pages with xsltproc even if gtk-doc is disabled + * shim: Add a minimal fallback version of PKG_CHECK_VAR + * Skip GLib tests when GLib is older than 2.38 + + -- Simon McVittie <smcv@collabora.com> Tue, 02 Apr 2019 17:32:19 +0100 + +libcapsule (0.20190130.0-0co1) upstream; urgency=medium + + * Be more compatible with older distributions: + - debian: Be compatible with Ubuntu 14.04's dpkg + - Compile on older glibc that didn't have secure_getenv() + - dump: Avoid naming collision with basename() + - capture-libs: Avoid shadowing global abs() + - process-pt-dynamic: Don't print name of R_386_SIZE32 if undefined + - devhelp2man: Add minimal support for older gtk-doc versions + - debian: Install libglib2.0-dev if building documentation, + as required by older gtk-doc versions + - debian: Use macros from libcapsule itself to build libGL proxy + - tests/utils.c: Tolerate archaic GLib versions + * autogen.sh: Install macros into m4/ + * capsule-init-project: Create m4/ + * debian: Separate documentation into libcapsule-doc + * debian: Don't build with fatal warnings + * debian: Explicitly disable silent makefile rules, + which debhelper didn't always do for us + * build: Be more specific about the tokens we don't want to see, + to future-proof against newer autoconf-archive versions + * ci: Check out the right git repository + * CapsuleTest: Tolerate bwrap not being installed at all + * tests/capture-libs.pl: Make executable + + -- Simon McVittie <smcv@collabora.com> Wed, 30 Jan 2019 15:48:43 +0000 + +libcapsule (0.20180517.0-0co1) upstream; urgency=medium + + * build: Bundle updated m4 macros in source releases so that they can + be built on older host OSs that do not have a recent autoconf-archive + * capture-libs test: Cope with i686, cmov CPU capability flags + * tests: Fix perl warnings for shadowing a local variable + * Fix cross-compilation for i386 on amd64: + - Architecture-qualify perl as :any + - Only build GLib-based tests on amd64 since libglib2.0-dev was not + multiarch co-installable until recently + - tests: Work when compiling for i386 on amd64 + * ci: Run autopkgtests on Debian stretch and SteamOS brewmaster + + -- Simon McVittie <smcv@collabora.com> Thu, 17 May 2018 21:35:21 +0100 + +libcapsule (0.20180430.0-0co1) upstream; urgency=medium + + [ Vivek Das Mohapatra ] + * Fix a potential segfault in the ELF memory dumper + * Replace the free/*alloc cluster inside the capsule + * Improve API documentation + * Improve debug output + * debian: Ignore some package/build debris + + [ Simon McVittie ] + * Add capture-libs tool and use it for tests/manual/gl.pl + * Don't stop reading symbol tables at an unknown ELFW_ST_TYPE, ELFW_ST_BIND + * Improve error propagation + * Improve debug output + * utils: Use getenv rather than secure_getenv to reduce glibc dependencies + (libcapsule itself still uses secure_getenv) + * libGL: Use the symbol set from GLVND libGL + * libGL: Add shims for libXi, libgbm, libGLX, libEGL, libOpenGL, libGLESv2 + * libGL: Don't exclude misc X libraries from encapsulation + * tests/manual/gl.pl: Bind-mount more host files into the container + * tests/manual/gl.pl: Merge /gl and /updates into /overrides + * tests/manual/gl.pl: Make anything from the host available via /run/host + * build: Install capsule-elf-dump + * build: Consolidate lists of GL shims using patsubst + * build: Add maintainer-update-gl-shims convenience target + * build: Make host prefix for tools modifiable + * build: Add a build option to add a -rpath to the tools + * build: Allow shared library to be disabled, if you only want the tools + * build: Don't run a test program, just link it, to help cross-compiling + * ci: Add a Jenkinsfile to build libcapsule on SteamOS + * debian: Add more test dependencies + + -- Simon McVittie <smcv@collabora.com> Mon, 30 Apr 2018 14:35:22 +0100 + +libcapsule (0.20171116.0-0co1) upstream; urgency=medium + + [ Vivek Das Mohapatra ] + * Fix NULL dereferences after dlclose() + * Improve dlsym() emulation + + [ Simon McVittie ] + * tests/manual/gl.pl: Provide finer-grained control over libraries + * tests/manual/gl.pl: Provide shortcuts for plausible approaches to + Mesa and Nvidia graphics stacks + * Adjust packaging so libcapsule-tools can be installed without + libcapsule-dev, for better ability to test multiarch containers + - capsule.pc has been replaced by libcapsule.pc (library) + and libcapsule-tools.pc (tools) + + -- Simon McVittie <smcv@collabora.com> Thu, 16 Nov 2017 13:42:22 +0000 + +libcapsule (0.20171113.0-0co1) upstream; urgency=medium + + [ Vivek Das Mohapatra ] + * Allow multiple encapsulated libraries, sharing one namespace per prefix + * Generate working Autotools projects for libraries with hyphen/minus in + their names + * Handle DT_REL entries + * Cope with shim libraries being dlclose()d + + [ Simon McVittie ] + * Change capsule-init-project invocation so it can generate multiple + shim libraries per Autotools project + * Mark libcapsule as non-deletable so its exported functions can never + disappear from library users' relocations + * Always use the same dlopen() implementation outside the capsule + * Automatically exclude glibc from encapsulation + * Install libGL shims in a new libcapsule-libgl1-shims binary package + * Install development tools into $(bindir) + + -- Simon McVittie <smcv@collabora.com> Mon, 13 Nov 2017 18:57:50 +0000 + +libcapsule (0.20171019.0-0co1) upstream; urgency=medium + + [ Vivek Das Mohapatra ] + * Initial release + + -- Simon McVittie <smcv@collabora.com> Thu, 19 Oct 2017 16:18:55 +0100 diff --git a/subprojects/libcapsule/debian/clean b/subprojects/libcapsule/debian/clean new file mode 100644 index 0000000000000000000000000000000000000000..13ce54a46a8b98deee3978a8fe7ab20209a1ea3d --- /dev/null +++ b/subprojects/libcapsule/debian/clean @@ -0,0 +1,4 @@ +gtk-doc.make +libcapsule-docs.xml +libcapsule-overrides.txt +libcapsule-sections.txt diff --git a/subprojects/libcapsule/debian/compat b/subprojects/libcapsule/debian/compat new file mode 100644 index 0000000000000000000000000000000000000000..ec635144f60048986bc560c5576355344005e6e7 --- /dev/null +++ b/subprojects/libcapsule/debian/compat @@ -0,0 +1 @@ +9 diff --git a/subprojects/libcapsule/debian/control b/subprojects/libcapsule/debian/control new file mode 100644 index 0000000000000000000000000000000000000000..8872be41fe91c00031db47b4b9cf8e9218d988b7 --- /dev/null +++ b/subprojects/libcapsule/debian/control @@ -0,0 +1,157 @@ +Source: libcapsule +Section: libs +Priority: optional +Maintainer: Vivek Das Mohapatra <vivek@collabora.com> +Standards-Version: 3.9.8 +Build-Depends: + autoconf, + autoconf-archive <pkg.libcapsule.from-git>, + automake, + bubblewrap, + chrpath, + debhelper, + dh-autoreconf, + gtk-doc-tools, + libelf-dev, + libipc-run-perl, + libglib2.0-0 <!nocheck>, + libglib2.0-dev [amd64], + libxml2 <!nocheck>, + perl:any | perl, + xsltproc, + zlib1g <!nocheck>, +Build-Depends-Indep: + libglib2.0-dev, +Vcs-Git: https://gitlab.collabora.com/vivek/libcapsule.git +Vcs-Browser: https://gitlab.collabora.com/vivek/libcapsule +Homepage: https://gitlab.collabora.com/vivek/libcapsule + +Package: libcapsule-dev +Architecture: amd64 i386 +Multi-Arch: same +Section: libdevel +Depends: + libc6-dev, + libcapsule0 (= ${binary:Version}), + libelf-dev, +Description: + libcapsule is a helper library used to implement segregated run-time + dynamic linking proxy libraries - used (for example) to load a library + that has dependencies incompatible with the regular libraries that are + part of the host OS, such as an incompatible libstdc++ version. + . + This package contains the files needed to build libcapsule proxy libraries. + +Package: libcapsule-doc +Architecture: all +Multi-Arch: foreign +Section: doc +Depends: + ${misc:Depends}, +Description: + libcapsule is a helper library used to implement segregated run-time + dynamic linking proxy libraries - used (for example) to load a library + that has dependencies incompatible with the regular libraries that are + part of the host OS, such as an incompatible libstdc++ version. + . + This package contains documentation. + +Package: libcapsule-tools +Architecture: amd64 i386 +Multi-Arch: same +Section: libdevel +Depends: + ${shlibs:Depends}, +Recommends: + libcapsule-dev (= ${binary:Version}), +Breaks: libcapsule-dev-bin (<< ${binary:Version}), +Replaces: libcapsule-dev-bin (<< ${binary:Version}), +Description: + libcapsule is a helper library used to implement segregated run-time + dynamic linking proxy libraries - used (for example) to load a library + that has dependencies incompatible with the regular libraries that are + part of the host OS, such as an incompatible libstdc++ version. + . + This package contains the files needed to generate libcapsule proxy libraries. + +Package: libcapsule-tools-relocatable +Architecture: amd64 i386 +Multi-Arch: same +Section: misc +Depends: + ${shlibs:Depends}, +Description: + libcapsule is a helper library used to implement segregated run-time + dynamic linking proxy libraries - used (for example) to load a library + that has dependencies incompatible with the regular libraries that are + part of the host OS, such as an incompatible libstdc++ version. + . + This package contains relocatable versions of the tools used to enumerate + library symbols and dependencies. After building this package on a system + with the oldest glibc that is to be supported, they can be bundled in + projects that need to run on an arbitrary host system and make use of + libcapsule to prepare a container. To do this, the relocatable tools + must be copied to PATH/bin/TUPLE-capsule-TOOL, and their required + non-glibc libraries (libelf and zlib) must be copied to PATH/lib/TUPLE. + +Package: libcapsule-gl-shims +Architecture: amd64 i386 +Multi-Arch: same +Section: libs +Breaks: libcapsule-libgl1-shims (<< ${binary:Version}), +Replaces: libcapsule-libgl1-shims (<< ${binary:Version}), +Description: OpenGL-related shim libraries + libcapsule is a helper library used to implement segregated run-time + dynamic linking proxy libraries - used (for example) to load a library + that has dependencies incompatible with the regular libraries that are + part of the host OS, such as an incompatible libstdc++ version. + . + This package contains libcapsule proxy libraries for libGL, libGLX, + libOpenGL and related libraries. + +Package: libcapsule-libgl1-shims +Architecture: amd64 i386 +Multi-Arch: same +Section: libs +Depends: libcapsule-gl-shims +Description: OpenGL-related shim libraries - transitional package + libcapsule is a helper library used to implement segregated run-time + dynamic linking proxy libraries - used (for example) to load a library + that has dependencies incompatible with the regular libraries that are + part of the host OS, such as an incompatible libstdc++ version. + . + This transitional package depends on libcapsule-gl-shims. + +Package: libcapsule-tests +Architecture: amd64 i386 +Section: misc +Depends: + bubblewrap, + libcapsule-gl-shims (= ${binary:Version}), + libipc-run-perl, + libsort-versions-perl, + mesa-utils, + perl, + pkg-config, + ${shlibs:Depends}, +Description: + libcapsule is a helper library used to implement segregated run-time + dynamic linking proxy libraries - used (for example) to load a library + that has dependencies incompatible with the regular libraries that are + part of the host OS, such as an incompatible libstdc++ version. + . + This package contains automated tests. + +Package: libcapsule0 +Architecture: amd64 i386 +Multi-Arch: same +Section: libs +Depends: + ${shlibs:Depends}, +Description: Segregated run-time linker library + libcapsule is a helper library used to implement segregated run-time + dynamic linking proxy libraries - used (for example) to load a library + that has dependencies incompatible with the regular libraries that are + part of the host OS, such as an incompatible libstdc++ version. + . + This package contains the run-time component(s) of libcapsule. diff --git a/subprojects/libcapsule/debian/copyright b/subprojects/libcapsule/debian/copyright new file mode 100644 index 0000000000000000000000000000000000000000..74a5eff2c220bcbde0b18a44904413af9fcca8dc --- /dev/null +++ b/subprojects/libcapsule/debian/copyright @@ -0,0 +1,88 @@ +Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ +Upstream-Name: libcapsule +Source: https://gitlab.collabora.com/vivek/libcapsule + +Files: + * + debian/* +Copyright: + © 1999-2017 Free Software Foundation, Inc. + © 2017 Collabora Ltd. +License: LGPL-2.1+ + +Files: + tests/CapsuleTestDpkg.pm +Copyright: + © 2007 Raphaël Hertzog + © 2009-2010 Modestas Vainius +License: GPL-2+ + +Files: + build-aux/git-version-gen +Copyright: + © 2007-2017 Free Software Foundation, Inc. +License: GPL-3+ + +Files: + m4/ax_is_release.m4 + m4/capsule_matching_abi.m4 +Copyright: + © 2015 Philip Withnall + © 2016-2019 Collabora Ltd. +License: Autoconf-permissive + +License: LGPL-2.1+ + libcapsule is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + . + libcapsule 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 Lesser General Public License for more details. + . + You should have received a copy of the GNU Lesser General Public + License along with this library. If not, see <http://www.gnu.org/licenses/>. +Comment: + On Debian systems, the full text of the GNU Lesser General Public License + version 2.1 can be found in the file '/usr/share/common-licenses/LGPL-2.1'. + +License: GPL-2+ + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + . + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + . + You should have received a copy of the GNU General Public License + along with this program. If not, see <https://www.gnu.org/licenses/>. +Comment: + On Debian systems, the full text of the GNU General Public License + version 2 can be found in the file '/usr/share/common-licenses/GPL-2'. + +License: GPL-3+ + 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 <https://www.gnu.org/licenses/>. +Comment: + On Debian systems, the full text of the GNU General Public License + version 3 can be found in the file '/usr/share/common-licenses/GPL-3'. + +License: Autoconf-permissive + Copying and distribution of this file, with or without modification, are + permitted in any medium without royalty provided the copyright notice + and this notice are preserved. diff --git a/subprojects/libcapsule/debian/git-version-gen.control b/subprojects/libcapsule/debian/git-version-gen.control new file mode 100644 index 0000000000000000000000000000000000000000..59073ac4c5c3c37bd7e547f99946fc83186fb0b8 --- /dev/null +++ b/subprojects/libcapsule/debian/git-version-gen.control @@ -0,0 +1 @@ +Upstream: yes diff --git a/subprojects/libcapsule/debian/libcapsule-dev.examples b/subprojects/libcapsule/debian/libcapsule-dev.examples new file mode 100644 index 0000000000000000000000000000000000000000..06bcb71ab4c700205540493a652834d6adc50790 --- /dev/null +++ b/subprojects/libcapsule/debian/libcapsule-dev.examples @@ -0,0 +1 @@ +debian/libGL-proxy diff --git a/subprojects/libcapsule/debian/libcapsule-dev.install b/subprojects/libcapsule/debian/libcapsule-dev.install new file mode 100644 index 0000000000000000000000000000000000000000..d1c0b917557de72ed1d518fc717ad2637b45e07d --- /dev/null +++ b/subprojects/libcapsule/debian/libcapsule-dev.install @@ -0,0 +1,4 @@ +usr/include/* +usr/lib/*/libcapsule.a +usr/lib/*/libcapsule.so +usr/lib/*/pkgconfig/libcapsule.pc diff --git a/subprojects/libcapsule/debian/libcapsule-doc.install b/subprojects/libcapsule/debian/libcapsule-doc.install new file mode 100644 index 0000000000000000000000000000000000000000..d2f34faa31564833410a6890b874202d8cb64f14 --- /dev/null +++ b/subprojects/libcapsule/debian/libcapsule-doc.install @@ -0,0 +1,3 @@ +usr/share/doc/libcapsule/* usr/share/doc/libcapsule-dev +usr/share/gtk-doc/html/libcapsule/* +usr/share/man/man3/* diff --git a/subprojects/libcapsule/debian/libcapsule-gl-shims.install b/subprojects/libcapsule/debian/libcapsule-gl-shims.install new file mode 100644 index 0000000000000000000000000000000000000000..05743115ad1e72a37cc9747e8fbef14ff218ce2a --- /dev/null +++ b/subprojects/libcapsule/debian/libcapsule-gl-shims.install @@ -0,0 +1 @@ +usr/lib/libcapsule/shims diff --git a/subprojects/libcapsule/debian/libcapsule-tests.install b/subprojects/libcapsule/debian/libcapsule-tests.install new file mode 100644 index 0000000000000000000000000000000000000000..c564fec23484316244c5a664df78fe055ed9f97e --- /dev/null +++ b/subprojects/libcapsule/debian/libcapsule-tests.install @@ -0,0 +1,2 @@ +usr/lib/libcapsule/installed-tests/libcapsule +usr/share/installed-tests/libcapsule diff --git a/subprojects/libcapsule/debian/libcapsule-tools-relocatable.install b/subprojects/libcapsule/debian/libcapsule-tools-relocatable.install new file mode 100644 index 0000000000000000000000000000000000000000..9084c938a29f73f325125b8df88139f89957a8b8 --- /dev/null +++ b/subprojects/libcapsule/debian/libcapsule-tools-relocatable.install @@ -0,0 +1 @@ +usr/lib/libcapsule/relocatable diff --git a/subprojects/libcapsule/debian/libcapsule-tools.install b/subprojects/libcapsule/debian/libcapsule-tools.install new file mode 100644 index 0000000000000000000000000000000000000000..dab3d19e16369a9c23c3ad97e507ad8d2b826643 --- /dev/null +++ b/subprojects/libcapsule/debian/libcapsule-tools.install @@ -0,0 +1,9 @@ +usr/bin/*-capsule-capture-libs +usr/bin/*-capsule-elf-dump +usr/bin/*-capsule-mkstublib +usr/bin/*-capsule-symbols +usr/bin/*-capsule-version +usr/bin/capsule-init-project +usr/lib/*/pkgconfig/libcapsule-tools.pc +usr/share/libcapsule/* +usr/share/man/man1/* diff --git a/subprojects/libcapsule/debian/libcapsule0.install b/subprojects/libcapsule/debian/libcapsule0.install new file mode 100644 index 0000000000000000000000000000000000000000..a372279df23c9dd4a31b0f47666614ab3b1bffe7 --- /dev/null +++ b/subprojects/libcapsule/debian/libcapsule0.install @@ -0,0 +1,2 @@ +usr/lib/*/libcapsule.so.0 +usr/lib/*/libcapsule.so.0.* diff --git a/subprojects/libcapsule/debian/libcapsule0.symbols b/subprojects/libcapsule/debian/libcapsule0.symbols new file mode 100644 index 0000000000000000000000000000000000000000..10bc06ffb183ec85f9b4039f25561e3b09f95f3f --- /dev/null +++ b/subprojects/libcapsule/debian/libcapsule0.symbols @@ -0,0 +1,10 @@ +libcapsule.so.0 libcapsule0 #MINVER# +* Build-Depends-Package: libcapsule-dev + capsule_close@Base 0.20180430.0 + capsule_external_dlopen@Base 0.20180430.0 + capsule_external_dlsym@Base 0.20180430.0 + capsule_get_prefix@Base 0.20180430.0 + capsule_init@Base 0.20180430.0 + capsule_shim_dlopen@Base 0.20180430.0 + capsule_shim_free@Base 0.20180430.0 + capsule_shim_realloc@Base 0.20180430.0 diff --git a/subprojects/libcapsule/debian/rules b/subprojects/libcapsule/debian/rules new file mode 100755 index 0000000000000000000000000000000000000000..d4d34932d1cbae7dd40ac88e42094d2ace733390 --- /dev/null +++ b/subprojects/libcapsule/debian/rules @@ -0,0 +1,133 @@ +#!/usr/bin/make -f + +-include /usr/share/dpkg/default.mk + +%: + dh $@ --with autoreconf + +binaries := $(shell dh_listpackages) +fake_rpath := /_ORIGIN_/__/lib/${DEB_HOST_MULTIARCH} +real_rpath := $${ORIGIN}/../lib/${DEB_HOST_MULTIARCH} +confflags = \ + --disable-Werror \ + --disable-silent-rules \ + --enable-host-prefix=$(DEB_HOST_MULTIARCH)- \ + --enable-tools-rpath='${fake_rpath}' \ + --libexecdir=\$${exec_prefix}/lib/libcapsule \ + $(NULL) + +ifneq ($(filter libcapsule-doc,$(binaries)),) +confflags += --enable-gtk-doc +else +confflags += --disable-gtk-doc +endif + +ifeq ($(DEB_HOST_ARCH),amd64) +confflags += --with-glib=auto +else +confflags += --without-glib +endif + +override_dh_clean: + rm -fr html + dh_clean + +override_dh_auto_configure: + dh_auto_configure -- $(confflags) + +proxy_vars = \ + CAPSULE_CFLAGS="-I$(CURDIR)" \ + CAPSULE_LIBS="-L$(CURDIR)/.libs -lcapsule" \ + CAPSULE_MKINC="$(CURDIR)/data" \ + CAPSULE_MKSTUBLIB_TOOL="$(CURDIR)/data/capsule-mkstublib" \ + CAPSULE_SYMBOLS_TOOL="$(CURDIR)/capsule-symbols" \ + CAPSULE_VERSION_TOOL="$(CURDIR)/capsule-version" \ + $(NULL) + +override_dh_auto_build: + dh_auto_build + $(proxy_vars) ./data/capsule-init-project \ + --capsule-pkgdatadir="$(CURDIR)/data" \ + --destination="$(CURDIR)/debian/libGL-proxy" \ + --no-autoreconf \ + --package-name=libGL-proxy \ + --search-tree=/nonexistent \ + --symbols-from-dir="$(CURDIR)/examples/shim" \ + libGL.so.1/1 \ + libX11.so.6/6 \ + libXext.so.6/6 \ + libXi.so.6/6 \ + libxcb-dri2.so.0/0 \ + libxcb-glx.so.0/0 \ + libxcb-present.so.0/0 \ + libxcb-sync.so.1/1 \ + libxcb.so.1/1 \ + $(NULL) + # Copy it, so we can install the original source as an example + cp -a debian/libGL-proxy debian/libGL-proxy-build + cd debian/libGL-proxy-build && autoreconf -fi -I $(CURDIR)/m4 + cd debian/libGL-proxy-build && ./configure --prefix=/usr $(proxy_vars) + $(MAKE) -C debian/libGL-proxy-build V=1 $(proxy_vars) + +override_dh_auto_install: + dh_auto_install + if [ $(DEB_HOST_MULTIARCH) != $(DEB_HOST_GNU_TYPE) ]; then \ + set -e; \ + cd debian/tmp/usr/bin; \ + for bin in $(DEB_HOST_MULTIARCH)-*; do \ + tail="$${bin#$(DEB_HOST_MULTIARCH)-}"; \ + ln -s "$$bin" "$(DEB_HOST_GNU_TYPE)-$$tail"; \ + done; \ + fi + install -d debian/tmp/usr/lib/libcapsule/relocatable + cp -a \ + debian/tmp/usr/bin/*-capsule-capture-libs \ + debian/tmp/usr/bin/*-capsule-elf-dump \ + debian/tmp/usr/bin/*-capsule-symbols \ + debian/tmp/usr/bin/*-capsule-version \ + debian/tmp/usr/lib/libcapsule/relocatable + set -e; for bin in debian/tmp/usr/bin/$(DEB_HOST_MULTIARCH)-*; do \ + case "$$bin" in \ + (*-capsule-mkstublib) \ + ;; \ + (*) \ + chrpath -d "$$bin"; \ + chrpath -l "$$bin" || :; \ + ;; \ + esac; \ + done + chrpath -r '${real_rpath}' \ + debian/tmp/usr/lib/libcapsule/relocatable/$(DEB_HOST_MULTIARCH)-* + chrpath -l debian/tmp/usr/lib/libcapsule/relocatable/* + $(MAKE) -C debian/libGL-proxy-build install V=1 $(proxy_vars) \ + DESTDIR=$(CURDIR)/debian/libGL-proxy-build/shims + install -d debian/tmp/usr/lib/libcapsule/shims/lib/$(DEB_HOST_MULTIARCH) + mv debian/libGL-proxy-build/shims/usr/lib/* \ + debian/tmp/usr/lib/libcapsule/shims/lib/$(DEB_HOST_MULTIARCH) + +override_dh_install: + rm -f debian/tmp/usr/lib/*/*.la + rm -f debian/tmp/usr/lib/libcapsule/shims/lib/*/*.la + find debian/tmp/usr/lib/libcapsule/installed-tests -name '*.la' -print -delete + # Only keep the architecture-qualified version, delete the unprefixed + # version, so we can be Multi-Arch: same + rm -f debian/tmp/usr/bin/capsule-capture-libs + rm -f debian/tmp/usr/bin/capsule-elf-dump + rm -f debian/tmp/usr/bin/capsule-symbols + rm -f debian/tmp/usr/bin/capsule-version + dh_install + +# The debug symbols for libcapsule-tools-relocatable would be the same +# as for libcapsule-tools, making them non-co-installable. +# Use the environment variable instead of --no-automatic-dbgsym for +# compatibility with Steam Runtime 1 'scout'. +override_dh_strip: + DEB_BUILD_OPTIONS='$(DEB_BUILD_OPTIONS) noautodbgsym' dh_strip -plibcapsule-tools-relocatable + dh_strip --remaining-packages + +override_dh_missing: + dh_missing --fail-missing + +override_dh_autoreconf: + NOCONFIGURE=1 dh_autoreconf ./autogen.sh -- $(confflags) + diff --git a/subprojects/libcapsule/debian/source/format b/subprojects/libcapsule/debian/source/format new file mode 100644 index 0000000000000000000000000000000000000000..163aaf8d82b6c54f23c45f32895dbdfdcc27b047 --- /dev/null +++ b/subprojects/libcapsule/debian/source/format @@ -0,0 +1 @@ +3.0 (quilt) diff --git a/subprojects/libcapsule/debian/tests/control b/subprojects/libcapsule/debian/tests/control new file mode 100644 index 0000000000000000000000000000000000000000..527b81ae4d305d12c717046a5fe65537b9d18ece --- /dev/null +++ b/subprojects/libcapsule/debian/tests/control @@ -0,0 +1,18 @@ +Tests: gnome-desktop-testing smoke +Restrictions: allow-stderr +Depends: + autoconf, + automake, + bubblewrap, + build-essential, + gnome-desktop-testing, + libcapsule-dev, + libcapsule-tests, + libcapsule-tools, + libipc-run-perl, + libglib2.0-0, + libxml2, + libtool, + perl, + pkg-config, + zlib1g, diff --git a/subprojects/libcapsule/debian/tests/gnome-desktop-testing b/subprojects/libcapsule/debian/tests/gnome-desktop-testing new file mode 100755 index 0000000000000000000000000000000000000000..2958be2dab55e801a2c51efa85e0fe4added2c6c --- /dev/null +++ b/subprojects/libcapsule/debian/tests/gnome-desktop-testing @@ -0,0 +1,5 @@ +#!/bin/sh + +set -e +exec 2>&1 +exec gnome-desktop-testing-runner libcapsule diff --git a/subprojects/libcapsule/debian/tests/smoke b/subprojects/libcapsule/debian/tests/smoke new file mode 100755 index 0000000000000000000000000000000000000000..cd52406f3f8497c9a3343700491f036a839d40cc --- /dev/null +++ b/subprojects/libcapsule/debian/tests/smoke @@ -0,0 +1,55 @@ +#!/usr/bin/perl + +# Copyright © 2017 Collabora Ltd. +# +# This library is free software; you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as +# published by the Free Software Foundation; either version 2.1 of the +# License, or (at your option) any later version. +# +# This library 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 Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library. If not, see <http://www.gnu.org/licenses/>. + +use autodie; +use File::Temp qw(); +use IPC::Run qw(run); +use Test::More; + +my $scratch = File::Temp->newdir(); +chdir($scratch); + +open(my $fh, '>', 'trivial.c'); +print {$fh} <<'EOF'; +#define _GNU_SOURCE + +#include <stdio.h> + +#include <capsule/capsule.h> + +int +main (void) +{ + char *prefix = capsule_get_prefix( "/host", "libfoo.so.0" ); + printf( "%s\n", prefix ); + return 0; +} +EOF +close($fh); + +ok(! system('gcc -c -o trivial.o trivial.c $(pkg-config --cflags libcapsule)'), + 'compiled'); +ok(! system('gcc -o trivial trivial.o $(pkg-config --libs libcapsule)'), + 'linked'); +ok(-x 'trivial', 'executable'); +my $output; +$ENV{CAPSULE_LIBFOO_SO_0_PREFIX} = '/foo'; +ok(run(['./trivial'], '>', \$output), 'executed successfully'); +is($output, "/foo\n", 'printed desired output'); + +chdir('/'); +done_testing; diff --git a/subprojects/libcapsule/doc/Building-libcapsule.txt b/subprojects/libcapsule/doc/Building-libcapsule.txt new file mode 100644 index 0000000000000000000000000000000000000000..db3d7c057d964df32a83dafe804a963b6a2dd4f0 --- /dev/null +++ b/subprojects/libcapsule/doc/Building-libcapsule.txt @@ -0,0 +1,10 @@ +Building libcapsule from git requires GNU autoconf-archive macros. The +oldest supported version is 20180313-1 from Debian 9 'stretch'. If your +development system or container is older, unpack or clone a suitable +version of autoconf-archive and use commands like: + + libcapsule$ ACLOCAL_PATH=/path/to/autoconf-archive/m4 NOCONFIGURE=1 ./autogen.sh + libcapsule$ ./configure + libcapsule$ make + +Building from tarball releases removes this requirement. diff --git a/subprojects/libcapsule/doc/Building.txt b/subprojects/libcapsule/doc/Building.txt new file mode 100644 index 0000000000000000000000000000000000000000..b1dab395df64b2e34f2ba78ccf223b3281cd8bbc --- /dev/null +++ b/subprojects/libcapsule/doc/Building.txt @@ -0,0 +1,191 @@ +libcapsule provides a few tools to help initialise a proxy library project: + +Assuming a host tree containing libGL mounted at /host, the simplest route +to a [hopefully] working project is as follows: + + ~$ $(pkg-config --variable=CAPSULE_INIT_PROJECT_TOOL libcapsule-tools) libGL.so.1 /host + + Generating project for /host : libGL.so.1 1.2.0 proxy + Creating project directory libGL-proxy + Working in /home/vivek/src/valve/dlmopen/libGL-proxy + Extracting dynamic symbols from /host : libGL.so.1 + Preparing proxy source files + Initialising configure.ac + Initialising Makefile.am + Bootstrapping autoconf templates for libGL.so.1 + Running initial configuration + autoreconf: Entering directory `.' + autoreconf: configure.ac: not using Gettext + autoreconf: running: aclocal --force + autoreconf: configure.ac: tracing + autoreconf: running: libtoolize --copy --force + libtoolize: putting auxiliary files in `.'. + libtoolize: copying file `./ltmain.sh' + libtoolize: putting macros in AC_CONFIG_MACRO_DIR, `m4'. + libtoolize: copying file `m4/libtool.m4' + libtoolize: copying file `m4/ltoptions.m4' + libtoolize: copying file `m4/ltsugar.m4' + libtoolize: copying file `m4/ltversion.m4' + libtoolize: copying file `m4/lt~obsolete.m4' + libtoolize: Consider adding `-I m4' to ACLOCAL_AMFLAGS in Makefile.am. + autoreconf: running: /usr/bin/autoconf --force + autoreconf: configure.ac: not using Autoheader + autoreconf: running: automake --add-missing --copy --force-missing + Makefile.am: installing './depcomp' + autoreconf: Leaving directory `.' + /home/x/src/valve/dlmopen/libGL-proxy is ready for ./configure, \ + see README for details + +This will set up a project to build a proxy for libGL.so.1, expected +to be found in a foreign filesystem tree mounted at /host. + +The target library (the real libGL.so.1) must be present in a well-formed +tree mounted at /host at the point at which you run this command. + +If you ever make changes which require the proxy library source files +to be regenerated (changing the foreign tree location from /host or +altering the list of symbols to export) then the foreign tree must +likewise be present. + +However you do not need the tree to be present to build the library itself +once the project has been initialised. + +The layout of the project will be as follows: + + + ./shim/ + ./shim/libGL.so.1.shared + ./shim/libGL.so.1.dlopen + ./shim/libGL.so.1.symbols.updated-for + ./shim/libGL.so.1.excluded + ./shim/libGL.so.1.dlsym + ./configure + ./build-aux/… + ./m4/… + ./README + ./dd.log + ./preconfigure.log + ./Makefile.am + ./configure.ac + ./aclocal.m4 + ./arena.log + ./Makefile.in + +A few of these files are of immediate interest: +---------------------------------------------------------------------------- +preconfigure.log: + + This records the initial bootstrapping step of the project, which uses + autoconf to set up the build system from libcapsule's templates. + + If things are working, there's little of interest here, but if something's + not quite right there may be important clues recorded here. +---------------------------------------------------------------------------- +Makefile.am + + A few important details are recorded here: + + CAPSULE_LIBRARY - The stub of the library name (‘GL’ in this case). + + CAPSULE_VERSION_libfoo.so.0 - + The version number of the proxy library for libfoo.so.0 + defaults to the same version as the library in /host + if that can be deduced from the /host tree. + + Only the major version (major.minor.sub) needs to match, + otherwise you may change this as desired. + + CAPSULE_TREE - The location of the foreign tree. + + You may change this, but doing so requires the source + to be regenerated, which requires the new foreign tree + to be present. + + CAPSULE_MAJOR - Major version of the library we're encapsulating +---------------------------------------------------------------------------- +shim/libGL.so.1.excluded + + This file should contain a list of libraries which should _NOT_ + be picked up from the /host tree and isolated in the capsule, + one per line. + + The following library is always considered to be excluded by + the code generator, so there's no need to add it explicitly: + + libdl.so.2 + + Other libraries which you may need to add, depending on the exact + API presented by the proxied library, are: + + libc.so.6 - if memory allocated inside the target library + is freed by the main program, or similar. + + libpthread.so.0 - if thread synchronisation or similar occurs + libpthread-2.19.so both inside the proxied library and your main + program AND the two need to be aware of one + another. + + +---------------------------------------------------------------------------- +shim/libGL.so.1.shared + + This file should contain any extra libraries whose symbols should _also_ be + exported from the capsule, along with those from the main library libGL.so. + + It will generally be empty, but in libGL.so's case it must contain: + + libxcb.so.1 + libxcb-glx.so.0 + libxcb-dri2.so.0 + libxcb-dri3.so.0 + libxcb-present.so.0 + libxcb-sync.so.1 + libX11.so + + Every time this file is modified, you will need to run + + make maintainer-update-capsule-symbols + + to update the list of exported symbols in shim/libGL.so.1.symbols. + This will also update shim/libGL.so.1.symbols.updated-for to record + the fact that it does not need updating again. You can use + + make maintainer-update-capsule-symbols CAPSULE_SEARCH_TREE=/sysroot + + to specify a custom root directory to search for these libraries. + +---------------------------------------------------------------------------- +shim/capsule/_int_dlopen.h + + This file is not created by default. + + The _int_dlopen wrapper is installed inside the capsule and takes + care of any /host path prefixing so that the encapsulated libraries + get their native targets with dlmopen (and don't break encapsulation + by calling dlopen). + + A default implementation provided by libcapsule is normally used. + See libcapsule's <capsule/_int_dlopen.h> for that implementation. + + To use a custom implementation, create the file and implement a + function with the same signature: + + static void *_int_dlopen (const char *filename, int flags) + { + ⋮ + } + +---------------------------------------------------------------------------- +That's it - you should be ready to: + + ./configure + make + +Your generated capsule libraries will be in .libs, +and you can use ‘make install’ et al, as usual, to install them. + +Note that you should be careful about this, as you are deliberately +generating a library which has the same name as a ‘real’ one, which +is very much not a normal situation, and carelessness here could break +your system. + diff --git a/subprojects/libcapsule/doc/Capsules.txt b/subprojects/libcapsule/doc/Capsules.txt new file mode 100644 index 0000000000000000000000000000000000000000..7b5023e6c8ceb1f336f3ab4403dfb554fd4e08d3 --- /dev/null +++ b/subprojects/libcapsule/doc/Capsules.txt @@ -0,0 +1,157 @@ +This document covers the details of how libcapsule manages proxy capsule +libraries. + +For details of how symbol hiding/relocation is done, see Strategy.txt. + +============================================================================ +Summary: + +Each capsule library acts as a shim for at least one target library, with +which it shares a soname, and whose symbols it exports to the libraries +that link to it (both at dynamic link time and via dlopen/dlsym). + +Capsules _may_ act as a shim for more than one library, but they can have +only one soname: This pattern of use was necessary during the early +development of libcapsule when we supported at most one capsule in any +given link chain but is no longer required. + +============================================================================ +Details: + +Each capsule library must + +a) depend on libcapsule (ie have libcapsule in a DT_NEEDED entry) + +b) #include "capsule/capsule-shim.h" + +c) declare a globally visible symbol called capsule_meta which should + be a populated struct of type capsule_metadata + + NOTE: it should not attempt to use this variable, only + the handle returned by capsule_init(). That handle will be + initialised based on the metadata. + +d) declare UNVERSIONED_STUB( f ); for each function ‘f’ it wishes to proxy + +e) declare a static global variable: + static capsule cap; + +f) In its constructor: + + assign the return of capsule_init to ‘cap’ + +g) In its destructor: + + call capsule_close + +============================================================================ +Metadata example (for a libGL.so.1 capsule): + +static const char *exclude[] = +{ // MUST NOT be pulled from the capsule + // prefixed filesystem tree: + "libdl.so.2", + NULL +}; + +// make sure this symbol has global visibility so +// that the libcapsule initialisation can find it +__attribute__ ((visibility("default"))) +capsule_metadata capsule_meta = +{ + .capsule_abi = 0, + .soname = "libGL.so.1", + .default_prefix = "/host", + .exclude = exclude, + .export = valid_dlsym_sources, +}; + +============================================================================ +Sequences of events: + +Normal linking + +A library or executable using a capsule is loaded + +The linker notes that the capsule is listed in DT_NEEDED and loads it + +The linker notes that libcapsule is DT_NEEDED by the capsule and loads _that_ +(if it is not already loaded). + +Various libraries are loaded - once all DT_NEEDEDs have been satisfied: + +The constructors for the loaded DSOs run in reverse dependency order, +in particular: + +The constructor for libcapsule runs, and does the following: + + - harvests metadata from all currently loaded capsules and caches them + - caches the addresses of the default implementations of dlopen and dlsym + - groups the metadata by filesystem prefix + - all capsules that specify /foo as their prefix will be grouped into + the same private namespace + - a capsule's prefix is determined by (in descending priority) + - the soname specific env var (eg CAPSULE_LIBGL_SO_1_PREFIX) + - the CAPSULE_PREFIX env var + - the static prefix in the capsule's metadata + - NOTE: env vars don't apply here to setuid/setgid processes, + or if libcapsule was compiled using glibc older than 2.17 + - aggregates the exclude and export metadata lists, grouping + them by prefix (and deduplicating the resulting lists) + - eg the ‘export’ list from a namespace will be all the sonames from + all the .export entries from all capsules that share that namespace + (as determined by their prefix) + +The constructor for each loaded capsule runs and calls +capsule_init( static-copy-of-soname ) which does the following: + + - calls _capsule_load to load the real target library + + - calls _capsule_relocate to update the global offset tables (GOTs) + of all DSOs outside the capsule to use the real symbols from the + target instead of the dummy entries in the capsule + + - calls _capsule_relocate_dlopen to install wrappers for any special-case + functions (typically just dlopen - dlsym is handled by _capsule_relocate) + that require careful handling. + +dlopen() of a capsule (from outside the capsule): + +This is similar to normal linking, except: + + - The libcapsule constructor does not run + - the capsule's constructor's call to capsule_init triggers a re-harvesting + of available metadata, and a re-calculation of the aggregated lists + - the _capsule_relocate and _capsule_relocate_dlopen sequences for _all_ + capsules are triggered + +dlopen() of a normal library (from outside the capsule): + + - the _capsule_relocate and _capsule_relocate_dlopen sequences for _all_ + capsules are triggered + +dlopen() of a normal library inside the capsule: + + - secretly translated into a capsule_shim_dlopen call + +dlopen() of a capsule from inside the capsule: + + - cats and dogs living together + - total chaos + - the end of the world + +dlsym() from inside the capsule: + + - nothing special, works as normal + - this might break if dlsym is called on the NULL handle + with the expectation that the calling namespace and + public namespace are the same + +dlsym() from outside the capsule + + - handled by the wrapper we installed earlier + - looks first inside each capsule namespace for the requested symbol + - if said symbol is found _and_ comes from a DSO in the export list + then it is returned + - otherwise we fall back to vanilla dlsym + diff --git a/subprojects/libcapsule/doc/Debugging.txt b/subprojects/libcapsule/doc/Debugging.txt new file mode 100644 index 0000000000000000000000000000000000000000..3ad0ff1a91ec4c724f286a2697107c85a93254ea --- /dev/null +++ b/subprojects/libcapsule/doc/Debugging.txt @@ -0,0 +1,26 @@ +If you wish to debug the library loading & symbol proxying process +libcapsule exposes a lot of detail depending on the contents of the +CAPSULE_DEBUG environment variable. + +CAPSULE_DEBUG is a comma-or-space separated variable, and currently +understand the following debug flags: + + path : path manipulation and translation + search : searching for DSOs + ldcache : loading/processing the ld cache + capsule : setting up the proxy capsule + mprotect: subverting mprotect (for RELRO linking) + wrappers: function wrappers installed in the capsule + reloc : patching capsule symbols into external DSOs + dlfunc : dlsym/dlfunc wrapper info as they execute (overlaps wrappers) + elf : detailed ELF introspection logging + all : all of the above + +At this point gdb does not yet know about dlmopen()ed DSOs and their +symbols, so debugging a libcapsule proxy library with gdb isn't very +convenient. It's on the list of things to fix. + +If you are debugging the unit tests, you might want to set +CAPSULE_TESTS_KEEP_TEMP to a non-empty value so temporary +directories will not be cleaned up. See tests/CapsuleTest.pm +or `perldoc -F tests/CapsuleTest.pm` for more details. diff --git a/subprojects/libcapsule/doc/Limitations.txt b/subprojects/libcapsule/doc/Limitations.txt new file mode 100644 index 0000000000000000000000000000000000000000..ca6a8b266d6f8892bc4a046ba76a5d7837a203e9 --- /dev/null +++ b/subprojects/libcapsule/doc/Limitations.txt @@ -0,0 +1,58 @@ +libcapsule currently has the following limitations: + +============================================================================ + +If there are > 1 copies of a library which handles allocation (eg +libstdc++) the capsule cannot protect you from an object allocated +by the encapsulated copy being freed by the public copy. + +Some libraries may be OK with this pattern of behaviour, others +may not. + +============================================================================ + +There can only ever be one copy of libdl shared between the capsule +and the public namespace. This behaviour is baked into the stub library +generator script. Probably nothing particularly sane will happen if you +try to ignore this limitation. + +============================================================================ + +Currently only functions are exported from the capsule. Unlike the +previous items, this limitation is not intrinsic and could be lifted. + +============================================================================ + +libcapsule relies on the ld.so.cache from the capsule tree to find +libraries. If the cache is not there, it may still find some libraries +in the default-standard locations but many paths will end up being ignored. + +============================================================================ + +libcapsule replaces any instances of dlopen in the encapsulated libraries +with a wrapper around dlmopen: It does this so that a capsule opened on the +"/host" prefix (for example) continues to use DSOs (libraries and modules) +from "/host", and to prevent dlopen() calls from within the capsule from +breaking encapsulation. + +NB: Currently a dlopen() call from inside a dlmopen() namespace is unsupported +by glibc anyway, and would result in a segfault. + +============================================================================ + +Related to the above: The dlmopen wrapper cannot support RTLD_GLOBAL. + +If the library you are opening relies on calling dlopen() with RTLD_GLOBAL +to expose symbols from one DSO to another it will not work. + +This is a glibc limitation - it could be lifted but would require +the dlmopen libc call (and the linker) to be modified. + +============================================================================ + +If you are using Mesa before v 17.x you will need to use `patchelf' v0.9 +or later to add a DT_NEEDED entry for libglapi.so.0 to your dri.so drivers +in order for the libGL capsule to be able to work (assuming it is +libGL.so tat you are trying to proxy). + +============================================================================ diff --git a/subprojects/libcapsule/doc/Quick-Start.txt b/subprojects/libcapsule/doc/Quick-Start.txt new file mode 100644 index 0000000000000000000000000000000000000000..29299b60755f4247d1b1e9fe70d4b90d08f2170a --- /dev/null +++ b/subprojects/libcapsule/doc/Quick-Start.txt @@ -0,0 +1,59 @@ +In the top level directory: + + autoreconf -ivf + ./configure + make + +libcapsule.so will be in .libs/ + +The test environment is currently: + +A chroot containing a debian/jessie filesystem with libGL.so removed, +the libcapsule .deb installed and with the "real" OS filesystem bind +mounted at /host. + +In the chroot: + +Generate a proxy project for libGL and related libraries: + + ~$ mkdir proxies + ~$ cd proxies + ~$ CAPTOOL=$(pkg-config --variable=CAPSULE_INIT_PROJECT_TOOL libcapsule-tools) + ~$ LIBS="xcb.so.1 xcb-glx.so.0 xcb-dri2.so.0 xcb-dri3.so.0 xcb-present.so.0" + ~$ LIBS="$LIBS xcb-sync.so.1 X11.so.6 GL.so.1 Xext.so.6" + ~$ for x in $LIBS; do $CAPTOOL lib$x /host; done + ~$ for x in *-proxy; do (cd $x && ./configure --prefix=/shim) || break; done + ~$ for x in *-proxy; do (cd $x && make) || break; done + ~$ for x in *-proxy; do (cd $x && sudo make install) || break; done + +Now make said proxy libraries available. You should also: +Ensure that ldd $(which glxgears) points to the libGL.so capsule +Make sure a full filesystem from the host OS is mounted at /host + + ~$ export LD_LIBRARY_PATH=/shim/lib + ~$ export DISPLAY=:0 # if you need this + +Make sure your chroot is set up so that trivial XOrg programs can run +(xterm, xev, xeyes or whatever). + +Run glxgears (or openarena, or whatever). + +If it fails: + + export CAPSULE_DEBUG=path,search,capsule,mprotect,wrappers,ldcache + glxgears 2>&1 | tee glxgears.capsule.log + +capsule debug flags: + path # path manipulation and translation + search # searching for DSOs + ldcache # loading/processing the ld cache + capsule # setting up the proxy capsule + mprotect # handling mprotect (for RELRO) + wrappers # function wrappers installed in the capsule + reloc # patching capsule symbols into external DSOs + elf # detailed ELF introspection logging + +NOTE: If your Mesa is older than 17.x, see the final item regarding patchelf +in the Limitations.txt file for a workaround. + +TODO: add instructions for bwrap instead of chroot here. diff --git a/subprojects/libcapsule/doc/Strategy.txt b/subprojects/libcapsule/doc/Strategy.txt new file mode 100644 index 0000000000000000000000000000000000000000..af0f8b788780f685d089db441891155aff193066 --- /dev/null +++ b/subprojects/libcapsule/doc/Strategy.txt @@ -0,0 +1,279 @@ +Everything You Never Wanted To Know About Dynamic Linking: +---------------------------------------------------------------------------- +If you have gotten this far you're probably interested in how +libcapsule does what it does: There's a lot of ground to cover +and there are many pieces of the puzzle but we'll try to explain +them all here. + +The background: +---------------------------------------------------------------------------- +First, a simplified outline of dynamic linking: When a program is +loaded, its type is determined. If it is ‘dynamically linked’, it +will contain a list of libraries (DSOs or Dynamically Shared Objects) +that it needs. Each of these DSOs will in turn contain a list +of its own dependencies (more DSOs). + +The component of your system known as the linker will assemble the +full list of all the required DSOs, open them, and following a +somewhat intricate set of rules, copy (map) chunks of these files +into memory. + +It then goes through every loaded DSO and fixes up each one by +scribbling the relevant addresses into its (the DSO's) lookup +table so that it can find the functions and variables (symbols) +it needs to from each of its dependencies. + +Once this is done, the program is good to go: The system can +start executing its instructions, jumping between the loaded +DSOs (via the lookup tables in each one) as required. + +The problem: +---------------------------------------------------------------------------- +So far so good: This is mostly the right thing to do, and solves +a lot of problems we won't discuss here - But it does make some +important assumptions. One these is the following: + + - that every DSO involved that requires a specific DSO is going to be + happy with the same copy of the same version of that requirement. + + In other words if a program needs libfoo.so and both the program + and libfoo.so require libbar.so, they will both be happy with a + single copy of libbar.so v3.14.159 (for example) provided by the + linker. + +This assumption is mostly true. In general things are happier when +everything is built on the same set of libraries, but occasionally +you can end up with a program that has conflicting requirements +for the same DSO - libcapsule tries to solve that problem. + +The approach (the basics): +---------------------------------------------------------------------------- +There are broadly two parts to the segregated-linker behaviour that we +require: + +The first and more fundamental is mapping in the DSOs we wish to isolate +in such a way that the linker does not (by default) use their symbols' +addresses to fill in the lookup tables of the other DSOs we have loaded. +Fortunately this part of the problem is addresses by the [relatively] +new dlmopen() C library call. dlmopen() is similar to the dlopen() call, +except that instead of opening a DSO (and all its dependencies) into the +default linker namespace it does so into a private one, either creating +a new namespace if requested, or using an existing namespace. [ There are +one or two intricacies to this that we've glossed over here: We'll revisit +this in greater detail later. ] + +The second part is making just the symbols we want available to the rest +of the DSOs our program consists of, ideally introducing as little overhead +as possible: There are a couple of approaches we could take to this, +each with problems of its own. + +dlmopen() (the gory details): +---------------------------------------------------------------------------- +When dlmopen() is called, the algorithm is roughly as follows: + + • find the requested DSO + ◦ if the path to the requested DSO is absolute, look for it there + ◦ if the path is relative (contains but does not begin with '/') + then look for it relative to the processes current working directory + ◦ if the path is bare, search for it using the standard linker algorithm: + ‣ look in RPATH baked into DSO (if there's no RUNPATH) + ‣ then LD_LIBRARY_PATH from environment + ‣ then RUNPATH baked into DSO + ‣ then contents of ld.so.cache + ‣ then /lib + ‣ & finally /usr/lib + • harvest the DT_NEEDED entries from the DSO + ◦ check if the DT_NEEDED entry is already in _our_ link map + (ie have we loaded the library for some other reason already) + we do not consider entries in the default link map, or any other + private namespaces created by dlmopen(). + ◦ if we haven't, open it as for the original DSO above, and repeat. + ◦ if we already have the DSO open, add its link map structure + to your list of link maps without opening it again. + • once we don't require any more DSOs (we've opened everything) + fix up the lookup tables using the link maps + +For the basic use case of opening a DSO without exposing its symbols +to any other DSOs we already have this is sufficient. However in the +libcapsule case there's a new wrinkle - we want to open a DSO (and +most of its dependencies) from an external source. To give a concrete +example: + +Consider an application inside a "runtime" (for our purposes, a mostly +self-contained set of DSOs, binaries and so forth separated from the main +system) which wants to use a library from the host OS (which is not +compatible with the set of DSOs in the runtime, hence libcapsule). + +We have made the "real" set of libraries available under /host in the +runtime (that is, the / of the real system is mounted at /host in the +runtime, which has its own /). + +If we naively dlmopen() a DSO from the /host tree, dlmopen will go +and find all the DT_NEEDED items required to satisfy it, but from +/ instead of from /host (since dlmopen knows nothing about /host): + +Less than ideal, since in our special case we don't just need to isolate +one DSO from the rest, but two whole set of DSOs from one another. So what's +the solution? + +The key lies in the algorithm presented above: Note that before opening a +DSO, we check to see if we already have it in our list of link maps: This +check is done against the ‘bare’ "libfoo.so.X" style name of the DSO, not +its full path. + +If we manually (and recursively) harvest the list of DSOs from the DT_NEEDED +entries, and open them in reverse dependency order (ie we start with the +thing that has no dependencies, then the things that only needed that, and +so on and so forth) then the dlmopen() call won't go looking for them +elsewhere. + +That's the theory - there are a few more details. For example: + +We need to be careful when resolving symlinks since the filesystem we +are interested in is remapped to /host, not /, so absolute symlinks +will point to the ‘wrong’ place). + +We must manually load the ld.so.cache from /host/etc/… and remap +all its entries to the right location under /host (same reason as +the symlink problem above). + +But having said that, the basic method works. Success! We can control +the DSOs loaded to satisfy our target, and isolate them all in a +private namespace. + +Controlling symbol visibility +---------------------------------------------------------------------------- +Having succeeded in opening a DSO without exposing any of its symbols, we +now need to make sure the other DSOs (including the main program) use the +symbols from the DSO we _do_ want to expose. + +The simplest (in that it requires the least knowledge of the details of +how a dynamically linked program works) approach would be to define, in +a proxy library, for each function we wish to export, a proxy function +with the same signature as its ‘real’ implementation, which could find +that address with dlsym() and re-dispatch the call there, then let our +program link against that (this last easily achieved by giving our proxy +library the same name as the ‘real’ one). + +However, this has a few disadvantages: + + • doesn't handle varargs functions + • requires knowledge of the signature of each function + ◦ this makes it much harder to automate proxy generation + • mechanism cannot be extended to handle variables + • doubles the function call overhead for every call: we have to + call our proxy function, which then makes the real call + +Ideally we'd like to find a different approach that has none of these +limitations. To do so we're going to have to examine, in greater detail, +how the "lookup tables" we've been talking about actually work. + +At the heart of all this is something called a Global Offset Table, +or GOT (the name is possibly slightly misleading - it's not the table +that's global, but the offsets). + +Each DSO contains a GOT, which consists of an array of entries, each +containing (after dynamic linking has completed) the real address of +a given function (or other object, but we're concerned with functions +for now). + +Each DSO also contains a Procedure Linkage Table (NOTE: this is a +lie, it contains two, but we turn out to care about only one of them). + +When an external function is called for the first time the following +happens: + + → The caller puts the function arguments on the stack. + the way in which it does so is determined by the ‘calling convention’, + a set of rules for passing arguments to and retrieving return values + from a function. Both DSOs involved in a call must agree on the + convention in order for this to work. + + → Jump to the PLT entry for the function + This is at a fixed offset, so the only thing we need to + know to find it is the base address (in memory) of the section of + the DSO in which the PLT resides. + + → The PLT entry points to an entry in the GOT. + The GOT is in a writable section of memory, but is of a fixed size + and has well defined entries of a fixed size also, so again, as long + as we know the base address, the PLT entry can always send us to + the right place. + + → The GOT entry contains a ‘relocation record’. There are many different + types of relocation record - fortunately we only care about one. + This relocation record consists of two or three values which can be used + to calculate the real address to which execution should jump. + + Having calculated our jump, we transfer execution there. + Now the _first_ time we encounter a GOT entry, it actually points + to a special function which finds the _real_ function address, + SCRIBBLES IT BACK INTO THE RELOCATION RECORD, then jumps to the + function proper. + + → The function grabs the arguments from the stack, handles them, + pushes its return value (if any) onto the stack (as per the calling + convention), then passes control back to its caller. + + → the caller pulls the return value off the stack, and does something + with it (or not). + +The _next_ time the function is called (from the same DSO), we will jump from +the GOT directly to the function, without hitting the special resolver. + + First call: Subsequent calls: + + ┌───────┬──────────┠┌───────┬──────────┠+ ┌─┤ DSO A │ call foo │<───────┠┌─┤ DSO A │ call foo │<─┠+ │ └───────┴──────────┘ │ │ └───────┴──────────┘ │ + └>┌───────┬──────────┠│ └>┌───────┬──────────┠│ + ┌─┤ DSO A │ PLT: foo │ │ ┌─┤ DSO A │ PLT: foo │ │ + │ └───────┴──────────┘ │ │ └───────┴──────────┘ │ + └>┌───────┬─────────────────┠│ └>┌───────┬──────────┠│ + ┌─┤ DSO A │ GOT: <Resolver> │ │ ┌─┤ DSO A │ GOT: foo │ │ + │ └───────┴───┬─────────────┘ │ │ └───────┴──────────┘ │ + │ ↿ <foo> │ └>┌───────┬──────────┠│ + └>┌───────┬───┴──────┠│ │ DSO B │ foo ├──┘ + ┌─┤ DSO A │ Resolver │ │ └───────┴──────────┘ + │ └───────┴──────────┘ │ + └>┌───────┬──────────┠│ + │ DSO B │ foo ├────────┘ + └───────┴──────────┘ + +The key to where we jump is the relocation record in the GOT of DSO A +It's worth noting here that the only things that know the signature +(ie the argument and return types of foo) are the caller in DSO A and +foo itself: None of the intermediates know or care. This is promising, +as not caring about the signature is one of our goals. + +An approach that should work, then, is as follows: + + • Prepare a proxy library with the same name as the real DSO + • Arrange for said proxy to have a public function for each one + we wish to proxy (signature unimportant, since we will never + actually call these stubroutines) + • In our [proxy] library initialisation stage: + ◦ walk the list of loaded DSOs + ◦ locate the PLT entry for each function we wish to proxy + ◦ fetch the address of the proxied function(s) using dlsym() + (this is the real address inside the private namespace) + ◦ follow the PLT to the GOT relocation record (RR) + ◦ scribble our real address into the GOT RR + +From now on, in each DSO we've scribbled on, calls to proxied functions +will jump directly from its GOT to the real function in the private +namespace, bypassing the resolver stage (and bypassing our proxy library +entirely). + +NOTE: The above is mostly true. There is the case of RELRO linking, +where the GOT:<Resolver> stage is handled early by the linker/loader, +resolving all function addresses whether or not they're ever called. + +In RELRO linking the GOT memory page(s) are also mprotect()ed to read-only, +which would prevent us from scribbling on the GOT(s) in question, but +we can work around this by finding the relevant mprotect()ed pages and +toggling the write permission bit on them before we start (and flipping +the bit back when we're done). + +We can only currently work around RELRO linking on Linux currently: I'm not +sure any other ELF systems expose the necessary mprotect meta data to us. diff --git a/subprojects/libcapsule/doc/capsule-init-project.xml b/subprojects/libcapsule/doc/capsule-init-project.xml new file mode 100644 index 0000000000000000000000000000000000000000..781a152faafa25e8ba60eedda495bb25c9ae35fe --- /dev/null +++ b/subprojects/libcapsule/doc/capsule-init-project.xml @@ -0,0 +1,192 @@ +<!-- +Copyright © 2017 Collabora Ltd + +This file is part of libcapsule. + +libcapsule is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation; either version 2.1 of the +License, or (at your option) any later version. + +libcapsule 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 Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with libcapsule. If not, see <http://www.gnu.org/licenses/>. +--> + +<refentry id="capsule-init-project"> + + <refentryinfo> + <title>capsule-init-project</title> + <productname>libcapsule</productname> + <authorgroup> + <author> + <contrib>Developer</contrib> + <firstname>Vivek</firstname> + <surname>Das Mohapatra</surname> + </author> + <author> + <contrib>Developer</contrib> + <firstname>Simon</firstname> + <surname>McVittie</surname> + </author> + </authorgroup> + </refentryinfo> + + <refmeta> + <refentrytitle>capsule-init-project</refentrytitle> + <manvolnum>1</manvolnum> + <refmiscinfo class="manual">User Commands</refmiscinfo> + </refmeta> + + <refnamediv> + <refname>capsule-init-project</refname> + <refpurpose>libcapsule proxy library project setup tool</refpurpose> + </refnamediv> + + <refsynopsisdiv> + <cmdsynopsis> + <command>capsule-init-project</command> + <arg choice="opt">OPTIONS</arg> + <arg choice="req" rep="repeat">TARGET</arg> + </cmdsynopsis> + </refsynopsisdiv> + + <refsect1> + <title>Description</title> + <para> + <command>capsule-init-project</command> creates a project which + uses libcapsule to create a proxy for a library, usually from + a foreign filesystem tree. + </para> + </refsect1> + + <refsect1> + <title>Arguments</title> + <variablelist> + + <varlistentry> + <term><option>TARGET</option></term> + <listitem> + <para> + The base name of the target, of the form libFOO.so.X (eg libz.so.1). + It can optionally be combined with a version number in the + form libFOO.so.X/VERSION (e.g. libz.so.1/1.2.3). + </para> + </listitem> + </varlistentry> + + </variablelist> + </refsect1> + + <refsect1> + <title>Options</title> + <variablelist> + + <varlistentry> + <term><option>--capsule-pkgdatadir=PATH</option></term> + <listitem> + <para>Use supporting files from PATH</para> + </listitem> + </varlistentry> + + <varlistentry> + <term><option>--capsule-symbols-tool=PATH</option></term> + <listitem> + <para>Use replacement capsule-symbols(1)</para> + </listitem> + </varlistentry> + + <varlistentry> + <term><option>--capsule-version-tool=PATH</option></term> + <listitem> + <para>Use replacement capsule-version(1)</para> + </listitem> + </varlistentry> + + <varlistentry> + <term><option>--search-tree=PATH</option></term> + <listitem> + <para> + Find libraries to be proxied in this chroot, + sysroot or container at compile-time. + </para> + <para>default: TREE or /host if unset</para> + </listitem> + </varlistentry> + + <varlistentry> + <term><option>--set-version=VERSION</option></term> + <listitem> + <para> + Use this as the package version + (major, major.minor or major.minor.micro) + instead of using capsule-version(1) on the first + <option>TARGET</option> + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><option>--symbols-from-dir=PATH</option></term> + <listitem> + <para> + Use symbols files in this directory instead of the + output of capsule-symbols(1) + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><option>--runtime-tree=PATH</option></term> + <listitem> + <para> + Find libraries to be proxied in this chroot, sysroot or + container at runtime. The default is /host. + </para> + <para> + If libcapsule was compiled with glibc 2.17 or later and the + process is not privileged, this can be overridden at runtime + by setting a SONAME-specific environment variable (for + example CAPSULE_LIBGL_SO_1_PREFIX for libGL.so.1) or by + setting the generic CAPSULE_PREFIX environment variable. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><option>--package-name=PACKAGE</option></term> + <listitem> + <para> + Use this name for the generated Autotools package, + instead of libFOO-proxy + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><option>--destination=PATH</option></term> + <listitem> + <para> + Use this name for the generated directory, + instead of libFOO-proxy or the argument of + <option>--package-name</option> + </para> + </listitem> + </varlistentry> + + </variablelist> + </refsect1> + + <refsect1><title>See also</title> + <para> + <citerefentry> + <refentrytitle>capsule-mkstublib</refentrytitle> + <manvolnum>1</manvolnum> + </citerefentry> + </para> + </refsect1> +</refentry> diff --git a/subprojects/libcapsule/doc/capsule-mkstublib.xml b/subprojects/libcapsule/doc/capsule-mkstublib.xml new file mode 100644 index 0000000000000000000000000000000000000000..56185ab66b862e0d70d15b6bd653afc6c6dfba48 --- /dev/null +++ b/subprojects/libcapsule/doc/capsule-mkstublib.xml @@ -0,0 +1,251 @@ +<!-- +Copyright © 2017 Collabora Ltd + +This file is part of libcapsule. + +libcapsule is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation; either version 2.1 of the +License, or (at your option) any later version. + +libcapsule 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 Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with libcapsule. If not, see <http://www.gnu.org/licenses/>. +--> + +<refentry id="capsule-mkstublib"> + + <refentryinfo> + <title>capsule-mkstublib</title> + <productname>libcapsule</productname> + <authorgroup> + <author> + <contrib>Developer</contrib> + <firstname>Vivek</firstname> + <surname>Das Mohapatra</surname> + </author> + <author> + <contrib>Developer</contrib> + <firstname>Simon</firstname> + <surname>McVittie</surname> + </author> + </authorgroup> + </refentryinfo> + + <refmeta> + <refentrytitle>capsule-mkstublib</refentrytitle> + <manvolnum>1</manvolnum> + <refmiscinfo class="manual">User Commands</refmiscinfo> + </refmeta> + + <refnamediv> + <refname>capsule-mkstublib</refname> + + <refpurpose> + Command used by the libcapsule build system to generate proxy + library source files. + </refpurpose> + + </refnamediv> + + <refsynopsisdiv> + <cmdsynopsis> + <command>capsule-mkstublib</command> + <arg choice="opt">OPTIONS</arg> + <arg choice="plain">TARGET</arg> + <arg choice="plain">EXCLUDES</arg> + <arg choice="plain">EXPORTS</arg> + <arg choice="plain">OUTPUT</arg> + <arg choice="opt">DSO-VERSION<arg choice="opt">TREE</arg></arg> + </cmdsynopsis> + </refsynopsisdiv> + + <refsect1> + <title>Description</title> + <para> + <command>capsule-mkstublib</command> generates the source code + for a libcapsule proxy library from a standard set of inputs + (described below). + </para> + </refsect1> + + <refsect1> + <title>Arguments</title> + <variablelist> + + <varlistentry> + <term><option>TARGET</option></term> + <listitem> + <para> + The SONAME of the target, of the form libFOO.so.X (eg libz.so.1) + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><option>EXCLUDES</option></term> + <listitem> + <para> + A file containing a list of SONAMEs (one on each line) not to + include in the libcapsule proxy: DSOs listed here will not + be loaded from the foreign tree and must come from the + normal set searched by the linker. + </para> + <para> + For example if libz.so.1 is listed here, and TARGET depends + on libz.so.1 then it will be run-time linked against the + copy of libz from / - not any copies which are found under TREE + </para> + <para> + This file is empty by default in new prjects. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><option>EXPORTS</option></term> + <listitem> + <para> + A file containing a list of SONAMEs (one on each line) whose + symbols should be exported. + </para> + <para>Symbols from TARGET are always exported.</para> + </listitem> + </varlistentry> + + <varlistentry> + <term><option>OUTPUT</option></term> + <listitem> + <para> + Output filename + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><option>DSO-VERSION</option></term> + <listitem> + <para> + The version of the target library our proxy will pretend to be, + in the format MAJOR:MINOR:SUB + </para> + <para> + The format is the same format as consumed by libtool. + Only MAJOR must be present if this argument is supplied. + </para> + <para> + The default value is determined by parsing the soname of the + library. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><option>TREE</option></term> + <listitem> + <para> + The mount point of the foreign filesystem tree from which TARGET + and its dependencies will be acquired. + </para> + <para> + This value can be overridden by --runtime-tree and --search-tree. + </para> + <para> + The default is the soname-specific environment variable (eg + CAPSULE_LIBGL_SO_1_PREFIX for libGL.so.1), or the CAPSULE_PREFIX + environment variable, or /host. The environment variables are + ignored if the process is privileged, or if libcapsule was + compiled with glibc older than 2.17. + </para> + </listitem> + </varlistentry> + + </variablelist> + + </refsect1> + + <refsect1> + <title>Options</title> + <variablelist> + + <varlistentry> + <term><option>--capsule-symbols-tool=PATH</option></term> + <listitem> + <para>Use replacement capsule-symbols(1)</para> + </listitem> + </varlistentry> + + <varlistentry> + <term><option>--symbols-from=PATH</option></term> + <listitem> + <para> + Use this symbols file instead of the output of capsule-symbols(1) + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><option>--[no]-update-symbols</option></term> + <listitem> + <para> + Do or don't update the exported symbol list if EXPORTS is + updated [default: do] + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><option>--search-tree=PATH</option></term> + <listitem> + <para> + Find libraries to be proxied in this chroot, + sysroot or container at compile-time. + </para> + <para>default: TREE or / if unset</para> + </listitem> + </varlistentry> + + <varlistentry> + <term><option>--runtime-tree=PATH</option></term> + <listitem> + <para> + Find libraries to be proxied in this chroot, sysroot or + container at runtime. The default is /host. + </para> + <para> + If libcapsule was compiled with glibc 2.17 or later and the + process is not privileged, this can be overridden at runtime + by setting a SONAME-specific environment variable (for + example CAPSULE_LIBGL_SO_1_PREFIX for libGL.so.1) or by + setting the generic CAPSULE_PREFIX environment variable. + </para> + </listitem> + </varlistentry> + + </variablelist> + </refsect1> + + <refsect1> + <title>Bugs</title> + <para> + The order of arguments is a bit pathological: This is for + historical reasons. Since the command is not expected to be + invoked outside of libcapsule's build scripts this is not + considered a major problem. + </para> + </refsect1> + + <refsect1> + <title>See also</title> + <para> + <citerefentry> + <refentrytitle>capsule-init-project</refentrytitle> + <manvolnum>1</manvolnum> + </citerefentry> + </para> + </refsect1> +</refentry> diff --git a/subprojects/libcapsule/doc/devhelp2man.xslt b/subprojects/libcapsule/doc/devhelp2man.xslt new file mode 100644 index 0000000000000000000000000000000000000000..974bf144c484e78c67ced153e1ea9ebecfa403b8 --- /dev/null +++ b/subprojects/libcapsule/doc/devhelp2man.xslt @@ -0,0 +1,225 @@ +<!-- +Copyright © 2017 Collabora Ltd + +This file is part of libcapsule. + +libcapsule is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as +published by the Free Software Foundation; either version 2.1 of the +License, or (at your option) any later version. + +libcapsule 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 Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with libcapsule. If not, see <http://www.gnu.org/licenses/>. +--> + +<xsl:stylesheet version = '1.0' + xmlns:xi="http://www.w3.org/2003/XInclude" + xmlns:xsl='http://www.w3.org/1999/XSL/Transform' + xmlns:saxon="http://icl.com/saxon" + xmlns:xslt="http://xml.apache.org/xslt" + xmlns:string="http://exslt.org/strings" + xmlns:ext="http://exslt.org/common" + extension-element-prefixes="saxon" + exclude-result-prefixes="xi"> + <xsl:strip-space elements="*" /> + <xsl:output omit-xml-declaration="yes" + indent="yes" + encoding="utf-8" + xslt:indent-amount="2"/> + + <xsl:template match="node()|@*" mode="description"> + <xsl:apply-templates/> + </xsl:template> + + <xsl:template name="paramdesc" match="refsect2[@role='function']" + mode="paramdesc"> + <xsl:param name="matched" select="indexterm/primary"/> + <xsl:if test="$matched = $target"> + <xsl:element name="title">Parameters:</xsl:element> + <xsl:element name="variablelist"> + <xsl:for-each select="refsect3[@role='parameters']//row"> + <xsl:element name="varlistentry"> + <xsl:element name="term"> + <xsl:element name="code"> + <xsl:value-of select="concat(entry[@role='parameter_name'], ': ')"/> + </xsl:element> + </xsl:element> + <xsl:element name="listitem"> + <xsl:element name="para"> + <xsl:value-of select="entry[@role='parameter_description']"/> + </xsl:element> + </xsl:element> + </xsl:element> + </xsl:for-each> + </xsl:element> + </xsl:if> + </xsl:template> + + <xsl:template name="retdesc" match="refsect2[@role='function']" + mode="retdesc"> + <xsl:param name="matched" select="indexterm/primary"/> + <xsl:if test="$matched = $target"> + <xsl:for-each select="refsect3[@role='returns']/*"> + <xsl:copy-of select="."/> + </xsl:for-each> + </xsl:if> + </xsl:template> + <!-- //////////////////////////////////////////////////////// --> + + <xsl:template match="node()|@*" name="ignore"> + <xsl:apply-templates/> + </xsl:template> + + <xsl:template match="entry[@role='function_name']/link" name="refnamediv"> + <xsl:param name="matched" select="."/> + <xsl:if test="$matched = $target"> + <xsl:element name="refname"> + <xsl:value-of select="."/> + </xsl:element> + <xsl:apply-templates/> + </xsl:if> + </xsl:template> + + <xsl:template match="refsynopsisdiv/synopsis/link" mode="refnamediv"> + <xsl:param name="matched" select="."/> + <xsl:if test="$matched = $target"> + <xsl:element name="refname"> + <xsl:value-of select="."/> + </xsl:element> + <xsl:apply-templates/> + </xsl:if> + </xsl:template> + + <xsl:template name="paramdef" match="parameter" mode="funcsig"> + <xsl:param name="tok" select="string:tokenize(., ' ')"/> + <xsl:param name="last" select="ext:node-set($tok)[last()]"/> + <xsl:param name="name" select="string:tokenize($last, '*')"/> + <xsl:param name="stars" select="substring-before($last, $name)"/> + <xsl:element name="paramdef"> + <xsl:for-each select="ext:node-set($tok)[position() != last()]"> + <xsl:value-of select="concat(., ' ')"/> + </xsl:for-each> + <xsl:value-of select="$stars"/> + <xsl:element name="parameter"> + <xsl:value-of select="$name"/> + </xsl:element> + </xsl:element> + </xsl:template> + + <xsl:template name="funcproto" match="refsect2[@role='function']"> + <xsl:param name="matched" select="indexterm/primary"/> + <xsl:if test="$matched = $target"> + <xsl:element name="funcprototype"> + <xsl:element name="funcdef"> + <xsl:value-of + select="concat(substring-before(programlisting,'
'), ' ')"/> + <xsl:element name="function"> + <xsl:value-of select="indexterm/primary"/> + </xsl:element> + </xsl:element> + <xsl:apply-templates select="programlisting" mode="funcsig"/> + </xsl:element> + </xsl:if> + </xsl:template> + + <xsl:template name="voiddesc" match="refsect2[@role='function']" + mode="voiddesc"> + <xsl:param name="matched" select="indexterm/primary"/> + <xsl:if test="$matched = $target"> + <xsl:for-each select="para"> + <xsl:copy-of select="."/> + </xsl:for-each> + </xsl:if> + </xsl:template> + + <xsl:template match="/refentry" name="top"> + <xsl:element name="refentry"> + <xsl:attribute name="id"><xsl:value-of select="@id"/></xsl:attribute> + <xsl:element name="refentryinfo"> + <xsl:element name="productname">libcapsule</xsl:element> + <xsl:element name="authorgroup"> + <xsl:element name="contrib">Author</xsl:element> + <xsl:element name="firstname">Vivek</xsl:element> + <xsl:element name="surname">Das Mohapatra</xsl:element> + </xsl:element> + <xsl:element name="authorgroup"> + <xsl:element name="contrib">Author</xsl:element> + <xsl:element name="firstname">Simon</xsl:element> + <xsl:element name="surname">McVittie</xsl:element> + </xsl:element> + </xsl:element> + <xsl:element name="refmeta"> + <xsl:element name="refentrytitle"> + <!-- xsl:value-of select="/refentry/refmeta/refentrytitle"/ --> + <xsl:value-of select="$target"/> + </xsl:element> + <xsl:element name="manvolnum"> + <xsl:value-of select="/refentry/refmeta/manvolnum"/> + </xsl:element> + <xsl:element name="refmiscinfo"> + <xsl:attribute name="class">manual</xsl:attribute> + <xsl:value-of select="normalize-space(refmeta/refmiscinfo)"/> + </xsl:element> + </xsl:element> + <xsl:element name="refnamediv"> + <xsl:choose> + <xsl:when test="refsect1[@role='functions_proto']"> + <xsl:apply-templates select="refsect1[@role='functions_proto']"/> + </xsl:when> + <xsl:otherwise> + <xsl:apply-templates select="refsynopsisdiv[@role='synopsis']" mode="refnamediv"/> + </xsl:otherwise> + </xsl:choose> + </xsl:element> + <xsl:element name="refsynopsisdiv"> + <xsl:element name="funcsynopsis"> + <xsl:element name="funcsynopsisinfo"> + <xsl:value-of select="$fsinfo"/> + </xsl:element> + </xsl:element> + <xsl:apply-templates select="refsect1[@role='details']"/> + </xsl:element> + <xsl:element name="refsect1"> + <xsl:element name="title">Description:</xsl:element> + <xsl:apply-templates + select="refsect1[@role='details' and contains(@id,'functions_details')]/refsect2" + mode="voiddesc"/> + <xsl:element name="refsect2"> + <xsl:apply-templates + select="refsect1[@role='details' and contains(@id,'functions_details')]/refsect2" + mode="paramdesc"/> + </xsl:element> + <xsl:element name="refsect2"> + <xsl:apply-templates + select="refsect1[@role='details' and contains(@id,'functions_details')]/refsect2" + mode="retdesc"/> + </xsl:element> + </xsl:element> + + <xsl:element name="refsect1"> + <xsl:element name="title">See Also:</xsl:element> + <xsl:element name="para"> + <xsl:for-each select="refsect1[@role='details']/refsect2[@role='function']/indexterm/primary"> + <xsl:element name="link"> + <xsl:element name="function"> + <xsl:value-of select="concat(.,'(3)')"/> + </xsl:element> + </xsl:element> + <xsl:choose> + <xsl:when test="position() != last()"> + <xsl:text>, </xsl:text> + </xsl:when> + </xsl:choose> + </xsl:for-each> + </xsl:element> + </xsl:element> + + </xsl:element> + </xsl:template> + +</xsl:stylesheet> diff --git a/subprojects/libcapsule/documentation.mk b/subprojects/libcapsule/documentation.mk new file mode 100644 index 0000000000000000000000000000000000000000..d9b784f0cd675ea9d9caca42cb0cb6ba191ce4a9 --- /dev/null +++ b/subprojects/libcapsule/documentation.mk @@ -0,0 +1,59 @@ +# Copyright © 2017 Collabora Ltd + +# This file is part of libcapsule. + +# libcapsule is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as +# published by the Free Software Foundation; either version 2.1 of the +# License, or (at your option) any later version. + +# libcapsule 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 Lesser General Public License for more details. + +# You should have received a copy of the GNU Lesser General Public +# License along with libcapsule. If not, see <http://www.gnu.org/licenses/>. + +# +# ============================================================================ +# standalone man pages from docbook source: +XSLTPROC_FLAGS = \ + --nonet \ + --stringparam man.output.quietly 1 \ + --stringparam funcsynopsis.style ansi \ + --stringparam man.th.extra1.suppress 1 \ + --stringparam man.authors.section.enabled 1 \ + --stringparam man.copyright.section.enabled 0 + +XSLT_DOMAIN = docbook.sourceforge.net +XSLT_MAN = http://$(XSLT_DOMAIN)/release/xsl/current/manpages/docbook.xsl +XSLT_CAPARGS = --stringparam fsinfo "$$(printf "\#define _GNU_SOURCE\012\#include <capsule/capsule.h>")" --stringparam target +XSLTPROC_STD = $(XSLTPROC) $(XSLTPROC_FLAGS) + +man_MANS = + +if HAVE_XSLTPROC +%.1: doc/%.xml + $(AM_V_GEN) $(XSLTPROC_STD) $(XSLT_MAN) $< + +man_MANS += capsule-init-project.1 capsule-mkstublib.1 +endif # HAVE_XSLTPROC + +if ENABLE_GTK_DOC + +xml/capsule.xml: docs + +%.3.xml: xml/capsule.xml doc/devhelp2man.xslt documentation.mk + $(AM_V_GEN) $(XSLTPROC_STD) $(XSLT_CAPARGS) $* $(srcdir)/doc/devhelp2man.xslt $< > $@ + +%.3: %.3.xml documentation.mk + $(AM_V_GEN) $(XSLTPROC_STD) $(XSLT_MAN) $< + +man_MANS += capsule_init.3 +man_MANS += capsule_shim_dlopen.3 +man_MANS += capsule_external_dlsym.3 + +endif # ENABLE_GTK_DOC + +CLEANFILES += $(man_MANS) diff --git a/subprojects/libcapsule/examples/.gitignore b/subprojects/libcapsule/examples/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..bca62216bdb700810ee6ec5cf67bfd115d53ea86 --- /dev/null +++ b/subprojects/libcapsule/examples/.gitignore @@ -0,0 +1,21 @@ +*.a +*.la +*.lo +*.o +.deps/ +.dirstamp +.libs/ +/Makefile +/Makefile.in +/aclocal.m4 +/autom4te.cache/ +/build-aux/ +/capsule-shim.mk +/config.log +/config.status +/configure +/libtool +/m4/ +/preconfigure.log +/shim/*.so.*.c +/shim/*.so.*.map diff --git a/subprojects/libcapsule/examples/shim/libEGL.so.1.excluded b/subprojects/libcapsule/examples/shim/libEGL.so.1.excluded new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/subprojects/libcapsule/examples/shim/libEGL.so.1.shared b/subprojects/libcapsule/examples/shim/libEGL.so.1.shared new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/subprojects/libcapsule/examples/shim/libEGL.so.1.symbols b/subprojects/libcapsule/examples/shim/libEGL.so.1.symbols new file mode 100644 index 0000000000000000000000000000000000000000..1c3b189f714340f3de71a4b9fb5a20ba5666973d --- /dev/null +++ b/subprojects/libcapsule/examples/shim/libEGL.so.1.symbols @@ -0,0 +1,46 @@ +eglBindAPI +eglBindTexImage +eglChooseConfig +eglClientWaitSync +eglCopyBuffers +eglCreateContext +eglCreateImage +eglCreatePbufferFromClientBuffer +eglCreatePbufferSurface +eglCreatePixmapSurface +eglCreatePlatformPixmapSurface +eglCreatePlatformPixmapSurfaceEXT +eglCreatePlatformWindowSurface +eglCreatePlatformWindowSurfaceEXT +eglCreateSync +eglCreateWindowSurface +eglDestroyContext +eglDestroyImage +eglDestroySurface +eglDestroySync +eglGetConfigAttrib +eglGetConfigs +eglGetCurrentContext +eglGetCurrentDisplay +eglGetCurrentSurface +eglGetDisplay +eglGetError +eglGetPlatformDisplay +eglGetProcAddress +eglGetSyncAttrib +eglInitialize +eglMakeCurrent +eglQueryAPI +eglQueryContext +eglQueryString +eglQuerySurface +eglReleaseTexImage +eglReleaseThread +eglSurfaceAttrib +eglSwapBuffers +eglSwapInterval +eglTerminate +eglWaitClient +eglWaitGL +eglWaitNative +eglWaitSync diff --git a/subprojects/libcapsule/examples/shim/libEGL.so.1.symbols.updated-for b/subprojects/libcapsule/examples/shim/libEGL.so.1.symbols.updated-for new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/subprojects/libcapsule/examples/shim/libGL.so.1.excluded b/subprojects/libcapsule/examples/shim/libGL.so.1.excluded new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/subprojects/libcapsule/examples/shim/libGL.so.1.shared b/subprojects/libcapsule/examples/shim/libGL.so.1.shared new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/subprojects/libcapsule/examples/shim/libGL.so.1.symbols b/subprojects/libcapsule/examples/shim/libGL.so.1.symbols new file mode 100644 index 0000000000000000000000000000000000000000..b9693fae76a2eca7c276ca1d51f1c774b5204a95 --- /dev/null +++ b/subprojects/libcapsule/examples/shim/libGL.so.1.symbols @@ -0,0 +1,3330 @@ +glAccum +glAccumxOES +glActiveProgramEXT +glActiveShaderProgram +glActiveShaderProgramEXT +glActiveStencilFaceEXT +glActiveTexture +glActiveTextureARB +glActiveVaryingNV +glAlphaFragmentOp1ATI +glAlphaFragmentOp2ATI +glAlphaFragmentOp3ATI +glAlphaFunc +glAlphaFuncQCOM +glAlphaFuncx +glAlphaFuncxOES +glApplyFramebufferAttachmentCMAAINTEL +glApplyTextureEXT +glAreProgramsResidentNV +glAreTexturesResident +glAreTexturesResidentEXT +glArrayElement +glArrayElementEXT +glArrayObjectATI +glAsyncMarkerSGIX +glAttachObjectARB +glAttachShader +glBegin +glBeginConditionalRender +glBeginConditionalRenderNV +glBeginConditionalRenderNVX +glBeginFragmentShaderATI +glBeginOcclusionQueryNV +glBeginPerfMonitorAMD +glBeginPerfQueryINTEL +glBeginQuery +glBeginQueryARB +glBeginQueryEXT +glBeginQueryIndexed +glBeginTransformFeedback +glBeginTransformFeedbackEXT +glBeginTransformFeedbackNV +glBeginVertexShaderEXT +glBeginVideoCaptureNV +glBindANCCaptureStreamBufferNVX +glBindAttribLocation +glBindAttribLocationARB +glBindBuffer +glBindBufferARB +glBindBufferBase +glBindBufferBaseEXT +glBindBufferBaseNV +glBindBufferOffsetEXT +glBindBufferOffsetNV +glBindBufferRange +glBindBufferRangeEXT +glBindBufferRangeNV +glBindBuffersBase +glBindBuffersRange +glBindFragDataLocation +glBindFragDataLocationEXT +glBindFragDataLocationIndexed +glBindFragDataLocationIndexedEXT +glBindFragmentShaderATI +glBindFramebuffer +glBindFramebufferEXT +glBindFramebufferOES +glBindImageTexture +glBindImageTextureEXT +glBindImageTextures +glBindLightParameterEXT +glBindMaterialParameterEXT +glBindMultiTextureEXT +glBindParameterEXT +glBindProgramARB +glBindProgramNV +glBindProgramPipeline +glBindProgramPipelineEXT +glBindRenderbuffer +glBindRenderbufferEXT +glBindRenderbufferOES +glBindSampler +glBindSamplers +glBindTexGenParameterEXT +glBindTexture +glBindTextureEXT +glBindTextureUnit +glBindTextureUnitParameterEXT +glBindTextures +glBindTransformFeedback +glBindTransformFeedbackEXT +glBindTransformFeedbackNV +glBindVertexArray +glBindVertexArrayAPPLE +glBindVertexArrayOES +glBindVertexBuffer +glBindVertexBuffers +glBindVertexShaderEXT +glBindVideoCaptureStreamBufferNV +glBindVideoCaptureStreamTextureNV +glBinormal3bEXT +glBinormal3bvEXT +glBinormal3dEXT +glBinormal3dvEXT +glBinormal3fEXT +glBinormal3fvEXT +glBinormal3iEXT +glBinormal3ivEXT +glBinormal3sEXT +glBinormal3svEXT +glBinormalPointerEXT +glBitmap +glBitmapxOES +glBlendBarrier +glBlendBarrierKHR +glBlendBarrierNV +glBlendColor +glBlendColorEXT +glBlendColorxOES +glBlendEquation +glBlendEquationEXT +glBlendEquationIndexedAMD +glBlendEquationOES +glBlendEquationSeparate +glBlendEquationSeparateEXT +glBlendEquationSeparateIndexedAMD +glBlendEquationSeparateOES +glBlendEquationSeparatei +glBlendEquationSeparateiARB +glBlendEquationSeparateiEXT +glBlendEquationSeparateiOES +glBlendEquationi +glBlendEquationiARB +glBlendEquationiEXT +glBlendEquationiOES +glBlendFunc +glBlendFuncIndexedAMD +glBlendFuncSeparate +glBlendFuncSeparateEXT +glBlendFuncSeparateINGR +glBlendFuncSeparateIndexedAMD +glBlendFuncSeparateOES +glBlendFuncSeparatei +glBlendFuncSeparateiARB +glBlendFuncSeparateiEXT +glBlendFuncSeparateiOES +glBlendFunci +glBlendFunciARB +glBlendFunciEXT +glBlendFunciOES +glBlendParameteriNV +glBlitFramebuffer +glBlitFramebufferANGLE +glBlitFramebufferEXT +glBlitFramebufferNV +glBlitNamedFramebuffer +glBufferAddressRangeNV +glBufferData +glBufferDataARB +glBufferDataSysmem +glBufferPageCommitmentARB +glBufferParameteriAPPLE +glBufferStorage +glBufferStorageEXT +glBufferSubData +glBufferSubDataARB +glCallCommandListNV +glCallList +glCallLists +glCheckFramebufferStatus +glCheckFramebufferStatusEXT +glCheckFramebufferStatusOES +glCheckNamedFramebufferStatus +glCheckNamedFramebufferStatusEXT +glClampColor +glClampColorARB +glClear +glClearAccum +glClearAccumxOES +glClearBufferData +glClearBufferSubData +glClearBufferfi +glClearBufferfv +glClearBufferiv +glClearBufferuiv +glClearColor +glClearColorIiEXT +glClearColorIuiEXT +glClearColorx +glClearColorxOES +glClearDepth +glClearDepthdNV +glClearDepthf +glClearDepthfOES +glClearDepthx +glClearDepthxOES +glClearIndex +glClearNamedBufferData +glClearNamedBufferDataEXT +glClearNamedBufferSubData +glClearNamedBufferSubDataEXT +glClearNamedFramebufferfi +glClearNamedFramebufferfv +glClearNamedFramebufferiv +glClearNamedFramebufferuiv +glClearStencil +glClearTexImage +glClearTexSubImage +glClientActiveTexture +glClientActiveTextureARB +glClientActiveVertexStreamATI +glClientAttribDefaultEXT +glClientWaitSync +glClientWaitSyncAPPLE +glClientWaitSyncValueuiNVX +glClipControl +glClipPlane +glClipPlanef +glClipPlanefIMG +glClipPlanefOES +glClipPlanex +glClipPlanexIMG +glClipPlanexOES +glColor3b +glColor3bv +glColor3d +glColor3dv +glColor3f +glColor3fVertex3fSUN +glColor3fVertex3fvSUN +glColor3fv +glColor3hNV +glColor3hvNV +glColor3i +glColor3iv +glColor3s +glColor3sv +glColor3ub +glColor3ubv +glColor3ui +glColor3uiv +glColor3us +glColor3usv +glColor3x +glColor3xOES +glColor3xvOES +glColor4b +glColor4bv +glColor4d +glColor4dv +glColor4f +glColor4fNormal3fVertex3fSUN +glColor4fNormal3fVertex3fvSUN +glColor4fv +glColor4hNV +glColor4hvNV +glColor4i +glColor4iv +glColor4s +glColor4sv +glColor4ub +glColor4ubVertex2fSUN +glColor4ubVertex2fvSUN +glColor4ubVertex3fSUN +glColor4ubVertex3fvSUN +glColor4ubv +glColor4ui +glColor4uiv +glColor4us +glColor4usv +glColor4x +glColor4xOES +glColor4xvOES +glColorFormatNV +glColorFragmentOp1ATI +glColorFragmentOp2ATI +glColorFragmentOp3ATI +glColorMask +glColorMaskIndexedEXT +glColorMaski +glColorMaskiEXT +glColorMaskiOES +glColorMaterial +glColorP3ui +glColorP3uiv +glColorP4ui +glColorP4uiv +glColorPointer +glColorPointerEXT +glColorPointerListIBM +glColorPointervINTEL +glColorSubTable +glColorSubTableEXT +glColorTable +glColorTableEXT +glColorTableParameterfv +glColorTableParameterfvSGI +glColorTableParameteriv +glColorTableParameterivSGI +glColorTableSGI +glCombinerInputNV +glCombinerOutputNV +glCombinerParameterfNV +glCombinerParameterfvNV +glCombinerParameteriNV +glCombinerParameterivNV +glCombinerStageParameterfvNV +glCommandListSegmentsNV +glCompileCommandListNV +glCompileShader +glCompileShaderARB +glCompileShaderIncludeARB +glCompressedMultiTexImage1DEXT +glCompressedMultiTexImage2DEXT +glCompressedMultiTexImage3DEXT +glCompressedMultiTexSubImage1DEXT +glCompressedMultiTexSubImage2DEXT +glCompressedMultiTexSubImage3DEXT +glCompressedTexImage1D +glCompressedTexImage1DARB +glCompressedTexImage2D +glCompressedTexImage2DARB +glCompressedTexImage3D +glCompressedTexImage3DARB +glCompressedTexImage3DNV +glCompressedTexImage3DOES +glCompressedTexSubImage1D +glCompressedTexSubImage1DARB +glCompressedTexSubImage2D +glCompressedTexSubImage2DARB +glCompressedTexSubImage3D +glCompressedTexSubImage3DARB +glCompressedTexSubImage3DNV +glCompressedTexSubImage3DOES +glCompressedTextureImage1DEXT +glCompressedTextureImage2DEXT +glCompressedTextureImage3DEXT +glCompressedTextureSubImage1D +glCompressedTextureSubImage1DEXT +glCompressedTextureSubImage2D +glCompressedTextureSubImage2DEXT +glCompressedTextureSubImage3D +glCompressedTextureSubImage3DEXT +glConservativeRasterParameterfNV +glConvolutionFilter1D +glConvolutionFilter1DEXT +glConvolutionFilter2D +glConvolutionFilter2DEXT +glConvolutionParameterf +glConvolutionParameterfEXT +glConvolutionParameterfv +glConvolutionParameterfvEXT +glConvolutionParameteri +glConvolutionParameteriEXT +glConvolutionParameteriv +glConvolutionParameterivEXT +glConvolutionParameterxOES +glConvolutionParameterxvOES +glCopyBufferSubData +glCopyBufferSubDataNV +glCopyColorSubTable +glCopyColorSubTableEXT +glCopyColorTable +glCopyColorTableSGI +glCopyConvolutionFilter1D +glCopyConvolutionFilter1DEXT +glCopyConvolutionFilter2D +glCopyConvolutionFilter2DEXT +glCopyImageSubData +glCopyImageSubDataEXT +glCopyImageSubDataNV +glCopyImageSubDataOES +glCopyMultiTexImage1DEXT +glCopyMultiTexImage2DEXT +glCopyMultiTexSubImage1DEXT +glCopyMultiTexSubImage2DEXT +glCopyMultiTexSubImage3DEXT +glCopyNamedBufferSubData +glCopyPathNV +glCopyPixels +glCopyTexImage1D +glCopyTexImage1DEXT +glCopyTexImage2D +glCopyTexImage2DEXT +glCopyTexSubImage1D +glCopyTexSubImage1DEXT +glCopyTexSubImage2D +glCopyTexSubImage2DEXT +glCopyTexSubImage3D +glCopyTexSubImage3DEXT +glCopyTexSubImage3DNV +glCopyTexSubImage3DOES +glCopyTextureImage1DEXT +glCopyTextureImage2DEXT +glCopyTextureLevelsAPPLE +glCopyTextureSubImage1D +glCopyTextureSubImage1DEXT +glCopyTextureSubImage2D +glCopyTextureSubImage2DEXT +glCopyTextureSubImage3D +glCopyTextureSubImage3DEXT +glCoverFillPathInstancedNV +glCoverFillPathNV +glCoverStrokePathInstancedNV +glCoverStrokePathNV +glCoverageMaskNV +glCoverageModulationNV +glCoverageModulationTableNV +glCoverageOperationNV +glCreateBuffers +glCreateCommandListsNV +glCreateFramebuffers +glCreatePerfQueryINTEL +glCreateProgram +glCreateProgramObjectARB +glCreateProgramPipelines +glCreateQueries +glCreateRenderbuffers +glCreateSamplers +glCreateShader +glCreateShaderObjectARB +glCreateShaderProgramEXT +glCreateShaderProgramv +glCreateShaderProgramvEXT +glCreateStatesNV +glCreateSyncFromCLeventARB +glCreateTextures +glCreateTransformFeedbacks +glCreateVertexArrays +glCullFace +glCullParameterdvEXT +glCullParameterfvEXT +glCurrentPaletteMatrixARB +glCurrentPaletteMatrixOES +glDebugMessageCallback +glDebugMessageCallbackAMD +glDebugMessageCallbackARB +glDebugMessageCallbackKHR +glDebugMessageCallbackOES +glDebugMessageControl +glDebugMessageControlARB +glDebugMessageControlKHR +glDebugMessageControlOES +glDebugMessageEnableAMD +glDebugMessageInsert +glDebugMessageInsertAMD +glDebugMessageInsertARB +glDebugMessageInsertKHR +glDebugMessageInsertOES +glDeformSGIX +glDeformationMap3dSGIX +glDeformationMap3fSGIX +glDeleteAsyncMarkersSGIX +glDeleteBuffers +glDeleteBuffersARB +glDeleteCommandListsNV +glDeleteFencesAPPLE +glDeleteFencesNV +glDeleteFragmentShaderATI +glDeleteFramebuffers +glDeleteFramebuffersEXT +glDeleteFramebuffersOES +glDeleteLists +glDeleteNamedStringARB +glDeleteNamesAMD +glDeleteObjectARB +glDeleteOcclusionQueriesNV +glDeletePathsNV +glDeletePerfMonitorsAMD +glDeletePerfQueryINTEL +glDeleteProgram +glDeleteProgramPipelines +glDeleteProgramPipelinesEXT +glDeleteProgramsARB +glDeleteProgramsNV +glDeleteQueries +glDeleteQueriesARB +glDeleteQueriesEXT +glDeleteRenderbuffers +glDeleteRenderbuffersEXT +glDeleteRenderbuffersOES +glDeleteSamplers +glDeleteShader +glDeleteStatesNV +glDeleteSync +glDeleteSyncAPPLE +glDeleteTextures +glDeleteTexturesEXT +glDeleteTransformFeedbacks +glDeleteTransformFeedbacksEXT +glDeleteTransformFeedbacksNV +glDeleteVertexArrays +glDeleteVertexArraysAPPLE +glDeleteVertexArraysOES +glDeleteVertexShaderEXT +glDepthBoundsEXT +glDepthBoundsdNV +glDepthFunc +glDepthMask +glDepthRange +glDepthRangeArraydvNV +glDepthRangeArrayfvNV +glDepthRangeArrayv +glDepthRangeIndexed +glDepthRangeIndexeddNV +glDepthRangeIndexedfNV +glDepthRangedNV +glDepthRangef +glDepthRangefOES +glDepthRangex +glDepthRangexOES +glDetachObjectARB +glDetachShader +glDetailTexFuncSGIS +glDevtoolsCorrelationIdNVX +glDevtoolsInvokeStreamedCallbackNVX +glDisable +glDisableClientState +glDisableClientStateIndexedEXT +glDisableClientStateiEXT +glDisableDriverControlQCOM +glDisableIndexedEXT +glDisableVariantClientStateEXT +glDisableVertexArrayAttrib +glDisableVertexArrayAttribEXT +glDisableVertexArrayEXT +glDisableVertexAttribAPPLE +glDisableVertexAttribArray +glDisableVertexAttribArrayARB +glDisablei +glDisableiEXT +glDisableiNV +glDisableiOES +glDiscardFramebufferEXT +glDispatchCompute +glDispatchComputeGroupSizeARB +glDispatchComputeIndirect +glDrawArrays +glDrawArraysEXT +glDrawArraysIndirect +glDrawArraysInstanced +glDrawArraysInstancedANGLE +glDrawArraysInstancedARB +glDrawArraysInstancedBaseInstance +glDrawArraysInstancedBaseInstanceEXT +glDrawArraysInstancedEXT +glDrawArraysInstancedNV +glDrawBuffer +glDrawBuffers +glDrawBuffersARB +glDrawBuffersATI +glDrawBuffersEXT +glDrawBuffersIndexedEXT +glDrawBuffersNV +glDrawCommandsAddressNV +glDrawCommandsNV +glDrawCommandsStatesAddressNV +glDrawCommandsStatesNV +glDrawElementArrayAPPLE +glDrawElementArrayATI +glDrawElements +glDrawElementsBaseVertex +glDrawElementsBaseVertexEXT +glDrawElementsBaseVertexOES +glDrawElementsIndirect +glDrawElementsInstanced +glDrawElementsInstancedANGLE +glDrawElementsInstancedARB +glDrawElementsInstancedBaseInstance +glDrawElementsInstancedBaseInstanceEXT +glDrawElementsInstancedBaseVertex +glDrawElementsInstancedBaseVertexBaseInstance +glDrawElementsInstancedBaseVertexBaseInstanceEXT +glDrawElementsInstancedBaseVertexEXT +glDrawElementsInstancedBaseVertexOES +glDrawElementsInstancedEXT +glDrawElementsInstancedNV +glDrawMeshArraysSUN +glDrawMeshNV +glDrawPixels +glDrawRangeElementArrayAPPLE +glDrawRangeElementArrayATI +glDrawRangeElements +glDrawRangeElementsBaseVertex +glDrawRangeElementsBaseVertexEXT +glDrawRangeElementsBaseVertexOES +glDrawRangeElementsEXT +glDrawTexfOES +glDrawTexfvOES +glDrawTexiOES +glDrawTexivOES +glDrawTexsOES +glDrawTexsvOES +glDrawTextureNV +glDrawTexxOES +glDrawTexxvOES +glDrawTransformFeedback +glDrawTransformFeedbackEXT +glDrawTransformFeedbackInstanced +glDrawTransformFeedbackNV +glDrawTransformFeedbackStream +glDrawTransformFeedbackStreamInstanced +glEGLImageTargetRenderbufferStorageOES +glEGLImageTargetTexture2DOES +glEdgeFlag +glEdgeFlagFormatNV +glEdgeFlagPointer +glEdgeFlagPointerEXT +glEdgeFlagPointerListIBM +glEdgeFlagv +glElementPointerAPPLE +glElementPointerATI +glEnable +glEnableClientState +glEnableClientStateIndexedEXT +glEnableClientStateiEXT +glEnableDriverControlQCOM +glEnableIndexedEXT +glEnableVariantClientStateEXT +glEnableVertexArrayAttrib +glEnableVertexArrayAttribEXT +glEnableVertexArrayEXT +glEnableVertexAttribAPPLE +glEnableVertexAttribArray +glEnableVertexAttribArrayARB +glEnablei +glEnableiEXT +glEnableiNV +glEnableiOES +glEnd +glEndConditionalRender +glEndConditionalRenderNV +glEndConditionalRenderNVX +glEndFragmentShaderATI +glEndList +glEndOcclusionQueryNV +glEndPerfMonitorAMD +glEndPerfQueryINTEL +glEndQuery +glEndQueryARB +glEndQueryEXT +glEndQueryIndexed +glEndTilingQCOM +glEndTransformFeedback +glEndTransformFeedbackEXT +glEndTransformFeedbackNV +glEndVertexShaderEXT +glEndVideoCaptureNV +glEvalCoord1d +glEvalCoord1dv +glEvalCoord1f +glEvalCoord1fv +glEvalCoord1xOES +glEvalCoord1xvOES +glEvalCoord2d +glEvalCoord2dv +glEvalCoord2f +glEvalCoord2fv +glEvalCoord2xOES +glEvalCoord2xvOES +glEvalMapsNV +glEvalMesh1 +glEvalMesh2 +glEvalPoint1 +glEvalPoint2 +glEvaluateDepthValuesARB +glExecuteProgramNV +glExtGetBufferPointervQCOM +glExtGetBuffersQCOM +glExtGetFramebuffersQCOM +glExtGetProgramBinarySourceQCOM +glExtGetProgramsQCOM +glExtGetRenderbuffersQCOM +glExtGetShadersQCOM +glExtGetTexLevelParameterivQCOM +glExtGetTexSubImageQCOM +glExtGetTexturesQCOM +glExtIsProgramBinaryQCOM +glExtTexObjectStateOverrideiQCOM +glExtractComponentEXT +glFeedbackBuffer +glFeedbackBufferxOES +glFenceSync +glFenceSyncAPPLE +glFenceValueuiNVX +glFinalCombinerInputNV +glFinish +glFinishAsyncSGIX +glFinishFenceAPPLE +glFinishFenceNV +glFinishObjectAPPLE +glFinishTextureSUNX +glFlush +glFlushMappedBufferRange +glFlushMappedBufferRangeAPPLE +glFlushMappedBufferRangeEXT +glFlushMappedNamedBufferRange +glFlushMappedNamedBufferRangeEXT +glFlushPixelDataRangeNV +glFlushRasterSGIX +glFlushStaticDataIBM +glFlushVertexArrayRangeAPPLE +glFlushVertexArrayRangeNV +glFogCoordFormatNV +glFogCoordPointer +glFogCoordPointerEXT +glFogCoordPointerListIBM +glFogCoordd +glFogCoorddEXT +glFogCoorddv +glFogCoorddvEXT +glFogCoordf +glFogCoordfEXT +glFogCoordfv +glFogCoordfvEXT +glFogCoordhNV +glFogCoordhvNV +glFogFuncSGIS +glFogf +glFogfv +glFogi +glFogiv +glFogx +glFogxOES +glFogxv +glFogxvOES +glFragmentColorMaterialSGIX +glFragmentCoverageColorNV +glFragmentLightModelfSGIX +glFragmentLightModelfvSGIX +glFragmentLightModeliSGIX +glFragmentLightModelivSGIX +glFragmentLightfSGIX +glFragmentLightfvSGIX +glFragmentLightiSGIX +glFragmentLightivSGIX +glFragmentMaterialfSGIX +glFragmentMaterialfvSGIX +glFragmentMaterialiSGIX +glFragmentMaterialivSGIX +glFrameTerminatorGREMEDY +glFrameZoomSGIX +glFramebufferDrawBufferEXT +glFramebufferDrawBuffersEXT +glFramebufferParameteri +glFramebufferReadBufferEXT +glFramebufferRenderbuffer +glFramebufferRenderbufferEXT +glFramebufferRenderbufferOES +glFramebufferSampleLocationsfvARB +glFramebufferSampleLocationsfvNV +glFramebufferTexture +glFramebufferTexture1D +glFramebufferTexture1DEXT +glFramebufferTexture2D +glFramebufferTexture2DEXT +glFramebufferTexture2DMultisampleEXT +glFramebufferTexture2DMultisampleIMG +glFramebufferTexture2DOES +glFramebufferTexture3D +glFramebufferTexture3DEXT +glFramebufferTexture3DOES +glFramebufferTextureARB +glFramebufferTextureEXT +glFramebufferTextureFaceARB +glFramebufferTextureFaceEXT +glFramebufferTextureLayer +glFramebufferTextureLayerARB +glFramebufferTextureLayerEXT +glFramebufferTextureLayerNV +glFramebufferTextureMultisampleMultiviewOVR +glFramebufferTextureMultiviewOVR +glFramebufferTextureOES +glFreeObjectBufferATI +glFrontFace +glFrustum +glFrustumf +glFrustumfOES +glFrustumx +glFrustumxOES +glGenAsyncMarkersSGIX +glGenBuffers +glGenBuffersARB +glGenFencesAPPLE +glGenFencesNV +glGenFragmentShadersATI +glGenFramebuffers +glGenFramebuffersEXT +glGenFramebuffersOES +glGenLists +glGenNamesAMD +glGenOcclusionQueriesNV +glGenPathsNV +glGenPerfMonitorsAMD +glGenProgramPipelines +glGenProgramPipelinesEXT +glGenProgramsARB +glGenProgramsNV +glGenQueries +glGenQueriesARB +glGenQueriesEXT +glGenRenderbuffers +glGenRenderbuffersEXT +glGenRenderbuffersOES +glGenSamplers +glGenSymbolsEXT +glGenTextures +glGenTexturesEXT +glGenTransformFeedbacks +glGenTransformFeedbacksEXT +glGenTransformFeedbacksNV +glGenVertexArrays +glGenVertexArraysAPPLE +glGenVertexArraysOES +glGenVertexShadersEXT +glGenerateMipmap +glGenerateMipmapEXT +glGenerateMipmapOES +glGenerateMultiTexMipmapEXT +glGenerateTextureMipmap +glGenerateTextureMipmapEXT +glGetActiveAtomicCounterBufferiv +glGetActiveAttrib +glGetActiveAttribARB +glGetActiveSubroutineName +glGetActiveSubroutineUniformName +glGetActiveSubroutineUniformiv +glGetActiveUniform +glGetActiveUniformARB +glGetActiveUniformBlockName +glGetActiveUniformBlockiv +glGetActiveUniformName +glGetActiveUniformsiv +glGetActiveVaryingNV +glGetArrayObjectfvATI +glGetArrayObjectivATI +glGetAttachedObjectsARB +glGetAttachedShaders +glGetAttribLocation +glGetAttribLocationARB +glGetBooleanIndexedvEXT +glGetBooleani_v +glGetBooleanv +glGetBufferParameteri64v +glGetBufferParameteriv +glGetBufferParameterivARB +glGetBufferParameterui64vNV +glGetBufferPointerv +glGetBufferPointervARB +glGetBufferPointervOES +glGetBufferSubData +glGetBufferSubDataARB +glGetClipPlane +glGetClipPlanef +glGetClipPlanefOES +glGetClipPlanex +glGetClipPlanexOES +glGetColorTable +glGetColorTableEXT +glGetColorTableParameterfv +glGetColorTableParameterfvEXT +glGetColorTableParameterfvSGI +glGetColorTableParameteriv +glGetColorTableParameterivEXT +glGetColorTableParameterivSGI +glGetColorTableSGI +glGetCombinerInputParameterfvNV +glGetCombinerInputParameterivNV +glGetCombinerOutputParameterfvNV +glGetCombinerOutputParameterivNV +glGetCombinerStageParameterfvNV +glGetCommandHeaderNV +glGetCompressedMultiTexImageEXT +glGetCompressedTexImage +glGetCompressedTexImageARB +glGetCompressedTexImageNV +glGetCompressedTextureImage +glGetCompressedTextureImageEXT +glGetCompressedTextureSubImage +glGetConvolutionFilter +glGetConvolutionFilterEXT +glGetConvolutionParameterfv +glGetConvolutionParameterfvEXT +glGetConvolutionParameteriv +glGetConvolutionParameterivEXT +glGetConvolutionParameterxvOES +glGetCoverageModulationTableNV +glGetDebugMessageLog +glGetDebugMessageLogAMD +glGetDebugMessageLogARB +glGetDebugMessageLogKHR +glGetDebugMessageLogOES +glGetDetailTexFuncSGIS +glGetDoubleIndexedvEXT +glGetDoublei_v +glGetDoublei_vEXT +glGetDoublev +glGetDriverControlStringQCOM +glGetDriverControlsQCOM +glGetError +glGetFenceivNV +glGetFinalCombinerInputParameterfvNV +glGetFinalCombinerInputParameterivNV +glGetFirstPerfQueryIdINTEL +glGetFixedv +glGetFixedvOES +glGetFloatIndexedvEXT +glGetFloati_v +glGetFloati_vEXT +glGetFloati_vNV +glGetFloatv +glGetFogFuncSGIS +glGetFragDataIndex +glGetFragDataIndexEXT +glGetFragDataLocation +glGetFragDataLocationEXT +glGetFragmentLightfvSGIX +glGetFragmentLightivSGIX +glGetFragmentMaterialfvSGIX +glGetFragmentMaterialivSGIX +glGetFramebufferAttachmentParameteriv +glGetFramebufferAttachmentParameterivEXT +glGetFramebufferAttachmentParameterivOES +glGetFramebufferParameteriv +glGetFramebufferParameterivEXT +glGetGraphicsResetStatus +glGetGraphicsResetStatusARB +glGetGraphicsResetStatusEXT +glGetGraphicsResetStatusKHR +glGetHandleARB +glGetHistogram +glGetHistogramEXT +glGetHistogramParameterfv +glGetHistogramParameterfvEXT +glGetHistogramParameteriv +glGetHistogramParameterivEXT +glGetHistogramParameterxvOES +glGetImageHandleARB +glGetImageHandleNV +glGetImageTransformParameterfvHP +glGetImageTransformParameterivHP +glGetInfoLogARB +glGetInstrumentsSGIX +glGetInteger64i_v +glGetInteger64v +glGetInteger64vAPPLE +glGetIntegerIndexedvEXT +glGetIntegeri_v +glGetIntegeri_vEXT +glGetIntegerui64i_vNV +glGetIntegerui64vNV +glGetIntegerv +glGetInternalformatSampleivNV +glGetInternalformati64v +glGetInternalformativ +glGetInvariantBooleanvEXT +glGetInvariantFloatvEXT +glGetInvariantIntegervEXT +glGetLightfv +glGetLightiv +glGetLightxOES +glGetLightxv +glGetLightxvOES +glGetListParameterfvSGIX +glGetListParameterivSGIX +glGetLocalConstantBooleanvEXT +glGetLocalConstantFloatvEXT +glGetLocalConstantIntegervEXT +glGetMapAttribParameterfvNV +glGetMapAttribParameterivNV +glGetMapControlPointsNV +glGetMapParameterfvNV +glGetMapParameterivNV +glGetMapdv +glGetMapfv +glGetMapiv +glGetMapxvOES +glGetMaterialfv +glGetMaterialiv +glGetMaterialxOES +glGetMaterialxv +glGetMaterialxvOES +glGetMinmax +glGetMinmaxEXT +glGetMinmaxParameterfv +glGetMinmaxParameterfvEXT +glGetMinmaxParameteriv +glGetMinmaxParameterivEXT +glGetMultiTexEnvfvEXT +glGetMultiTexEnvivEXT +glGetMultiTexGendvEXT +glGetMultiTexGenfvEXT +glGetMultiTexGenivEXT +glGetMultiTexImageEXT +glGetMultiTexLevelParameterfvEXT +glGetMultiTexLevelParameterivEXT +glGetMultiTexParameterIivEXT +glGetMultiTexParameterIuivEXT +glGetMultiTexParameterfvEXT +glGetMultiTexParameterivEXT +glGetMultisamplefv +glGetMultisamplefvNV +glGetNamedBufferParameteri64v +glGetNamedBufferParameteri64vEXT +glGetNamedBufferParameteriv +glGetNamedBufferParameterivEXT +glGetNamedBufferParameterui64vNV +glGetNamedBufferPointerv +glGetNamedBufferPointervEXT +glGetNamedBufferSubData +glGetNamedBufferSubDataEXT +glGetNamedFramebufferAttachmentParameteriv +glGetNamedFramebufferAttachmentParameterivEXT +glGetNamedFramebufferParameteriv +glGetNamedFramebufferParameterivEXT +glGetNamedProgramLocalParameterIivEXT +glGetNamedProgramLocalParameterIuivEXT +glGetNamedProgramLocalParameterdvEXT +glGetNamedProgramLocalParameterfvEXT +glGetNamedProgramStringEXT +glGetNamedProgramivEXT +glGetNamedRenderbufferParameteriv +glGetNamedRenderbufferParameterivEXT +glGetNamedStringARB +glGetNamedStringivARB +glGetNextPerfQueryIdINTEL +glGetObjectBufferfvATI +glGetObjectBufferivATI +glGetObjectLabel +glGetObjectLabelEXT +glGetObjectLabelKHR +glGetObjectLabelOES +glGetObjectParameterfvARB +glGetObjectParameterivAPPLE +glGetObjectParameterivARB +glGetObjectPtrLabel +glGetObjectPtrLabelKHR +glGetObjectPtrLabelOES +glGetOcclusionQueryivNV +glGetOcclusionQueryuivNV +glGetPathColorGenfvNV +glGetPathColorGenivNV +glGetPathCommandsNV +glGetPathCoordsNV +glGetPathDashArrayNV +glGetPathLengthNV +glGetPathMetricRangeNV +glGetPathMetricsNV +glGetPathParameterfvNV +glGetPathParameterivNV +glGetPathSpacingNV +glGetPathTexGenfvNV +glGetPathTexGenivNV +glGetPerfCounterInfoINTEL +glGetPerfMonitorCounterDataAMD +glGetPerfMonitorCounterInfoAMD +glGetPerfMonitorCounterStringAMD +glGetPerfMonitorCountersAMD +glGetPerfMonitorGroupStringAMD +glGetPerfMonitorGroupsAMD +glGetPerfQueryDataINTEL +glGetPerfQueryIdByNameINTEL +glGetPerfQueryInfoINTEL +glGetPixelMapfv +glGetPixelMapuiv +glGetPixelMapusv +glGetPixelMapxv +glGetPixelTexGenParameterfvSGIS +glGetPixelTexGenParameterivSGIS +glGetPixelTransformParameterfvEXT +glGetPixelTransformParameterivEXT +glGetPointerIndexedvEXT +glGetPointeri_vEXT +glGetPointerv +glGetPointervEXT +glGetPointervKHR +glGetPointervOES +glGetPolygonStipple +glGetProgramBinary +glGetProgramBinaryOES +glGetProgramEnvParameterIivNV +glGetProgramEnvParameterIuivNV +glGetProgramEnvParameterdvARB +glGetProgramEnvParameterfvARB +glGetProgramInfoLog +glGetProgramInterfaceiv +glGetProgramLocalParameterIivNV +glGetProgramLocalParameterIuivNV +glGetProgramLocalParameterdvARB +glGetProgramLocalParameterfvARB +glGetProgramNamedParameterdvNV +glGetProgramNamedParameterfvNV +glGetProgramParameterdvNV +glGetProgramParameterfvNV +glGetProgramPipelineInfoLog +glGetProgramPipelineInfoLogEXT +glGetProgramPipelineiv +glGetProgramPipelineivEXT +glGetProgramResourceIndex +glGetProgramResourceLocation +glGetProgramResourceLocationIndex +glGetProgramResourceLocationIndexEXT +glGetProgramResourceName +glGetProgramResourcefvNV +glGetProgramResourceiv +glGetProgramStageiv +glGetProgramStringARB +glGetProgramStringNV +glGetProgramSubroutineParameteruivNV +glGetProgramiv +glGetProgramivARB +glGetProgramivNV +glGetQueryBufferObjecti64v +glGetQueryBufferObjectiv +glGetQueryBufferObjectui64v +glGetQueryBufferObjectuiv +glGetQueryIndexediv +glGetQueryObjecti64v +glGetQueryObjecti64vEXT +glGetQueryObjectiv +glGetQueryObjectivARB +glGetQueryObjectivEXT +glGetQueryObjectui64v +glGetQueryObjectui64vEXT +glGetQueryObjectui64vNV +glGetQueryObjectuiv +glGetQueryObjectuivARB +glGetQueryObjectuivEXT +glGetQueryiv +glGetQueryivARB +glGetQueryivEXT +glGetRenderbufferParameteriv +glGetRenderbufferParameterivEXT +glGetRenderbufferParameterivOES +glGetSamplerParameterIiv +glGetSamplerParameterIivEXT +glGetSamplerParameterIivOES +glGetSamplerParameterIuiv +glGetSamplerParameterIuivEXT +glGetSamplerParameterIuivOES +glGetSamplerParameterfv +glGetSamplerParameteriv +glGetSeparableFilter +glGetSeparableFilterEXT +glGetShaderInfoLog +glGetShaderPrecisionFormat +glGetShaderSource +glGetShaderSourceARB +glGetShaderiv +glGetSharpenTexFuncSGIS +glGetStageIndexNV +glGetString +glGetStringi +glGetSubroutineIndex +glGetSubroutineUniformLocation +glGetSynciv +glGetSyncivAPPLE +glGetSyncv64NVX +glGetTexBumpParameterfvATI +glGetTexBumpParameterivATI +glGetTexEnvfv +glGetTexEnviv +glGetTexEnvxv +glGetTexEnvxvOES +glGetTexFilterFuncSGIS +glGetTexGendv +glGetTexGenfv +glGetTexGenfvOES +glGetTexGeniv +glGetTexGenivOES +glGetTexGenxvOES +glGetTexImage +glGetTexImageNV +glGetTexLevelParameterfv +glGetTexLevelParameterfvNV +glGetTexLevelParameteriv +glGetTexLevelParameterivNV +glGetTexLevelParameterxvOES +glGetTexParameterIiv +glGetTexParameterIivEXT +glGetTexParameterIivOES +glGetTexParameterIuiv +glGetTexParameterIuivEXT +glGetTexParameterIuivOES +glGetTexParameterPointervAPPLE +glGetTexParameterfv +glGetTexParameteriv +glGetTexParameterxv +glGetTexParameterxvOES +glGetTextureHandleARB +glGetTextureHandleNV +glGetTextureImage +glGetTextureImageEXT +glGetTextureLevelParameterfv +glGetTextureLevelParameterfvEXT +glGetTextureLevelParameteriv +glGetTextureLevelParameterivEXT +glGetTextureParameterIiv +glGetTextureParameterIivEXT +glGetTextureParameterIuiv +glGetTextureParameterIuivEXT +glGetTextureParameterfv +glGetTextureParameterfvEXT +glGetTextureParameteriv +glGetTextureParameterivEXT +glGetTextureSamplerHandleARB +glGetTextureSamplerHandleNV +glGetTextureSubImage +glGetTrackMatrixivNV +glGetTransformFeedbackVarying +glGetTransformFeedbackVaryingEXT +glGetTransformFeedbackVaryingNV +glGetTransformFeedbacki64_v +glGetTransformFeedbacki_v +glGetTransformFeedbackiv +glGetTranslatedShaderSourceANGLE +glGetUniformBlockIndex +glGetUniformBufferSizeEXT +glGetUniformIndices +glGetUniformLocation +glGetUniformLocationARB +glGetUniformOffsetEXT +glGetUniformSubroutineuiv +glGetUniformdv +glGetUniformfv +glGetUniformfvARB +glGetUniformi64vARB +glGetUniformi64vNV +glGetUniformiv +glGetUniformivARB +glGetUniformui64vARB +glGetUniformui64vNV +glGetUniformuiv +glGetUniformuivEXT +glGetVariantArrayObjectfvATI +glGetVariantArrayObjectivATI +glGetVariantBooleanvEXT +glGetVariantFloatvEXT +glGetVariantIntegervEXT +glGetVariantPointervEXT +glGetVaryingLocationNV +glGetVertexArrayIndexed64iv +glGetVertexArrayIndexediv +glGetVertexArrayIntegeri_vEXT +glGetVertexArrayIntegervEXT +glGetVertexArrayPointeri_vEXT +glGetVertexArrayPointervEXT +glGetVertexArrayiv +glGetVertexAttribArrayObjectfvATI +glGetVertexAttribArrayObjectivATI +glGetVertexAttribIiv +glGetVertexAttribIivEXT +glGetVertexAttribIuiv +glGetVertexAttribIuivEXT +glGetVertexAttribLdv +glGetVertexAttribLdvEXT +glGetVertexAttribLi64vNV +glGetVertexAttribLui64vARB +glGetVertexAttribLui64vNV +glGetVertexAttribPointerv +glGetVertexAttribPointervARB +glGetVertexAttribPointervNV +glGetVertexAttribdv +glGetVertexAttribdvARB +glGetVertexAttribdvNV +glGetVertexAttribfv +glGetVertexAttribfvARB +glGetVertexAttribfvNV +glGetVertexAttribiv +glGetVertexAttribivARB +glGetVertexAttribivNV +glGetVideoCaptureANCStreamivNVX +glGetVideoCaptureStreamdvNV +glGetVideoCaptureStreamfvNV +glGetVideoCaptureStreamivNV +glGetVideoCaptureivNV +glGetVideoi64vNV +glGetVideoivNV +glGetVideoui64vNV +glGetVideouivNV +glGetnColorTable +glGetnColorTableARB +glGetnCompressedTexImage +glGetnCompressedTexImageARB +glGetnConvolutionFilter +glGetnConvolutionFilterARB +glGetnHistogram +glGetnHistogramARB +glGetnMapdv +glGetnMapdvARB +glGetnMapfv +glGetnMapfvARB +glGetnMapiv +glGetnMapivARB +glGetnMinmax +glGetnMinmaxARB +glGetnPixelMapfv +glGetnPixelMapfvARB +glGetnPixelMapuiv +glGetnPixelMapuivARB +glGetnPixelMapusv +glGetnPixelMapusvARB +glGetnPolygonStipple +glGetnPolygonStippleARB +glGetnSeparableFilter +glGetnSeparableFilterARB +glGetnTexImage +glGetnTexImageARB +glGetnUniformdv +glGetnUniformdvARB +glGetnUniformfv +glGetnUniformfvARB +glGetnUniformfvEXT +glGetnUniformfvKHR +glGetnUniformi64vARB +glGetnUniformiv +glGetnUniformivARB +glGetnUniformivEXT +glGetnUniformivKHR +glGetnUniformui64vARB +glGetnUniformuiv +glGetnUniformuivARB +glGetnUniformuivKHR +glGlobalAlphaFactorbSUN +glGlobalAlphaFactordSUN +glGlobalAlphaFactorfSUN +glGlobalAlphaFactoriSUN +glGlobalAlphaFactorsSUN +glGlobalAlphaFactorubSUN +glGlobalAlphaFactoruiSUN +glGlobalAlphaFactorusSUN +glGpuSyncAcquireNVX +glGpuSyncCreateNVX +glGpuSyncDestroyNVX +glGpuSyncReleaseNVX +glHint +glHintPGI +glHistogram +glHistogramEXT +glIglooInterfaceSGIX +glImageTransformParameterfHP +glImageTransformParameterfvHP +glImageTransformParameteriHP +glImageTransformParameterivHP +glImportSyncEXT +glIndexFormatNV +glIndexFuncEXT +glIndexMask +glIndexMaterialEXT +glIndexPointer +glIndexPointerEXT +glIndexPointerListIBM +glIndexd +glIndexdv +glIndexf +glIndexfv +glIndexi +glIndexiv +glIndexs +glIndexsv +glIndexub +glIndexubv +glIndexxOES +glIndexxvOES +glInitNames +glInsertComponentEXT +glInsertEventMarkerEXT +glInstrumentsBufferSGIX +glInterleavedArrays +glInterpolatePathsNV +glInvalidateBufferData +glInvalidateBufferSubData +glInvalidateFramebuffer +glInvalidateNamedFramebufferData +glInvalidateNamedFramebufferSubData +glInvalidateSubFramebuffer +glInvalidateTexImage +glInvalidateTexSubImage +glIsAsyncMarkerSGIX +glIsBuffer +glIsBufferARB +glIsBufferResidentNV +glIsCommandListNV +glIsEnabled +glIsEnabledIndexedEXT +glIsEnabledi +glIsEnablediEXT +glIsEnablediNV +glIsEnablediOES +glIsFenceAPPLE +glIsFenceNV +glIsFramebuffer +glIsFramebufferEXT +glIsFramebufferOES +glIsImageHandleResidentARB +glIsImageHandleResidentNV +glIsList +glIsNameAMD +glIsNamedBufferResidentNV +glIsNamedStringARB +glIsObjectBufferATI +glIsOcclusionQueryNV +glIsPathNV +glIsPointInFillPathNV +glIsPointInStrokePathNV +glIsProgram +glIsProgramARB +glIsProgramNV +glIsProgramPipeline +glIsProgramPipelineEXT +glIsQuery +glIsQueryARB +glIsQueryEXT +glIsRenderbuffer +glIsRenderbufferEXT +glIsRenderbufferOES +glIsSampler +glIsShader +glIsStateNV +glIsSync +glIsSyncAPPLE +glIsTexture +glIsTextureEXT +glIsTextureHandleResidentARB +glIsTextureHandleResidentNV +glIsTransformFeedback +glIsTransformFeedbackEXT +glIsTransformFeedbackNV +glIsVariantEnabledEXT +glIsVertexArray +glIsVertexArrayAPPLE +glIsVertexArrayOES +glIsVertexAttribEnabledAPPLE +glLabelObjectEXT +glLightEnviSGIX +glLightModelf +glLightModelfv +glLightModeli +glLightModeliv +glLightModelx +glLightModelxOES +glLightModelxv +glLightModelxvOES +glLightf +glLightfv +glLighti +glLightiv +glLightx +glLightxOES +glLightxv +glLightxvOES +glLineStipple +glLineWidth +glLineWidthx +glLineWidthxOES +glLinkProgram +glLinkProgramARB +glListBase +glListDrawCommandsStatesClientNV +glListParameterfSGIX +glListParameterfvSGIX +glListParameteriSGIX +glListParameterivSGIX +glLoadIdentity +glLoadIdentityDeformationMapSGIX +glLoadMatrixd +glLoadMatrixf +glLoadMatrixx +glLoadMatrixxOES +glLoadName +glLoadPaletteFromModelViewMatrixOES +glLoadProgramNV +glLoadTransformEXT +glLoadTransposeMatrixd +glLoadTransposeMatrixdARB +glLoadTransposeMatrixf +glLoadTransposeMatrixfARB +glLoadTransposeMatrixxOES +glLockArraysEXT +glLogicOp +glMakeBufferNonResidentNV +glMakeBufferResidentNV +glMakeImageHandleNonResidentARB +glMakeImageHandleNonResidentNV +glMakeImageHandleResidentARB +glMakeImageHandleResidentNV +glMakeNamedBufferNonResidentNV +glMakeNamedBufferResidentNV +glMakeTextureHandleNonResidentARB +glMakeTextureHandleNonResidentNV +glMakeTextureHandleResidentARB +glMakeTextureHandleResidentNV +glMap1d +glMap1f +glMap1xOES +glMap2d +glMap2f +glMap2xOES +glMapBuffer +glMapBufferARB +glMapBufferOES +glMapBufferRange +glMapBufferRangeEXT +glMapControlPointsNV +glMapGrid1d +glMapGrid1f +glMapGrid1xOES +glMapGrid2d +glMapGrid2f +glMapGrid2xOES +glMapNamedBuffer +glMapNamedBufferEXT +glMapNamedBufferRange +glMapNamedBufferRangeEXT +glMapObjectBufferATI +glMapParameterfvNV +glMapParameterivNV +glMapTexture2DINTEL +glMapVertexAttrib1dAPPLE +glMapVertexAttrib1fAPPLE +glMapVertexAttrib2dAPPLE +glMapVertexAttrib2fAPPLE +glMaterialf +glMaterialfv +glMateriali +glMaterialiv +glMaterialx +glMaterialxOES +glMaterialxv +glMaterialxvOES +glMatrixFrustumEXT +glMatrixIndexPointerARB +glMatrixIndexPointerOES +glMatrixIndexubvARB +glMatrixIndexuivARB +glMatrixIndexusvARB +glMatrixLoad3x2fNV +glMatrixLoad3x3fNV +glMatrixLoadIdentityEXT +glMatrixLoadTranspose3x3fNV +glMatrixLoadTransposedEXT +glMatrixLoadTransposefEXT +glMatrixLoaddEXT +glMatrixLoadfEXT +glMatrixMode +glMatrixMult3x2fNV +glMatrixMult3x3fNV +glMatrixMultTranspose3x3fNV +glMatrixMultTransposedEXT +glMatrixMultTransposefEXT +glMatrixMultdEXT +glMatrixMultfEXT +glMatrixOrthoEXT +glMatrixPopEXT +glMatrixPushEXT +glMatrixRotatedEXT +glMatrixRotatefEXT +glMatrixScaledEXT +glMatrixScalefEXT +glMatrixTranslatedEXT +glMatrixTranslatefEXT +glMaxShaderCompilerThreadsARB +glMemoryBarrier +glMemoryBarrierByRegion +glMemoryBarrierEXT +glMinSampleShading +glMinSampleShadingARB +glMinSampleShadingOES +glMinmax +glMinmaxEXT +glMultMatrixd +glMultMatrixf +glMultMatrixx +glMultMatrixxOES +glMultTransformEXT +glMultTransposeMatrixd +glMultTransposeMatrixdARB +glMultTransposeMatrixf +glMultTransposeMatrixfARB +glMultTransposeMatrixxOES +glMultiDrawArrays +glMultiDrawArraysEXT +glMultiDrawArraysIndirect +glMultiDrawArraysIndirectAMD +glMultiDrawArraysIndirectBindlessCountNV +glMultiDrawArraysIndirectBindlessNV +glMultiDrawArraysIndirectCountARB +glMultiDrawArraysIndirectEXT +glMultiDrawElementArrayAPPLE +glMultiDrawElements +glMultiDrawElementsBaseVertex +glMultiDrawElementsBaseVertexEXT +glMultiDrawElementsBaseVertexOES +glMultiDrawElementsEXT +glMultiDrawElementsIndirect +glMultiDrawElementsIndirectAMD +glMultiDrawElementsIndirectBindlessCountNV +glMultiDrawElementsIndirectBindlessNV +glMultiDrawElementsIndirectCountARB +glMultiDrawElementsIndirectEXT +glMultiDrawRangeElementArrayAPPLE +glMultiModeDrawArraysIBM +glMultiModeDrawElementsIBM +glMultiTexBufferEXT +glMultiTexCoord1bOES +glMultiTexCoord1bvOES +glMultiTexCoord1d +glMultiTexCoord1dARB +glMultiTexCoord1dv +glMultiTexCoord1dvARB +glMultiTexCoord1f +glMultiTexCoord1fARB +glMultiTexCoord1fv +glMultiTexCoord1fvARB +glMultiTexCoord1hNV +glMultiTexCoord1hvNV +glMultiTexCoord1i +glMultiTexCoord1iARB +glMultiTexCoord1iv +glMultiTexCoord1ivARB +glMultiTexCoord1s +glMultiTexCoord1sARB +glMultiTexCoord1sv +glMultiTexCoord1svARB +glMultiTexCoord1x +glMultiTexCoord1xOES +glMultiTexCoord1xv +glMultiTexCoord1xvOES +glMultiTexCoord2bOES +glMultiTexCoord2bvOES +glMultiTexCoord2d +glMultiTexCoord2dARB +glMultiTexCoord2dv +glMultiTexCoord2dvARB +glMultiTexCoord2f +glMultiTexCoord2fARB +glMultiTexCoord2fv +glMultiTexCoord2fvARB +glMultiTexCoord2hNV +glMultiTexCoord2hvNV +glMultiTexCoord2i +glMultiTexCoord2iARB +glMultiTexCoord2iv +glMultiTexCoord2ivARB +glMultiTexCoord2s +glMultiTexCoord2sARB +glMultiTexCoord2sv +glMultiTexCoord2svARB +glMultiTexCoord2x +glMultiTexCoord2xOES +glMultiTexCoord2xv +glMultiTexCoord2xvOES +glMultiTexCoord3bOES +glMultiTexCoord3bvOES +glMultiTexCoord3d +glMultiTexCoord3dARB +glMultiTexCoord3dv +glMultiTexCoord3dvARB +glMultiTexCoord3f +glMultiTexCoord3fARB +glMultiTexCoord3fv +glMultiTexCoord3fvARB +glMultiTexCoord3hNV +glMultiTexCoord3hvNV +glMultiTexCoord3i +glMultiTexCoord3iARB +glMultiTexCoord3iv +glMultiTexCoord3ivARB +glMultiTexCoord3s +glMultiTexCoord3sARB +glMultiTexCoord3sv +glMultiTexCoord3svARB +glMultiTexCoord3x +glMultiTexCoord3xOES +glMultiTexCoord3xv +glMultiTexCoord3xvOES +glMultiTexCoord4bOES +glMultiTexCoord4bvOES +glMultiTexCoord4d +glMultiTexCoord4dARB +glMultiTexCoord4dv +glMultiTexCoord4dvARB +glMultiTexCoord4f +glMultiTexCoord4fARB +glMultiTexCoord4fv +glMultiTexCoord4fvARB +glMultiTexCoord4hNV +glMultiTexCoord4hvNV +glMultiTexCoord4i +glMultiTexCoord4iARB +glMultiTexCoord4iv +glMultiTexCoord4ivARB +glMultiTexCoord4s +glMultiTexCoord4sARB +glMultiTexCoord4sv +glMultiTexCoord4svARB +glMultiTexCoord4x +glMultiTexCoord4xOES +glMultiTexCoord4xv +glMultiTexCoord4xvOES +glMultiTexCoordP1ui +glMultiTexCoordP1uiv +glMultiTexCoordP2ui +glMultiTexCoordP2uiv +glMultiTexCoordP3ui +glMultiTexCoordP3uiv +glMultiTexCoordP4ui +glMultiTexCoordP4uiv +glMultiTexCoordPointerEXT +glMultiTexEnvfEXT +glMultiTexEnvfvEXT +glMultiTexEnviEXT +glMultiTexEnvivEXT +glMultiTexGendEXT +glMultiTexGendvEXT +glMultiTexGenfEXT +glMultiTexGenfvEXT +glMultiTexGeniEXT +glMultiTexGenivEXT +glMultiTexImage1DEXT +glMultiTexImage2DEXT +glMultiTexImage3DEXT +glMultiTexParameterIivEXT +glMultiTexParameterIuivEXT +glMultiTexParameterfEXT +glMultiTexParameterfvEXT +glMultiTexParameteriEXT +glMultiTexParameterivEXT +glMultiTexRenderbufferEXT +glMultiTexSubImage1DEXT +glMultiTexSubImage2DEXT +glMultiTexSubImage3DEXT +glNVENCInterOpFunctionNVX +glNamedBufferData +glNamedBufferDataEXT +glNamedBufferPageCommitmentARB +glNamedBufferPageCommitmentEXT +glNamedBufferStorage +glNamedBufferStorageEXT +glNamedBufferSubData +glNamedBufferSubDataEXT +glNamedCopyBufferSubDataEXT +glNamedFramebufferDrawBuffer +glNamedFramebufferDrawBuffers +glNamedFramebufferParameteri +glNamedFramebufferParameteriEXT +glNamedFramebufferReadBuffer +glNamedFramebufferRenderbuffer +glNamedFramebufferRenderbufferEXT +glNamedFramebufferSampleLocationsfvARB +glNamedFramebufferSampleLocationsfvNV +glNamedFramebufferTexture +glNamedFramebufferTexture1DEXT +glNamedFramebufferTexture2DEXT +glNamedFramebufferTexture3DEXT +glNamedFramebufferTextureEXT +glNamedFramebufferTextureFaceEXT +glNamedFramebufferTextureLayer +glNamedFramebufferTextureLayerEXT +glNamedProgramLocalParameter4dEXT +glNamedProgramLocalParameter4dvEXT +glNamedProgramLocalParameter4fEXT +glNamedProgramLocalParameter4fvEXT +glNamedProgramLocalParameterI4iEXT +glNamedProgramLocalParameterI4ivEXT +glNamedProgramLocalParameterI4uiEXT +glNamedProgramLocalParameterI4uivEXT +glNamedProgramLocalParameters4fvEXT +glNamedProgramLocalParametersI4ivEXT +glNamedProgramLocalParametersI4uivEXT +glNamedProgramStringEXT +glNamedRenderbufferStorage +glNamedRenderbufferStorageEXT +glNamedRenderbufferStorageMultisample +glNamedRenderbufferStorageMultisampleCoverageEXT +glNamedRenderbufferStorageMultisampleEXT +glNamedStringARB +glNewList +glNewObjectBufferATI +glNormal3b +glNormal3bv +glNormal3d +glNormal3dv +glNormal3f +glNormal3fVertex3fSUN +glNormal3fVertex3fvSUN +glNormal3fv +glNormal3hNV +glNormal3hvNV +glNormal3i +glNormal3iv +glNormal3s +glNormal3sv +glNormal3x +glNormal3xOES +glNormal3xvOES +glNormalFormatNV +glNormalP3ui +glNormalP3uiv +glNormalPointer +glNormalPointerEXT +glNormalPointerListIBM +glNormalPointervINTEL +glNormalStream3bATI +glNormalStream3bvATI +glNormalStream3dATI +glNormalStream3dvATI +glNormalStream3fATI +glNormalStream3fvATI +glNormalStream3iATI +glNormalStream3ivATI +glNormalStream3sATI +glNormalStream3svATI +glObjectLabel +glObjectLabelKHR +glObjectLabelOES +glObjectPtrLabel +glObjectPtrLabelKHR +glObjectPtrLabelOES +glObjectPurgeableAPPLE +glObjectUnpurgeableAPPLE +glOrtho +glOrthof +glOrthofOES +glOrthox +glOrthoxOES +glPNTrianglesfATI +glPNTrianglesiATI +glPassTexCoordATI +glPassThrough +glPassThroughxOES +glPatchParameterfv +glPatchParameterfvEXT +glPatchParameterfvNV +glPatchParameteri +glPatchParameteriEXT +glPatchParameteriNV +glPatchParameteriOES +glPathColorGenNV +glPathCommandsNV +glPathCoordsNV +glPathCoverDepthFuncNV +glPathDashArrayNV +glPathFogGenNV +glPathGlyphIndexArrayNV +glPathGlyphIndexRangeNV +glPathGlyphRangeNV +glPathGlyphsNV +glPathMemoryGlyphIndexArrayNV +glPathParameterfNV +glPathParameterfvNV +glPathParameteriNV +glPathParameterivNV +glPathStencilDepthOffsetNV +glPathStencilFuncNV +glPathStringNV +glPathSubCommandsNV +glPathSubCoordsNV +glPathTexGenNV +glPauseTransformFeedback +glPauseTransformFeedbackEXT +glPauseTransformFeedbackNV +glPixelDataRangeNV +glPixelMapfv +glPixelMapuiv +glPixelMapusv +glPixelMapx +glPixelStoref +glPixelStorei +glPixelStorex +glPixelTexGenParameterfSGIS +glPixelTexGenParameterfvSGIS +glPixelTexGenParameteriSGIS +glPixelTexGenParameterivSGIS +glPixelTexGenSGIX +glPixelTransferf +glPixelTransferi +glPixelTransferxOES +glPixelTransformParameterfEXT +glPixelTransformParameterfvEXT +glPixelTransformParameteriEXT +glPixelTransformParameterivEXT +glPixelZoom +glPixelZoomxOES +glPointAlongPathNV +glPointParameterf +glPointParameterfARB +glPointParameterfEXT +glPointParameterfSGIS +glPointParameterfv +glPointParameterfvARB +glPointParameterfvEXT +glPointParameterfvSGIS +glPointParameteri +glPointParameteriNV +glPointParameteriv +glPointParameterivNV +glPointParameterx +glPointParameterxOES +glPointParameterxv +glPointParameterxvOES +glPointSize +glPointSizePointerOES +glPointSizex +glPointSizexOES +glPollAsyncSGIX +glPollInstrumentsSGIX +glPolygonMode +glPolygonModeNV +glPolygonOffset +glPolygonOffsetClampEXT +glPolygonOffsetEXT +glPolygonOffsetx +glPolygonOffsetxOES +glPolygonStipple +glPopAttrib +glPopClientAttrib +glPopDebugGroup +glPopDebugGroupKHR +glPopDebugGroupOES +glPopGroupMarkerEXT +glPopMatrix +glPopName +glPresentFrameDualFillNV +glPresentFrameKeyedNV +glPrimitiveBoundingBox +glPrimitiveBoundingBoxARB +glPrimitiveBoundingBoxEXT +glPrimitiveBoundingBoxOES +glPrimitiveRestart +glPrimitiveRestartIndex +glPrimitiveRestartIndexNV +glPrimitiveRestartNV +glPrioritizeTextures +glPrioritizeTexturesEXT +glPrioritizeTexturesxOES +glProgramBinary +glProgramBinaryOES +glProgramBufferParametersIivNV +glProgramBufferParametersIuivNV +glProgramBufferParametersfvNV +glProgramEnvParameter4dARB +glProgramEnvParameter4dvARB +glProgramEnvParameter4fARB +glProgramEnvParameter4fvARB +glProgramEnvParameterI4iNV +glProgramEnvParameterI4ivNV +glProgramEnvParameterI4uiNV +glProgramEnvParameterI4uivNV +glProgramEnvParameters4fvEXT +glProgramEnvParametersI4ivNV +glProgramEnvParametersI4uivNV +glProgramLocalParameter4dARB +glProgramLocalParameter4dvARB +glProgramLocalParameter4fARB +glProgramLocalParameter4fvARB +glProgramLocalParameterI4iNV +glProgramLocalParameterI4ivNV +glProgramLocalParameterI4uiNV +glProgramLocalParameterI4uivNV +glProgramLocalParameters4fvEXT +glProgramLocalParametersI4ivNV +glProgramLocalParametersI4uivNV +glProgramNamedParameter4dNV +glProgramNamedParameter4dvNV +glProgramNamedParameter4fNV +glProgramNamedParameter4fvNV +glProgramParameter4dNV +glProgramParameter4dvNV +glProgramParameter4fNV +glProgramParameter4fvNV +glProgramParameteri +glProgramParameteriARB +glProgramParameteriEXT +glProgramParameters4dvNV +glProgramParameters4fvNV +glProgramPathFragmentInputGenNV +glProgramStringARB +glProgramSubroutineParametersuivNV +glProgramUniform1d +glProgramUniform1dEXT +glProgramUniform1dv +glProgramUniform1dvEXT +glProgramUniform1f +glProgramUniform1fEXT +glProgramUniform1fv +glProgramUniform1fvEXT +glProgramUniform1i +glProgramUniform1i64ARB +glProgramUniform1i64NV +glProgramUniform1i64vARB +glProgramUniform1i64vNV +glProgramUniform1iEXT +glProgramUniform1iv +glProgramUniform1ivEXT +glProgramUniform1ui +glProgramUniform1ui64ARB +glProgramUniform1ui64NV +glProgramUniform1ui64vARB +glProgramUniform1ui64vNV +glProgramUniform1uiEXT +glProgramUniform1uiv +glProgramUniform1uivEXT +glProgramUniform2d +glProgramUniform2dEXT +glProgramUniform2dv +glProgramUniform2dvEXT +glProgramUniform2f +glProgramUniform2fEXT +glProgramUniform2fv +glProgramUniform2fvEXT +glProgramUniform2i +glProgramUniform2i64ARB +glProgramUniform2i64NV +glProgramUniform2i64vARB +glProgramUniform2i64vNV +glProgramUniform2iEXT +glProgramUniform2iv +glProgramUniform2ivEXT +glProgramUniform2ui +glProgramUniform2ui64ARB +glProgramUniform2ui64NV +glProgramUniform2ui64vARB +glProgramUniform2ui64vNV +glProgramUniform2uiEXT +glProgramUniform2uiv +glProgramUniform2uivEXT +glProgramUniform3d +glProgramUniform3dEXT +glProgramUniform3dv +glProgramUniform3dvEXT +glProgramUniform3f +glProgramUniform3fEXT +glProgramUniform3fv +glProgramUniform3fvEXT +glProgramUniform3i +glProgramUniform3i64ARB +glProgramUniform3i64NV +glProgramUniform3i64vARB +glProgramUniform3i64vNV +glProgramUniform3iEXT +glProgramUniform3iv +glProgramUniform3ivEXT +glProgramUniform3ui +glProgramUniform3ui64ARB +glProgramUniform3ui64NV +glProgramUniform3ui64vARB +glProgramUniform3ui64vNV +glProgramUniform3uiEXT +glProgramUniform3uiv +glProgramUniform3uivEXT +glProgramUniform4d +glProgramUniform4dEXT +glProgramUniform4dv +glProgramUniform4dvEXT +glProgramUniform4f +glProgramUniform4fEXT +glProgramUniform4fv +glProgramUniform4fvEXT +glProgramUniform4i +glProgramUniform4i64ARB +glProgramUniform4i64NV +glProgramUniform4i64vARB +glProgramUniform4i64vNV +glProgramUniform4iEXT +glProgramUniform4iv +glProgramUniform4ivEXT +glProgramUniform4ui +glProgramUniform4ui64ARB +glProgramUniform4ui64NV +glProgramUniform4ui64vARB +glProgramUniform4ui64vNV +glProgramUniform4uiEXT +glProgramUniform4uiv +glProgramUniform4uivEXT +glProgramUniformHandleui64ARB +glProgramUniformHandleui64NV +glProgramUniformHandleui64vARB +glProgramUniformHandleui64vNV +glProgramUniformMatrix2dv +glProgramUniformMatrix2dvEXT +glProgramUniformMatrix2fv +glProgramUniformMatrix2fvEXT +glProgramUniformMatrix2x3dv +glProgramUniformMatrix2x3dvEXT +glProgramUniformMatrix2x3fv +glProgramUniformMatrix2x3fvEXT +glProgramUniformMatrix2x4dv +glProgramUniformMatrix2x4dvEXT +glProgramUniformMatrix2x4fv +glProgramUniformMatrix2x4fvEXT +glProgramUniformMatrix3dv +glProgramUniformMatrix3dvEXT +glProgramUniformMatrix3fv +glProgramUniformMatrix3fvEXT +glProgramUniformMatrix3x2dv +glProgramUniformMatrix3x2dvEXT +glProgramUniformMatrix3x2fv +glProgramUniformMatrix3x2fvEXT +glProgramUniformMatrix3x4dv +glProgramUniformMatrix3x4dvEXT +glProgramUniformMatrix3x4fv +glProgramUniformMatrix3x4fvEXT +glProgramUniformMatrix4dv +glProgramUniformMatrix4dvEXT +glProgramUniformMatrix4fv +glProgramUniformMatrix4fvEXT +glProgramUniformMatrix4x2dv +glProgramUniformMatrix4x2dvEXT +glProgramUniformMatrix4x2fv +glProgramUniformMatrix4x2fvEXT +glProgramUniformMatrix4x3dv +glProgramUniformMatrix4x3dvEXT +glProgramUniformMatrix4x3fv +glProgramUniformMatrix4x3fvEXT +glProgramUniformui64NV +glProgramUniformui64vNV +glProgramVertexLimitNV +glProvokingVertex +glProvokingVertexEXT +glPushAttrib +glPushClientAttrib +glPushClientAttribDefaultEXT +glPushDebugGroup +glPushDebugGroupKHR +glPushDebugGroupOES +glPushGroupMarkerEXT +glPushMatrix +glPushName +glQueryCounter +glQueryCounterEXT +glQueryCounterNV +glQueryMatrixxOES +glQueryObjectParameteruiAMD +glRasterPos2d +glRasterPos2dv +glRasterPos2f +glRasterPos2fv +glRasterPos2i +glRasterPos2iv +glRasterPos2s +glRasterPos2sv +glRasterPos2xOES +glRasterPos2xvOES +glRasterPos3d +glRasterPos3dv +glRasterPos3f +glRasterPos3fv +glRasterPos3i +glRasterPos3iv +glRasterPos3s +glRasterPos3sv +glRasterPos3xOES +glRasterPos3xvOES +glRasterPos4d +glRasterPos4dv +glRasterPos4f +glRasterPos4fv +glRasterPos4i +glRasterPos4iv +glRasterPos4s +glRasterPos4sv +glRasterPos4xOES +glRasterPos4xvOES +glRasterSamplesEXT +glReadBuffer +glReadBufferIndexedEXT +glReadBufferNV +glReadInstrumentsSGIX +glReadPixels +glReadnPixels +glReadnPixelsARB +glReadnPixelsEXT +glReadnPixelsKHR +glRectd +glRectdv +glRectf +glRectfv +glRecti +glRectiv +glRects +glRectsv +glRectxOES +glRectxvOES +glReferencePlaneSGIX +glReleaseShaderCompiler +glRenderMode +glRenderbufferStorage +glRenderbufferStorageEXT +glRenderbufferStorageMultisample +glRenderbufferStorageMultisampleANGLE +glRenderbufferStorageMultisampleAPPLE +glRenderbufferStorageMultisampleCoverageNV +glRenderbufferStorageMultisampleEXT +glRenderbufferStorageMultisampleIMG +glRenderbufferStorageMultisampleNV +glRenderbufferStorageOES +glReplacementCodePointerSUN +glReplacementCodeubSUN +glReplacementCodeubvSUN +glReplacementCodeuiColor3fVertex3fSUN +glReplacementCodeuiColor3fVertex3fvSUN +glReplacementCodeuiColor4fNormal3fVertex3fSUN +glReplacementCodeuiColor4fNormal3fVertex3fvSUN +glReplacementCodeuiColor4ubVertex3fSUN +glReplacementCodeuiColor4ubVertex3fvSUN +glReplacementCodeuiNormal3fVertex3fSUN +glReplacementCodeuiNormal3fVertex3fvSUN +glReplacementCodeuiSUN +glReplacementCodeuiTexCoord2fColor4fNormal3fVertex3fSUN +glReplacementCodeuiTexCoord2fColor4fNormal3fVertex3fvSUN +glReplacementCodeuiTexCoord2fNormal3fVertex3fSUN +glReplacementCodeuiTexCoord2fNormal3fVertex3fvSUN +glReplacementCodeuiTexCoord2fVertex3fSUN +glReplacementCodeuiTexCoord2fVertex3fvSUN +glReplacementCodeuiVertex3fSUN +glReplacementCodeuiVertex3fvSUN +glReplacementCodeuivSUN +glReplacementCodeusSUN +glReplacementCodeusvSUN +glRequestResidentProgramsNV +glResetHistogram +glResetHistogramEXT +glResetMinmax +glResetMinmaxEXT +glResizeBuffersMESA +glResolveDepthValuesNV +glResolveMultisampleFramebufferAPPLE +glResumeTransformFeedback +glResumeTransformFeedbackEXT +glResumeTransformFeedbackNV +glRotated +glRotatef +glRotatex +glRotatexOES +glSampleCoverage +glSampleCoverageARB +glSampleCoveragex +glSampleCoveragexOES +glSampleMapATI +glSampleMaskEXT +glSampleMaskIndexedNV +glSampleMaskSGIS +glSampleMaski +glSamplePatternEXT +glSamplePatternSGIS +glSamplerParameterIiv +glSamplerParameterIivEXT +glSamplerParameterIivOES +glSamplerParameterIuiv +glSamplerParameterIuivEXT +glSamplerParameterIuivOES +glSamplerParameterf +glSamplerParameterfv +glSamplerParameteri +glSamplerParameteriv +glScaled +glScalef +glScalex +glScalexOES +glScissor +glScissorArrayv +glScissorArrayvNV +glScissorIndexed +glScissorIndexedNV +glScissorIndexedv +glScissorIndexedvNV +glSecondaryColor3b +glSecondaryColor3bEXT +glSecondaryColor3bv +glSecondaryColor3bvEXT +glSecondaryColor3d +glSecondaryColor3dEXT +glSecondaryColor3dv +glSecondaryColor3dvEXT +glSecondaryColor3f +glSecondaryColor3fEXT +glSecondaryColor3fv +glSecondaryColor3fvEXT +glSecondaryColor3hNV +glSecondaryColor3hvNV +glSecondaryColor3i +glSecondaryColor3iEXT +glSecondaryColor3iv +glSecondaryColor3ivEXT +glSecondaryColor3s +glSecondaryColor3sEXT +glSecondaryColor3sv +glSecondaryColor3svEXT +glSecondaryColor3ub +glSecondaryColor3ubEXT +glSecondaryColor3ubv +glSecondaryColor3ubvEXT +glSecondaryColor3ui +glSecondaryColor3uiEXT +glSecondaryColor3uiv +glSecondaryColor3uivEXT +glSecondaryColor3us +glSecondaryColor3usEXT +glSecondaryColor3usv +glSecondaryColor3usvEXT +glSecondaryColorFormatNV +glSecondaryColorP3ui +glSecondaryColorP3uiv +glSecondaryColorPointer +glSecondaryColorPointerEXT +glSecondaryColorPointerListIBM +glSelectBuffer +glSelectPerfMonitorCountersAMD +glSeparableFilter2D +glSeparableFilter2DEXT +glSetFenceAPPLE +glSetFenceNV +glSetFragmentShaderConstantATI +glSetInvariantEXT +glSetLocalConstantEXT +glSetMultisamplefvAMD +glShadeModel +glShaderBinary +glShaderOp1EXT +glShaderOp2EXT +glShaderOp3EXT +glShaderSource +glShaderSourceARB +glShaderStorageBlockBinding +glSharpenTexFuncSGIS +glSpriteParameterfSGIX +glSpriteParameterfvSGIX +glSpriteParameteriSGIX +glSpriteParameterivSGIX +glStartInstrumentsSGIX +glStartTilingQCOM +glStateCaptureNV +glStencilClearTagEXT +glStencilFillPathInstancedNV +glStencilFillPathNV +glStencilFunc +glStencilFuncSeparate +glStencilFuncSeparateATI +glStencilMask +glStencilMaskSeparate +glStencilOp +glStencilOpSeparate +glStencilOpSeparateATI +glStencilOpValueAMD +glStencilStrokePathInstancedNV +glStencilStrokePathNV +glStencilThenCoverFillPathInstancedNV +glStencilThenCoverFillPathNV +glStencilThenCoverStrokePathInstancedNV +glStencilThenCoverStrokePathNV +glStopInstrumentsSGIX +glStringMarkerGREMEDY +glSubpixelPrecisionBiasNV +glSwizzleEXT +glSyncTextureINTEL +glTagSampleBufferSGIX +glTangent3bEXT +glTangent3bvEXT +glTangent3dEXT +glTangent3dvEXT +glTangent3fEXT +glTangent3fvEXT +glTangent3iEXT +glTangent3ivEXT +glTangent3sEXT +glTangent3svEXT +glTangentPointerEXT +glTbufferMask3DFX +glTessellationFactorAMD +glTessellationModeAMD +glTestFenceAPPLE +glTestFenceNV +glTestObjectAPPLE +glTexBuffer +glTexBufferARB +glTexBufferEXT +glTexBufferOES +glTexBufferRange +glTexBufferRangeEXT +glTexBufferRangeOES +glTexBumpParameterfvATI +glTexBumpParameterivATI +glTexCoord1bOES +glTexCoord1bvOES +glTexCoord1d +glTexCoord1dv +glTexCoord1f +glTexCoord1fv +glTexCoord1hNV +glTexCoord1hvNV +glTexCoord1i +glTexCoord1iv +glTexCoord1s +glTexCoord1sv +glTexCoord1xOES +glTexCoord1xvOES +glTexCoord2bOES +glTexCoord2bvOES +glTexCoord2d +glTexCoord2dv +glTexCoord2f +glTexCoord2fColor3fVertex3fSUN +glTexCoord2fColor3fVertex3fvSUN +glTexCoord2fColor4fNormal3fVertex3fSUN +glTexCoord2fColor4fNormal3fVertex3fvSUN +glTexCoord2fColor4ubVertex3fSUN +glTexCoord2fColor4ubVertex3fvSUN +glTexCoord2fNormal3fVertex3fSUN +glTexCoord2fNormal3fVertex3fvSUN +glTexCoord2fVertex3fSUN +glTexCoord2fVertex3fvSUN +glTexCoord2fv +glTexCoord2hNV +glTexCoord2hvNV +glTexCoord2i +glTexCoord2iv +glTexCoord2s +glTexCoord2sv +glTexCoord2xOES +glTexCoord2xvOES +glTexCoord3bOES +glTexCoord3bvOES +glTexCoord3d +glTexCoord3dv +glTexCoord3f +glTexCoord3fv +glTexCoord3hNV +glTexCoord3hvNV +glTexCoord3i +glTexCoord3iv +glTexCoord3s +glTexCoord3sv +glTexCoord3xOES +glTexCoord3xvOES +glTexCoord4bOES +glTexCoord4bvOES +glTexCoord4d +glTexCoord4dv +glTexCoord4f +glTexCoord4fColor4fNormal3fVertex4fSUN +glTexCoord4fColor4fNormal3fVertex4fvSUN +glTexCoord4fVertex4fSUN +glTexCoord4fVertex4fvSUN +glTexCoord4fv +glTexCoord4hNV +glTexCoord4hvNV +glTexCoord4i +glTexCoord4iv +glTexCoord4s +glTexCoord4sv +glTexCoord4xOES +glTexCoord4xvOES +glTexCoordFormatNV +glTexCoordP1ui +glTexCoordP1uiv +glTexCoordP2ui +glTexCoordP2uiv +glTexCoordP3ui +glTexCoordP3uiv +glTexCoordP4ui +glTexCoordP4uiv +glTexCoordPointer +glTexCoordPointerEXT +glTexCoordPointerListIBM +glTexCoordPointervINTEL +glTexEnvf +glTexEnvfv +glTexEnvi +glTexEnviv +glTexEnvx +glTexEnvxOES +glTexEnvxv +glTexEnvxvOES +glTexFilterFuncSGIS +glTexGend +glTexGendv +glTexGenf +glTexGenfOES +glTexGenfv +glTexGenfvOES +glTexGeni +glTexGeniOES +glTexGeniv +glTexGenivOES +glTexGenxOES +glTexGenxvOES +glTexImage1D +glTexImage2D +glTexImage2DMultisample +glTexImage2DMultisampleCoverageNV +glTexImage3D +glTexImage3DEXT +glTexImage3DMultisample +glTexImage3DMultisampleCoverageNV +glTexImage3DNV +glTexImage3DOES +glTexImage4DSGIS +glTexPageCommitmentARB +glTexPageCommitmentEXT +glTexParameterIiv +glTexParameterIivEXT +glTexParameterIivOES +glTexParameterIuiv +glTexParameterIuivEXT +glTexParameterIuivOES +glTexParameterf +glTexParameterfv +glTexParameteri +glTexParameteriv +glTexParameterx +glTexParameterxOES +glTexParameterxv +glTexParameterxvOES +glTexRenderbufferNV +glTexStorage1D +glTexStorage1DEXT +glTexStorage2D +glTexStorage2DEXT +glTexStorage2DMultisample +glTexStorage3D +glTexStorage3DEXT +glTexStorage3DMultisample +glTexStorage3DMultisampleOES +glTexStorageSparseAMD +glTexSubImage1D +glTexSubImage1DEXT +glTexSubImage2D +glTexSubImage2DEXT +glTexSubImage3D +glTexSubImage3DEXT +glTexSubImage3DNV +glTexSubImage3DOES +glTexSubImage4DSGIS +glTextureBarrier +glTextureBarrierNV +glTextureBuffer +glTextureBufferEXT +glTextureBufferRange +glTextureBufferRangeEXT +glTextureColorMaskSGIS +glTextureImage1DEXT +glTextureImage2DEXT +glTextureImage2DMultisampleCoverageNV +glTextureImage2DMultisampleNV +glTextureImage3DEXT +glTextureImage3DMultisampleCoverageNV +glTextureImage3DMultisampleNV +glTextureLightEXT +glTextureMaterialEXT +glTextureNormalEXT +glTexturePageCommitmentEXT +glTextureParameterIiv +glTextureParameterIivEXT +glTextureParameterIuiv +glTextureParameterIuivEXT +glTextureParameterf +glTextureParameterfEXT +glTextureParameterfv +glTextureParameterfvEXT +glTextureParameteri +glTextureParameteriEXT +glTextureParameteriv +glTextureParameterivEXT +glTextureRangeAPPLE +glTextureRenderbufferEXT +glTextureStorage1D +glTextureStorage1DEXT +glTextureStorage2D +glTextureStorage2DEXT +glTextureStorage2DMultisample +glTextureStorage2DMultisampleEXT +glTextureStorage3D +glTextureStorage3DEXT +glTextureStorage3DMultisample +glTextureStorage3DMultisampleEXT +glTextureStorageSparseAMD +glTextureSubImage1D +glTextureSubImage1DEXT +glTextureSubImage2D +glTextureSubImage2DEXT +glTextureSubImage3D +glTextureSubImage3DEXT +glTextureView +glTextureViewEXT +glTextureViewOES +glTrackMatrixNV +glTransformFeedbackAttribsNV +glTransformFeedbackBufferBase +glTransformFeedbackBufferRange +glTransformFeedbackStreamAttribsNV +glTransformFeedbackVaryings +glTransformFeedbackVaryingsEXT +glTransformFeedbackVaryingsNV +glTransformPathNV +glTranslated +glTranslatef +glTranslatex +glTranslatexOES +glUniform1d +glUniform1dv +glUniform1f +glUniform1fARB +glUniform1fv +glUniform1fvARB +glUniform1i +glUniform1i64ARB +glUniform1i64NV +glUniform1i64vARB +glUniform1i64vNV +glUniform1iARB +glUniform1iv +glUniform1ivARB +glUniform1ui +glUniform1ui64ARB +glUniform1ui64NV +glUniform1ui64vARB +glUniform1ui64vNV +glUniform1uiEXT +glUniform1uiv +glUniform1uivEXT +glUniform2d +glUniform2dv +glUniform2f +glUniform2fARB +glUniform2fv +glUniform2fvARB +glUniform2i +glUniform2i64ARB +glUniform2i64NV +glUniform2i64vARB +glUniform2i64vNV +glUniform2iARB +glUniform2iv +glUniform2ivARB +glUniform2ui +glUniform2ui64ARB +glUniform2ui64NV +glUniform2ui64vARB +glUniform2ui64vNV +glUniform2uiEXT +glUniform2uiv +glUniform2uivEXT +glUniform3d +glUniform3dv +glUniform3f +glUniform3fARB +glUniform3fv +glUniform3fvARB +glUniform3i +glUniform3i64ARB +glUniform3i64NV +glUniform3i64vARB +glUniform3i64vNV +glUniform3iARB +glUniform3iv +glUniform3ivARB +glUniform3ui +glUniform3ui64ARB +glUniform3ui64NV +glUniform3ui64vARB +glUniform3ui64vNV +glUniform3uiEXT +glUniform3uiv +glUniform3uivEXT +glUniform4d +glUniform4dv +glUniform4f +glUniform4fARB +glUniform4fv +glUniform4fvARB +glUniform4i +glUniform4i64ARB +glUniform4i64NV +glUniform4i64vARB +glUniform4i64vNV +glUniform4iARB +glUniform4iv +glUniform4ivARB +glUniform4ui +glUniform4ui64ARB +glUniform4ui64NV +glUniform4ui64vARB +glUniform4ui64vNV +glUniform4uiEXT +glUniform4uiv +glUniform4uivEXT +glUniformBlockBinding +glUniformBufferEXT +glUniformHandleui64ARB +glUniformHandleui64NV +glUniformHandleui64vARB +glUniformHandleui64vNV +glUniformMatrix2dv +glUniformMatrix2fv +glUniformMatrix2fvARB +glUniformMatrix2x3dv +glUniformMatrix2x3fv +glUniformMatrix2x3fvNV +glUniformMatrix2x4dv +glUniformMatrix2x4fv +glUniformMatrix2x4fvNV +glUniformMatrix3dv +glUniformMatrix3fv +glUniformMatrix3fvARB +glUniformMatrix3x2dv +glUniformMatrix3x2fv +glUniformMatrix3x2fvNV +glUniformMatrix3x4dv +glUniformMatrix3x4fv +glUniformMatrix3x4fvNV +glUniformMatrix4dv +glUniformMatrix4fv +glUniformMatrix4fvARB +glUniformMatrix4x2dv +glUniformMatrix4x2fv +glUniformMatrix4x2fvNV +glUniformMatrix4x3dv +glUniformMatrix4x3fv +glUniformMatrix4x3fvNV +glUniformSubroutinesuiv +glUniformui64NV +glUniformui64vNV +glUnlockArraysEXT +glUnmapBuffer +glUnmapBufferARB +glUnmapBufferOES +glUnmapNamedBuffer +glUnmapNamedBufferEXT +glUnmapObjectBufferATI +glUnmapTexture2DINTEL +glUpdateObjectBufferATI +glUseProgram +glUseProgramObjectARB +glUseProgramStages +glUseProgramStagesEXT +glUseShaderProgramEXT +glVDPAUFiniNV +glVDPAUGetSurfaceivNV +glVDPAUInitNV +glVDPAUIsSurfaceNV +glVDPAUMapSurfacesNV +glVDPAURegisterOutputSurfaceNV +glVDPAURegisterVideoSurfaceNV +glVDPAUSurfaceAccessNV +glVDPAUUnmapSurfacesNV +glVDPAUUnregisterSurfaceNV +glValidBackBufferHintAutodesk +glValidateProgram +glValidateProgramARB +glValidateProgramPipeline +glValidateProgramPipelineEXT +glVariantArrayObjectATI +glVariantPointerEXT +glVariantbvEXT +glVariantdvEXT +glVariantfvEXT +glVariantivEXT +glVariantsvEXT +glVariantubvEXT +glVariantuivEXT +glVariantusvEXT +glVertex2bOES +glVertex2bvOES +glVertex2d +glVertex2dv +glVertex2f +glVertex2fv +glVertex2hNV +glVertex2hvNV +glVertex2i +glVertex2iv +glVertex2s +glVertex2sv +glVertex2xOES +glVertex2xvOES +glVertex3bOES +glVertex3bvOES +glVertex3d +glVertex3dv +glVertex3f +glVertex3fv +glVertex3hNV +glVertex3hvNV +glVertex3i +glVertex3iv +glVertex3s +glVertex3sv +glVertex3xOES +glVertex3xvOES +glVertex4bOES +glVertex4bvOES +glVertex4d +glVertex4dv +glVertex4f +glVertex4fv +glVertex4hNV +glVertex4hvNV +glVertex4i +glVertex4iv +glVertex4s +glVertex4sv +glVertex4xOES +glVertex4xvOES +glVertexArrayAttribBinding +glVertexArrayAttribFormat +glVertexArrayAttribIFormat +glVertexArrayAttribLFormat +glVertexArrayBindVertexBufferEXT +glVertexArrayBindingDivisor +glVertexArrayColorOffsetEXT +glVertexArrayEdgeFlagOffsetEXT +glVertexArrayElementBuffer +glVertexArrayFogCoordOffsetEXT +glVertexArrayIndexOffsetEXT +glVertexArrayMultiTexCoordOffsetEXT +glVertexArrayNormalOffsetEXT +glVertexArrayParameteriAPPLE +glVertexArrayRangeAPPLE +glVertexArrayRangeNV +glVertexArraySecondaryColorOffsetEXT +glVertexArrayTexCoordOffsetEXT +glVertexArrayVertexAttribBindingEXT +glVertexArrayVertexAttribDivisorEXT +glVertexArrayVertexAttribFormatEXT +glVertexArrayVertexAttribIFormatEXT +glVertexArrayVertexAttribIOffsetEXT +glVertexArrayVertexAttribLFormatEXT +glVertexArrayVertexAttribLOffsetEXT +glVertexArrayVertexAttribOffsetEXT +glVertexArrayVertexBindingDivisorEXT +glVertexArrayVertexBuffer +glVertexArrayVertexBuffers +glVertexArrayVertexOffsetEXT +glVertexAttrib1d +glVertexAttrib1dARB +glVertexAttrib1dNV +glVertexAttrib1dv +glVertexAttrib1dvARB +glVertexAttrib1dvNV +glVertexAttrib1f +glVertexAttrib1fARB +glVertexAttrib1fNV +glVertexAttrib1fv +glVertexAttrib1fvARB +glVertexAttrib1fvNV +glVertexAttrib1hNV +glVertexAttrib1hvNV +glVertexAttrib1s +glVertexAttrib1sARB +glVertexAttrib1sNV +glVertexAttrib1sv +glVertexAttrib1svARB +glVertexAttrib1svNV +glVertexAttrib2d +glVertexAttrib2dARB +glVertexAttrib2dNV +glVertexAttrib2dv +glVertexAttrib2dvARB +glVertexAttrib2dvNV +glVertexAttrib2f +glVertexAttrib2fARB +glVertexAttrib2fNV +glVertexAttrib2fv +glVertexAttrib2fvARB +glVertexAttrib2fvNV +glVertexAttrib2hNV +glVertexAttrib2hvNV +glVertexAttrib2s +glVertexAttrib2sARB +glVertexAttrib2sNV +glVertexAttrib2sv +glVertexAttrib2svARB +glVertexAttrib2svNV +glVertexAttrib3d +glVertexAttrib3dARB +glVertexAttrib3dNV +glVertexAttrib3dv +glVertexAttrib3dvARB +glVertexAttrib3dvNV +glVertexAttrib3f +glVertexAttrib3fARB +glVertexAttrib3fNV +glVertexAttrib3fv +glVertexAttrib3fvARB +glVertexAttrib3fvNV +glVertexAttrib3hNV +glVertexAttrib3hvNV +glVertexAttrib3s +glVertexAttrib3sARB +glVertexAttrib3sNV +glVertexAttrib3sv +glVertexAttrib3svARB +glVertexAttrib3svNV +glVertexAttrib4Nbv +glVertexAttrib4NbvARB +glVertexAttrib4Niv +glVertexAttrib4NivARB +glVertexAttrib4Nsv +glVertexAttrib4NsvARB +glVertexAttrib4Nub +glVertexAttrib4NubARB +glVertexAttrib4Nubv +glVertexAttrib4NubvARB +glVertexAttrib4Nuiv +glVertexAttrib4NuivARB +glVertexAttrib4Nusv +glVertexAttrib4NusvARB +glVertexAttrib4bv +glVertexAttrib4bvARB +glVertexAttrib4d +glVertexAttrib4dARB +glVertexAttrib4dNV +glVertexAttrib4dv +glVertexAttrib4dvARB +glVertexAttrib4dvNV +glVertexAttrib4f +glVertexAttrib4fARB +glVertexAttrib4fNV +glVertexAttrib4fv +glVertexAttrib4fvARB +glVertexAttrib4fvNV +glVertexAttrib4hNV +glVertexAttrib4hvNV +glVertexAttrib4iv +glVertexAttrib4ivARB +glVertexAttrib4s +glVertexAttrib4sARB +glVertexAttrib4sNV +glVertexAttrib4sv +glVertexAttrib4svARB +glVertexAttrib4svNV +glVertexAttrib4ubNV +glVertexAttrib4ubv +glVertexAttrib4ubvARB +glVertexAttrib4ubvNV +glVertexAttrib4uiv +glVertexAttrib4uivARB +glVertexAttrib4usv +glVertexAttrib4usvARB +glVertexAttribArrayObjectATI +glVertexAttribBinding +glVertexAttribDivisor +glVertexAttribDivisorANGLE +glVertexAttribDivisorARB +glVertexAttribDivisorEXT +glVertexAttribDivisorNV +glVertexAttribFormat +glVertexAttribFormatNV +glVertexAttribI1i +glVertexAttribI1iEXT +glVertexAttribI1iv +glVertexAttribI1ivEXT +glVertexAttribI1ui +glVertexAttribI1uiEXT +glVertexAttribI1uiv +glVertexAttribI1uivEXT +glVertexAttribI2i +glVertexAttribI2iEXT +glVertexAttribI2iv +glVertexAttribI2ivEXT +glVertexAttribI2ui +glVertexAttribI2uiEXT +glVertexAttribI2uiv +glVertexAttribI2uivEXT +glVertexAttribI3i +glVertexAttribI3iEXT +glVertexAttribI3iv +glVertexAttribI3ivEXT +glVertexAttribI3ui +glVertexAttribI3uiEXT +glVertexAttribI3uiv +glVertexAttribI3uivEXT +glVertexAttribI4bv +glVertexAttribI4bvEXT +glVertexAttribI4i +glVertexAttribI4iEXT +glVertexAttribI4iv +glVertexAttribI4ivEXT +glVertexAttribI4sv +glVertexAttribI4svEXT +glVertexAttribI4ubv +glVertexAttribI4ubvEXT +glVertexAttribI4ui +glVertexAttribI4uiEXT +glVertexAttribI4uiv +glVertexAttribI4uivEXT +glVertexAttribI4usv +glVertexAttribI4usvEXT +glVertexAttribIFormat +glVertexAttribIFormatNV +glVertexAttribIPointer +glVertexAttribIPointerEXT +glVertexAttribL1d +glVertexAttribL1dEXT +glVertexAttribL1dv +glVertexAttribL1dvEXT +glVertexAttribL1i64NV +glVertexAttribL1i64vNV +glVertexAttribL1ui64ARB +glVertexAttribL1ui64NV +glVertexAttribL1ui64vARB +glVertexAttribL1ui64vNV +glVertexAttribL2d +glVertexAttribL2dEXT +glVertexAttribL2dv +glVertexAttribL2dvEXT +glVertexAttribL2i64NV +glVertexAttribL2i64vNV +glVertexAttribL2ui64NV +glVertexAttribL2ui64vNV +glVertexAttribL3d +glVertexAttribL3dEXT +glVertexAttribL3dv +glVertexAttribL3dvEXT +glVertexAttribL3i64NV +glVertexAttribL3i64vNV +glVertexAttribL3ui64NV +glVertexAttribL3ui64vNV +glVertexAttribL4d +glVertexAttribL4dEXT +glVertexAttribL4dv +glVertexAttribL4dvEXT +glVertexAttribL4i64NV +glVertexAttribL4i64vNV +glVertexAttribL4ui64NV +glVertexAttribL4ui64vNV +glVertexAttribLFormat +glVertexAttribLFormatNV +glVertexAttribLPointer +glVertexAttribLPointerEXT +glVertexAttribP1ui +glVertexAttribP1uiv +glVertexAttribP2ui +glVertexAttribP2uiv +glVertexAttribP3ui +glVertexAttribP3uiv +glVertexAttribP4ui +glVertexAttribP4uiv +glVertexAttribParameteriAMD +glVertexAttribPointer +glVertexAttribPointerARB +glVertexAttribPointerNV +glVertexAttribs1dvNV +glVertexAttribs1fvNV +glVertexAttribs1hvNV +glVertexAttribs1svNV +glVertexAttribs2dvNV +glVertexAttribs2fvNV +glVertexAttribs2hvNV +glVertexAttribs2svNV +glVertexAttribs3dvNV +glVertexAttribs3fvNV +glVertexAttribs3hvNV +glVertexAttribs3svNV +glVertexAttribs4dvNV +glVertexAttribs4fvNV +glVertexAttribs4hvNV +glVertexAttribs4svNV +glVertexAttribs4ubvNV +glVertexBindingDivisor +glVertexBlendARB +glVertexBlendEnvfATI +glVertexBlendEnviATI +glVertexFormatNV +glVertexP2ui +glVertexP2uiv +glVertexP3ui +glVertexP3uiv +glVertexP4ui +glVertexP4uiv +glVertexPointer +glVertexPointerEXT +glVertexPointerListIBM +glVertexPointervINTEL +glVertexStream1dATI +glVertexStream1dvATI +glVertexStream1fATI +glVertexStream1fvATI +glVertexStream1iATI +glVertexStream1ivATI +glVertexStream1sATI +glVertexStream1svATI +glVertexStream2dATI +glVertexStream2dvATI +glVertexStream2fATI +glVertexStream2fvATI +glVertexStream2iATI +glVertexStream2ivATI +glVertexStream2sATI +glVertexStream2svATI +glVertexStream3dATI +glVertexStream3dvATI +glVertexStream3fATI +glVertexStream3fvATI +glVertexStream3iATI +glVertexStream3ivATI +glVertexStream3sATI +glVertexStream3svATI +glVertexStream4dATI +glVertexStream4dvATI +glVertexStream4fATI +glVertexStream4fvATI +glVertexStream4iATI +glVertexStream4ivATI +glVertexStream4sATI +glVertexStream4svATI +glVertexWeightPointerEXT +glVertexWeightfEXT +glVertexWeightfvEXT +glVertexWeighthNV +glVertexWeighthvNV +glVideoCaptureNV +glVideoCaptureStreamParameterdvNV +glVideoCaptureStreamParameterfvNV +glVideoCaptureStreamParameterivNV +glViewport +glViewportArrayv +glViewportArrayvNV +glViewportIndexedf +glViewportIndexedfNV +glViewportIndexedfv +glViewportIndexedfvNV +glViewportSwizzleNV +glWaitSync +glWaitSyncAPPLE +glWaitSyncValueuiNVX +glWeightPathsNV +glWeightPointerARB +glWeightPointerOES +glWeightbvARB +glWeightdvARB +glWeightfvARB +glWeightivARB +glWeightsvARB +glWeightubvARB +glWeightuivARB +glWeightusvARB +glWindowBackBufferHintAutodesk +glWindowPos2d +glWindowPos2dARB +glWindowPos2dMESA +glWindowPos2dv +glWindowPos2dvARB +glWindowPos2dvMESA +glWindowPos2f +glWindowPos2fARB +glWindowPos2fMESA +glWindowPos2fv +glWindowPos2fvARB +glWindowPos2fvMESA +glWindowPos2i +glWindowPos2iARB +glWindowPos2iMESA +glWindowPos2iv +glWindowPos2ivARB +glWindowPos2ivMESA +glWindowPos2s +glWindowPos2sARB +glWindowPos2sMESA +glWindowPos2sv +glWindowPos2svARB +glWindowPos2svMESA +glWindowPos3d +glWindowPos3dARB +glWindowPos3dMESA +glWindowPos3dv +glWindowPos3dvARB +glWindowPos3dvMESA +glWindowPos3f +glWindowPos3fARB +glWindowPos3fMESA +glWindowPos3fv +glWindowPos3fvARB +glWindowPos3fvMESA +glWindowPos3i +glWindowPos3iARB +glWindowPos3iMESA +glWindowPos3iv +glWindowPos3ivARB +glWindowPos3ivMESA +glWindowPos3s +glWindowPos3sARB +glWindowPos3sMESA +glWindowPos3sv +glWindowPos3svARB +glWindowPos3svMESA +glWindowPos4dMESA +glWindowPos4dvMESA +glWindowPos4fMESA +glWindowPos4fvMESA +glWindowPos4iMESA +glWindowPos4ivMESA +glWindowPos4sMESA +glWindowPos4svMESA +glWriteMaskEXT +glXAllocateMemoryNV +glXBindChannelToWindowSGIX +glXBindHyperpipeSGIX +glXBindSwapBarrierNV +glXBindSwapBarrierSGIX +glXBindTexImageEXT +glXBindVideoCaptureDeviceNV +glXBindVideoDeviceNV +glXBindVideoImageNV +glXBlitContextFramebufferAMD +glXChannelRectSGIX +glXChannelRectSyncSGIX +glXChooseFBConfig +glXChooseFBConfigSGIX +glXChooseVisual +glXCopyBufferSubDataNV +glXCopyContext +glXCopyImageSubDataNV +glXCopySubBufferMESA +glXCreateAssociatedContextAMD +glXCreateAssociatedContextAttribsAMD +glXCreateContext +glXCreateContextAttribsARB +glXCreateContextWithConfigSGIX +glXCreateGLXPbufferSGIX +glXCreateGLXPixmap +glXCreateGLXPixmapMESA +glXCreateGLXPixmapWithConfigSGIX +glXCreateNewContext +glXCreatePbuffer +glXCreatePixmap +glXCreateWindow +glXCushionSGI +glXDelayBeforeSwapNV +glXDeleteAssociatedContextAMD +glXDestroyContext +glXDestroyGLXPbufferSGIX +glXDestroyGLXPixmap +glXDestroyHyperpipeConfigSGIX +glXDestroyPbuffer +glXDestroyPixmap +glXDestroyWindow +glXEnumerateVideoCaptureDevicesNV +glXEnumerateVideoDevicesNV +glXFreeContextEXT +glXFreeMemoryNV +glXGetAGPOffsetMESA +glXGetClientString +glXGetConfig +glXGetContextGPUIDAMD +glXGetContextIDEXT +glXGetCurrentAssociatedContextAMD +glXGetCurrentContext +glXGetCurrentDisplay +glXGetCurrentDisplayEXT +glXGetCurrentDrawable +glXGetCurrentReadDrawable +glXGetCurrentReadDrawableSGI +glXGetDriverConfig +glXGetFBConfigAttrib +glXGetFBConfigAttribSGIX +glXGetFBConfigFromVisualSGIX +glXGetFBConfigs +glXGetGPUIDsAMD +glXGetGPUInfoAMD +glXGetMscRateOML +glXGetProcAddress +glXGetProcAddressARB +glXGetRefreshRateSGI +glXGetScreenDriver +glXGetSelectedEvent +glXGetSelectedEventSGIX +glXGetSyncValuesOML +glXGetTransparentIndexSUN +glXGetVideoDeviceNV +glXGetVideoInfoNV +glXGetVideoSyncSGI +glXGetVisualFromFBConfig +glXGetVisualFromFBConfigSGIX +glXHyperpipeAttribSGIX +glXHyperpipeConfigSGIX +glXImportContextEXT +glXIsDirect +glXJoinSwapGroupNV +glXJoinSwapGroupSGIX +glXLockVideoCaptureDeviceNV +glXMakeAssociatedContextCurrentAMD +glXMakeContextCurrent +glXMakeCurrent +glXMakeCurrentReadSGI +glXNamedCopyBufferSubDataNV +glXQueryChannelDeltasSGIX +glXQueryChannelRectSGIX +glXQueryContext +glXQueryContextInfoEXT +glXQueryCurrentRendererIntegerMESA +glXQueryCurrentRendererStringMESA +glXQueryDrawable +glXQueryExtension +glXQueryExtensionsString +glXQueryFrameCountNV +glXQueryGLXPbufferSGIX +glXQueryHyperpipeAttribSGIX +glXQueryHyperpipeBestAttribSGIX +glXQueryHyperpipeConfigSGIX +glXQueryHyperpipeNetworkSGIX +glXQueryMaxSwapBarriersSGIX +glXQueryMaxSwapGroupsNV +glXQueryRendererIntegerMESA +glXQueryRendererStringMESA +glXQueryServerString +glXQuerySwapGroupNV +glXQueryVersion +glXQueryVideoCaptureDeviceNV +glXReleaseBuffersMESA +glXReleaseTexImageEXT +glXReleaseVideoCaptureDeviceNV +glXReleaseVideoDeviceNV +glXReleaseVideoImageNV +glXResetFrameCountNV +glXSelectEvent +glXSelectEventSGIX +glXSendPbufferToVideoNV +glXSet3DfxModeMESA +glXSwapBuffers +glXSwapBuffersMscOML +glXSwapIntervalEXT +glXSwapIntervalSGI +glXUseXFont +glXWaitForMscOML +glXWaitForSbcOML +glXWaitGL +glXWaitVideoSyncSGI +glXWaitX diff --git a/subprojects/libcapsule/examples/shim/libGL.so.1.symbols.updated-for b/subprojects/libcapsule/examples/shim/libGL.so.1.symbols.updated-for new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/subprojects/libcapsule/examples/shim/libGLESv2.so.2.excluded b/subprojects/libcapsule/examples/shim/libGLESv2.so.2.excluded new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/subprojects/libcapsule/examples/shim/libGLESv2.so.2.shared b/subprojects/libcapsule/examples/shim/libGLESv2.so.2.shared new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/subprojects/libcapsule/examples/shim/libGLESv2.so.2.symbols b/subprojects/libcapsule/examples/shim/libGLESv2.so.2.symbols new file mode 100644 index 0000000000000000000000000000000000000000..3a792998b832d851f495160ad51dad0cb41ed8b7 --- /dev/null +++ b/subprojects/libcapsule/examples/shim/libGLESv2.so.2.symbols @@ -0,0 +1,365 @@ +glActiveShaderProgram +glActiveTexture +glAttachShader +glBeginQuery +glBeginTransformFeedback +glBindAttribLocation +glBindBuffer +glBindBufferBase +glBindBufferRange +glBindFragDataLocationEXT +glBindFramebuffer +glBindImageTexture +glBindProgramPipeline +glBindRenderbuffer +glBindSampler +glBindTexture +glBindTransformFeedback +glBindVertexArray +glBindVertexBuffer +glBlendBarrier +glBlendColor +glBlendEquation +glBlendEquationSeparate +glBlendEquationSeparatei +glBlendEquationi +glBlendFunc +glBlendFuncSeparate +glBlendFuncSeparatei +glBlendFunci +glBlitFramebuffer +glBufferData +glBufferSubData +glCheckFramebufferStatus +glClear +glClearBufferfi +glClearBufferfv +glClearBufferiv +glClearBufferuiv +glClearColor +glClearDepthf +glClearStencil +glClientWaitSync +glColorMask +glColorMaski +glCompileShader +glCompressedTexImage2D +glCompressedTexImage3D +glCompressedTexSubImage2D +glCompressedTexSubImage3D +glCopyBufferSubData +glCopyImageSubData +glCopyTexImage2D +glCopyTexSubImage2D +glCopyTexSubImage3D +glCreateProgram +glCreateShader +glCreateShaderProgramv +glCullFace +glDebugMessageCallback +glDebugMessageControl +glDebugMessageInsert +glDeleteBuffers +glDeleteFramebuffers +glDeleteProgram +glDeleteProgramPipelines +glDeleteQueries +glDeleteRenderbuffers +glDeleteSamplers +glDeleteShader +glDeleteSync +glDeleteTextures +glDeleteTransformFeedbacks +glDeleteVertexArrays +glDepthFunc +glDepthMask +glDepthRangef +glDetachShader +glDisable +glDisableVertexAttribArray +glDisablei +glDispatchCompute +glDispatchComputeIndirect +glDrawArrays +glDrawArraysIndirect +glDrawArraysInstanced +glDrawBuffers +glDrawElements +glDrawElementsBaseVertex +glDrawElementsIndirect +glDrawElementsInstanced +glDrawElementsInstancedBaseVertex +glDrawRangeElements +glDrawRangeElementsBaseVertex +glEnable +glEnableVertexAttribArray +glEnablei +glEndQuery +glEndTransformFeedback +glFenceSync +glFinish +glFlush +glFlushMappedBufferRange +glFramebufferParameteri +glFramebufferRenderbuffer +glFramebufferTexture +glFramebufferTexture2D +glFramebufferTextureLayer +glFrontFace +glGenBuffers +glGenFramebuffers +glGenProgramPipelines +glGenQueries +glGenRenderbuffers +glGenSamplers +glGenTextures +glGenTransformFeedbacks +glGenVertexArrays +glGenerateMipmap +glGetActiveAttrib +glGetActiveUniform +glGetActiveUniformBlockName +glGetActiveUniformBlockiv +glGetActiveUniformsiv +glGetAttachedShaders +glGetAttribLocation +glGetBooleani_v +glGetBooleanv +glGetBufferParameteri64v +glGetBufferParameteriv +glGetBufferPointerv +glGetDebugMessageLog +glGetError +glGetFloatv +glGetFragDataLocation +glGetFramebufferAttachmentParameteriv +glGetFramebufferParameteriv +glGetGraphicsResetStatus +glGetInteger64i_v +glGetInteger64v +glGetIntegeri_v +glGetIntegerv +glGetInternalformativ +glGetMultisamplefv +glGetObjectLabel +glGetObjectPtrLabel +glGetPointerv +glGetProgramBinary +glGetProgramInfoLog +glGetProgramInterfaceiv +glGetProgramPipelineInfoLog +glGetProgramPipelineiv +glGetProgramResourceIndex +glGetProgramResourceLocation +glGetProgramResourceName +glGetProgramResourceiv +glGetProgramiv +glGetQueryObjectuiv +glGetQueryiv +glGetRenderbufferParameteriv +glGetSamplerParameterIiv +glGetSamplerParameterIuiv +glGetSamplerParameterfv +glGetSamplerParameteriv +glGetShaderInfoLog +glGetShaderPrecisionFormat +glGetShaderSource +glGetShaderiv +glGetString +glGetStringi +glGetSynciv +glGetTexLevelParameterfv +glGetTexLevelParameteriv +glGetTexParameterIiv +glGetTexParameterIivEXT +glGetTexParameterIuiv +glGetTexParameterIuivEXT +glGetTexParameterfv +glGetTexParameteriv +glGetTransformFeedbackVarying +glGetUniformBlockIndex +glGetUniformIndices +glGetUniformLocation +glGetUniformfv +glGetUniformiv +glGetUniformuiv +glGetVertexAttribIiv +glGetVertexAttribIuiv +glGetVertexAttribPointerv +glGetVertexAttribfv +glGetVertexAttribiv +glGetnUniformfv +glGetnUniformiv +glGetnUniformuiv +glHint +glInvalidateFramebuffer +glInvalidateSubFramebuffer +glIsBuffer +glIsEnabled +glIsEnabledi +glIsFramebuffer +glIsProgram +glIsProgramPipeline +glIsQuery +glIsRenderbuffer +glIsSampler +glIsShader +glIsSync +glIsTexture +glIsTransformFeedback +glIsVertexArray +glLineWidth +glLinkProgram +glMapBufferRange +glMemoryBarrier +glMemoryBarrierByRegion +glMinSampleShading +glMultiDrawArraysEXT +glMultiDrawElementsEXT +glObjectLabel +glObjectPtrLabel +glPatchParameteri +glPauseTransformFeedback +glPixelStorei +glPolygonOffset +glPopDebugGroup +glPrimitiveBoundingBox +glProgramBinary +glProgramParameteri +glProgramUniform1f +glProgramUniform1fv +glProgramUniform1i +glProgramUniform1iv +glProgramUniform1ui +glProgramUniform1uiv +glProgramUniform2f +glProgramUniform2fv +glProgramUniform2i +glProgramUniform2iv +glProgramUniform2ui +glProgramUniform2uiv +glProgramUniform3f +glProgramUniform3fv +glProgramUniform3i +glProgramUniform3iv +glProgramUniform3ui +glProgramUniform3uiv +glProgramUniform4f +glProgramUniform4fv +glProgramUniform4i +glProgramUniform4iv +glProgramUniform4ui +glProgramUniform4uiv +glProgramUniformMatrix2fv +glProgramUniformMatrix2x3fv +glProgramUniformMatrix2x4fv +glProgramUniformMatrix3fv +glProgramUniformMatrix3x2fv +glProgramUniformMatrix3x4fv +glProgramUniformMatrix4fv +glProgramUniformMatrix4x2fv +glProgramUniformMatrix4x3fv +glPushDebugGroup +glReadBuffer +glReadPixels +glReadnPixels +glReleaseShaderCompiler +glRenderbufferStorage +glRenderbufferStorageMultisample +glResumeTransformFeedback +glSampleCoverage +glSampleMaski +glSamplerParameterIiv +glSamplerParameterIuiv +glSamplerParameterf +glSamplerParameterfv +glSamplerParameteri +glSamplerParameteriv +glScissor +glShaderBinary +glShaderSource +glStencilFunc +glStencilFuncSeparate +glStencilMask +glStencilMaskSeparate +glStencilOp +glStencilOpSeparate +glTexBuffer +glTexBufferRange +glTexImage2D +glTexImage3D +glTexParameterIiv +glTexParameterIivEXT +glTexParameterIuiv +glTexParameterIuivEXT +glTexParameterf +glTexParameterfv +glTexParameteri +glTexParameteriv +glTexStorage2D +glTexStorage2DMultisample +glTexStorage3D +glTexStorage3DMultisample +glTexSubImage2D +glTexSubImage3D +glTransformFeedbackVaryings +glUniform1f +glUniform1fv +glUniform1i +glUniform1iv +glUniform1ui +glUniform1uiv +glUniform2f +glUniform2fv +glUniform2i +glUniform2iv +glUniform2ui +glUniform2uiv +glUniform3f +glUniform3fv +glUniform3i +glUniform3iv +glUniform3ui +glUniform3uiv +glUniform4f +glUniform4fv +glUniform4i +glUniform4iv +glUniform4ui +glUniform4uiv +glUniformBlockBinding +glUniformMatrix2fv +glUniformMatrix2x3fv +glUniformMatrix2x4fv +glUniformMatrix3fv +glUniformMatrix3x2fv +glUniformMatrix3x4fv +glUniformMatrix4fv +glUniformMatrix4x2fv +glUniformMatrix4x3fv +glUnmapBuffer +glUseProgram +glUseProgramStages +glValidateProgram +glValidateProgramPipeline +glVertexAttrib1f +glVertexAttrib1fv +glVertexAttrib2f +glVertexAttrib2fv +glVertexAttrib3f +glVertexAttrib3fv +glVertexAttrib4f +glVertexAttrib4fv +glVertexAttribBinding +glVertexAttribDivisor +glVertexAttribFormat +glVertexAttribI4i +glVertexAttribI4iv +glVertexAttribI4ui +glVertexAttribI4uiv +glVertexAttribIFormat +glVertexAttribIPointer +glVertexAttribPointer +glVertexBindingDivisor +glViewport +glWaitSync diff --git a/subprojects/libcapsule/examples/shim/libGLESv2.so.2.symbols.updated-for b/subprojects/libcapsule/examples/shim/libGLESv2.so.2.symbols.updated-for new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/subprojects/libcapsule/examples/shim/libGLX.so.0.excluded b/subprojects/libcapsule/examples/shim/libGLX.so.0.excluded new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/subprojects/libcapsule/examples/shim/libGLX.so.0.shared b/subprojects/libcapsule/examples/shim/libGLX.so.0.shared new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/subprojects/libcapsule/examples/shim/libGLX.so.0.symbols b/subprojects/libcapsule/examples/shim/libGLX.so.0.symbols new file mode 100644 index 0000000000000000000000000000000000000000..1577c60fac36ba790841e0c914a8492ca1304d76 --- /dev/null +++ b/subprojects/libcapsule/examples/shim/libGLX.so.0.symbols @@ -0,0 +1,42 @@ +__GLXGL_CORE_FUNCTIONS +__glXGLLoadGLXFunction +glXChooseFBConfig +glXChooseVisual +glXCopyContext +glXCreateContext +glXCreateGLXPixmap +glXCreateNewContext +glXCreatePbuffer +glXCreatePixmap +glXCreateWindow +glXDestroyContext +glXDestroyGLXPixmap +glXDestroyPbuffer +glXDestroyPixmap +glXDestroyWindow +glXGetClientString +glXGetConfig +glXGetCurrentContext +glXGetCurrentDisplay +glXGetCurrentDrawable +glXGetCurrentReadDrawable +glXGetFBConfigAttrib +glXGetFBConfigs +glXGetProcAddress +glXGetProcAddressARB +glXGetSelectedEvent +glXGetVisualFromFBConfig +glXIsDirect +glXMakeContextCurrent +glXMakeCurrent +glXQueryContext +glXQueryDrawable +glXQueryExtension +glXQueryExtensionsString +glXQueryServerString +glXQueryVersion +glXSelectEvent +glXSwapBuffers +glXUseXFont +glXWaitGL +glXWaitX diff --git a/subprojects/libcapsule/examples/shim/libGLX.so.0.symbols.updated-for b/subprojects/libcapsule/examples/shim/libGLX.so.0.symbols.updated-for new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/subprojects/libcapsule/examples/shim/libOpenGL.so.0.excluded b/subprojects/libcapsule/examples/shim/libOpenGL.so.0.excluded new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/subprojects/libcapsule/examples/shim/libOpenGL.so.0.shared b/subprojects/libcapsule/examples/shim/libOpenGL.so.0.shared new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/subprojects/libcapsule/examples/shim/libOpenGL.so.0.symbols b/subprojects/libcapsule/examples/shim/libOpenGL.so.0.symbols new file mode 100644 index 0000000000000000000000000000000000000000..6f21479973fe17a93dfe167e334b75d427dbfa52 --- /dev/null +++ b/subprojects/libcapsule/examples/shim/libOpenGL.so.0.symbols @@ -0,0 +1,1044 @@ +glAccum +glActiveShaderProgram +glActiveTexture +glAlphaFunc +glAreTexturesResident +glArrayElement +glAttachShader +glBegin +glBeginConditionalRender +glBeginQuery +glBeginQueryIndexed +glBeginTransformFeedback +glBindAttribLocation +glBindBuffer +glBindBufferBase +glBindBufferRange +glBindBuffersBase +glBindBuffersRange +glBindFragDataLocation +glBindFragDataLocationIndexed +glBindFramebuffer +glBindImageTexture +glBindImageTextures +glBindProgramPipeline +glBindRenderbuffer +glBindSampler +glBindSamplers +glBindTexture +glBindTextureUnit +glBindTextures +glBindTransformFeedback +glBindVertexArray +glBindVertexBuffer +glBindVertexBuffers +glBitmap +glBlendColor +glBlendEquation +glBlendEquationSeparate +glBlendEquationSeparatei +glBlendEquationi +glBlendFunc +glBlendFuncSeparate +glBlendFuncSeparatei +glBlendFunci +glBlitFramebuffer +glBlitNamedFramebuffer +glBufferData +glBufferStorage +glBufferSubData +glCallList +glCallLists +glCheckFramebufferStatus +glCheckNamedFramebufferStatus +glClampColor +glClear +glClearAccum +glClearBufferData +glClearBufferSubData +glClearBufferfi +glClearBufferfv +glClearBufferiv +glClearBufferuiv +glClearColor +glClearDepth +glClearDepthf +glClearIndex +glClearNamedBufferData +glClearNamedBufferSubData +glClearNamedFramebufferfi +glClearNamedFramebufferfv +glClearNamedFramebufferiv +glClearNamedFramebufferuiv +glClearStencil +glClearTexImage +glClearTexSubImage +glClientActiveTexture +glClientWaitSync +glClipControl +glClipPlane +glColor3b +glColor3bv +glColor3d +glColor3dv +glColor3f +glColor3fv +glColor3i +glColor3iv +glColor3s +glColor3sv +glColor3ub +glColor3ubv +glColor3ui +glColor3uiv +glColor3us +glColor3usv +glColor4b +glColor4bv +glColor4d +glColor4dv +glColor4f +glColor4fv +glColor4i +glColor4iv +glColor4s +glColor4sv +glColor4ub +glColor4ubv +glColor4ui +glColor4uiv +glColor4us +glColor4usv +glColorMask +glColorMaski +glColorMaterial +glColorP3ui +glColorP3uiv +glColorP4ui +glColorP4uiv +glColorPointer +glCompileShader +glCompressedTexImage1D +glCompressedTexImage2D +glCompressedTexImage3D +glCompressedTexSubImage1D +glCompressedTexSubImage2D +glCompressedTexSubImage3D +glCompressedTextureSubImage1D +glCompressedTextureSubImage2D +glCompressedTextureSubImage3D +glCopyBufferSubData +glCopyImageSubData +glCopyNamedBufferSubData +glCopyPixels +glCopyTexImage1D +glCopyTexImage2D +glCopyTexSubImage1D +glCopyTexSubImage2D +glCopyTexSubImage3D +glCopyTextureSubImage1D +glCopyTextureSubImage2D +glCopyTextureSubImage3D +glCreateBuffers +glCreateFramebuffers +glCreateProgram +glCreateProgramPipelines +glCreateQueries +glCreateRenderbuffers +glCreateSamplers +glCreateShader +glCreateShaderProgramv +glCreateTextures +glCreateTransformFeedbacks +glCreateVertexArrays +glCullFace +glDebugMessageCallback +glDebugMessageControl +glDebugMessageInsert +glDeleteBuffers +glDeleteFramebuffers +glDeleteLists +glDeleteProgram +glDeleteProgramPipelines +glDeleteQueries +glDeleteRenderbuffers +glDeleteSamplers +glDeleteShader +glDeleteSync +glDeleteTextures +glDeleteTransformFeedbacks +glDeleteVertexArrays +glDepthFunc +glDepthMask +glDepthRange +glDepthRangeArrayv +glDepthRangeIndexed +glDepthRangef +glDetachShader +glDisable +glDisableClientState +glDisableVertexArrayAttrib +glDisableVertexAttribArray +glDisablei +glDispatchCompute +glDispatchComputeIndirect +glDrawArrays +glDrawArraysIndirect +glDrawArraysInstanced +glDrawArraysInstancedBaseInstance +glDrawBuffer +glDrawBuffers +glDrawElements +glDrawElementsBaseVertex +glDrawElementsIndirect +glDrawElementsInstanced +glDrawElementsInstancedBaseInstance +glDrawElementsInstancedBaseVertex +glDrawElementsInstancedBaseVertexBaseInstance +glDrawPixels +glDrawRangeElements +glDrawRangeElementsBaseVertex +glDrawTransformFeedback +glDrawTransformFeedbackInstanced +glDrawTransformFeedbackStream +glDrawTransformFeedbackStreamInstanced +glEdgeFlag +glEdgeFlagPointer +glEdgeFlagv +glEnable +glEnableClientState +glEnableVertexArrayAttrib +glEnableVertexAttribArray +glEnablei +glEnd +glEndConditionalRender +glEndList +glEndQuery +glEndQueryIndexed +glEndTransformFeedback +glEvalCoord1d +glEvalCoord1dv +glEvalCoord1f +glEvalCoord1fv +glEvalCoord2d +glEvalCoord2dv +glEvalCoord2f +glEvalCoord2fv +glEvalMesh1 +glEvalMesh2 +glEvalPoint1 +glEvalPoint2 +glFeedbackBuffer +glFenceSync +glFinish +glFlush +glFlushMappedBufferRange +glFlushMappedNamedBufferRange +glFogCoordPointer +glFogCoordd +glFogCoorddv +glFogCoordf +glFogCoordfv +glFogf +glFogfv +glFogi +glFogiv +glFramebufferParameteri +glFramebufferRenderbuffer +glFramebufferTexture +glFramebufferTexture1D +glFramebufferTexture2D +glFramebufferTexture3D +glFramebufferTextureLayer +glFrontFace +glFrustum +glGenBuffers +glGenFramebuffers +glGenLists +glGenProgramPipelines +glGenQueries +glGenRenderbuffers +glGenSamplers +glGenTextures +glGenTransformFeedbacks +glGenVertexArrays +glGenerateMipmap +glGenerateTextureMipmap +glGetActiveAtomicCounterBufferiv +glGetActiveAttrib +glGetActiveSubroutineName +glGetActiveSubroutineUniformName +glGetActiveSubroutineUniformiv +glGetActiveUniform +glGetActiveUniformBlockName +glGetActiveUniformBlockiv +glGetActiveUniformName +glGetActiveUniformsiv +glGetAttachedShaders +glGetAttribLocation +glGetBooleani_v +glGetBooleanv +glGetBufferParameteri64v +glGetBufferParameteriv +glGetBufferPointerv +glGetBufferSubData +glGetClipPlane +glGetCompressedTexImage +glGetCompressedTextureImage +glGetCompressedTextureSubImage +glGetDebugMessageLog +glGetDoublei_v +glGetDoublev +glGetError +glGetFloati_v +glGetFloatv +glGetFragDataIndex +glGetFragDataLocation +glGetFramebufferAttachmentParameteriv +glGetFramebufferParameteriv +glGetGraphicsResetStatus +glGetInteger64i_v +glGetInteger64v +glGetIntegeri_v +glGetIntegerv +glGetInternalformati64v +glGetInternalformativ +glGetLightfv +glGetLightiv +glGetMapdv +glGetMapfv +glGetMapiv +glGetMaterialfv +glGetMaterialiv +glGetMultisamplefv +glGetNamedBufferParameteri64v +glGetNamedBufferParameteriv +glGetNamedBufferPointerv +glGetNamedBufferSubData +glGetNamedFramebufferAttachmentParameteriv +glGetNamedFramebufferParameteriv +glGetNamedRenderbufferParameteriv +glGetObjectLabel +glGetObjectPtrLabel +glGetPixelMapfv +glGetPixelMapuiv +glGetPixelMapusv +glGetPointerv +glGetPolygonStipple +glGetProgramBinary +glGetProgramInfoLog +glGetProgramInterfaceiv +glGetProgramPipelineInfoLog +glGetProgramPipelineiv +glGetProgramResourceIndex +glGetProgramResourceLocation +glGetProgramResourceLocationIndex +glGetProgramResourceName +glGetProgramResourceiv +glGetProgramStageiv +glGetProgramiv +glGetQueryBufferObjecti64v +glGetQueryBufferObjectiv +glGetQueryBufferObjectui64v +glGetQueryBufferObjectuiv +glGetQueryIndexediv +glGetQueryObjecti64v +glGetQueryObjectiv +glGetQueryObjectui64v +glGetQueryObjectuiv +glGetQueryiv +glGetRenderbufferParameteriv +glGetSamplerParameterIiv +glGetSamplerParameterIuiv +glGetSamplerParameterfv +glGetSamplerParameteriv +glGetShaderInfoLog +glGetShaderPrecisionFormat +glGetShaderSource +glGetShaderiv +glGetString +glGetStringi +glGetSubroutineIndex +glGetSubroutineUniformLocation +glGetSynciv +glGetTexEnvfv +glGetTexEnviv +glGetTexGendv +glGetTexGenfv +glGetTexGeniv +glGetTexImage +glGetTexLevelParameterfv +glGetTexLevelParameteriv +glGetTexParameterIiv +glGetTexParameterIuiv +glGetTexParameterfv +glGetTexParameteriv +glGetTextureImage +glGetTextureLevelParameterfv +glGetTextureLevelParameteriv +glGetTextureParameterIiv +glGetTextureParameterIuiv +glGetTextureParameterfv +glGetTextureParameteriv +glGetTextureSubImage +glGetTransformFeedbackVarying +glGetTransformFeedbacki64_v +glGetTransformFeedbacki_v +glGetTransformFeedbackiv +glGetUniformBlockIndex +glGetUniformIndices +glGetUniformLocation +glGetUniformSubroutineuiv +glGetUniformdv +glGetUniformfv +glGetUniformiv +glGetUniformuiv +glGetVertexArrayIndexed64iv +glGetVertexArrayIndexediv +glGetVertexArrayiv +glGetVertexAttribIiv +glGetVertexAttribIuiv +glGetVertexAttribLdv +glGetVertexAttribPointerv +glGetVertexAttribdv +glGetVertexAttribfv +glGetVertexAttribiv +glGetnColorTable +glGetnCompressedTexImage +glGetnConvolutionFilter +glGetnHistogram +glGetnMapdv +glGetnMapfv +glGetnMapiv +glGetnMinmax +glGetnPixelMapfv +glGetnPixelMapuiv +glGetnPixelMapusv +glGetnPolygonStipple +glGetnSeparableFilter +glGetnTexImage +glGetnUniformdv +glGetnUniformfv +glGetnUniformiv +glGetnUniformuiv +glHint +glIndexMask +glIndexPointer +glIndexd +glIndexdv +glIndexf +glIndexfv +glIndexi +glIndexiv +glIndexs +glIndexsv +glIndexub +glIndexubv +glInitNames +glInterleavedArrays +glInvalidateBufferData +glInvalidateBufferSubData +glInvalidateFramebuffer +glInvalidateNamedFramebufferData +glInvalidateNamedFramebufferSubData +glInvalidateSubFramebuffer +glInvalidateTexImage +glInvalidateTexSubImage +glIsBuffer +glIsEnabled +glIsEnabledi +glIsFramebuffer +glIsList +glIsProgram +glIsProgramPipeline +glIsQuery +glIsRenderbuffer +glIsSampler +glIsShader +glIsSync +glIsTexture +glIsTransformFeedback +glIsVertexArray +glLightModelf +glLightModelfv +glLightModeli +glLightModeliv +glLightf +glLightfv +glLighti +glLightiv +glLineStipple +glLineWidth +glLinkProgram +glListBase +glLoadIdentity +glLoadMatrixd +glLoadMatrixf +glLoadName +glLoadTransposeMatrixd +glLoadTransposeMatrixf +glLogicOp +glMap1d +glMap1f +glMap2d +glMap2f +glMapBuffer +glMapBufferRange +glMapGrid1d +glMapGrid1f +glMapGrid2d +glMapGrid2f +glMapNamedBuffer +glMapNamedBufferRange +glMaterialf +glMaterialfv +glMateriali +glMaterialiv +glMatrixMode +glMemoryBarrier +glMemoryBarrierByRegion +glMinSampleShading +glMultMatrixd +glMultMatrixf +glMultTransposeMatrixd +glMultTransposeMatrixf +glMultiDrawArrays +glMultiDrawArraysIndirect +glMultiDrawElements +glMultiDrawElementsBaseVertex +glMultiDrawElementsIndirect +glMultiTexCoord1d +glMultiTexCoord1dv +glMultiTexCoord1f +glMultiTexCoord1fv +glMultiTexCoord1i +glMultiTexCoord1iv +glMultiTexCoord1s +glMultiTexCoord1sv +glMultiTexCoord2d +glMultiTexCoord2dv +glMultiTexCoord2f +glMultiTexCoord2fv +glMultiTexCoord2i +glMultiTexCoord2iv +glMultiTexCoord2s +glMultiTexCoord2sv +glMultiTexCoord3d +glMultiTexCoord3dv +glMultiTexCoord3f +glMultiTexCoord3fv +glMultiTexCoord3i +glMultiTexCoord3iv +glMultiTexCoord3s +glMultiTexCoord3sv +glMultiTexCoord4d +glMultiTexCoord4dv +glMultiTexCoord4f +glMultiTexCoord4fv +glMultiTexCoord4i +glMultiTexCoord4iv +glMultiTexCoord4s +glMultiTexCoord4sv +glMultiTexCoordP1ui +glMultiTexCoordP1uiv +glMultiTexCoordP2ui +glMultiTexCoordP2uiv +glMultiTexCoordP3ui +glMultiTexCoordP3uiv +glMultiTexCoordP4ui +glMultiTexCoordP4uiv +glNamedBufferData +glNamedBufferStorage +glNamedBufferSubData +glNamedFramebufferDrawBuffer +glNamedFramebufferDrawBuffers +glNamedFramebufferParameteri +glNamedFramebufferReadBuffer +glNamedFramebufferRenderbuffer +glNamedFramebufferTexture +glNamedFramebufferTextureLayer +glNamedRenderbufferStorage +glNamedRenderbufferStorageMultisample +glNewList +glNormal3b +glNormal3bv +glNormal3d +glNormal3dv +glNormal3f +glNormal3fv +glNormal3i +glNormal3iv +glNormal3s +glNormal3sv +glNormalP3ui +glNormalP3uiv +glNormalPointer +glObjectLabel +glObjectPtrLabel +glOrtho +glPassThrough +glPatchParameterfv +glPatchParameteri +glPauseTransformFeedback +glPixelMapfv +glPixelMapuiv +glPixelMapusv +glPixelStoref +glPixelStorei +glPixelTransferf +glPixelTransferi +glPixelZoom +glPointParameterf +glPointParameterfv +glPointParameteri +glPointParameteriv +glPointSize +glPolygonMode +glPolygonOffset +glPolygonStipple +glPopAttrib +glPopClientAttrib +glPopDebugGroup +glPopMatrix +glPopName +glPrimitiveRestartIndex +glPrioritizeTextures +glProgramBinary +glProgramParameteri +glProgramUniform1d +glProgramUniform1dv +glProgramUniform1f +glProgramUniform1fv +glProgramUniform1i +glProgramUniform1iv +glProgramUniform1ui +glProgramUniform1uiv +glProgramUniform2d +glProgramUniform2dv +glProgramUniform2f +glProgramUniform2fv +glProgramUniform2i +glProgramUniform2iv +glProgramUniform2ui +glProgramUniform2uiv +glProgramUniform3d +glProgramUniform3dv +glProgramUniform3f +glProgramUniform3fv +glProgramUniform3i +glProgramUniform3iv +glProgramUniform3ui +glProgramUniform3uiv +glProgramUniform4d +glProgramUniform4dv +glProgramUniform4f +glProgramUniform4fv +glProgramUniform4i +glProgramUniform4iv +glProgramUniform4ui +glProgramUniform4uiv +glProgramUniformMatrix2dv +glProgramUniformMatrix2fv +glProgramUniformMatrix2x3dv +glProgramUniformMatrix2x3fv +glProgramUniformMatrix2x4dv +glProgramUniformMatrix2x4fv +glProgramUniformMatrix3dv +glProgramUniformMatrix3fv +glProgramUniformMatrix3x2dv +glProgramUniformMatrix3x2fv +glProgramUniformMatrix3x4dv +glProgramUniformMatrix3x4fv +glProgramUniformMatrix4dv +glProgramUniformMatrix4fv +glProgramUniformMatrix4x2dv +glProgramUniformMatrix4x2fv +glProgramUniformMatrix4x3dv +glProgramUniformMatrix4x3fv +glProvokingVertex +glPushAttrib +glPushClientAttrib +glPushDebugGroup +glPushMatrix +glPushName +glQueryCounter +glRasterPos2d +glRasterPos2dv +glRasterPos2f +glRasterPos2fv +glRasterPos2i +glRasterPos2iv +glRasterPos2s +glRasterPos2sv +glRasterPos3d +glRasterPos3dv +glRasterPos3f +glRasterPos3fv +glRasterPos3i +glRasterPos3iv +glRasterPos3s +glRasterPos3sv +glRasterPos4d +glRasterPos4dv +glRasterPos4f +glRasterPos4fv +glRasterPos4i +glRasterPos4iv +glRasterPos4s +glRasterPos4sv +glReadBuffer +glReadPixels +glReadnPixels +glRectd +glRectdv +glRectf +glRectfv +glRecti +glRectiv +glRects +glRectsv +glReleaseShaderCompiler +glRenderMode +glRenderbufferStorage +glRenderbufferStorageMultisample +glResumeTransformFeedback +glRotated +glRotatef +glSampleCoverage +glSampleMaski +glSamplerParameterIiv +glSamplerParameterIuiv +glSamplerParameterf +glSamplerParameterfv +glSamplerParameteri +glSamplerParameteriv +glScaled +glScalef +glScissor +glScissorArrayv +glScissorIndexed +glScissorIndexedv +glSecondaryColor3b +glSecondaryColor3bv +glSecondaryColor3d +glSecondaryColor3dv +glSecondaryColor3f +glSecondaryColor3fv +glSecondaryColor3i +glSecondaryColor3iv +glSecondaryColor3s +glSecondaryColor3sv +glSecondaryColor3ub +glSecondaryColor3ubv +glSecondaryColor3ui +glSecondaryColor3uiv +glSecondaryColor3us +glSecondaryColor3usv +glSecondaryColorP3ui +glSecondaryColorP3uiv +glSecondaryColorPointer +glSelectBuffer +glShadeModel +glShaderBinary +glShaderSource +glShaderStorageBlockBinding +glStencilFunc +glStencilFuncSeparate +glStencilMask +glStencilMaskSeparate +glStencilOp +glStencilOpSeparate +glTexBuffer +glTexBufferRange +glTexCoord1d +glTexCoord1dv +glTexCoord1f +glTexCoord1fv +glTexCoord1i +glTexCoord1iv +glTexCoord1s +glTexCoord1sv +glTexCoord2d +glTexCoord2dv +glTexCoord2f +glTexCoord2fv +glTexCoord2i +glTexCoord2iv +glTexCoord2s +glTexCoord2sv +glTexCoord3d +glTexCoord3dv +glTexCoord3f +glTexCoord3fv +glTexCoord3i +glTexCoord3iv +glTexCoord3s +glTexCoord3sv +glTexCoord4d +glTexCoord4dv +glTexCoord4f +glTexCoord4fv +glTexCoord4i +glTexCoord4iv +glTexCoord4s +glTexCoord4sv +glTexCoordP1ui +glTexCoordP1uiv +glTexCoordP2ui +glTexCoordP2uiv +glTexCoordP3ui +glTexCoordP3uiv +glTexCoordP4ui +glTexCoordP4uiv +glTexCoordPointer +glTexEnvf +glTexEnvfv +glTexEnvi +glTexEnviv +glTexGend +glTexGendv +glTexGenf +glTexGenfv +glTexGeni +glTexGeniv +glTexImage1D +glTexImage2D +glTexImage2DMultisample +glTexImage3D +glTexImage3DMultisample +glTexParameterIiv +glTexParameterIuiv +glTexParameterf +glTexParameterfv +glTexParameteri +glTexParameteriv +glTexStorage1D +glTexStorage2D +glTexStorage2DMultisample +glTexStorage3D +glTexStorage3DMultisample +glTexSubImage1D +glTexSubImage2D +glTexSubImage3D +glTextureBarrier +glTextureBuffer +glTextureBufferRange +glTextureParameterIiv +glTextureParameterIuiv +glTextureParameterf +glTextureParameterfv +glTextureParameteri +glTextureParameteriv +glTextureStorage1D +glTextureStorage2D +glTextureStorage2DMultisample +glTextureStorage3D +glTextureStorage3DMultisample +glTextureSubImage1D +glTextureSubImage2D +glTextureSubImage3D +glTextureView +glTransformFeedbackBufferBase +glTransformFeedbackBufferRange +glTransformFeedbackVaryings +glTranslated +glTranslatef +glUniform1d +glUniform1dv +glUniform1f +glUniform1fv +glUniform1i +glUniform1iv +glUniform1ui +glUniform1uiv +glUniform2d +glUniform2dv +glUniform2f +glUniform2fv +glUniform2i +glUniform2iv +glUniform2ui +glUniform2uiv +glUniform3d +glUniform3dv +glUniform3f +glUniform3fv +glUniform3i +glUniform3iv +glUniform3ui +glUniform3uiv +glUniform4d +glUniform4dv +glUniform4f +glUniform4fv +glUniform4i +glUniform4iv +glUniform4ui +glUniform4uiv +glUniformBlockBinding +glUniformMatrix2dv +glUniformMatrix2fv +glUniformMatrix2x3dv +glUniformMatrix2x3fv +glUniformMatrix2x4dv +glUniformMatrix2x4fv +glUniformMatrix3dv +glUniformMatrix3fv +glUniformMatrix3x2dv +glUniformMatrix3x2fv +glUniformMatrix3x4dv +glUniformMatrix3x4fv +glUniformMatrix4dv +glUniformMatrix4fv +glUniformMatrix4x2dv +glUniformMatrix4x2fv +glUniformMatrix4x3dv +glUniformMatrix4x3fv +glUniformSubroutinesuiv +glUnmapBuffer +glUnmapNamedBuffer +glUseProgram +glUseProgramStages +glValidateProgram +glValidateProgramPipeline +glVertex2d +glVertex2dv +glVertex2f +glVertex2fv +glVertex2i +glVertex2iv +glVertex2s +glVertex2sv +glVertex3d +glVertex3dv +glVertex3f +glVertex3fv +glVertex3i +glVertex3iv +glVertex3s +glVertex3sv +glVertex4d +glVertex4dv +glVertex4f +glVertex4fv +glVertex4i +glVertex4iv +glVertex4s +glVertex4sv +glVertexArrayAttribBinding +glVertexArrayAttribFormat +glVertexArrayAttribIFormat +glVertexArrayAttribLFormat +glVertexArrayBindingDivisor +glVertexArrayElementBuffer +glVertexArrayVertexBuffer +glVertexArrayVertexBuffers +glVertexAttrib1d +glVertexAttrib1dv +glVertexAttrib1f +glVertexAttrib1fv +glVertexAttrib1s +glVertexAttrib1sv +glVertexAttrib2d +glVertexAttrib2dv +glVertexAttrib2f +glVertexAttrib2fv +glVertexAttrib2s +glVertexAttrib2sv +glVertexAttrib3d +glVertexAttrib3dv +glVertexAttrib3f +glVertexAttrib3fv +glVertexAttrib3s +glVertexAttrib3sv +glVertexAttrib4Nbv +glVertexAttrib4Niv +glVertexAttrib4Nsv +glVertexAttrib4Nub +glVertexAttrib4Nubv +glVertexAttrib4Nuiv +glVertexAttrib4Nusv +glVertexAttrib4bv +glVertexAttrib4d +glVertexAttrib4dv +glVertexAttrib4f +glVertexAttrib4fv +glVertexAttrib4iv +glVertexAttrib4s +glVertexAttrib4sv +glVertexAttrib4ubv +glVertexAttrib4uiv +glVertexAttrib4usv +glVertexAttribBinding +glVertexAttribDivisor +glVertexAttribFormat +glVertexAttribI1i +glVertexAttribI1iv +glVertexAttribI1ui +glVertexAttribI1uiv +glVertexAttribI2i +glVertexAttribI2iv +glVertexAttribI2ui +glVertexAttribI2uiv +glVertexAttribI3i +glVertexAttribI3iv +glVertexAttribI3ui +glVertexAttribI3uiv +glVertexAttribI4bv +glVertexAttribI4i +glVertexAttribI4iv +glVertexAttribI4sv +glVertexAttribI4ubv +glVertexAttribI4ui +glVertexAttribI4uiv +glVertexAttribI4usv +glVertexAttribIFormat +glVertexAttribIPointer +glVertexAttribL1d +glVertexAttribL1dv +glVertexAttribL2d +glVertexAttribL2dv +glVertexAttribL3d +glVertexAttribL3dv +glVertexAttribL4d +glVertexAttribL4dv +glVertexAttribLFormat +glVertexAttribLPointer +glVertexAttribP1ui +glVertexAttribP1uiv +glVertexAttribP2ui +glVertexAttribP2uiv +glVertexAttribP3ui +glVertexAttribP3uiv +glVertexAttribP4ui +glVertexAttribP4uiv +glVertexAttribPointer +glVertexBindingDivisor +glVertexP2ui +glVertexP2uiv +glVertexP3ui +glVertexP3uiv +glVertexP4ui +glVertexP4uiv +glVertexPointer +glViewport +glViewportArrayv +glViewportIndexedf +glViewportIndexedfv +glWaitSync +glWindowPos2d +glWindowPos2dv +glWindowPos2f +glWindowPos2fv +glWindowPos2i +glWindowPos2iv +glWindowPos2s +glWindowPos2sv +glWindowPos3d +glWindowPos3dv +glWindowPos3f +glWindowPos3fv +glWindowPos3i +glWindowPos3iv +glWindowPos3s +glWindowPos3sv diff --git a/subprojects/libcapsule/examples/shim/libOpenGL.so.0.symbols.updated-for b/subprojects/libcapsule/examples/shim/libOpenGL.so.0.symbols.updated-for new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/subprojects/libcapsule/examples/shim/libX11.so.6.excluded b/subprojects/libcapsule/examples/shim/libX11.so.6.excluded new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/subprojects/libcapsule/examples/shim/libX11.so.6.shared b/subprojects/libcapsule/examples/shim/libX11.so.6.shared new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/subprojects/libcapsule/examples/shim/libX11.so.6.symbols b/subprojects/libcapsule/examples/shim/libX11.so.6.symbols new file mode 100644 index 0000000000000000000000000000000000000000..22a1469f11f15d709ea706e2bf2f2c253bc73ac2 --- /dev/null +++ b/subprojects/libcapsule/examples/shim/libX11.so.6.symbols @@ -0,0 +1,1233 @@ +KeySymToUcs4 +XActivateScreenSaver +XAddConnectionWatch +XAddExtension +XAddHost +XAddHosts +XAddPixel +XAddToExtensionList +XAddToSaveSet +XAllPlanes +XAllocClassHint +XAllocColor +XAllocColorCells +XAllocColorPlanes +XAllocIconSize +XAllocNamedColor +XAllocSizeHints +XAllocStandardColormap +XAllocWMHints +XAllowEvents +XAutoRepeatOff +XAutoRepeatOn +XBaseFontNameListOfFontSet +XBell +XBitmapBitOrder +XBitmapPad +XBitmapUnit +XBlackPixel +XBlackPixelOfScreen +XCellsOfScreen +XChangeActivePointerGrab +XChangeGC +XChangeKeyboardControl +XChangeKeyboardMapping +XChangePointerControl +XChangeProperty +XChangeSaveSet +XChangeWindowAttributes +XCheckIfEvent +XCheckMaskEvent +XCheckTypedEvent +XCheckTypedWindowEvent +XCheckWindowEvent +XCirculateSubwindows +XCirculateSubwindowsDown +XCirculateSubwindowsUp +XClearArea +XClearWindow +XClipBox +XCloseDisplay +XCloseIM +XCloseOM +XConfigureWindow +XConnectionNumber +XContextDependentDrawing +XContextualDrawing +XConvertCase +XConvertSelection +XCopyArea +XCopyColormapAndFree +XCopyGC +XCopyPlane +XCreateBitmapFromData +XCreateColormap +XCreateFontCursor +XCreateFontSet +XCreateGC +XCreateGlyphCursor +XCreateIC +XCreateImage +XCreateOC +XCreatePixmap +XCreatePixmapCursor +XCreatePixmapFromBitmapData +XCreateRegion +XCreateSimpleWindow +XCreateWindow +XDefaultColormap +XDefaultColormapOfScreen +XDefaultDepth +XDefaultDepthOfScreen +XDefaultGC +XDefaultGCOfScreen +XDefaultRootWindow +XDefaultScreen +XDefaultScreenOfDisplay +XDefaultString +XDefaultVisual +XDefaultVisualOfScreen +XDefineCursor +XDeleteContext +XDeleteModifiermapEntry +XDeleteProperty +XDestroyIC +XDestroyImage +XDestroyOC +XDestroyRegion +XDestroySubwindows +XDestroyWindow +XDirectionalDependentDrawing +XDisableAccessControl +XDisplayCells +XDisplayHeight +XDisplayHeightMM +XDisplayKeycodes +XDisplayMotionBufferSize +XDisplayName +XDisplayOfIM +XDisplayOfOM +XDisplayOfScreen +XDisplayPlanes +XDisplayString +XDisplayWidth +XDisplayWidthMM +XDoesBackingStore +XDoesSaveUnders +XDrawArc +XDrawArcs +XDrawImageString +XDrawImageString16 +XDrawLine +XDrawLines +XDrawPoint +XDrawPoints +XDrawRectangle +XDrawRectangles +XDrawSegments +XDrawString +XDrawString16 +XDrawText +XDrawText16 +XEHeadOfExtensionList +XESetBeforeFlush +XESetCloseDisplay +XESetCopyEventCookie +XESetCopyGC +XESetCreateFont +XESetCreateGC +XESetError +XESetErrorString +XESetEventToWire +XESetFlushGC +XESetFreeFont +XESetFreeGC +XESetPrintErrorValues +XESetWireToError +XESetWireToEvent +XESetWireToEventCookie +XEmptyRegion +XEnableAccessControl +XEqualRegion +XEventMaskOfScreen +XEventsQueued +XExtendedMaxRequestSize +XExtentsOfFontSet +XFetchBuffer +XFetchBytes +XFetchName +XFillArc +XFillArcs +XFillPolygon +XFillRectangle +XFillRectangles +XFilterEvent +XFindContext +XFindOnExtensionList +XFlush +XFlushGC +XFontsOfFontSet +XForceScreenSaver +XFree +XFreeColormap +XFreeColors +XFreeCursor +XFreeEventData +XFreeExtensionList +XFreeFont +XFreeFontInfo +XFreeFontNames +XFreeFontPath +XFreeFontSet +XFreeGC +XFreeModifiermap +XFreePixmap +XFreeStringList +XGContextFromGC +XGeometry +XGetAtomName +XGetAtomNames +XGetClassHint +XGetCommand +XGetDefault +XGetErrorDatabaseText +XGetErrorText +XGetEventData +XGetFontPath +XGetFontProperty +XGetGCValues +XGetGeometry +XGetICValues +XGetIMValues +XGetIconName +XGetIconSizes +XGetImage +XGetInputFocus +XGetKeyboardControl +XGetKeyboardMapping +XGetModifierMapping +XGetMotionEvents +XGetNormalHints +XGetOCValues +XGetOMValues +XGetPixel +XGetPointerControl +XGetPointerMapping +XGetRGBColormaps +XGetScreenSaver +XGetSelectionOwner +XGetSizeHints +XGetStandardColormap +XGetSubImage +XGetTextProperty +XGetTransientForHint +XGetVisualInfo +XGetWMClientMachine +XGetWMColormapWindows +XGetWMHints +XGetWMIconName +XGetWMName +XGetWMNormalHints +XGetWMProtocols +XGetWMSizeHints +XGetWindowAttributes +XGetWindowProperty +XGetZoomHints +XGrabButton +XGrabKey +XGrabKeyboard +XGrabPointer +XGrabServer +XHeightMMOfScreen +XHeightOfScreen +XIMOfIC +XIconifyWindow +XIfEvent +XImageByteOrder +XInitExtension +XInitImage +XInitThreads +XInsertModifiermapEntry +XInstallColormap +XInternAtom +XInternAtoms +XInternalConnectionNumbers +XIntersectRegion +XKeycodeToKeysym +XKeysymToKeycode +XKeysymToString +XKillClient +XLastKnownRequestProcessed +XListDepths +XListExtensions +XListFonts +XListFontsWithInfo +XListHosts +XListInstalledColormaps +XListPixmapFormats +XListProperties +XLoadFont +XLoadQueryFont +XLocaleOfFontSet +XLocaleOfIM +XLocaleOfOM +XLockDisplay +XLookupColor +XLookupKeysym +XLookupString +XLowerWindow +XMapRaised +XMapSubwindows +XMapWindow +XMaskEvent +XMatchVisualInfo +XMaxCmapsOfScreen +XMaxRequestSize +XMinCmapsOfScreen +XMoveResizeWindow +XMoveWindow +XNewModifiermap +XNextEvent +XNextRequest +XNoOp +XOMOfOC +XOffsetRegion +XOpenDisplay +XOpenIM +XOpenOM +XParseColor +XParseGeometry +XPeekEvent +XPeekIfEvent +XPending +XPlanesOfScreen +XPointInRegion +XPolygonRegion +XProcessInternalConnection +XProtocolRevision +XProtocolVersion +XPutBackEvent +XPutImage +XPutPixel +XQLength +XQueryBestCursor +XQueryBestSize +XQueryBestStipple +XQueryBestTile +XQueryColor +XQueryColors +XQueryExtension +XQueryFont +XQueryKeymap +XQueryPointer +XQueryTextExtents +XQueryTextExtents16 +XQueryTree +XRaiseWindow +XReadBitmapFile +XReadBitmapFileData +XRebindKeysym +XRecolorCursor +XReconfigureWMWindow +XRectInRegion +XRefreshKeyboardMapping +XRegisterIMInstantiateCallback +XRemoveConnectionWatch +XRemoveFromSaveSet +XRemoveHost +XRemoveHosts +XReparentWindow +XResetScreenSaver +XResizeWindow +XResourceManagerString +XRestackWindows +XRootWindow +XRootWindowOfScreen +XRotateBuffers +XRotateWindowProperties +XSaveContext +XScreenCount +XScreenNumberOfScreen +XScreenOfDisplay +XScreenResourceString +XSelectInput +XSendEvent +XServerVendor +XSetAccessControl +XSetAfterFunction +XSetArcMode +XSetAuthorization +XSetBackground +XSetClassHint +XSetClipMask +XSetClipOrigin +XSetClipRectangles +XSetCloseDownMode +XSetCommand +XSetDashes +XSetErrorHandler +XSetFillRule +XSetFillStyle +XSetFont +XSetFontPath +XSetForeground +XSetFunction +XSetGraphicsExposures +XSetICFocus +XSetICValues +XSetIMValues +XSetIOErrorHandler +XSetIconName +XSetIconSizes +XSetInputFocus +XSetLineAttributes +XSetLocaleModifiers +XSetModifierMapping +XSetNormalHints +XSetOCValues +XSetOMValues +XSetPlaneMask +XSetPointerMapping +XSetRGBColormaps +XSetRegion +XSetScreenSaver +XSetSelectionOwner +XSetSizeHints +XSetStandardColormap +XSetStandardProperties +XSetState +XSetStipple +XSetSubwindowMode +XSetTSOrigin +XSetTextProperty +XSetTile +XSetTransientForHint +XSetWMClientMachine +XSetWMColormapWindows +XSetWMHints +XSetWMIconName +XSetWMName +XSetWMNormalHints +XSetWMProperties +XSetWMProtocols +XSetWMSizeHints +XSetWindowBackground +XSetWindowBackgroundPixmap +XSetWindowBorder +XSetWindowBorderPixmap +XSetWindowBorderWidth +XSetWindowColormap +XSetZoomHints +XShrinkRegion +XStoreBuffer +XStoreBytes +XStoreColor +XStoreColors +XStoreName +XStoreNamedColor +XStringListToTextProperty +XStringToKeysym +XSubImage +XSubtractRegion +XSupportsLocale +XSync +XSynchronize +XTextExtents +XTextExtents16 +XTextPropertyToStringList +XTextWidth +XTextWidth16 +XTranslateCoordinates +XUndefineCursor +XUngrabButton +XUngrabKey +XUngrabKeyboard +XUngrabPointer +XUngrabServer +XUninstallColormap +XUnionRectWithRegion +XUnionRegion +XUnloadFont +XUnlockDisplay +XUnmapSubwindows +XUnmapWindow +XUnregisterIMInstantiateCallback +XUnsetICFocus +XVaCreateNestedList +XVendorRelease +XVisualIDFromVisual +XWMGeometry +XWarpPointer +XWhitePixel +XWhitePixelOfScreen +XWidthMMOfScreen +XWidthOfScreen +XWindowEvent +XWithdrawWindow +XWriteBitmapFile +XXorRegion +XcmsAddColorSpace +XcmsAddFunctionSet +XcmsAllocColor +XcmsAllocNamedColor +XcmsCCCOfColormap +XcmsCIELabClipL +XcmsCIELabClipLab +XcmsCIELabClipab +XcmsCIELabColorSpace +XcmsCIELabQueryMaxC +XcmsCIELabQueryMaxL +XcmsCIELabQueryMaxLC +XcmsCIELabQueryMinL +XcmsCIELabToCIEXYZ +XcmsCIELabWhiteShiftColors +XcmsCIELuvClipL +XcmsCIELuvClipLuv +XcmsCIELuvClipuv +XcmsCIELuvColorSpace +XcmsCIELuvQueryMaxC +XcmsCIELuvQueryMaxL +XcmsCIELuvQueryMaxLC +XcmsCIELuvQueryMinL +XcmsCIELuvToCIEuvY +XcmsCIELuvWhiteShiftColors +XcmsCIEXYZColorSpace +XcmsCIEXYZToCIELab +XcmsCIEXYZToCIEuvY +XcmsCIEXYZToCIExyY +XcmsCIEXYZToRGBi +XcmsCIEuvYColorSpace +XcmsCIEuvYToCIELuv +XcmsCIEuvYToCIEXYZ +XcmsCIEuvYToTekHVC +XcmsCIExyYColorSpace +XcmsCIExyYToCIEXYZ +XcmsClientWhitePointOfCCC +XcmsConvertColors +XcmsCreateCCC +XcmsDefaultCCC +XcmsDisplayOfCCC +XcmsFormatOfPrefix +XcmsFreeCCC +XcmsLinearRGBFunctionSet +XcmsLookupColor +XcmsPrefixOfFormat +XcmsQueryBlack +XcmsQueryBlue +XcmsQueryColor +XcmsQueryColors +XcmsQueryGreen +XcmsQueryRed +XcmsQueryWhite +XcmsRGBColorSpace +XcmsRGBToRGBi +XcmsRGBiColorSpace +XcmsRGBiToCIEXYZ +XcmsRGBiToRGB +XcmsScreenNumberOfCCC +XcmsScreenWhitePointOfCCC +XcmsSetCCCOfColormap +XcmsSetCompressionProc +XcmsSetWhiteAdjustProc +XcmsSetWhitePoint +XcmsStoreColor +XcmsStoreColors +XcmsTekHVCClipC +XcmsTekHVCClipV +XcmsTekHVCClipVC +XcmsTekHVCColorSpace +XcmsTekHVCQueryMaxC +XcmsTekHVCQueryMaxV +XcmsTekHVCQueryMaxVC +XcmsTekHVCQueryMaxVSamples +XcmsTekHVCQueryMinV +XcmsTekHVCToCIEuvY +XcmsTekHVCWhiteShiftColors +XcmsUNDEFINEDColorSpace +XcmsVisualOfCCC +XkbAddDeviceLedInfo +XkbAddGeomColor +XkbAddGeomDoodad +XkbAddGeomKey +XkbAddGeomKeyAlias +XkbAddGeomOutline +XkbAddGeomOverlay +XkbAddGeomOverlayKey +XkbAddGeomOverlayRow +XkbAddGeomProperty +XkbAddGeomRow +XkbAddGeomSection +XkbAddGeomShape +XkbAddKeyType +XkbAllocClientMap +XkbAllocCompatMap +XkbAllocControls +XkbAllocDeviceInfo +XkbAllocGeomColors +XkbAllocGeomDoodads +XkbAllocGeomKeyAliases +XkbAllocGeomKeys +XkbAllocGeomOutlines +XkbAllocGeomOverlayKeys +XkbAllocGeomOverlayRows +XkbAllocGeomOverlays +XkbAllocGeomPoints +XkbAllocGeomProps +XkbAllocGeomRows +XkbAllocGeomSectionDoodads +XkbAllocGeomSections +XkbAllocGeomShapes +XkbAllocGeometry +XkbAllocIndicatorMaps +XkbAllocKeyboard +XkbAllocNames +XkbAllocServerMap +XkbApplyCompatMapToKey +XkbApplyVirtualModChanges +XkbBell +XkbBellEvent +XkbChangeDeviceInfo +XkbChangeEnabledControls +XkbChangeKeycodeRange +XkbChangeMap +XkbChangeNames +XkbChangeTypesOfKey +XkbComputeEffectiveMap +XkbComputeRowBounds +XkbComputeSectionBounds +XkbComputeShapeBounds +XkbComputeShapeTop +XkbCopyKeyType +XkbCopyKeyTypes +XkbDeviceBell +XkbDeviceBellEvent +XkbFindOverlayForKey +XkbForceBell +XkbForceDeviceBell +XkbFreeClientMap +XkbFreeCompatMap +XkbFreeComponentList +XkbFreeControls +XkbFreeDeviceInfo +XkbFreeGeomColors +XkbFreeGeomDoodads +XkbFreeGeomKeyAliases +XkbFreeGeomKeys +XkbFreeGeomOutlines +XkbFreeGeomOverlayKeys +XkbFreeGeomOverlayRows +XkbFreeGeomOverlays +XkbFreeGeomPoints +XkbFreeGeomProperties +XkbFreeGeomRows +XkbFreeGeomSections +XkbFreeGeomShapes +XkbFreeGeometry +XkbFreeIndicatorMaps +XkbFreeKeyboard +XkbFreeNames +XkbFreeServerMap +XkbGetAutoRepeatRate +XkbGetAutoResetControls +XkbGetCompatMap +XkbGetControls +XkbGetDetectableAutoRepeat +XkbGetDeviceButtonActions +XkbGetDeviceInfo +XkbGetDeviceInfoChanges +XkbGetDeviceLedInfo +XkbGetGeometry +XkbGetIndicatorMap +XkbGetIndicatorState +XkbGetKeyActions +XkbGetKeyBehaviors +XkbGetKeyExplicitComponents +XkbGetKeyModifierMap +XkbGetKeySyms +XkbGetKeyTypes +XkbGetKeyVirtualModMap +XkbGetKeyboard +XkbGetKeyboardByName +XkbGetMap +XkbGetMapChanges +XkbGetNamedDeviceIndicator +XkbGetNamedGeometry +XkbGetNamedIndicator +XkbGetNames +XkbGetPerClientControls +XkbGetState +XkbGetUpdatedMap +XkbGetVirtualMods +XkbGetXlibControls +XkbIgnoreExtension +XkbInitCanonicalKeyTypes +XkbKeyTypesForCoreSymbols +XkbKeycodeToKeysym +XkbKeysymToModifiers +XkbLatchGroup +XkbLatchModifiers +XkbLibraryVersion +XkbListComponents +XkbLockGroup +XkbLockModifiers +XkbLookupKeyBinding +XkbLookupKeySym +XkbNoteControlsChanges +XkbNoteDeviceChanges +XkbNoteMapChanges +XkbNoteNameChanges +XkbOpenDisplay +XkbQueryExtension +XkbRefreshKeyboardMapping +XkbResizeDeviceButtonActions +XkbResizeKeyActions +XkbResizeKeySyms +XkbResizeKeyType +XkbSelectEventDetails +XkbSelectEvents +XkbSetAtomFuncs +XkbSetAutoRepeatRate +XkbSetAutoResetControls +XkbSetCompatMap +XkbSetControls +XkbSetDebuggingFlags +XkbSetDetectableAutoRepeat +XkbSetDeviceButtonActions +XkbSetDeviceInfo +XkbSetDeviceLedInfo +XkbSetGeometry +XkbSetIgnoreLockMods +XkbSetIndicatorMap +XkbSetMap +XkbSetNamedDeviceIndicator +XkbSetNamedIndicator +XkbSetNames +XkbSetPerClientControls +XkbSetServerInternalMods +XkbSetXlibControls +XkbToControl +XkbTranslateKey +XkbTranslateKeyCode +XkbTranslateKeySym +XkbUpdateActionVirtualMods +XkbUpdateKeyTypeVirtualMods +XkbUpdateMapFromCore +XkbUseExtension +XkbVirtualModsToReal +XkbXlibControlsImplemented +XmbDrawImageString +XmbDrawString +XmbDrawText +XmbLookupString +XmbResetIC +XmbSetWMProperties +XmbTextEscapement +XmbTextExtents +XmbTextListToTextProperty +XmbTextPerCharExtents +XmbTextPropertyToTextList +Xpermalloc +XrmCombineDatabase +XrmCombineFileDatabase +XrmDestroyDatabase +XrmEnumerateDatabase +XrmGetDatabase +XrmGetFileDatabase +XrmGetResource +XrmGetStringDatabase +XrmInitialize +XrmLocaleOfDatabase +XrmMergeDatabases +XrmParseCommand +XrmPermStringToQuark +XrmPutFileDatabase +XrmPutLineResource +XrmPutResource +XrmPutStringResource +XrmQGetResource +XrmQGetSearchList +XrmQGetSearchResource +XrmQPutResource +XrmQPutStringResource +XrmQuarkToString +XrmSetDatabase +XrmStringToBindingQuarkList +XrmStringToQuark +XrmStringToQuarkList +XrmUniqueQuark +Xutf8DrawImageString +Xutf8DrawString +Xutf8DrawText +Xutf8LookupString +Xutf8ResetIC +Xutf8SetWMProperties +Xutf8TextEscapement +Xutf8TextExtents +Xutf8TextListToTextProperty +Xutf8TextPerCharExtents +Xutf8TextPropertyToTextList +XwcDrawImageString +XwcDrawString +XwcDrawText +XwcFreeStringList +XwcLookupString +XwcResetIC +XwcTextEscapement +XwcTextExtents +XwcTextListToTextProperty +XwcTextPerCharExtents +XwcTextPropertyToTextList +_Utf8GetConvByName +_XAllocID +_XAllocIDs +_XAllocScratch +_XAllocTemp +_XAsyncErrorHandler +_XCloseLC +_XColor_to_XcmsRGB +_XConnectXCB +_XCopyEventCookie +_XCopyToArg +_XCreateMutex_fn +_XData32 +_XDefaultError +_XDefaultIOError +_XDefaultOpenIM +_XDefaultOpenOM +_XDefaultWireError +_XDeq +_XDeqAsyncHandler +_XEatData +_XEatDataWords +_XEnq +_XError +_XErrorFunction +_XEventToWire +_XEventsQueued +_XF86BigfontFreeFontMetrics +_XF86LoadQueryLocaleFont +_XFetchEventCookie +_XFlush +_XFlushGCCache +_XFreeAtomTable +_XFreeDisplayLock_fn +_XFreeDisplayStructure +_XFreeEventCookies +_XFreeExtData +_XFreeMutex_fn +_XFreeTemp +_XFreeX11XCBStructure +_XGetAsyncData +_XGetAsyncReply +_XGetBitsPerPixel +_XGetHostname +_XGetLCValues +_XGetRequest +_XGetScanlinePad +_XGetWindowAttributes +_XHeadOfDisplayList +_XIMCompileResourceList +_XIOError +_XIOErrorFunction +_XInitDisplayLock_fn +_XInitIM +_XInitImageFuncPtrs +_XInitKeysymDB +_XInitOM +_XIsEventCookie +_XKeyInitialize +_XKeycodeToKeysym +_XKeysymToKeycode +_XKeysymToModifiers +_XLockMutex_fn +_XLookupKeysym +_XLookupString +_XNoticeCreateBitmap +_XNoticePutBitmap +_XOpenLC +_XParseBaseFontNameList +_XPollfdCacheAdd +_XPollfdCacheDel +_XPollfdCacheInit +_XProcessInternalConnection +_XProcessWindowAttributes +_XPutBackEvent +_XRead +_XRead32 +_XReadEvents +_XReadPad +_XRefreshKeyboardMapping +_XRegisterFilterByMask +_XRegisterFilterByType +_XRegisterInternalConnection +_XReply +_XReverse_Bytes +_XScreenOfWindow +_XSend +_XSetClipRectangles +_XSetImage +_XSetLastRequestRead +_XStoreEventCookie +_XTextHeight +_XTextHeight16 +_XTranslateKey +_XTranslateKeySym +_XTryShapeBitmapCursor +_XUnknownCopyEventCookie +_XUnknownNativeEvent +_XUnknownWireEvent +_XUnknownWireEventCookie +_XUnlockMutex_fn +_XUnregisterFilter +_XUnregisterInternalConnection +_XUnresolveColor +_XUpdateAtomCache +_XUpdateGCCache +_XVIDtoVisual +_XWireToEvent +_XcmsAddCmapRec +_XcmsArcTangent +_XcmsCIELabQueryMaxLCRGB +_XcmsCIELab_prefix +_XcmsCIELuvQueryMaxLCRGB +_XcmsCIELuv_prefix +_XcmsCIEXYZ_ValidSpec +_XcmsCIEXYZ_prefix +_XcmsCIEuvY_ValidSpec +_XcmsCIEuvY_prefix +_XcmsCIExyY_prefix +_XcmsConvertColorsWithWhitePt +_XcmsCopyCmapRecAndFree +_XcmsCopyISOLatin1Lowered +_XcmsCopyPointerArray +_XcmsCosine +_XcmsCubeRoot +_XcmsDDColorSpaces +_XcmsDDColorSpacesInit +_XcmsDDConvertColors +_XcmsDIColorSpaces +_XcmsDIColorSpacesInit +_XcmsDIConvertColors +_XcmsDeleteCmapRec +_XcmsEqualWhitePts +_XcmsFreeIntensityMaps +_XcmsFreePointerArray +_XcmsGetElement +_XcmsGetIntensityMap +_XcmsGetProperty +_XcmsInitDefaultCCCs +_XcmsInitScrnInfo +_XcmsLRGB_InitScrnDefault +_XcmsPushPointerArray +_XcmsRGB_prefix +_XcmsRGB_to_XColor +_XcmsRGBi_prefix +_XcmsRegColorSpaces +_XcmsRegFormatOfPrefix +_XcmsResolveColor +_XcmsResolveColorString +_XcmsSCCFuncSets +_XcmsSCCFuncSetsInit +_XcmsSetGetColor +_XcmsSetGetColors +_XcmsSine +_XcmsSquareRoot +_XcmsTekHVCQueryMaxVCRGB +_XcmsTekHVC_CheckModify +_XcmsTekHVC_prefix +_XcmsUnresolveColor +_Xdebug +_Xevent_to_mask +_Xglobal_lock +_Xi18n_lock +_XimCbDispatch +_XimCheckCreateICValues +_XimCheckDataSize +_XimCheckICMode +_XimCheckIMMode +_XimCheckIfLocalProcessing +_XimCheckIfThaiProcessing +_XimCheckLocalInputStyle +_XimCommitCallback +_XimConnect +_XimDecodeICATTRIBUTE +_XimDecodeIMATTRIBUTE +_XimDecodeLocalICAttr +_XimDecodeLocalIMAttr +_XimDestroyIMStructureList +_XimDispatchInit +_XimEncodeICATTRIBUTE +_XimEncodeIMATTRIBUTE +_XimEncodeLocalICAttr +_XimEncodeLocalIMAttr +_XimError +_XimErrorCallback +_XimExtension +_XimFilterWaitEvent +_XimFlush +_XimForwardEvent +_XimForwardEventCallback +_XimFreeCommitInfo +_XimFreeProtoIntrCallback +_XimFreeTransIntrCallback +_XimGetAttributeID +_XimGetCharCode +_XimGetCurrentICValues +_XimGetCurrentIMValues +_XimGetICValueData +_XimGetIMValueData +_XimGetLocaleCode +_XimGetMyEndian +_XimGetResourceListRec +_XimGetResourceListRecByQuark +_XimGetWindowEventmask +_XimICOfXICID +_XimImSportRec +_XimInitialResourceInfo +_XimLcctstombs +_XimLcctstoutf8 +_XimLcctstowcs +_XimLocalCreateIC +_XimLocalFilter +_XimLocalGetICValues +_XimLocalGetIMValues +_XimLocalIMFree +_XimLocalMbLookupString +_XimLocalOpenIM +_XimLocalSetICValues +_XimLocalSetIMValues +_XimLocalUtf8LookupString +_XimLocalWcLookupString +_XimLookupMBText +_XimLookupUTF8Text +_XimLookupWCText +_XimMakeICAttrIDList +_XimMakeIMAttrIDList +_XimOpenIM +_XimParseStringFile +_XimProcError +_XimProcSyncReply +_XimProtoCreateIC +_XimProtoEventToWire +_XimProtoIMFree +_XimProtoMbLookupString +_XimProtoOpenIM +_XimProtoUtf8LookupString +_XimProtoWcLookupString +_XimProtoWireToEvent +_XimRead +_XimRegProtoIntrCallback +_XimRegisterDispatcher +_XimRegisterFilter +_XimRegisterIMInstantiateCallback +_XimRegisterServerFilter +_XimRegisterTriggerKeysCallback +_XimReregisterFilter +_XimResetIMInstantiateCallback +_XimRespSyncReply +_XimServerDestroy +_XimSetCurrentICValues +_XimSetCurrentIMValues +_XimSetEventMaskCallback +_XimSetHeader +_XimSetICDefaults +_XimSetICMode +_XimSetICResourceList +_XimSetICValueData +_XimSetIMMode +_XimSetIMResourceList +_XimSetIMValueData +_XimSetInnerICResourceList +_XimSetInnerIMResourceList +_XimSetLocalIMDefaults +_XimShutdown +_XimSync +_XimSyncCallback +_XimThaiCloseIM +_XimThaiCreateIC +_XimThaiFilter +_XimThaiIMFree +_XimThaiOpenIM +_XimTransCallDispatcher +_XimTransConf +_XimTransFilterWaitEvent +_XimTransFlush +_XimTransInternalConnection +_XimTransRead +_XimTransRegisterDispatcher +_XimTransWrite +_XimTransportRec +_XimTriggerNotify +_XimUnRegisterIMInstantiateCallback +_XimUnregisterFilter +_XimUnregisterServerFilter +_XimWrite +_XimXConf +_XimXTransBytesReadable +_XimXTransClose +_XimXTransCloseForCloning +_XimXTransConnect +_XimXTransDisconnect +_XimXTransFreeConnInfo +_XimXTransGetConnectionNumber +_XimXTransGetHostname +_XimXTransGetMyAddr +_XimXTransGetPeerAddr +_XimXTransIsLocal +_XimXTransOpenCLTSClient +_XimXTransOpenCOTSClient +_XimXTransRead +_XimXTransReadv +_XimXTransSetOption +_XimXTransSocketINET6Funcs +_XimXTransSocketINETFuncs +_XimXTransSocketLocalFuncs +_XimXTransSocketTCPFuncs +_XimXTransSocketUNIXFuncs +_XimXTransWrite +_XimXTransWritev +_Ximctstombs +_Ximctstoutf8 +_Ximctstowcs +_XkbCopyFromReadBuffer +_XkbFreeReadBuffer +_XkbGetAtomNameFunc +_XkbGetCharset +_XkbGetConverters +_XkbGetReadBufferCountedString +_XkbGetReadBufferPtr +_XkbInitReadBuffer +_XkbInternAtomFunc +_XkbNoteCoreMapChanges +_XkbPeekAtReadBuffer +_XkbReadBufferCopy32 +_XkbReadBufferCopyKeySyms +_XkbReadCopyData32 +_XkbReadCopyKeySyms +_XkbReadGetCompatMapReply +_XkbReadGetGeometryReply +_XkbReadGetIndicatorMapReply +_XkbReadGetMapReply +_XkbReadGetNamesReply +_XkbReloadDpy +_XkbSkipReadBufferData +_XkbWriteCopyData32 +_XkbWriteCopyKeySyms +_XkeyTable +_XlcAddCT +_XlcAddCharSet +_XlcAddGB18030LocaleConverters +_XlcAddLoader +_XlcAddUtf8Converters +_XlcAddUtf8LocaleConverters +_XlcCloseConverter +_XlcCompareISOLatin1 +_XlcCompileResourceList +_XlcConvert +_XlcCopyFromArg +_XlcCopyToArg +_XlcCountVaList +_XlcCreateDefaultCharSet +_XlcCreateLC +_XlcCreateLocaleDataBase +_XlcCurrentLC +_XlcDbg_printValue +_XlcDeInitLoader +_XlcDefaultLoader +_XlcDefaultMapModifiers +_XlcDestroyLC +_XlcDestroyLocaleDataBase +_XlcFileName +_XlcGenericLoader +_XlcGenericMethods +_XlcGetCSValues +_XlcGetCharSet +_XlcGetCharSetWithSide +_XlcGetLocaleDataBase +_XlcGetResource +_XlcGetValues +_XlcInitCTInfo +_XlcInitLoader +_XlcLocaleDirName +_XlcLocaleLibDirName +_XlcMapOSLocaleName +_XlcNCompareISOLatin1 +_XlcOpenConverter +_XlcParseCharSet +_XlcParse_scopemaps +_XlcPublicMethods +_XlcRemoveLoader +_XlcResetConverter +_XlcResolveI18NPath +_XlcResolveLocaleName +_XlcSetConverter +_XlcSetValues +_XlcUtf8Loader +_XlcVaToArgList +_XlcValidModSyntax +_Xlcmbstoutf8 +_Xlcmbstowcs +_Xlcmbtowc +_Xlcwcstombs +_Xlcwctomb +_XmbDefaultDrawImageString +_XmbDefaultDrawString +_XmbDefaultTextEscapement +_XmbDefaultTextExtents +_XmbDefaultTextPerCharExtents +_XmbGenericDrawImageString +_XmbGenericDrawString +_XmbGenericTextEscapement +_XmbGenericTextExtents +_XmbGenericTextPerCharExtents +_XmbTextListToTextProperty +_XmbTextPropertyToTextList +_Xmblen +_Xmbstoutf8 +_Xmbstowcs +_Xmbtowc +_XomConvert +_XomGenericDrawString +_XomGenericOpenOM +_XomGenericTextExtents +_XomGetFontDataFromFontSet +_XomInitConverter +_XrmDefaultInitParseInfo +_XrmInitParseInfo +_XrmInternalStringToQuark +_Xthread_self_fn +_Xutf8DefaultDrawImageString +_Xutf8DefaultDrawString +_Xutf8DefaultTextEscapement +_Xutf8DefaultTextExtents +_Xutf8DefaultTextPerCharExtents +_Xutf8GenericDrawImageString +_Xutf8GenericDrawString +_Xutf8GenericTextEscapement +_Xutf8GenericTextExtents +_Xutf8GenericTextPerCharExtents +_Xutf8TextListToTextProperty +_Xutf8TextPropertyToTextList +_XwcDefaultDrawImageString +_XwcDefaultDrawString +_XwcDefaultTextEscapement +_XwcDefaultTextExtents +_XwcDefaultTextPerCharExtents +_XwcFreeStringList +_XwcGenericDrawImageString +_XwcGenericDrawString +_XwcGenericTextEscapement +_XwcGenericTextExtents +_XwcGenericTextPerCharExtents +_XwcTextListToTextProperty +_XwcTextPropertyToTextList +_Xwcscmp +_Xwcscpy +_Xwcslen +_Xwcsncmp +_Xwcsncpy +_Xwcstombs +_Xwctomb +_qfree +read_EncodingInfo +xlocaledir diff --git a/subprojects/libcapsule/examples/shim/libX11.so.6.symbols.updated-for b/subprojects/libcapsule/examples/shim/libX11.so.6.symbols.updated-for new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/subprojects/libcapsule/examples/shim/libXext.so.6.excluded b/subprojects/libcapsule/examples/shim/libXext.so.6.excluded new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/subprojects/libcapsule/examples/shim/libXext.so.6.shared b/subprojects/libcapsule/examples/shim/libXext.so.6.shared new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/subprojects/libcapsule/examples/shim/libXext.so.6.symbols b/subprojects/libcapsule/examples/shim/libXext.so.6.symbols new file mode 100644 index 0000000000000000000000000000000000000000..985808d7fdfb44adf13fbfee1de5d23e6f8e13cd --- /dev/null +++ b/subprojects/libcapsule/examples/shim/libXext.so.6.symbols @@ -0,0 +1,135 @@ +DPMSCapable +DPMSDisable +DPMSEnable +DPMSForceLevel +DPMSGetTimeouts +DPMSGetVersion +DPMSInfo +DPMSQueryExtension +DPMSSetTimeouts +XGEQueryExtension +XGEQueryVersion +XLbxGetEventBase +XLbxQueryExtension +XLbxQueryVersion +XMITMiscGetBugMode +XMITMiscQueryExtension +XMITMiscSetBugMode +XMissingExtension +XSecurityAllocXauth +XSecurityFreeXauth +XSecurityGenerateAuthorization +XSecurityQueryExtension +XSecurityRevokeAuthorization +XSetExtensionErrorHandler +XShapeCombineMask +XShapeCombineRectangles +XShapeCombineRegion +XShapeCombineShape +XShapeGetRectangles +XShapeInputSelected +XShapeOffsetShape +XShapeQueryExtension +XShapeQueryExtents +XShapeQueryVersion +XShapeSelectInput +XShmAttach +XShmCreateImage +XShmCreatePixmap +XShmDetach +XShmGetEventBase +XShmGetImage +XShmPixmapFormat +XShmPutImage +XShmQueryExtension +XShmQueryVersion +XSyncAwait +XSyncAwaitFence +XSyncChangeAlarm +XSyncChangeCounter +XSyncCreateAlarm +XSyncCreateCounter +XSyncCreateFence +XSyncDestroyAlarm +XSyncDestroyCounter +XSyncDestroyFence +XSyncFreeSystemCounterList +XSyncGetPriority +XSyncInitialize +XSyncIntToValue +XSyncIntsToValue +XSyncListSystemCounters +XSyncMaxValue +XSyncMinValue +XSyncQueryAlarm +XSyncQueryCounter +XSyncQueryExtension +XSyncQueryFence +XSyncResetFence +XSyncSetCounter +XSyncSetPriority +XSyncTriggerFence +XSyncValueAdd +XSyncValueEqual +XSyncValueGreaterOrEqual +XSyncValueGreaterThan +XSyncValueHigh32 +XSyncValueIsNegative +XSyncValueIsPositive +XSyncValueIsZero +XSyncValueLessOrEqual +XSyncValueLessThan +XSyncValueLow32 +XSyncValueSubtract +XTestFakeAckType +XTestFakeInput +XTestFlush +XTestGetInput +XTestInputActionType +XTestMovePointer +XTestPressButton +XTestPressKey +XTestQueryInputSize +XTestReset +XTestStopInput +XagCreateAssociation +XagCreateEmbeddedApplicationGroup +XagCreateNonembeddedApplicationGroup +XagDestroyApplicationGroup +XagDestroyAssociation +XagGetApplicationGroupAttributes +XagQueryApplicationGroup +XagQueryVersion +XcupGetReservedColormapEntries +XcupQueryVersion +XcupStoreColors +XdbeAllocateBackBufferName +XdbeBeginIdiom +XdbeDeallocateBackBufferName +XdbeEndIdiom +XdbeFreeVisualInfo +XdbeGetBackBufferAttributes +XdbeGetVisualInfo +XdbeQueryExtension +XdbeSwapBuffers +XeviGetVisualInfo +XeviQueryExtension +XeviQueryVersion +XextAddDisplay +XextCreateExtension +XextDestroyExtension +XextFindDisplay +XextRemoveDisplay +XmbufChangeBufferAttributes +XmbufChangeWindowAttributes +XmbufClearBufferArea +XmbufCreateBuffers +XmbufCreateStereoWindow +XmbufDestroyBuffers +XmbufDisplayBuffers +XmbufGetBufferAttributes +XmbufGetScreenInfo +XmbufGetVersion +XmbufGetWindowAttributes +XmbufQueryExtension +_XExtensionErrorFunction diff --git a/subprojects/libcapsule/examples/shim/libXext.so.6.symbols.updated-for b/subprojects/libcapsule/examples/shim/libXext.so.6.symbols.updated-for new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/subprojects/libcapsule/examples/shim/libXi.so.6.excluded b/subprojects/libcapsule/examples/shim/libXi.so.6.excluded new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/subprojects/libcapsule/examples/shim/libXi.so.6.shared b/subprojects/libcapsule/examples/shim/libXi.so.6.shared new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/subprojects/libcapsule/examples/shim/libXi.so.6.symbols b/subprojects/libcapsule/examples/shim/libXi.so.6.symbols new file mode 100644 index 0000000000000000000000000000000000000000..0edf1efe273d08e7bed12456656b6038655a0755 --- /dev/null +++ b/subprojects/libcapsule/examples/shim/libXi.so.6.symbols @@ -0,0 +1,84 @@ +XAllowDeviceEvents +XChangeDeviceControl +XChangeDeviceDontPropagateList +XChangeDeviceKeyMapping +XChangeDeviceProperty +XChangeFeedbackControl +XChangeKeyboardDevice +XChangePointerDevice +XCloseDevice +XDeleteDeviceProperty +XDeviceBell +XFreeDeviceControl +XFreeDeviceList +XFreeDeviceMotionEvents +XFreeDeviceState +XFreeFeedbackList +XGetDeviceButtonMapping +XGetDeviceControl +XGetDeviceDontPropagateList +XGetDeviceFocus +XGetDeviceKeyMapping +XGetDeviceModifierMapping +XGetDeviceMotionEvents +XGetDeviceProperty +XGetExtensionVersion +XGetFeedbackControl +XGetSelectedExtensionEvents +XGrabDevice +XGrabDeviceButton +XGrabDeviceKey +XIAllowEvents +XIAllowTouchEvents +XIBarrierReleasePointer +XIBarrierReleasePointers +XIChangeHierarchy +XIChangeProperty +XIDefineCursor +XIDeleteProperty +XIFreeDeviceInfo +XIGetClientPointer +XIGetFocus +XIGetProperty +XIGetSelectedEvents +XIGrabButton +XIGrabDevice +XIGrabEnter +XIGrabFocusIn +XIGrabKeycode +XIGrabTouchBegin +XIListProperties +XIQueryDevice +XIQueryPointer +XIQueryVersion +XISelectEvents +XISetClientPointer +XISetFocus +XIUndefineCursor +XIUngrabButton +XIUngrabDevice +XIUngrabEnter +XIUngrabFocusIn +XIUngrabKeycode +XIUngrabTouchBegin +XIWarpPointer +XListDeviceProperties +XListInputDevices +XOpenDevice +XQueryDeviceState +XSelectExtensionEvent +XSendExtensionEvent +XSetDeviceButtonMapping +XSetDeviceFocus +XSetDeviceMode +XSetDeviceModifierMapping +XSetDeviceValuators +XUngrabDevice +XUngrabDeviceButton +XUngrabDeviceKey +_XiGetDevicePresenceNotifyEvent +_xibadclass +_xibaddevice +_xibadevent +_xibadmode +_xidevicebusy diff --git a/subprojects/libcapsule/examples/shim/libXi.so.6.symbols.updated-for b/subprojects/libcapsule/examples/shim/libXi.so.6.symbols.updated-for new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/subprojects/libcapsule/examples/shim/libgbm.so.1.excluded b/subprojects/libcapsule/examples/shim/libgbm.so.1.excluded new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/subprojects/libcapsule/examples/shim/libgbm.so.1.shared b/subprojects/libcapsule/examples/shim/libgbm.so.1.shared new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/subprojects/libcapsule/examples/shim/libgbm.so.1.symbols b/subprojects/libcapsule/examples/shim/libgbm.so.1.symbols new file mode 100644 index 0000000000000000000000000000000000000000..2925c84e89bd38546b1271daad672bf1e40c6828 --- /dev/null +++ b/subprojects/libcapsule/examples/shim/libgbm.so.1.symbols @@ -0,0 +1,33 @@ +gbm_bo_create +gbm_bo_create_with_modifiers +gbm_bo_destroy +gbm_bo_get_device +gbm_bo_get_fd +gbm_bo_get_format +gbm_bo_get_handle +gbm_bo_get_handle_for_plane +gbm_bo_get_height +gbm_bo_get_modifier +gbm_bo_get_offset +gbm_bo_get_plane_count +gbm_bo_get_stride +gbm_bo_get_stride_for_plane +gbm_bo_get_user_data +gbm_bo_get_width +gbm_bo_import +gbm_bo_map +gbm_bo_set_user_data +gbm_bo_unmap +gbm_bo_write +gbm_create_device +gbm_device_destroy +gbm_device_get_backend_name +gbm_device_get_fd +gbm_device_is_format_supported +gbm_surface_create +gbm_surface_create_with_modifiers +gbm_surface_destroy +gbm_surface_has_free_buffers +gbm_surface_lock_front_buffer +gbm_surface_release_buffer +wl_drm_interface diff --git a/subprojects/libcapsule/examples/shim/libgbm.so.1.symbols.updated-for b/subprojects/libcapsule/examples/shim/libgbm.so.1.symbols.updated-for new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/subprojects/libcapsule/examples/shim/libxcb-dri2.so.0.excluded b/subprojects/libcapsule/examples/shim/libxcb-dri2.so.0.excluded new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/subprojects/libcapsule/examples/shim/libxcb-dri2.so.0.shared b/subprojects/libcapsule/examples/shim/libxcb-dri2.so.0.shared new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/subprojects/libcapsule/examples/shim/libxcb-dri2.so.0.symbols b/subprojects/libcapsule/examples/shim/libxcb-dri2.so.0.symbols new file mode 100644 index 0000000000000000000000000000000000000000..7cdbd25a0ba61126ca170318136decc795eb80d6 --- /dev/null +++ b/subprojects/libcapsule/examples/shim/libxcb-dri2.so.0.symbols @@ -0,0 +1,62 @@ +xcb_dri2_attach_format_end +xcb_dri2_attach_format_next +xcb_dri2_authenticate +xcb_dri2_authenticate_reply +xcb_dri2_authenticate_unchecked +xcb_dri2_connect +xcb_dri2_connect_alignment_pad +xcb_dri2_connect_alignment_pad_end +xcb_dri2_connect_alignment_pad_length +xcb_dri2_connect_device_name +xcb_dri2_connect_device_name_end +xcb_dri2_connect_device_name_length +xcb_dri2_connect_driver_name +xcb_dri2_connect_driver_name_end +xcb_dri2_connect_driver_name_length +xcb_dri2_connect_reply +xcb_dri2_connect_sizeof +xcb_dri2_connect_unchecked +xcb_dri2_copy_region +xcb_dri2_copy_region_reply +xcb_dri2_copy_region_unchecked +xcb_dri2_create_drawable +xcb_dri2_create_drawable_checked +xcb_dri2_destroy_drawable +xcb_dri2_destroy_drawable_checked +xcb_dri2_dri2_buffer_end +xcb_dri2_dri2_buffer_next +xcb_dri2_get_buffers +xcb_dri2_get_buffers_buffers +xcb_dri2_get_buffers_buffers_iterator +xcb_dri2_get_buffers_buffers_length +xcb_dri2_get_buffers_reply +xcb_dri2_get_buffers_sizeof +xcb_dri2_get_buffers_unchecked +xcb_dri2_get_buffers_with_format +xcb_dri2_get_buffers_with_format_buffers +xcb_dri2_get_buffers_with_format_buffers_iterator +xcb_dri2_get_buffers_with_format_buffers_length +xcb_dri2_get_buffers_with_format_reply +xcb_dri2_get_buffers_with_format_sizeof +xcb_dri2_get_buffers_with_format_unchecked +xcb_dri2_get_msc +xcb_dri2_get_msc_reply +xcb_dri2_get_msc_unchecked +xcb_dri2_get_param +xcb_dri2_get_param_reply +xcb_dri2_get_param_unchecked +xcb_dri2_id +xcb_dri2_query_version +xcb_dri2_query_version_reply +xcb_dri2_query_version_unchecked +xcb_dri2_swap_buffers +xcb_dri2_swap_buffers_reply +xcb_dri2_swap_buffers_unchecked +xcb_dri2_swap_interval +xcb_dri2_swap_interval_checked +xcb_dri2_wait_msc +xcb_dri2_wait_msc_reply +xcb_dri2_wait_msc_unchecked +xcb_dri2_wait_sbc +xcb_dri2_wait_sbc_reply +xcb_dri2_wait_sbc_unchecked diff --git a/subprojects/libcapsule/examples/shim/libxcb-dri2.so.0.symbols.updated-for b/subprojects/libcapsule/examples/shim/libxcb-dri2.so.0.symbols.updated-for new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/subprojects/libcapsule/examples/shim/libxcb-glx.so.0.excluded b/subprojects/libcapsule/examples/shim/libxcb-glx.so.0.excluded new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/subprojects/libcapsule/examples/shim/libxcb-glx.so.0.shared b/subprojects/libcapsule/examples/shim/libxcb-glx.so.0.shared new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/subprojects/libcapsule/examples/shim/libxcb-glx.so.0.symbols b/subprojects/libcapsule/examples/shim/libxcb-glx.so.0.symbols new file mode 100644 index 0000000000000000000000000000000000000000..25d257feda23a51a31275561e2a5f728d8fea6f5 --- /dev/null +++ b/subprojects/libcapsule/examples/shim/libxcb-glx.so.0.symbols @@ -0,0 +1,520 @@ +xcb_glx_are_textures_resident +xcb_glx_are_textures_resident_data +xcb_glx_are_textures_resident_data_end +xcb_glx_are_textures_resident_data_length +xcb_glx_are_textures_resident_reply +xcb_glx_are_textures_resident_sizeof +xcb_glx_are_textures_resident_unchecked +xcb_glx_bool32_end +xcb_glx_bool32_next +xcb_glx_change_drawable_attributes +xcb_glx_change_drawable_attributes_checked +xcb_glx_change_drawable_attributes_sizeof +xcb_glx_client_info +xcb_glx_client_info_checked +xcb_glx_client_info_sizeof +xcb_glx_context_end +xcb_glx_context_next +xcb_glx_context_tag_end +xcb_glx_context_tag_next +xcb_glx_copy_context +xcb_glx_copy_context_checked +xcb_glx_create_context +xcb_glx_create_context_attribs_arb +xcb_glx_create_context_attribs_arb_checked +xcb_glx_create_context_attribs_arb_sizeof +xcb_glx_create_context_checked +xcb_glx_create_glx_pixmap +xcb_glx_create_glx_pixmap_checked +xcb_glx_create_new_context +xcb_glx_create_new_context_checked +xcb_glx_create_pbuffer +xcb_glx_create_pbuffer_checked +xcb_glx_create_pbuffer_sizeof +xcb_glx_create_pixmap +xcb_glx_create_pixmap_checked +xcb_glx_create_pixmap_sizeof +xcb_glx_create_window +xcb_glx_create_window_checked +xcb_glx_create_window_sizeof +xcb_glx_delete_lists +xcb_glx_delete_lists_checked +xcb_glx_delete_queries_arb +xcb_glx_delete_queries_arb_checked +xcb_glx_delete_queries_arb_sizeof +xcb_glx_delete_textures +xcb_glx_delete_textures_checked +xcb_glx_delete_textures_sizeof +xcb_glx_delete_window +xcb_glx_delete_window_checked +xcb_glx_destroy_context +xcb_glx_destroy_context_checked +xcb_glx_destroy_glx_pixmap +xcb_glx_destroy_glx_pixmap_checked +xcb_glx_destroy_pbuffer +xcb_glx_destroy_pbuffer_checked +xcb_glx_destroy_pixmap +xcb_glx_destroy_pixmap_checked +xcb_glx_drawable_end +xcb_glx_drawable_next +xcb_glx_end_list +xcb_glx_end_list_checked +xcb_glx_fbconfig_end +xcb_glx_fbconfig_next +xcb_glx_feedback_buffer +xcb_glx_feedback_buffer_checked +xcb_glx_finish +xcb_glx_finish_reply +xcb_glx_finish_unchecked +xcb_glx_float32_end +xcb_glx_float32_next +xcb_glx_float64_end +xcb_glx_float64_next +xcb_glx_flush +xcb_glx_flush_checked +xcb_glx_gen_lists +xcb_glx_gen_lists_reply +xcb_glx_gen_lists_unchecked +xcb_glx_gen_queries_arb +xcb_glx_gen_queries_arb_data +xcb_glx_gen_queries_arb_data_end +xcb_glx_gen_queries_arb_data_length +xcb_glx_gen_queries_arb_reply +xcb_glx_gen_queries_arb_sizeof +xcb_glx_gen_queries_arb_unchecked +xcb_glx_gen_textures +xcb_glx_gen_textures_data +xcb_glx_gen_textures_data_end +xcb_glx_gen_textures_data_length +xcb_glx_gen_textures_reply +xcb_glx_gen_textures_sizeof +xcb_glx_gen_textures_unchecked +xcb_glx_get_booleanv +xcb_glx_get_booleanv_data +xcb_glx_get_booleanv_data_end +xcb_glx_get_booleanv_data_length +xcb_glx_get_booleanv_reply +xcb_glx_get_booleanv_sizeof +xcb_glx_get_booleanv_unchecked +xcb_glx_get_clip_plane +xcb_glx_get_clip_plane_data +xcb_glx_get_clip_plane_data_end +xcb_glx_get_clip_plane_data_length +xcb_glx_get_clip_plane_reply +xcb_glx_get_clip_plane_sizeof +xcb_glx_get_clip_plane_unchecked +xcb_glx_get_color_table +xcb_glx_get_color_table_data +xcb_glx_get_color_table_data_end +xcb_glx_get_color_table_data_length +xcb_glx_get_color_table_parameterfv +xcb_glx_get_color_table_parameterfv_data +xcb_glx_get_color_table_parameterfv_data_end +xcb_glx_get_color_table_parameterfv_data_length +xcb_glx_get_color_table_parameterfv_reply +xcb_glx_get_color_table_parameterfv_sizeof +xcb_glx_get_color_table_parameterfv_unchecked +xcb_glx_get_color_table_parameteriv +xcb_glx_get_color_table_parameteriv_data +xcb_glx_get_color_table_parameteriv_data_end +xcb_glx_get_color_table_parameteriv_data_length +xcb_glx_get_color_table_parameteriv_reply +xcb_glx_get_color_table_parameteriv_sizeof +xcb_glx_get_color_table_parameteriv_unchecked +xcb_glx_get_color_table_reply +xcb_glx_get_color_table_sizeof +xcb_glx_get_color_table_unchecked +xcb_glx_get_compressed_tex_image_arb +xcb_glx_get_compressed_tex_image_arb_data +xcb_glx_get_compressed_tex_image_arb_data_end +xcb_glx_get_compressed_tex_image_arb_data_length +xcb_glx_get_compressed_tex_image_arb_reply +xcb_glx_get_compressed_tex_image_arb_sizeof +xcb_glx_get_compressed_tex_image_arb_unchecked +xcb_glx_get_convolution_filter +xcb_glx_get_convolution_filter_data +xcb_glx_get_convolution_filter_data_end +xcb_glx_get_convolution_filter_data_length +xcb_glx_get_convolution_filter_reply +xcb_glx_get_convolution_filter_sizeof +xcb_glx_get_convolution_filter_unchecked +xcb_glx_get_convolution_parameterfv +xcb_glx_get_convolution_parameterfv_data +xcb_glx_get_convolution_parameterfv_data_end +xcb_glx_get_convolution_parameterfv_data_length +xcb_glx_get_convolution_parameterfv_reply +xcb_glx_get_convolution_parameterfv_sizeof +xcb_glx_get_convolution_parameterfv_unchecked +xcb_glx_get_convolution_parameteriv +xcb_glx_get_convolution_parameteriv_data +xcb_glx_get_convolution_parameteriv_data_end +xcb_glx_get_convolution_parameteriv_data_length +xcb_glx_get_convolution_parameteriv_reply +xcb_glx_get_convolution_parameteriv_sizeof +xcb_glx_get_convolution_parameteriv_unchecked +xcb_glx_get_doublev +xcb_glx_get_doublev_data +xcb_glx_get_doublev_data_end +xcb_glx_get_doublev_data_length +xcb_glx_get_doublev_reply +xcb_glx_get_doublev_sizeof +xcb_glx_get_doublev_unchecked +xcb_glx_get_drawable_attributes +xcb_glx_get_drawable_attributes_attribs +xcb_glx_get_drawable_attributes_attribs_end +xcb_glx_get_drawable_attributes_attribs_length +xcb_glx_get_drawable_attributes_reply +xcb_glx_get_drawable_attributes_sizeof +xcb_glx_get_drawable_attributes_unchecked +xcb_glx_get_error +xcb_glx_get_error_reply +xcb_glx_get_error_unchecked +xcb_glx_get_fb_configs +xcb_glx_get_fb_configs_property_list +xcb_glx_get_fb_configs_property_list_end +xcb_glx_get_fb_configs_property_list_length +xcb_glx_get_fb_configs_reply +xcb_glx_get_fb_configs_sizeof +xcb_glx_get_fb_configs_unchecked +xcb_glx_get_floatv +xcb_glx_get_floatv_data +xcb_glx_get_floatv_data_end +xcb_glx_get_floatv_data_length +xcb_glx_get_floatv_reply +xcb_glx_get_floatv_sizeof +xcb_glx_get_floatv_unchecked +xcb_glx_get_histogram +xcb_glx_get_histogram_data +xcb_glx_get_histogram_data_end +xcb_glx_get_histogram_data_length +xcb_glx_get_histogram_parameterfv +xcb_glx_get_histogram_parameterfv_data +xcb_glx_get_histogram_parameterfv_data_end +xcb_glx_get_histogram_parameterfv_data_length +xcb_glx_get_histogram_parameterfv_reply +xcb_glx_get_histogram_parameterfv_sizeof +xcb_glx_get_histogram_parameterfv_unchecked +xcb_glx_get_histogram_parameteriv +xcb_glx_get_histogram_parameteriv_data +xcb_glx_get_histogram_parameteriv_data_end +xcb_glx_get_histogram_parameteriv_data_length +xcb_glx_get_histogram_parameteriv_reply +xcb_glx_get_histogram_parameteriv_sizeof +xcb_glx_get_histogram_parameteriv_unchecked +xcb_glx_get_histogram_reply +xcb_glx_get_histogram_sizeof +xcb_glx_get_histogram_unchecked +xcb_glx_get_integerv +xcb_glx_get_integerv_data +xcb_glx_get_integerv_data_end +xcb_glx_get_integerv_data_length +xcb_glx_get_integerv_reply +xcb_glx_get_integerv_sizeof +xcb_glx_get_integerv_unchecked +xcb_glx_get_lightfv +xcb_glx_get_lightfv_data +xcb_glx_get_lightfv_data_end +xcb_glx_get_lightfv_data_length +xcb_glx_get_lightfv_reply +xcb_glx_get_lightfv_sizeof +xcb_glx_get_lightfv_unchecked +xcb_glx_get_lightiv +xcb_glx_get_lightiv_data +xcb_glx_get_lightiv_data_end +xcb_glx_get_lightiv_data_length +xcb_glx_get_lightiv_reply +xcb_glx_get_lightiv_sizeof +xcb_glx_get_lightiv_unchecked +xcb_glx_get_mapdv +xcb_glx_get_mapdv_data +xcb_glx_get_mapdv_data_end +xcb_glx_get_mapdv_data_length +xcb_glx_get_mapdv_reply +xcb_glx_get_mapdv_sizeof +xcb_glx_get_mapdv_unchecked +xcb_glx_get_mapfv +xcb_glx_get_mapfv_data +xcb_glx_get_mapfv_data_end +xcb_glx_get_mapfv_data_length +xcb_glx_get_mapfv_reply +xcb_glx_get_mapfv_sizeof +xcb_glx_get_mapfv_unchecked +xcb_glx_get_mapiv +xcb_glx_get_mapiv_data +xcb_glx_get_mapiv_data_end +xcb_glx_get_mapiv_data_length +xcb_glx_get_mapiv_reply +xcb_glx_get_mapiv_sizeof +xcb_glx_get_mapiv_unchecked +xcb_glx_get_materialfv +xcb_glx_get_materialfv_data +xcb_glx_get_materialfv_data_end +xcb_glx_get_materialfv_data_length +xcb_glx_get_materialfv_reply +xcb_glx_get_materialfv_sizeof +xcb_glx_get_materialfv_unchecked +xcb_glx_get_materialiv +xcb_glx_get_materialiv_data +xcb_glx_get_materialiv_data_end +xcb_glx_get_materialiv_data_length +xcb_glx_get_materialiv_reply +xcb_glx_get_materialiv_sizeof +xcb_glx_get_materialiv_unchecked +xcb_glx_get_minmax +xcb_glx_get_minmax_data +xcb_glx_get_minmax_data_end +xcb_glx_get_minmax_data_length +xcb_glx_get_minmax_parameterfv +xcb_glx_get_minmax_parameterfv_data +xcb_glx_get_minmax_parameterfv_data_end +xcb_glx_get_minmax_parameterfv_data_length +xcb_glx_get_minmax_parameterfv_reply +xcb_glx_get_minmax_parameterfv_sizeof +xcb_glx_get_minmax_parameterfv_unchecked +xcb_glx_get_minmax_parameteriv +xcb_glx_get_minmax_parameteriv_data +xcb_glx_get_minmax_parameteriv_data_end +xcb_glx_get_minmax_parameteriv_data_length +xcb_glx_get_minmax_parameteriv_reply +xcb_glx_get_minmax_parameteriv_sizeof +xcb_glx_get_minmax_parameteriv_unchecked +xcb_glx_get_minmax_reply +xcb_glx_get_minmax_sizeof +xcb_glx_get_minmax_unchecked +xcb_glx_get_pixel_mapfv +xcb_glx_get_pixel_mapfv_data +xcb_glx_get_pixel_mapfv_data_end +xcb_glx_get_pixel_mapfv_data_length +xcb_glx_get_pixel_mapfv_reply +xcb_glx_get_pixel_mapfv_sizeof +xcb_glx_get_pixel_mapfv_unchecked +xcb_glx_get_pixel_mapuiv +xcb_glx_get_pixel_mapuiv_data +xcb_glx_get_pixel_mapuiv_data_end +xcb_glx_get_pixel_mapuiv_data_length +xcb_glx_get_pixel_mapuiv_reply +xcb_glx_get_pixel_mapuiv_sizeof +xcb_glx_get_pixel_mapuiv_unchecked +xcb_glx_get_pixel_mapusv +xcb_glx_get_pixel_mapusv_data +xcb_glx_get_pixel_mapusv_data_end +xcb_glx_get_pixel_mapusv_data_length +xcb_glx_get_pixel_mapusv_reply +xcb_glx_get_pixel_mapusv_sizeof +xcb_glx_get_pixel_mapusv_unchecked +xcb_glx_get_polygon_stipple +xcb_glx_get_polygon_stipple_data +xcb_glx_get_polygon_stipple_data_end +xcb_glx_get_polygon_stipple_data_length +xcb_glx_get_polygon_stipple_reply +xcb_glx_get_polygon_stipple_sizeof +xcb_glx_get_polygon_stipple_unchecked +xcb_glx_get_query_objectiv_arb +xcb_glx_get_query_objectiv_arb_data +xcb_glx_get_query_objectiv_arb_data_end +xcb_glx_get_query_objectiv_arb_data_length +xcb_glx_get_query_objectiv_arb_reply +xcb_glx_get_query_objectiv_arb_sizeof +xcb_glx_get_query_objectiv_arb_unchecked +xcb_glx_get_query_objectuiv_arb +xcb_glx_get_query_objectuiv_arb_data +xcb_glx_get_query_objectuiv_arb_data_end +xcb_glx_get_query_objectuiv_arb_data_length +xcb_glx_get_query_objectuiv_arb_reply +xcb_glx_get_query_objectuiv_arb_sizeof +xcb_glx_get_query_objectuiv_arb_unchecked +xcb_glx_get_queryiv_arb +xcb_glx_get_queryiv_arb_data +xcb_glx_get_queryiv_arb_data_end +xcb_glx_get_queryiv_arb_data_length +xcb_glx_get_queryiv_arb_reply +xcb_glx_get_queryiv_arb_sizeof +xcb_glx_get_queryiv_arb_unchecked +xcb_glx_get_separable_filter +xcb_glx_get_separable_filter_reply +xcb_glx_get_separable_filter_rows_and_cols +xcb_glx_get_separable_filter_rows_and_cols_end +xcb_glx_get_separable_filter_rows_and_cols_length +xcb_glx_get_separable_filter_sizeof +xcb_glx_get_separable_filter_unchecked +xcb_glx_get_string +xcb_glx_get_string_reply +xcb_glx_get_string_sizeof +xcb_glx_get_string_string +xcb_glx_get_string_string_end +xcb_glx_get_string_string_length +xcb_glx_get_string_unchecked +xcb_glx_get_tex_envfv +xcb_glx_get_tex_envfv_data +xcb_glx_get_tex_envfv_data_end +xcb_glx_get_tex_envfv_data_length +xcb_glx_get_tex_envfv_reply +xcb_glx_get_tex_envfv_sizeof +xcb_glx_get_tex_envfv_unchecked +xcb_glx_get_tex_enviv +xcb_glx_get_tex_enviv_data +xcb_glx_get_tex_enviv_data_end +xcb_glx_get_tex_enviv_data_length +xcb_glx_get_tex_enviv_reply +xcb_glx_get_tex_enviv_sizeof +xcb_glx_get_tex_enviv_unchecked +xcb_glx_get_tex_gendv +xcb_glx_get_tex_gendv_data +xcb_glx_get_tex_gendv_data_end +xcb_glx_get_tex_gendv_data_length +xcb_glx_get_tex_gendv_reply +xcb_glx_get_tex_gendv_sizeof +xcb_glx_get_tex_gendv_unchecked +xcb_glx_get_tex_genfv +xcb_glx_get_tex_genfv_data +xcb_glx_get_tex_genfv_data_end +xcb_glx_get_tex_genfv_data_length +xcb_glx_get_tex_genfv_reply +xcb_glx_get_tex_genfv_sizeof +xcb_glx_get_tex_genfv_unchecked +xcb_glx_get_tex_geniv +xcb_glx_get_tex_geniv_data +xcb_glx_get_tex_geniv_data_end +xcb_glx_get_tex_geniv_data_length +xcb_glx_get_tex_geniv_reply +xcb_glx_get_tex_geniv_sizeof +xcb_glx_get_tex_geniv_unchecked +xcb_glx_get_tex_image +xcb_glx_get_tex_image_data +xcb_glx_get_tex_image_data_end +xcb_glx_get_tex_image_data_length +xcb_glx_get_tex_image_reply +xcb_glx_get_tex_image_sizeof +xcb_glx_get_tex_image_unchecked +xcb_glx_get_tex_level_parameterfv +xcb_glx_get_tex_level_parameterfv_data +xcb_glx_get_tex_level_parameterfv_data_end +xcb_glx_get_tex_level_parameterfv_data_length +xcb_glx_get_tex_level_parameterfv_reply +xcb_glx_get_tex_level_parameterfv_sizeof +xcb_glx_get_tex_level_parameterfv_unchecked +xcb_glx_get_tex_level_parameteriv +xcb_glx_get_tex_level_parameteriv_data +xcb_glx_get_tex_level_parameteriv_data_end +xcb_glx_get_tex_level_parameteriv_data_length +xcb_glx_get_tex_level_parameteriv_reply +xcb_glx_get_tex_level_parameteriv_sizeof +xcb_glx_get_tex_level_parameteriv_unchecked +xcb_glx_get_tex_parameterfv +xcb_glx_get_tex_parameterfv_data +xcb_glx_get_tex_parameterfv_data_end +xcb_glx_get_tex_parameterfv_data_length +xcb_glx_get_tex_parameterfv_reply +xcb_glx_get_tex_parameterfv_sizeof +xcb_glx_get_tex_parameterfv_unchecked +xcb_glx_get_tex_parameteriv +xcb_glx_get_tex_parameteriv_data +xcb_glx_get_tex_parameteriv_data_end +xcb_glx_get_tex_parameteriv_data_length +xcb_glx_get_tex_parameteriv_reply +xcb_glx_get_tex_parameteriv_sizeof +xcb_glx_get_tex_parameteriv_unchecked +xcb_glx_get_visual_configs +xcb_glx_get_visual_configs_property_list +xcb_glx_get_visual_configs_property_list_end +xcb_glx_get_visual_configs_property_list_length +xcb_glx_get_visual_configs_reply +xcb_glx_get_visual_configs_sizeof +xcb_glx_get_visual_configs_unchecked +xcb_glx_id +xcb_glx_is_direct +xcb_glx_is_direct_reply +xcb_glx_is_direct_unchecked +xcb_glx_is_list +xcb_glx_is_list_reply +xcb_glx_is_list_unchecked +xcb_glx_is_query_arb +xcb_glx_is_query_arb_reply +xcb_glx_is_query_arb_unchecked +xcb_glx_is_texture +xcb_glx_is_texture_reply +xcb_glx_is_texture_unchecked +xcb_glx_make_context_current +xcb_glx_make_context_current_reply +xcb_glx_make_context_current_unchecked +xcb_glx_make_current +xcb_glx_make_current_reply +xcb_glx_make_current_unchecked +xcb_glx_new_list +xcb_glx_new_list_checked +xcb_glx_pbuffer_end +xcb_glx_pbuffer_next +xcb_glx_pixel_storef +xcb_glx_pixel_storef_checked +xcb_glx_pixel_storei +xcb_glx_pixel_storei_checked +xcb_glx_pixmap_end +xcb_glx_pixmap_next +xcb_glx_query_context +xcb_glx_query_context_attribs +xcb_glx_query_context_attribs_end +xcb_glx_query_context_attribs_length +xcb_glx_query_context_reply +xcb_glx_query_context_sizeof +xcb_glx_query_context_unchecked +xcb_glx_query_extensions_string +xcb_glx_query_extensions_string_reply +xcb_glx_query_extensions_string_unchecked +xcb_glx_query_server_string +xcb_glx_query_server_string_reply +xcb_glx_query_server_string_sizeof +xcb_glx_query_server_string_string +xcb_glx_query_server_string_string_end +xcb_glx_query_server_string_string_length +xcb_glx_query_server_string_unchecked +xcb_glx_query_version +xcb_glx_query_version_reply +xcb_glx_query_version_unchecked +xcb_glx_read_pixels +xcb_glx_read_pixels_data +xcb_glx_read_pixels_data_end +xcb_glx_read_pixels_data_length +xcb_glx_read_pixels_reply +xcb_glx_read_pixels_sizeof +xcb_glx_read_pixels_unchecked +xcb_glx_render +xcb_glx_render_checked +xcb_glx_render_large +xcb_glx_render_large_checked +xcb_glx_render_large_sizeof +xcb_glx_render_mode +xcb_glx_render_mode_data +xcb_glx_render_mode_data_end +xcb_glx_render_mode_data_length +xcb_glx_render_mode_reply +xcb_glx_render_mode_sizeof +xcb_glx_render_mode_unchecked +xcb_glx_render_sizeof +xcb_glx_select_buffer +xcb_glx_select_buffer_checked +xcb_glx_set_client_info_2arb +xcb_glx_set_client_info_2arb_checked +xcb_glx_set_client_info_2arb_sizeof +xcb_glx_set_client_info_arb +xcb_glx_set_client_info_arb_checked +xcb_glx_set_client_info_arb_sizeof +xcb_glx_swap_buffers +xcb_glx_swap_buffers_checked +xcb_glx_use_x_font +xcb_glx_use_x_font_checked +xcb_glx_vendor_private +xcb_glx_vendor_private_checked +xcb_glx_vendor_private_sizeof +xcb_glx_vendor_private_with_reply +xcb_glx_vendor_private_with_reply_data_2 +xcb_glx_vendor_private_with_reply_data_2_end +xcb_glx_vendor_private_with_reply_data_2_length +xcb_glx_vendor_private_with_reply_reply +xcb_glx_vendor_private_with_reply_sizeof +xcb_glx_vendor_private_with_reply_unchecked +xcb_glx_wait_gl +xcb_glx_wait_gl_checked +xcb_glx_wait_x +xcb_glx_wait_x_checked +xcb_glx_window_end +xcb_glx_window_next diff --git a/subprojects/libcapsule/examples/shim/libxcb-glx.so.0.symbols.updated-for b/subprojects/libcapsule/examples/shim/libxcb-glx.so.0.symbols.updated-for new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/subprojects/libcapsule/examples/shim/libxcb-present.so.0.excluded b/subprojects/libcapsule/examples/shim/libxcb-present.so.0.excluded new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/subprojects/libcapsule/examples/shim/libxcb-present.so.0.shared b/subprojects/libcapsule/examples/shim/libxcb-present.so.0.shared new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/subprojects/libcapsule/examples/shim/libxcb-present.so.0.symbols b/subprojects/libcapsule/examples/shim/libxcb-present.so.0.symbols new file mode 100644 index 0000000000000000000000000000000000000000..aafd15f17cecacd1bd9f5f0d0ec59f2e23683def --- /dev/null +++ b/subprojects/libcapsule/examples/shim/libxcb-present.so.0.symbols @@ -0,0 +1,19 @@ +xcb_present_event_end +xcb_present_event_next +xcb_present_id +xcb_present_notify_end +xcb_present_notify_msc +xcb_present_notify_msc_checked +xcb_present_notify_next +xcb_present_pixmap +xcb_present_pixmap_checked +xcb_present_pixmap_sizeof +xcb_present_query_capabilities +xcb_present_query_capabilities_reply +xcb_present_query_capabilities_unchecked +xcb_present_query_version +xcb_present_query_version_reply +xcb_present_query_version_unchecked +xcb_present_redirect_notify_sizeof +xcb_present_select_input +xcb_present_select_input_checked diff --git a/subprojects/libcapsule/examples/shim/libxcb-present.so.0.symbols.updated-for b/subprojects/libcapsule/examples/shim/libxcb-present.so.0.symbols.updated-for new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/subprojects/libcapsule/examples/shim/libxcb-sync.so.1.excluded b/subprojects/libcapsule/examples/shim/libxcb-sync.so.1.excluded new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/subprojects/libcapsule/examples/shim/libxcb-sync.so.1.shared b/subprojects/libcapsule/examples/shim/libxcb-sync.so.1.shared new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/subprojects/libcapsule/examples/shim/libxcb-sync.so.1.symbols b/subprojects/libcapsule/examples/shim/libxcb-sync.so.1.symbols new file mode 100644 index 0000000000000000000000000000000000000000..649602392a28eea809894dcf4f2d42705c720eb9 --- /dev/null +++ b/subprojects/libcapsule/examples/shim/libxcb-sync.so.1.symbols @@ -0,0 +1,80 @@ +xcb_sync_alarm_end +xcb_sync_alarm_next +xcb_sync_await +xcb_sync_await_checked +xcb_sync_await_fence +xcb_sync_await_fence_checked +xcb_sync_await_fence_sizeof +xcb_sync_await_sizeof +xcb_sync_change_alarm +xcb_sync_change_alarm_aux +xcb_sync_change_alarm_aux_checked +xcb_sync_change_alarm_checked +xcb_sync_change_alarm_value_list_serialize +xcb_sync_change_alarm_value_list_sizeof +xcb_sync_change_alarm_value_list_unpack +xcb_sync_change_counter +xcb_sync_change_counter_checked +xcb_sync_counter_end +xcb_sync_counter_next +xcb_sync_create_alarm +xcb_sync_create_alarm_aux +xcb_sync_create_alarm_aux_checked +xcb_sync_create_alarm_checked +xcb_sync_create_alarm_value_list_serialize +xcb_sync_create_alarm_value_list_sizeof +xcb_sync_create_alarm_value_list_unpack +xcb_sync_create_counter +xcb_sync_create_counter_checked +xcb_sync_create_fence +xcb_sync_create_fence_checked +xcb_sync_destroy_alarm +xcb_sync_destroy_alarm_checked +xcb_sync_destroy_counter +xcb_sync_destroy_counter_checked +xcb_sync_destroy_fence +xcb_sync_destroy_fence_checked +xcb_sync_fence_end +xcb_sync_fence_next +xcb_sync_get_priority +xcb_sync_get_priority_reply +xcb_sync_get_priority_unchecked +xcb_sync_id +xcb_sync_initialize +xcb_sync_initialize_reply +xcb_sync_initialize_unchecked +xcb_sync_int64_end +xcb_sync_int64_next +xcb_sync_list_system_counters +xcb_sync_list_system_counters_counters_iterator +xcb_sync_list_system_counters_counters_length +xcb_sync_list_system_counters_reply +xcb_sync_list_system_counters_sizeof +xcb_sync_list_system_counters_unchecked +xcb_sync_query_alarm +xcb_sync_query_alarm_reply +xcb_sync_query_alarm_unchecked +xcb_sync_query_counter +xcb_sync_query_counter_reply +xcb_sync_query_counter_unchecked +xcb_sync_query_fence +xcb_sync_query_fence_reply +xcb_sync_query_fence_unchecked +xcb_sync_reset_fence +xcb_sync_reset_fence_checked +xcb_sync_set_counter +xcb_sync_set_counter_checked +xcb_sync_set_priority +xcb_sync_set_priority_checked +xcb_sync_systemcounter_end +xcb_sync_systemcounter_name +xcb_sync_systemcounter_name_end +xcb_sync_systemcounter_name_length +xcb_sync_systemcounter_next +xcb_sync_systemcounter_sizeof +xcb_sync_trigger_end +xcb_sync_trigger_fence +xcb_sync_trigger_fence_checked +xcb_sync_trigger_next +xcb_sync_waitcondition_end +xcb_sync_waitcondition_next diff --git a/subprojects/libcapsule/examples/shim/libxcb-sync.so.1.symbols.updated-for b/subprojects/libcapsule/examples/shim/libxcb-sync.so.1.symbols.updated-for new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/subprojects/libcapsule/examples/shim/libxcb.so.1.excluded b/subprojects/libcapsule/examples/shim/libxcb.so.1.excluded new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/subprojects/libcapsule/examples/shim/libxcb.so.1.shared b/subprojects/libcapsule/examples/shim/libxcb.so.1.shared new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/subprojects/libcapsule/examples/shim/libxcb.so.1.symbols b/subprojects/libcapsule/examples/shim/libxcb.so.1.symbols new file mode 100644 index 0000000000000000000000000000000000000000..2edb3b679ddefbaa379f4690dca9ab6b880a2626 --- /dev/null +++ b/subprojects/libcapsule/examples/shim/libxcb.so.1.symbols @@ -0,0 +1,557 @@ +xcb_alloc_color +xcb_alloc_color_cells +xcb_alloc_color_cells_masks +xcb_alloc_color_cells_masks_end +xcb_alloc_color_cells_masks_length +xcb_alloc_color_cells_pixels +xcb_alloc_color_cells_pixels_end +xcb_alloc_color_cells_pixels_length +xcb_alloc_color_cells_reply +xcb_alloc_color_cells_sizeof +xcb_alloc_color_cells_unchecked +xcb_alloc_color_planes +xcb_alloc_color_planes_pixels +xcb_alloc_color_planes_pixels_end +xcb_alloc_color_planes_pixels_length +xcb_alloc_color_planes_reply +xcb_alloc_color_planes_sizeof +xcb_alloc_color_planes_unchecked +xcb_alloc_color_reply +xcb_alloc_color_unchecked +xcb_alloc_named_color +xcb_alloc_named_color_reply +xcb_alloc_named_color_sizeof +xcb_alloc_named_color_unchecked +xcb_allow_events +xcb_allow_events_checked +xcb_arc_end +xcb_arc_next +xcb_atom_end +xcb_atom_next +xcb_bell +xcb_bell_checked +xcb_big_requests_enable +xcb_big_requests_enable_reply +xcb_big_requests_enable_unchecked +xcb_big_requests_id +xcb_button_end +xcb_button_next +xcb_change_active_pointer_grab +xcb_change_active_pointer_grab_checked +xcb_change_gc +xcb_change_gc_checked +xcb_change_gc_sizeof +xcb_change_hosts +xcb_change_hosts_checked +xcb_change_hosts_sizeof +xcb_change_keyboard_control +xcb_change_keyboard_control_checked +xcb_change_keyboard_control_sizeof +xcb_change_keyboard_mapping +xcb_change_keyboard_mapping_checked +xcb_change_keyboard_mapping_sizeof +xcb_change_pointer_control +xcb_change_pointer_control_checked +xcb_change_property +xcb_change_property_checked +xcb_change_property_sizeof +xcb_change_save_set +xcb_change_save_set_checked +xcb_change_window_attributes +xcb_change_window_attributes_checked +xcb_change_window_attributes_sizeof +xcb_char2b_end +xcb_char2b_next +xcb_charinfo_end +xcb_charinfo_next +xcb_circulate_window +xcb_circulate_window_checked +xcb_clear_area +xcb_clear_area_checked +xcb_client_message_data_end +xcb_client_message_data_next +xcb_close_font +xcb_close_font_checked +xcb_coloritem_end +xcb_coloritem_next +xcb_colormap_end +xcb_colormap_next +xcb_configure_window +xcb_configure_window_checked +xcb_configure_window_sizeof +xcb_connect +xcb_connect_to_display_with_auth_info +xcb_connect_to_fd +xcb_connection_has_error +xcb_convert_selection +xcb_convert_selection_checked +xcb_copy_area +xcb_copy_area_checked +xcb_copy_colormap_and_free +xcb_copy_colormap_and_free_checked +xcb_copy_gc +xcb_copy_gc_checked +xcb_copy_plane +xcb_copy_plane_checked +xcb_create_colormap +xcb_create_colormap_checked +xcb_create_cursor +xcb_create_cursor_checked +xcb_create_gc +xcb_create_gc_checked +xcb_create_gc_sizeof +xcb_create_glyph_cursor +xcb_create_glyph_cursor_checked +xcb_create_pixmap +xcb_create_pixmap_checked +xcb_create_window +xcb_create_window_checked +xcb_create_window_sizeof +xcb_cursor_end +xcb_cursor_next +xcb_delete_property +xcb_delete_property_checked +xcb_depth_end +xcb_depth_next +xcb_depth_sizeof +xcb_depth_visuals +xcb_depth_visuals_iterator +xcb_depth_visuals_length +xcb_destroy_subwindows +xcb_destroy_subwindows_checked +xcb_destroy_window +xcb_destroy_window_checked +xcb_discard_reply +xcb_disconnect +xcb_drawable_end +xcb_drawable_next +xcb_fill_poly +xcb_fill_poly_checked +xcb_fill_poly_sizeof +xcb_flush +xcb_font_end +xcb_font_next +xcb_fontable_end +xcb_fontable_next +xcb_fontprop_end +xcb_fontprop_next +xcb_force_screen_saver +xcb_force_screen_saver_checked +xcb_format_end +xcb_format_next +xcb_free_colormap +xcb_free_colormap_checked +xcb_free_colors +xcb_free_colors_checked +xcb_free_colors_sizeof +xcb_free_cursor +xcb_free_cursor_checked +xcb_free_gc +xcb_free_gc_checked +xcb_free_pixmap +xcb_free_pixmap_checked +xcb_gcontext_end +xcb_gcontext_next +xcb_generate_id +xcb_get_atom_name +xcb_get_atom_name_name +xcb_get_atom_name_name_end +xcb_get_atom_name_name_length +xcb_get_atom_name_reply +xcb_get_atom_name_sizeof +xcb_get_atom_name_unchecked +xcb_get_extension_data +xcb_get_file_descriptor +xcb_get_font_path +xcb_get_font_path_path_iterator +xcb_get_font_path_path_length +xcb_get_font_path_reply +xcb_get_font_path_sizeof +xcb_get_font_path_unchecked +xcb_get_geometry +xcb_get_geometry_reply +xcb_get_geometry_unchecked +xcb_get_image +xcb_get_image_data +xcb_get_image_data_end +xcb_get_image_data_length +xcb_get_image_reply +xcb_get_image_sizeof +xcb_get_image_unchecked +xcb_get_input_focus +xcb_get_input_focus_reply +xcb_get_input_focus_unchecked +xcb_get_keyboard_control +xcb_get_keyboard_control_reply +xcb_get_keyboard_control_unchecked +xcb_get_keyboard_mapping +xcb_get_keyboard_mapping_keysyms +xcb_get_keyboard_mapping_keysyms_end +xcb_get_keyboard_mapping_keysyms_length +xcb_get_keyboard_mapping_reply +xcb_get_keyboard_mapping_sizeof +xcb_get_keyboard_mapping_unchecked +xcb_get_maximum_request_length +xcb_get_modifier_mapping +xcb_get_modifier_mapping_keycodes +xcb_get_modifier_mapping_keycodes_end +xcb_get_modifier_mapping_keycodes_length +xcb_get_modifier_mapping_reply +xcb_get_modifier_mapping_sizeof +xcb_get_modifier_mapping_unchecked +xcb_get_motion_events +xcb_get_motion_events_events +xcb_get_motion_events_events_iterator +xcb_get_motion_events_events_length +xcb_get_motion_events_reply +xcb_get_motion_events_sizeof +xcb_get_motion_events_unchecked +xcb_get_pointer_control +xcb_get_pointer_control_reply +xcb_get_pointer_control_unchecked +xcb_get_pointer_mapping +xcb_get_pointer_mapping_map +xcb_get_pointer_mapping_map_end +xcb_get_pointer_mapping_map_length +xcb_get_pointer_mapping_reply +xcb_get_pointer_mapping_sizeof +xcb_get_pointer_mapping_unchecked +xcb_get_property +xcb_get_property_reply +xcb_get_property_sizeof +xcb_get_property_unchecked +xcb_get_property_value +xcb_get_property_value_end +xcb_get_property_value_length +xcb_get_reply_fds +xcb_get_screen_saver +xcb_get_screen_saver_reply +xcb_get_screen_saver_unchecked +xcb_get_selection_owner +xcb_get_selection_owner_reply +xcb_get_selection_owner_unchecked +xcb_get_setup +xcb_get_window_attributes +xcb_get_window_attributes_reply +xcb_get_window_attributes_unchecked +xcb_grab_button +xcb_grab_button_checked +xcb_grab_key +xcb_grab_key_checked +xcb_grab_keyboard +xcb_grab_keyboard_reply +xcb_grab_keyboard_unchecked +xcb_grab_pointer +xcb_grab_pointer_reply +xcb_grab_pointer_unchecked +xcb_grab_server +xcb_grab_server_checked +xcb_host_address +xcb_host_address_end +xcb_host_address_length +xcb_host_end +xcb_host_next +xcb_host_sizeof +xcb_image_text_16 +xcb_image_text_16_checked +xcb_image_text_16_sizeof +xcb_image_text_8 +xcb_image_text_8_checked +xcb_image_text_8_sizeof +xcb_install_colormap +xcb_install_colormap_checked +xcb_intern_atom +xcb_intern_atom_reply +xcb_intern_atom_sizeof +xcb_intern_atom_unchecked +xcb_keycode_end +xcb_keycode_next +xcb_keysym_end +xcb_keysym_next +xcb_kill_client +xcb_kill_client_checked +xcb_list_extensions +xcb_list_extensions_names_iterator +xcb_list_extensions_names_length +xcb_list_extensions_reply +xcb_list_extensions_sizeof +xcb_list_extensions_unchecked +xcb_list_fonts +xcb_list_fonts_names_iterator +xcb_list_fonts_names_length +xcb_list_fonts_reply +xcb_list_fonts_sizeof +xcb_list_fonts_unchecked +xcb_list_fonts_with_info +xcb_list_fonts_with_info_name +xcb_list_fonts_with_info_name_end +xcb_list_fonts_with_info_name_length +xcb_list_fonts_with_info_properties +xcb_list_fonts_with_info_properties_iterator +xcb_list_fonts_with_info_properties_length +xcb_list_fonts_with_info_reply +xcb_list_fonts_with_info_sizeof +xcb_list_fonts_with_info_unchecked +xcb_list_hosts +xcb_list_hosts_hosts_iterator +xcb_list_hosts_hosts_length +xcb_list_hosts_reply +xcb_list_hosts_sizeof +xcb_list_hosts_unchecked +xcb_list_installed_colormaps +xcb_list_installed_colormaps_cmaps +xcb_list_installed_colormaps_cmaps_end +xcb_list_installed_colormaps_cmaps_length +xcb_list_installed_colormaps_reply +xcb_list_installed_colormaps_sizeof +xcb_list_installed_colormaps_unchecked +xcb_list_properties +xcb_list_properties_atoms +xcb_list_properties_atoms_end +xcb_list_properties_atoms_length +xcb_list_properties_reply +xcb_list_properties_sizeof +xcb_list_properties_unchecked +xcb_lookup_color +xcb_lookup_color_reply +xcb_lookup_color_sizeof +xcb_lookup_color_unchecked +xcb_map_subwindows +xcb_map_subwindows_checked +xcb_map_window +xcb_map_window_checked +xcb_no_operation +xcb_no_operation_checked +xcb_open_font +xcb_open_font_checked +xcb_open_font_sizeof +xcb_parse_display +xcb_pixmap_end +xcb_pixmap_next +xcb_point_end +xcb_point_next +xcb_poll_for_event +xcb_poll_for_queued_event +xcb_poll_for_reply +xcb_poll_for_special_event +xcb_poly_arc +xcb_poly_arc_checked +xcb_poly_arc_sizeof +xcb_poly_fill_arc +xcb_poly_fill_arc_checked +xcb_poly_fill_arc_sizeof +xcb_poly_fill_rectangle +xcb_poly_fill_rectangle_checked +xcb_poly_fill_rectangle_sizeof +xcb_poly_line +xcb_poly_line_checked +xcb_poly_line_sizeof +xcb_poly_point +xcb_poly_point_checked +xcb_poly_point_sizeof +xcb_poly_rectangle +xcb_poly_rectangle_checked +xcb_poly_rectangle_sizeof +xcb_poly_segment +xcb_poly_segment_checked +xcb_poly_segment_sizeof +xcb_poly_text_16 +xcb_poly_text_16_checked +xcb_poly_text_16_sizeof +xcb_poly_text_8 +xcb_poly_text_8_checked +xcb_poly_text_8_sizeof +xcb_popcount +xcb_prefetch_extension_data +xcb_prefetch_maximum_request_length +xcb_put_image +xcb_put_image_checked +xcb_put_image_sizeof +xcb_query_best_size +xcb_query_best_size_reply +xcb_query_best_size_unchecked +xcb_query_colors +xcb_query_colors_colors +xcb_query_colors_colors_iterator +xcb_query_colors_colors_length +xcb_query_colors_reply +xcb_query_colors_sizeof +xcb_query_colors_unchecked +xcb_query_extension +xcb_query_extension_reply +xcb_query_extension_sizeof +xcb_query_extension_unchecked +xcb_query_font +xcb_query_font_char_infos +xcb_query_font_char_infos_iterator +xcb_query_font_char_infos_length +xcb_query_font_properties +xcb_query_font_properties_iterator +xcb_query_font_properties_length +xcb_query_font_reply +xcb_query_font_sizeof +xcb_query_font_unchecked +xcb_query_keymap +xcb_query_keymap_reply +xcb_query_keymap_unchecked +xcb_query_pointer +xcb_query_pointer_reply +xcb_query_pointer_unchecked +xcb_query_text_extents +xcb_query_text_extents_reply +xcb_query_text_extents_sizeof +xcb_query_text_extents_unchecked +xcb_query_tree +xcb_query_tree_children +xcb_query_tree_children_end +xcb_query_tree_children_length +xcb_query_tree_reply +xcb_query_tree_sizeof +xcb_query_tree_unchecked +xcb_recolor_cursor +xcb_recolor_cursor_checked +xcb_rectangle_end +xcb_rectangle_next +xcb_register_for_special_xge +xcb_reparent_window +xcb_reparent_window_checked +xcb_request_check +xcb_rgb_end +xcb_rgb_next +xcb_rotate_properties +xcb_rotate_properties_checked +xcb_rotate_properties_sizeof +xcb_screen_allowed_depths_iterator +xcb_screen_allowed_depths_length +xcb_screen_end +xcb_screen_next +xcb_screen_sizeof +xcb_segment_end +xcb_segment_next +xcb_send_event +xcb_send_event_checked +xcb_send_fd +xcb_send_request +xcb_set_access_control +xcb_set_access_control_checked +xcb_set_clip_rectangles +xcb_set_clip_rectangles_checked +xcb_set_clip_rectangles_sizeof +xcb_set_close_down_mode +xcb_set_close_down_mode_checked +xcb_set_dashes +xcb_set_dashes_checked +xcb_set_dashes_sizeof +xcb_set_font_path +xcb_set_font_path_checked +xcb_set_font_path_sizeof +xcb_set_input_focus +xcb_set_input_focus_checked +xcb_set_modifier_mapping +xcb_set_modifier_mapping_reply +xcb_set_modifier_mapping_sizeof +xcb_set_modifier_mapping_unchecked +xcb_set_pointer_mapping +xcb_set_pointer_mapping_reply +xcb_set_pointer_mapping_sizeof +xcb_set_pointer_mapping_unchecked +xcb_set_screen_saver +xcb_set_screen_saver_checked +xcb_set_selection_owner +xcb_set_selection_owner_checked +xcb_setup_authenticate_end +xcb_setup_authenticate_next +xcb_setup_authenticate_reason +xcb_setup_authenticate_reason_end +xcb_setup_authenticate_reason_length +xcb_setup_authenticate_sizeof +xcb_setup_end +xcb_setup_failed_end +xcb_setup_failed_next +xcb_setup_failed_reason +xcb_setup_failed_reason_end +xcb_setup_failed_reason_length +xcb_setup_failed_sizeof +xcb_setup_next +xcb_setup_pixmap_formats +xcb_setup_pixmap_formats_iterator +xcb_setup_pixmap_formats_length +xcb_setup_request_authorization_protocol_data +xcb_setup_request_authorization_protocol_data_end +xcb_setup_request_authorization_protocol_data_length +xcb_setup_request_authorization_protocol_name +xcb_setup_request_authorization_protocol_name_end +xcb_setup_request_authorization_protocol_name_length +xcb_setup_request_end +xcb_setup_request_next +xcb_setup_request_sizeof +xcb_setup_roots_iterator +xcb_setup_roots_length +xcb_setup_sizeof +xcb_setup_vendor +xcb_setup_vendor_end +xcb_setup_vendor_length +xcb_store_colors +xcb_store_colors_checked +xcb_store_colors_sizeof +xcb_store_named_color +xcb_store_named_color_checked +xcb_store_named_color_sizeof +xcb_str_end +xcb_str_name +xcb_str_name_end +xcb_str_name_length +xcb_str_next +xcb_str_sizeof +xcb_sumof +xcb_take_socket +xcb_timecoord_end +xcb_timecoord_next +xcb_timestamp_end +xcb_timestamp_next +xcb_translate_coordinates +xcb_translate_coordinates_reply +xcb_translate_coordinates_unchecked +xcb_ungrab_button +xcb_ungrab_button_checked +xcb_ungrab_key +xcb_ungrab_key_checked +xcb_ungrab_keyboard +xcb_ungrab_keyboard_checked +xcb_ungrab_pointer +xcb_ungrab_pointer_checked +xcb_ungrab_server +xcb_ungrab_server_checked +xcb_uninstall_colormap +xcb_uninstall_colormap_checked +xcb_unmap_subwindows +xcb_unmap_subwindows_checked +xcb_unmap_window +xcb_unmap_window_checked +xcb_unregister_for_special_event +xcb_visualid_end +xcb_visualid_next +xcb_visualtype_end +xcb_visualtype_next +xcb_wait_for_event +xcb_wait_for_reply +xcb_wait_for_special_event +xcb_warp_pointer +xcb_warp_pointer_checked +xcb_window_end +xcb_window_next +xcb_writev +xcb_xc_misc_get_version +xcb_xc_misc_get_version_reply +xcb_xc_misc_get_version_unchecked +xcb_xc_misc_get_xid_list +xcb_xc_misc_get_xid_list_ids +xcb_xc_misc_get_xid_list_ids_end +xcb_xc_misc_get_xid_list_ids_length +xcb_xc_misc_get_xid_list_reply +xcb_xc_misc_get_xid_list_sizeof +xcb_xc_misc_get_xid_list_unchecked +xcb_xc_misc_get_xid_range +xcb_xc_misc_get_xid_range_reply +xcb_xc_misc_get_xid_range_unchecked +xcb_xc_misc_id diff --git a/subprojects/libcapsule/examples/shim/libxcb.so.1.symbols.updated-for b/subprojects/libcapsule/examples/shim/libxcb.so.1.symbols.updated-for new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/subprojects/libcapsule/m4/ax_is_release.m4 b/subprojects/libcapsule/m4/ax_is_release.m4 new file mode 100644 index 0000000000000000000000000000000000000000..9097ddb6a92a8143a940f7327211d5a9be2694c6 --- /dev/null +++ b/subprojects/libcapsule/m4/ax_is_release.m4 @@ -0,0 +1,80 @@ +# =========================================================================== +# https://www.gnu.org/software/autoconf-archive/ax_is_release.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_IS_RELEASE(POLICY) +# +# DESCRIPTION +# +# Determine whether the code is being configured as a release, or from +# git. Set the ax_is_release variable to 'yes' or 'no'. +# +# If building a release version, it is recommended that the configure +# script disable compiler errors and debug features, by conditionalising +# them on the ax_is_release variable. If building from git, these +# features should be enabled. +# +# The POLICY parameter specifies how ax_is_release is determined. It can +# take the following values: +# +# * git-directory: ax_is_release will be 'no' if a '.git' directory exists +# * minor-version: ax_is_release will be 'no' if the minor version number +# in $PACKAGE_VERSION is odd; this assumes +# $PACKAGE_VERSION follows the 'major.minor.micro' scheme +# * micro-version: ax_is_release will be 'no' if the micro version number +# in $PACKAGE_VERSION is odd; this assumes +# $PACKAGE_VERSION follows the 'major.minor.micro' scheme +# * dash-version: ax_is_release will be 'no' if there is a dash '-' +# in $PACKAGE_VERSION, for example 1.2-pre3, 1.2.42-a8b9 +# or 2.0-dirty (in particular this is suitable for use +# with git-version-gen) +# * always: ax_is_release will always be 'yes' +# * never: ax_is_release will always be 'no' +# +# Other policies may be added in future. +# +# LICENSE +# +# Copyright (c) 2015 Philip Withnall <philip@tecnocode.co.uk> +# Copyright (c) 2016 Collabora Ltd. +# +# Copying and distribution of this file, with or without modification, are +# permitted in any medium without royalty provided the copyright notice +# and this notice are preserved. + +#serial 7 + +AC_DEFUN([AX_IS_RELEASE],[ + AC_BEFORE([AC_INIT],[$0]) + + m4_case([$1], + [git-directory],[ + # $is_release = (.git directory does not exist) + AS_IF([test -d ${srcdir}/.git],[ax_is_release=no],[ax_is_release=yes]) + ], + [minor-version],[ + # $is_release = ($minor_version is even) + minor_version=`echo "$PACKAGE_VERSION" | sed 's/[[^.]][[^.]]*.\([[^.]][[^.]]*\).*/\1/'` + AS_IF([test "$(( $minor_version % 2 ))" -ne 0], + [ax_is_release=no],[ax_is_release=yes]) + ], + [micro-version],[ + # $is_release = ($micro_version is even) + micro_version=`echo "$PACKAGE_VERSION" | sed 's/[[^.]]*\.[[^.]]*\.\([[^.]]*\).*/\1/'` + AS_IF([test "$(( $micro_version % 2 ))" -ne 0], + [ax_is_release=no],[ax_is_release=yes]) + ], + [dash-version],[ + # $is_release = ($PACKAGE_VERSION has a dash) + AS_CASE([$PACKAGE_VERSION], + [*-*], [ax_is_release=no], + [*], [ax_is_release=yes]) + ], + [always],[ax_is_release=yes], + [never],[ax_is_release=no], + [ + AC_MSG_ERROR([Invalid policy. Valid policies: git-directory, minor-version, micro-version, dash-version, always, never.]) + ]) +]) diff --git a/subprojects/libcapsule/m4/capsule_matching_abi.m4 b/subprojects/libcapsule/m4/capsule_matching_abi.m4 new file mode 100644 index 0000000000000000000000000000000000000000..603a56fbf86fa76a94c51415d4e9112c5752ee21 --- /dev/null +++ b/subprojects/libcapsule/m4/capsule_matching_abi.m4 @@ -0,0 +1,76 @@ +# Copyright 2019 Collabora Ltd. +# +# Copying and distribution of this file, with or without modification, are +# permitted in any medium without royalty provided the copyright notice +# and this notice are preserved. This file is offered as-is, without any +# warranty. + +#serial 1 + +dnl CAPSULE_MATCHING_ABI() +dnl +dnl Assert that the --host architecture is consistent with the selected +dnl compiler. +AC_DEFUN([CAPSULE_MATCHING_ABI], [ + AC_CANONICAL_HOST + AC_LANG_PUSH([C]) + + AS_CASE([${host_cpu}-${host_os}], + [i?86-*], [ + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[]], [[ +#if defined(__i386__) +/* OK */ +#else +#error not i386 +#endif + ]] + )], + [], + [ + AC_MSG_ERROR([Host system $host must use an i386 compiler such as CC='gcc -m32' or CC='${host_cpu}-${host_os}-gcc']) + ] + ) + ], + + [x86_64-*-gnu], [ + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[]], [[ +#if defined(__x86_64__) && defined(__LP64__) +/* OK */ +#else +#error not x86_64 +#endif + ]] + )], + [], + [ + AC_MSG_ERROR([Host system $host must use an x86_64 compiler such as CC='gcc -m64' or CC='${host_cpu}-${host_os}-gcc']) + ] + ) + ], + + # libcapsule doesn't actually support x32, but for completeness... + [x86_64-*-gnux32], [ + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[]], [[ +#if defined(__x86_64__) && defined(__ILP32__) +/* OK */ +#else +#error not x32 +#endif + ]] + )], + [], + [ + AC_MSG_ERROR([Host system $host must use an x32 compiler such as CC='gcc -mx32' or CC='${host_cpu}-${host_os}-gcc']) + ] + ) + ], + + # For non-x86 we assume users are more aware of how to select the + # right cross-compiler. (libcapsule doesn't support non-x86 yet + # anyway.) + ) + + AC_LANG_POP([C]) +])dnl CAPSULE_MATCHING_ABI + +# vim:set sw=4 sts=4 et: diff --git a/subprojects/libcapsule/tests/CapsuleTest.pm b/subprojects/libcapsule/tests/CapsuleTest.pm new file mode 100644 index 0000000000000000000000000000000000000000..38e3f4d6ef82c7d3f8711edf6c8319290b679b1b --- /dev/null +++ b/subprojects/libcapsule/tests/CapsuleTest.pm @@ -0,0 +1,486 @@ +# Copyright © 2017 Collabora Ltd +# +# This file is part of libcapsule. +# +# libcapsule is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as +# published by the Free Software Foundation; either version 2.1 of the +# License, or (at your option) any later version. +# +# libcapsule 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 Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with libcapsule. If not, see <http://www.gnu.org/licenses/>. +# +# Note that get_symbols_with_nm() uses some GPL-2+ code taken from dpkg. + +package CapsuleTest; + +use strict; +use warnings; + +use Cwd qw(abs_path); +use File::Glob qw(bsd_glob); +use FindBin; +use Exporter qw(import); +use IPC::Run qw(run); +use Test::More; + +our @EXPORT = qw( + assert_run_verbose + bind_usr + diag_multiline + explain_wait_status + get_symbols_with_nm + libcapsule_uninstalled + run_assert_exit_status + run_ok + run_verbose + skip_all_unless_bwrap + skip_all_unless_nm + $CAPSULE_CAPTURE_LIBS_TOOL + $CAPSULE_INIT_PROJECT_TOOL + $CAPSULE_SYMBOLS_TOOL + $CAPSULE_VERSION_TOOL + $NM + $PKG_CONFIG + $builddir + $srcdir +); + +=encoding utf8 + +=head1 NAME + +CapsuleTest - utilities for libcapsule automated and manual tests + +=head1 EXPORTED VARIABLES + +=over + +=cut + +=item $PKG_CONFIG + +The B<pkg-config>(1) utility. + +=cut + +our $PKG_CONFIG = $ENV{PKG_CONFIG}; +$PKG_CONFIG = 'pkg-config' unless length $PKG_CONFIG; + +=item $CAPSULE_CAPTURE_LIBS_TOOL + +The B<capsule-capture-libs>(1) development tool. + +=cut + +our $CAPSULE_CAPTURE_LIBS_TOOL = $ENV{CAPSULE_CAPTURE_LIBS_TOOL}; + +if (! length $CAPSULE_CAPTURE_LIBS_TOOL) { + $CAPSULE_CAPTURE_LIBS_TOOL = `$PKG_CONFIG --variable=CAPSULE_CAPTURE_LIBS_TOOL libcapsule-tools`; + chomp $CAPSULE_CAPTURE_LIBS_TOOL; +} + +=item $CAPSULE_INIT_PROJECT_TOOL + +The B<capsule-init-project>(1) development tool. + +=cut + +our $CAPSULE_INIT_PROJECT_TOOL = $ENV{CAPSULE_INIT_PROJECT_TOOL}; + +if (! length $CAPSULE_INIT_PROJECT_TOOL) { + $CAPSULE_INIT_PROJECT_TOOL = `$PKG_CONFIG --variable=CAPSULE_INIT_PROJECT_TOOL libcapsule-tools`; + chomp $CAPSULE_INIT_PROJECT_TOOL; +} + +=item $CAPSULE_SYMBOLS_TOOL + +The B<capsule-symbols>(1) development tool. + +=cut + +our $CAPSULE_SYMBOLS_TOOL = $ENV{CAPSULE_SYMBOLS_TOOL}; + +if (! length $CAPSULE_SYMBOLS_TOOL) { + $CAPSULE_SYMBOLS_TOOL = `$PKG_CONFIG --variable=CAPSULE_SYMBOLS_TOOL libcapsule-tools`; + chomp $CAPSULE_SYMBOLS_TOOL; +} + +=item $CAPSULE_VERSION_TOOL + +The B<capsule-version>(1) development tool. + +=cut + +our $CAPSULE_VERSION_TOOL = $ENV{CAPSULE_VERSION_TOOL}; + +unless (defined $CAPSULE_VERSION_TOOL) { + $CAPSULE_VERSION_TOOL = `$PKG_CONFIG --variable=CAPSULE_VERSION_TOOL libcapsule-tools`; + chomp $CAPSULE_VERSION_TOOL; +} + +=item $NM + +The B<nm>(1) symbol-name-listing utility, configured for BSD output format. + +=cut + +our $NM = $ENV{NM}; + +if (! length $NM) { + $NM = 'nm -B'; +} + +=item $srcdir + +An appropriate directory to find non-generated files: the top directory +of the source tree, or the root directory of this package's GLib-style +installed-tests. + +=cut + +# G_TEST_* convention stolen from GLib, even though we aren't using GTest +my (undef, $here, undef) = File::Spec->splitpath($INC{'CapsuleTest.pm'}); +our $srcdir = $ENV{G_TEST_SRCDIR}; +$srcdir = abs_path("$here/..") unless defined $srcdir; + +=item $builddir + +An appropriate directory to find non-generated files: the top directory +of the build tree, or the root directory of this package's GLib-style +installed-tests. + +=cut + +our $builddir = $ENV{G_TEST_BUILDDIR}; +$builddir = abs_path("$here/..") unless defined $builddir; + +diag "Source or installation directory: $srcdir"; +diag "Build or installation directory: $builddir"; + +=back + +=head1 EXPORTED FUNCTIONS + +=over + +=item assert_run_verbose(I<ARGV>, ...) + +Log the given command, run it, and die if it didn't return success. +I<ARGV> is an array-reference containing arguments. +Subsequent parameters are passed to C<IPC::Run::run> and can be used +to redirect output. + +=cut + +sub assert_run_verbose { + my $argv = shift; + my $debug = join(' ', @$argv); + diag($debug); + if (! run($argv, @_)) { + my $explained = explain_wait_status($?); + die "Command exited with status $? ($explained): '$debug'"; + } +} + +=item diag_multiline(I<TEXT>) + +Split I<TEXT> into lines and emit them as TAP diagnostics. + +=cut + +sub diag_multiline { + foreach my $line (split /^/m, shift) { + chomp $line; + diag " $line"; + } +} + +=item explain_wait_status(I<CODE>) + +Convert Unix-style wait status I<CODE> into something human-readable. + +=cut + +sub explain_wait_status { + my $status = shift; + my @ret; + my $signal = $status & 127; + my $code = ($status >> 8); + + if ($signal) { + push @ret, "killed by signal $signal"; + } + + if ($status & 128) { + push @ret, 'core dumped'; + } + + if ($code & 128) { + my $maybe = $code & 127; + unshift @ret, + "exited with code $code (child process killed by signal $maybe?)"; + } + elsif ($code || ! @ret) { + unshift @ret, "exited with code $code"; + } + + return join(', ', @ret); +} + +=item run_assert_exit_status(I<STATUS>, I<ARGV>, ...) + +A TAP assertion that the given command exits with I<STATUS>. I<ARGV> is an +array-reference containing arguments. Subsequent parameters are +passed to C<IPC::Run::run> and can be used to redirect output. + +=cut + +sub run_assert_exit_status { + my $exp_status = shift; + my $argv = shift; + my $debug = join(' ', @$argv); + diag($debug); + run($argv, @_); + my $explained = explain_wait_status($?); + my $actual_status = ($? >> 8); + if ($actual_status == $exp_status) { + ok(1, "Command exited with status $? ($explained): '$debug'"); + } else { + ok(0, "Command unexpectedly exited with status $? ($explained): '$debug'"); + } +} + +=item run_ok(I<ARGV>, ...) + +A TAP assertion that the given command exits 0. I<ARGV> is an +array-reference containing arguments. Subsequent parameters are +passed to C<IPC::Run::run> and can be used to redirect output. + +=cut + +sub run_ok { + my $argv = shift; + my $debug = join(' ', @$argv); + diag($debug); + if (run($argv, @_)) { + ok(1, qq{Command successful as expected: '$debug'}); + } + else { + my $explained = explain_wait_status($?); + ok(0, "Command exited with status $? ($explained): '$debug'"); + } +} + +=item run_verbose(I<ARGV>, ...) + +Log the given command, run it, and return the same thing as +C<IPC::Run::run>. I<ARGV> is an array-reference containing arguments. +Subsequent parameters are passed to C<IPC::Run::run> and can be used +to redirect output. + +=cut + +sub run_verbose { + my $argv = shift; + my $debug = join(' ', @$argv); + diag($debug); + return run($argv, @_); +} + +=item skip_all_unless_bwrap() + +If we cannot run B<bwrap>(1), log a TAP report that all tests have been +skipped (as if via C<plan skip_all =E<gt> ...>), and exit. + +=cut + +sub skip_all_unless_bwrap { + if (! run([qw( + env bwrap --ro-bind / / --unshare-ipc --unshare-net + --unshare-pid --unshare-user --unshare-uts true + )], '>&2')) { + plan(skip_all => 'Cannot run bwrap'); + } +} + +=item skip_all_unless_nm() + +If we cannot run B<nm>(1) to implement B<get_symbols_with_nm>, log a +TAP report that all tests have been skipped (as if via +C<plan skip_all =E<gt> ...>), and exit. + +=cut + +sub skip_all_unless_nm { + if (! run([split(' ', $NM), + qw(--dynamic --extern-only --defined-only + --with-symbol-versions /bin/true)], '>/dev/null')) { + plan(skip_all => + 'Cannot run nm (no support for --with-symbol-versions?)'); + } +} + +=item get_symbols_with_nm(I<LIBRARY>) + +Return a list of symbols found in I<LIBRARY>, in the same format +that capsule-symbols would use. + +=cut + +sub get_symbols_with_nm { + my $library = shift; + my $output; + + run_ok([split(' ', $NM), '--dynamic', '--extern-only', '--defined-only', + '--with-symbol-versions', $library], '>', \$output); + my @symbols_produced; + foreach my $line (split /\n/, $output) { + if ($line =~ m/^[[:xdigit:]]+\s+[ABCDGIRSTW]+\s+([^@]+)(\@\@?.*)?/) { + my $symbol = $1; + my $version = $2; + if (defined $version && $version =~ m/^(\@\@?[^@]+)\1$/) { + # Ignore doubled symbol-version suffix. + # https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=964457 + $version = $1; + } + require CapsuleTestDpkg; + next if CapsuleTestDpkg::symbol_is_blacklisted($symbol); + next if defined $version && "\@\@$symbol" eq $version; + + # Put them in the same format that capsule-symbols uses + if (length $version && $version ne '@@Base') { + push @symbols_produced, "$symbol $version"; + } + else { + push @symbols_produced, "$symbol "; + } + } + } + foreach my $sym (@symbols_produced) { + diag "- $sym"; + } + return sort @symbols_produced; +} + +=item bind_usr(I<TREE>[, I<DEST>]) + +Return B<bwrap>(1) arguments that would bind-mount I<TREE> on I<DEST>. +If I<TREE> has a F<usr> directory, it is assumed to be a complete +sysroot with F<bin>, F<sbin>, F<lib*> and F<usr> directories, which +will be mounted on I<DEST>. If not, it is assumed to be a merged +F</usr>, and will be mounted on I<DEST>F</usr>, with compatibility +symbolic links in I<DEST> for the F<bin>, F<sbin> and F<lib*> +subdirectories. + +=cut + +sub bind_usr { + my $tree = shift; + my $dest = shift; + $dest = '' unless defined $dest; + my @bwrap; + + my $has_usr = (-d "$tree/usr"); + + if ($has_usr) { + push @bwrap, '--ro-bind', "$tree/usr", "$dest/usr"; + } + else { + push @bwrap, '--ro-bind', "$tree", "$dest/usr"; + } + + opendir(my $dir, $tree); + while (defined(my $subdir = readdir $dir)) { + if ($subdir =~ /^lib/ || $subdir =~ /^s?bin$/) { + if ($has_usr) { + push @bwrap, '--ro-bind', "$tree/$subdir", "$dest/$subdir"; + } + else { + push @bwrap, '--symlink', "usr/$subdir", "$dest/$subdir"; + } + } + } + closedir $dir; + + foreach my $ldso (bsd_glob("$tree/etc/ld.so.cache"), + bsd_glob("$tree/etc/ld-*.cache"), + bsd_glob("$tree/etc/ld-*.path")) { + next unless -e $ldso; + + my $base = $ldso; + $base =~ s,.*/,,; + push @bwrap, "--ro-bind", $ldso, "$dest/etc/$base"; + } + + # TODO: This wouldn't be necessary in a purely glvnd system + if (-d "$tree/etc/alternatives") { + push @bwrap, "--ro-bind", "$tree/etc/alternatives", + "$dest/etc/alternatives"; + } + + return @bwrap; +} + +=item libcapsule_uninstalled() + +Return true if libcapsule is uninstalled (build-time tests) and false +if we are carrying out installed-tests. + +=cut + +sub libcapsule_uninstalled { + return exists $ENV{CAPSULE_TESTS_UNINSTALLED}; +} + +=back + +=head1 ENVIRONMENT + +=over + +=item CAPSULE_INIT_PROJECT_TOOL + +B<capsule-init-project>(1) + +=item CAPSULE_SYMBOLS_TOOL + +B<capsule-symbols>(1) + +=item CAPSULE_TESTS_KEEP_TEMP + +If set to a non-empty value, temporary directories created by this test +will not be cleaned up. + +=cut + +if (length $ENV{CAPSULE_TESTS_KEEP_TEMP}) { + $File::Temp::KEEP_ALL = 1; +} + +=item CAPSULE_VERSION_TOOL + +B<capsule-version>(1) + +=item NM + +The B<nm>(1) symbol-name-listing utility, if not C<nm -B>. + +=item PKG_CONFIG + +B<pkg-config>(1) + +=back + +=head1 SEE ALSO + +B<Test::More>(3pm), B<bwrap>(1) + +=cut + +1; diff --git a/subprojects/libcapsule/tests/CapsuleTestDpkg.pm b/subprojects/libcapsule/tests/CapsuleTestDpkg.pm new file mode 100644 index 0000000000000000000000000000000000000000..dd23d788d0d44248466bb31bf85342fd6a8270f9 --- /dev/null +++ b/subprojects/libcapsule/tests/CapsuleTestDpkg.pm @@ -0,0 +1,81 @@ +# Exerpts from Dpkg::Shlibs::SymbolFile +# +# Copyright © 2007 Raphaël Hertzog <hertzog@debian.org> +# Copyright © 2009-2010 Modestas Vainius <modax@debian.org> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. + +package CapsuleTestDpkg; + +use strict; +use warnings; + +my %blacklist = ( + __bss_end__ => 1, # arm + __bss_end => 1, # arm + _bss_end__ => 1, # arm + __bss_start => 1, # ALL + __bss_start__ => 1, # arm + __data_start => 1, # arm + __do_global_ctors_aux => 1, # ia64 + __do_global_dtors_aux => 1, # ia64 + __do_jv_register_classes => 1, # ia64 + _DYNAMIC => 1, # ALL + _edata => 1, # ALL + _end => 1, # ALL + __end__ => 1, # arm + __exidx_end => 1, # armel + __exidx_start => 1, # armel + _fbss => 1, # mips, mipsel + _fdata => 1, # mips, mipsel + _fini => 1, # ALL + _ftext => 1, # mips, mipsel + _GLOBAL_OFFSET_TABLE_ => 1, # hppa, mips, mipsel + __gmon_start__ => 1, # hppa + __gnu_local_gp => 1, # mips, mipsel + _gp => 1, # mips, mipsel + _init => 1, # ALL + _PROCEDURE_LINKAGE_TABLE_ => 1, # sparc, alpha + _SDA2_BASE_ => 1, # powerpc + _SDA_BASE_ => 1, # powerpc +); + +for my $i (14 .. 31) { + # Many powerpc specific symbols + $blacklist{"_restfpr_$i"} = 1; + $blacklist{"_restfpr_$i\_x"} = 1; + $blacklist{"_restgpr_$i"} = 1; + $blacklist{"_restgpr_$i\_x"} = 1; + $blacklist{"_savefpr_$i"} = 1; + $blacklist{"_savegpr_$i"} = 1; +} + +sub symbol_is_blacklisted { + my ($symbol, $include_groups) = @_; + + return 1 if exists $blacklist{$symbol}; + + # The ARM Embedded ABI spec states symbols under this namespace as + # possibly appearing in output objects. + return 1 if not ${$include_groups}{aeabi} and $symbol =~ /^__aeabi_/; + + # The GNU implementation of the OpenMP spec, specifies symbols under + # this namespace as possibly appearing in output objects. + return 1 if not ${$include_groups}{gomp} + and $symbol =~ /^\.gomp_critical_user_/; + + return 0; +} + +1; diff --git a/subprojects/libcapsule/tests/capture-libs.pl b/subprojects/libcapsule/tests/capture-libs.pl new file mode 100755 index 0000000000000000000000000000000000000000..93a9fa06cd7e51b51b509c07f2d98a903162baad --- /dev/null +++ b/subprojects/libcapsule/tests/capture-libs.pl @@ -0,0 +1,716 @@ +#!/usr/bin/perl + +# Copyright © 2017 Collabora Ltd +# +# This file is part of libcapsule. +# +# libcapsule is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as +# published by the Free Software Foundation; either version 2.1 of the +# License, or (at your option) any later version. +# +# libcapsule 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 Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with libcapsule. If not, see <http://www.gnu.org/licenses/>. + +use autodie; +use warnings; +use strict; + +use Cwd qw(abs_path); +use File::Temp qw(); +use IPC::Run qw(run); +use Test::More; + +use FindBin; +use lib $FindBin::Bin; + +use CapsuleTest; + +sub tolerant_readlink ($) +{ + no autodie; + return readlink shift; +} + +skip_all_unless_bwrap; + +my $LIBDIR = qr{/(?:usr/(?:\w+-(?:\w+-)?linux-gnu\w*/)?)?lib(?:32|64|x32)?(?:/\w+-linux-gnu\w*)?(?:/i686)?(?:/cmov)?}; + +my $test_tempdir = File::Temp->newdir(); +diag "Working directory: $test_tempdir"; +my $host = "${test_tempdir}/host"; +mkdir($host); +my $container = "${test_tempdir}/container"; +mkdir($container); +my $libdir = "${test_tempdir}/libdir"; + +{ + open(my $fh, '>', "${test_tempdir}/empty"); + close $fh; +} +{ + open(my $fh, '>', "${test_tempdir}/libc"); + print $fh "# comment\n"; + print $fh "\n"; + print $fh "soname:libc.so.6\n"; + print $fh "soname:libc.so.6"; # deliberately no trailing newline + close $fh; +} +{ + open(my $fh, '>', "${test_tempdir}/capture-libs.ini"); + print $fh "[Library /opt/libversionedsymbols.so.1]\n"; + print $fh "CompareBy=versions;name;symbols;\n"; + print $fh "[Library /opt/libversionedlikedbus.so.1]\n"; + print $fh "CompareBy=container;\n"; + print $fh "[Library /opt/libversionedprivatenothidden.so.1]\n"; + print $fh "CompareBy=versions;\n"; + print $fh "PublicSymbolVersions=!LIBVERSIONEDPRIVATE*;*;\n"; + close $fh; +} + +run_ok(['rm', '-fr', $libdir]); +mkdir($libdir); +run_ok([$CAPSULE_CAPTURE_LIBS_TOOL, 'soname:libc.so.6'], '>&2', + init => sub { chdir $libdir or die $!; }); +ok(-e "$libdir/libc.so.6", 'libc.so.6 was captured'); +like(readlink "$libdir/libc.so.6", qr{^$LIBDIR/libc\.so\.6$}, + '$libdir/libc.so.6 is a symlink to the real libc.so.6'); +my $libc = abs_path("$libdir/libc.so.6"); +ok(-e "$libdir/libBrokenLocale.so.1", 'related libraries were captured'); +ok(-e "$libdir/libm.so.6", 'related libraries were captured'); +ok(-e "$libdir/libpthread.so.0", 'related libraries were captured'); +ok(-e "$libdir/libmemusage.so", 'related libraries were captured'); +like(readlink "$libdir/libm.so.6", qr{^$LIBDIR/libm\.so\.6$}, + 'libm is a symlink to the right place'); +my @libc_family; +{ + opendir(my $dir_iter, $libdir); + @libc_family = sort grep { $_ ne '.' and $_ ne '..' } readdir $dir_iter; + closedir $dir_iter; + foreach my $symlink (@libc_family) { + diag "- $symlink -> ".readlink("$libdir/$symlink"); + } +} + +run_ok(['rm', '-fr', $libdir]); +mkdir($libdir); +run_ok([$CAPSULE_CAPTURE_LIBS_TOOL, '--link-target=/run/host', + "--dest=$libdir", 'soname-match:libc.so.6'], '>&2'); +{ + opendir(my $dir_iter, $libdir); + my @links = sort grep { $_ ne '.' and $_ ne '..' } readdir $dir_iter; + foreach my $symlink (@links) { + diag "- $symlink -> ".readlink("$libdir/$symlink"); + } + closedir $dir_iter; + is_deeply(\@links, \@libc_family, + 'the same libraries are captured when using --link-target'); +} +like(readlink "$libdir/libc.so.6", qr{^/run/host\Q$libc\E$}, + '$libdir/libc.so.6 is a symlink to /run/host + realpath of libc.so.6'); + +run_ok(['rm', '-fr', $libdir]); +mkdir($libdir); +run_ok([$CAPSULE_CAPTURE_LIBS_TOOL, '--link-target=/run/host', + '--remap-link-prefix=/run/host/=/run/media/', "--dest=$libdir", + 'soname-match:libc.so.6'], '>&2'); +{ + opendir(my $dir_iter, $libdir); + my @links = sort grep { $_ ne '.' and $_ ne '..' } readdir $dir_iter; + foreach my $symlink (@links) { + diag "- $symlink -> ".readlink("$libdir/$symlink"); + } + closedir $dir_iter; + is_deeply(\@links, \@libc_family, + 'the same libraries are captured when using --link-target'); +} +like(readlink "$libdir/libc.so.6", qr{^/run/media\Q$libc\E$}, + '$libdir/libc.so.6 is a symlink to /run/media + realpath of libc.so.6'); + +run_ok(['rm', '-fr', $libdir]); +mkdir($libdir); +run_ok([$CAPSULE_CAPTURE_LIBS_TOOL, '--link-target=/run/host', + '--remap-link-prefix=/one=/two', '--remap-link-prefix=/run/ho=/run/co', + "--dest=$libdir", 'soname-match:libc.so.6'], '>&2'); +{ + opendir(my $dir_iter, $libdir); + my @links = sort grep { $_ ne '.' and $_ ne '..' } readdir $dir_iter; + foreach my $symlink (@links) { + diag "- $symlink -> ".readlink("$libdir/$symlink"); + } + closedir $dir_iter; + is_deeply(\@links, \@libc_family, + 'the same libraries are captured when using --link-target'); +} +like(readlink "$libdir/libc.so.6", qr{^/run/cost\Q$libc\E$}, + '$libdir/libc.so.6 is a symlink to /run/cost + realpath of libc.so.6'); + +run_ok(['rm', '-fr', $libdir]); +mkdir($libdir); +run_ok([$CAPSULE_CAPTURE_LIBS_TOOL, '--link-target=/run/host', + '--remap-link-prefix=/run/host2=/run/media', "--dest=$libdir", + 'soname-match:libc.so.6'], '>&2'); +{ + opendir(my $dir_iter, $libdir); + my @links = sort grep { $_ ne '.' and $_ ne '..' } readdir $dir_iter; + foreach my $symlink (@links) { + diag "- $symlink -> ".readlink("$libdir/$symlink"); + } + closedir $dir_iter; + is_deeply(\@links, \@libc_family, + 'the same libraries are captured when using --link-target'); +} +like(readlink "$libdir/libc.so.6", qr{^/run/host\Q$libc\E$}, + '$libdir/libc.so.6 is a symlink to /run/host + realpath of libc.so.6'); + +run_ok(['rm', '-fr', $libdir]); +mkdir($libdir); +# We already asserted that libc will be either in /usr/ or /lib* +run_ok([$CAPSULE_CAPTURE_LIBS_TOOL, '--remap-link-prefix=/usr/=/run/host/usr/', + '--remap-link-prefix=/lib=/run/host/lib', "--dest=$libdir", + 'soname-match:libc.so.6'], '>&2'); +like(readlink "$libdir/libc.so.6", qr{^/run/host\Q$libc\E$}, + "$libdir/libc.so.6 is a symlink to /run/host + realpath of libc.so.6"); + +run_ok(['rm', '-fr', $libdir]); +mkdir($libdir); +# We already asserted that libc will be either in /usr/ or /lib* +run_ok([$CAPSULE_CAPTURE_LIBS_TOOL, '--remap-link-prefix=/usr/=/tmp+foo=what/usr/', + '--remap-link-prefix=/lib=/tmp+foo=what/lib', "--dest=$libdir", + 'soname-match:libc.so.6'], '>&2'); +like(readlink "$libdir/libc.so.6", qr{^/tmp\+foo=what\Q$libc\E$}, + "$libdir/libc.so.6 is a symlink to /run/host + realpath of libc.so.6"); + +run_ok(['rm', '-fr', $libdir]); +mkdir($libdir); +run_ok([$CAPSULE_CAPTURE_LIBS_TOOL, '--remap-link-prefix=/opt/=/OPT/', + "--dest=$libdir", 'soname-match:libc.so.6'], '>&2'); +like(readlink "$libdir/libc.so.6", qr{^$LIBDIR/libc\.so\.6$}, + "$libdir/libc.so.6 is a symlink to the real libc.so.6"); + +run_ok(['rm', '-fr', $libdir]); +mkdir($libdir); +my $stderr; +run_assert_exit_status(1, [$CAPSULE_CAPTURE_LIBS_TOOL, + '--remap-link-prefix==/run/host', + "--dest=$libdir", 'soname-match:libc.so.6'], + '2>', \$stderr, '>&2'); +like($stderr, qr{--remap-link-prefix value must follow the FROM=TO pattern$}, + '--remap-link-prefix should follow the pattern FROM=TO'); + +run_assert_exit_status(1, [$CAPSULE_CAPTURE_LIBS_TOOL, '--remap-link-prefix=/usr=', + "--dest=$libdir", 'soname-match:libc.so.6'], '2>', \$stderr, '>&2'); +like($stderr, qr{--remap-link-prefix value must follow the FROM=TO pattern$}, + '--remap-link-prefix should follow the pattern FROM=TO'); + +run_assert_exit_status(1, [$CAPSULE_CAPTURE_LIBS_TOOL, '--remap-link-prefix=/usr', + "--dest=$libdir", 'soname-match:libc.so.6'], '2>', \$stderr, '>&2'); +like($stderr, qr{--remap-link-prefix value must follow the FROM=TO pattern$}, + '--remap-link-prefix should follow the pattern FROM=TO'); + +run_ok(['rm', '-fr', $libdir]); +mkdir($libdir); +run_ok([qw(bwrap --ro-bind / / --ro-bind /), $host, + '--bind', $libdir, $libdir, + qw(--dev-bind /dev /dev), + $CAPSULE_CAPTURE_LIBS_TOOL, + "--dest=$libdir", + "--provider=$host", + "from:$test_tempdir/empty", + "from:$test_tempdir/libc"], '>&2'); +{ + opendir(my $dir_iter, $libdir); + my @links = sort grep { $_ ne '.' and $_ ne '..' } readdir $dir_iter; + foreach my $symlink (@links) { + diag "- $symlink -> ".readlink("$libdir/$symlink"); + } + closedir $dir_iter; + is_deeply(\@links, \@libc_family, + 'the same libraries are captured when using $host'); +} +like(readlink "$libdir/libc.so.6", qr{^\Q$host\E\Q$libc\E$}, + '$libdir/libc.so.6 is a symlink to $host + realpath of libc.so.6'); + +run_ok(['rm', '-fr', $libdir]); +mkdir($libdir); +run_ok([qw(bwrap --ro-bind / / --ro-bind /), $host, + '--bind', $libdir, $libdir, + qw(--dev-bind /dev /dev), + $CAPSULE_CAPTURE_LIBS_TOOL, '--link-target=/run/host', + "--dest=$libdir", + "--provider=$host", 'soname:libc.so.6'], '>&2'); +{ + opendir(my $dir_iter, $libdir); + my @links = sort grep { $_ ne '.' and $_ ne '..' } readdir $dir_iter; + foreach my $symlink (@links) { + diag "- $symlink -> ".readlink("$libdir/$symlink"); + } + closedir $dir_iter; + is_deeply(\@links, \@libc_family, + 'the same libraries are captured when using $host and --link-target'); +} +like(readlink "$libdir/libc.so.6", qr{^/run/host\Q$libc\E$}, + '$libdir/libc.so.6 is a symlink to /run/host + realpath of libc.so.6'); + +run_ok(['rm', '-fr', $libdir]); +mkdir($libdir); +run_ok([qw(bwrap --ro-bind / / --ro-bind /), $host, + '--bind', $libdir, $libdir, + qw(--dev-bind /dev /dev), + $CAPSULE_CAPTURE_LIBS_TOOL, '--link-target=/', + "--dest=$libdir", "--provider=$host", + 'soname-match:lib*ml2.so.2*'], '>&2'); +{ + opendir(my $dir_iter, $libdir); + foreach my $symlink (readdir $dir_iter) { + next if $symlink eq '.' || $symlink eq '..'; + diag "- $symlink -> ".readlink("$libdir/$symlink"); + } + closedir $dir_iter; +} +like(readlink "$libdir/libxml2.so.2", qr{^$LIBDIR/libxml2\.so\.2(?:[0-9.]+)$}, + '$libdir/libxml2.so.2 is a symlink to /run/host + realpath of libxml2'); + +run_ok(['rm', '-fr', $libdir]); +mkdir($libdir); +my $result = run_verbose([qw(bwrap --ro-bind / / --ro-bind /), $host, + '--bind', $libdir, $libdir, + qw(--dev-bind /dev /dev), + $CAPSULE_CAPTURE_LIBS_TOOL, '--link-target=/', + "--dest=$libdir", "--provider=$host", + 'soname-match:this*library*does?not?exist'], + '2>', \$stderr, '>&2'); +ok(! $result, 'a non-matching wildcard should fail'); +like($stderr, qr{"this\*library\*does\?not\?exist"}); + +run_ok(['rm', '-fr', $libdir]); +mkdir($libdir); +run_ok([qw(bwrap --ro-bind / / --ro-bind /), $host, + '--bind', $libdir, $libdir, + qw(--dev-bind /dev /dev), + $CAPSULE_CAPTURE_LIBS_TOOL, '--link-target=/', + "--dest=$libdir", "--provider=$host", + 'if-exists:soname-match:this*library*does?not?exist']); + +# libgobject-2.0.so.0 depends on libglib-2.0.so.0 so normally, +# capturing GObject captures GLib +run_ok(['rm', '-fr', $libdir]); +mkdir($libdir); +run_ok([qw(bwrap --ro-bind / / --ro-bind /), $host, + '--bind', $libdir, $libdir, + qw(--dev-bind /dev /dev), + $CAPSULE_CAPTURE_LIBS_TOOL, '--link-target=/', + "--dest=$libdir", "--provider=$host", + 'soname:libgobject-2.0.so.0']); +{ + opendir(my $dir_iter, $libdir); + foreach my $symlink (readdir $dir_iter) { + next if $symlink eq '.' || $symlink eq '..'; + diag "- $symlink -> ".readlink("$libdir/$symlink"); + } + closedir $dir_iter; +} +like(readlink "$libdir/libgobject-2.0.so.0", + qr{^$LIBDIR/libgobject-2\.0\.so\.0(?:[0-9.]+)$}, + '$libdir/libgobject-2.0.so.0 is captured'); +like(readlink "$libdir/libglib-2.0.so.0", + qr{^$LIBDIR/libglib-2\.0\.so\.0(?:[0-9.]+)$}, + '$libdir/libglib-2.0.so.0 is captured as a dependency'); + +# only-dependencies: captures the dependency, GLib but not the +# dependent library, GObject +run_ok(['rm', '-fr', $libdir]); +mkdir($libdir); +run_ok([qw(bwrap --ro-bind / / --ro-bind /), $host, + '--bind', $libdir, $libdir, + qw(--dev-bind /dev /dev), + $CAPSULE_CAPTURE_LIBS_TOOL, '--link-target=/', + "--dest=$libdir", "--provider=$host", + 'only-dependencies:soname:libgobject-2.0.so.0']); +{ + opendir(my $dir_iter, $libdir); + foreach my $symlink (readdir $dir_iter) { + next if $symlink eq '.' || $symlink eq '..'; + diag "- $symlink -> ".readlink("$libdir/$symlink"); + } + closedir $dir_iter; +} +ok(! -e "$libdir/libgobject-2.0.so.0", + 'only-dependencies: does not capture the library itself'); +like(readlink "$libdir/libglib-2.0.so.0", + qr{^$LIBDIR/libglib-2\.0\.so\.0(?:[0-9.]+)$}, + '$libdir/libglib-2.0.so.0 is captured as a dependency'); + +# no-dependencies: captures the dependent library, GObject but not the +# dependency, GLib (not amazingly useful) +run_ok(['rm', '-fr', $libdir]); +mkdir($libdir); +run_ok([qw(bwrap --ro-bind / / --ro-bind /), $host, + '--bind', $libdir, $libdir, + qw(--dev-bind /dev /dev), + $CAPSULE_CAPTURE_LIBS_TOOL, '--link-target=/', + "--dest=$libdir", "--provider=$host", + 'no-dependencies:soname:libgobject-2.0.so.0']); +{ + opendir(my $dir_iter, $libdir); + foreach my $symlink (readdir $dir_iter) { + next if $symlink eq '.' || $symlink eq '..'; + diag "- $symlink -> ".readlink("$libdir/$symlink"); + } + closedir $dir_iter; +} +like(readlink "$libdir/libgobject-2.0.so.0", + qr{^$LIBDIR/libgobject-2\.0\.so\.0(?:[0-9.]+)$}, + '$libdir/libgobject-2.0.so.0 is captured'); +ok(! -e "$libdir/libglib-2.0.so.0", + 'no-dependencies: does not capture libglib-2.0.so.0'); + +# --no-glibc doesn't capture glibc even if other dependencies are +# captured +run_ok(['rm', '-fr', $libdir]); +mkdir($libdir); +run_ok([qw(bwrap --ro-bind / / --ro-bind /), $host, + '--bind', $libdir, $libdir, + qw(--dev-bind /dev /dev), + $CAPSULE_CAPTURE_LIBS_TOOL, '--link-target=/', + "--dest=$libdir", "--provider=$host", "--no-glibc", + 'soname:libgobject-2.0.so.0']); +{ + opendir(my $dir_iter, $libdir); + foreach my $symlink (readdir $dir_iter) { + next if $symlink eq '.' || $symlink eq '..'; + diag "- $symlink -> ".readlink("$libdir/$symlink"); + } + closedir $dir_iter; +} +like(readlink "$libdir/libgobject-2.0.so.0", + qr{^$LIBDIR/libgobject-2\.0\.so\.0(?:[0-9.]+)$}, + '$libdir/libgobject-2.0.so.0 is captured'); +like(readlink "$libdir/libglib-2.0.so.0", + qr{^$LIBDIR/libglib-2\.0\.so\.0(?:[0-9.]+)$}, + '$libdir/libglib-2.0.so.0 is captured'); +ok(! -e "$libdir/libc.so.6", + '--no-glibc does not capture libc.so.6'); + +# only-dependencies:no-dependencies: (either way round) is completely useless +run_ok(['rm', '-fr', $libdir]); +mkdir($libdir); +$result = run_verbose([qw(bwrap --ro-bind / / --ro-bind /), $host, + '--bind', $libdir, $libdir, + qw(--dev-bind /dev /dev), + $CAPSULE_CAPTURE_LIBS_TOOL, '--link-target=/', + "--dest=$libdir", "--provider=$host", + 'only-dependencies:no-dependencies:libglib-2.0.so.0'], + '>&2'); +ok(! $result, 'only-dependencies:no-dependencies: is useless'); +run_ok(['rm', '-fr', $libdir]); +mkdir($libdir); +$result = run_verbose([qw(bwrap --ro-bind / / --ro-bind /), $host, + '--bind', $libdir, $libdir, + qw(--dev-bind /dev /dev), + $CAPSULE_CAPTURE_LIBS_TOOL, '--link-target=/', + "--dest=$libdir", "--provider=$host", + 'no-dependencies:only-dependencies:libglib-2.0.so.0'], + '>&2'); +ok(! $result, 'no-dependencies:only-dependencies: is useless'); + +SKIP: { + skip "not on Linux? Good luck!", 1 unless $^O eq 'linux'; + my $stdout; + my $ld_so; + + run_ok([$CAPSULE_CAPTURE_LIBS_TOOL, '--print-ld.so'], '>', \$ld_so); + chomp $ld_so; + diag "ld.so is $ld_so"; + + run_ok([$CAPSULE_CAPTURE_LIBS_TOOL, '--resolve-ld.so=/'], '>', \$stdout); + my $resolved = $stdout; + chomp $resolved; + + is(abs_path($resolved), abs_path($ld_so), + '--resolve-ld.so=/ should print a path to '.abs_path($ld_so)); + ok(! -l $resolved, + '--resolve-ld.so=/ should not print a symlink'); + + # Autotools considers an unexpected pass to be a test failure, so we + # have to second-guess whether this is going to work. + if ($stdout eq abs_path($ld_so)."\n") { + is($stdout, abs_path($ld_so)."\n", + '--resolve-ld.so=/ should print '.abs_path($ld_so)); + } + else { + TODO: { + local $TODO = 'symlinks before the last component are not resolved, ' + .'e.g. /lib64 -> usr/lib on Arch Linux'; + is($stdout, abs_path($ld_so)."\n", + '--resolve-ld.so=/ should print '.abs_path($ld_so)); + } + } + + run_ok([qw(bwrap --ro-bind / / --ro-bind /), $host, + $CAPSULE_CAPTURE_LIBS_TOOL, "--resolve-ld.so=$host"], + '>', \$stdout); + $resolved = $stdout; + chomp $resolved; + + is(abs_path($resolved), abs_path($ld_so), + "--resolve-ld.so=$host should print a path to ".abs_path($ld_so)); + ok(! -l $resolved, + "--resolve-ld.so=$host should not print a symlink"); + + if ($stdout eq abs_path($ld_so)."\n") { + is($stdout, abs_path($ld_so)."\n", + "--resolve-ld.so=$host should print ".abs_path($ld_so)); + } + else { + TODO: { + local $TODO = 'symlinks before the last component are not resolved, ' + .'e.g. /lib64 -> usr/lib on Arch Linux'; + is($stdout, abs_path($ld_so)."\n", + "--resolve-ld.so=$host should print ".abs_path($ld_so)); + } + } +}; + +SKIP: { + my $multiarch; + my $other_multiarch; + + # For simplicity, we only consider this case on x86. + skip "$ENV{CAPSULE_TESTS_GNU_HOST} not x86_64 or i386", 1 + unless $ENV{CAPSULE_TESTS_GNU_HOST} =~ m/^(x86_64|i.86)-/; + + if ($ENV{CAPSULE_TESTS_GNU_HOST} =~ m/^x86_64-/) { + $other_multiarch = 'i386-linux-gnu'; + } + else { + $other_multiarch = 'x86_64-linux-gnu'; + } + + skip "$other_multiarch libxml2.so.2 not available", 1 + unless -e "/usr/lib/$other_multiarch/libxml2.so.2"; + + # Normally, the wrong ABI is an error... + run_ok(['rm', '-fr', $libdir]); + mkdir($libdir); + $result = run_verbose([qw(bwrap --ro-bind / / --ro-bind /), $host, + '--bind', $libdir, $libdir, + qw(--dev-bind /dev /dev), + $CAPSULE_CAPTURE_LIBS_TOOL, '--link-target=/', + "--dest=$libdir", "--provider=$host", + "path:/usr/lib/$other_multiarch/libxml2.so.2"], + '>&2'); + ok(! $result, 'library of wrong ABI yields an error'); + ok(! -e "$libdir/libxml2.so.2"); + + # ... but when we're dealing with a glob match, other ABIs are silently + # ignored. + run_ok(['rm', '-fr', $libdir]); + mkdir($libdir); + $result = run_verbose([qw(bwrap --ro-bind / / --ro-bind /), $host, + '--bind', $libdir, $libdir, + qw(--dev-bind /dev /dev), + $CAPSULE_CAPTURE_LIBS_TOOL, '--link-target=/', + "--dest=$libdir", "--provider=$host", + "path-match:/usr/lib/$other_multiarch/libxml2.so.2"], + '>&2'); + ok($result, 'library of wrong ABI ignored when using path-match'); + ok(! -e "$libdir/libxml2.so.2"); + + # We can also ignore this case explicitly. + run_ok(['rm', '-fr', $libdir]); + mkdir($libdir); + $result = run_verbose([qw(bwrap --ro-bind / / --ro-bind /), $host, + '--bind', $libdir, $libdir, + qw(--dev-bind /dev /dev), + $CAPSULE_CAPTURE_LIBS_TOOL, '--link-target=/', + "--dest=$libdir", "--provider=$host", + "if-same-abi:path:/usr/lib/$other_multiarch/libxml2.so.2"], + '>&2'); + ok($result, 'library of wrong ABI ignored when using if-same-abi'); + ok(! -e "$libdir/libxml2.so.2"); +}; + +SKIP: { + my $version1 = "$builddir/tests/version1"; + $version1 .= '/.libs' if libcapsule_uninstalled; + my $version2 = "$builddir/tests/version2"; + $version2 .= '/.libs' if libcapsule_uninstalled; + + skip "shared libraries disabled", 1 unless -e "$version1/libunversionedsymbols.so.1"; + + run_ok(['rm', '-fr', $libdir]); + mkdir($libdir); + $result = run_verbose([qw(bwrap --ro-bind / /), + '--tmpfs', $host, + bind_usr('/', $host), + '--tmpfs', $container, + bind_usr('/', $container), + '--ro-bind', $version1, "$host/opt", + '--ro-bind', $version2, "$container/opt", + '--bind', $libdir, $libdir, + qw(--dev-bind /dev /dev), + 'env', 'CAPSULE_DEBUG=all', + $CAPSULE_CAPTURE_LIBS_TOOL, '--link-target=/run/host', + "--dest=$libdir", "--provider=$host", + "--compare-by=versions,name,symbols,provider", + "--container=$container", + 'path-match:/opt/lib*.so.1'], + '2>', \$stderr, '>&2'); + diag $stderr; + ok($result); + is(tolerant_readlink("$libdir/libunversionedabibreak.so.1"), + '/run/host/opt/libunversionedabibreak.so.1.0.0', + "should take provider's version when neither's symbols are a superset of the other"); + ok(! -l "$libdir/libunversionednumber.so.1", + "should not take provider's version when container's numeric tail is strictly newer"); + ok(! -l "$libdir/libunversionedsymbols.so.1", + "should not take provider's version when container has strictly more symbols"); + is(tolerant_readlink("$libdir/libversionedabibreak.so.1"), + '/run/host/opt/libversionedabibreak.so.1.0.0', + "should take provider's version when neither's symbol-versions are a superset of the other"); + ok(! -l "$libdir/libversionedlikedbus.so.1", + "should not take provider's older fake libdbus"); + ok(! -l "$libdir/libversionedlikeglibc.so.1", + "should not take provider's older fake glibc"); + ok(! -l "$libdir/libversionednumber.so.1", + "should not take provider's version when container's numeric tail is strictly newer and symbols are equal"); + ok(! -l "$libdir/libversionedsymbols.so.1", + "should not take provider's version when container has strictly more symbols"); + ok(! -l "$libdir/libversionedupgrade.so.1", + "should not take provider's version when container has strictly more symbols"); + + # The opposite. + run_ok(['rm', '-fr', $libdir]); + mkdir($libdir); + $result = run_verbose([qw(bwrap --ro-bind / /), + '--tmpfs', $host, + bind_usr('/', $host), + '--tmpfs', $container, + bind_usr('/', $container), + '--ro-bind', $version1, "$container/opt", + '--ro-bind', $version2, "$host/opt", + '--bind', $libdir, $libdir, + qw(--dev-bind /dev /dev), + 'env', 'CAPSULE_DEBUG=all', + $CAPSULE_CAPTURE_LIBS_TOOL, '--link-target=/run/host', + "--dest=$libdir", "--provider=$host", + # This time we leave the trailing ,provider implict + "--compare-by=versions,name,symbols", + "--container=$container", + 'path-match:/opt/lib*.so.1'], + '2>', \$stderr, '>&2'); + diag $stderr; + ok($result); + is(tolerant_readlink("$libdir/libunversionedabibreak.so.1"), + '/run/host/opt/libunversionedabibreak.so.1.0.0', + "should take provider's version when neither's symbols are a superset of the other"); + is(tolerant_readlink("$libdir/libunversionednumber.so.1"), + '/run/host/opt/libunversionednumber.so.1.2.3', + "should take provider's version when container's numeric tail is strictly older"); + is(tolerant_readlink("$libdir/libunversionedsymbols.so.1"), + '/run/host/opt/libunversionedsymbols.so.1.0.0', + "should take provider's version when it has strictly more symbols than container"); + is(tolerant_readlink("$libdir/libversionedabibreak.so.1"), + '/run/host/opt/libversionedabibreak.so.1.0.0', + "should take provider's version when neither's symbol-versions are a superset of the other"); + is(tolerant_readlink("$libdir/libversionedlikedbus.so.1"), + '/run/host/opt/libversionedlikedbus.so.1.2.0', + "should take provider's newer fake libdbus"); + is(tolerant_readlink("$libdir/libversionedlikeglibc.so.1"), + '/run/host/opt/libversionedlikeglibc.so.1.0.0', + "should take provider's newer fake glibc"); + is(tolerant_readlink("$libdir/libversionednumber.so.1"), + '/run/host/opt/libversionednumber.so.1.2.3', + "should take provider's version when its numeric tail is strictly newer and symbols are equal"); + is(tolerant_readlink("$libdir/libversionedsymbols.so.1"), + '/run/host/opt/libversionedsymbols.so.1.0.0', + "should take provider's version when it has strictly more symbols"); + is(tolerant_readlink("$libdir/libversionedupgrade.so.1"), + '/run/host/opt/libversionedupgrade.so.1.0.0', + "should take provider's version when it has strictly more symbols"); + + # The default is equivalent to --compare-by=name,provider. + run_ok(['rm', '-fr', $libdir]); + mkdir($libdir); + $result = run_verbose([qw(bwrap --ro-bind / /), + '--tmpfs', $host, + bind_usr('/', $host), + '--tmpfs', $container, + bind_usr('/', $container), + '--ro-bind', $version1, "$host/opt", + '--ro-bind', $version2, "$container/opt", + '--bind', $libdir, $libdir, + qw(--dev-bind /dev /dev), + 'env', 'CAPSULE_DEBUG=all', + $CAPSULE_CAPTURE_LIBS_TOOL, '--link-target=/run/host', + "--dest=$libdir", "--provider=$host", + "--container=$container", + 'path-match:/opt/lib*.so.1'], + '2>', \$stderr, '>&2'); + diag $stderr; + ok($result); + is(tolerant_readlink("$libdir/libunversionedabibreak.so.1"), + '/run/host/opt/libunversionedabibreak.so.1.0.0', + "should take provider's version by default if name is the same"); + is(tolerant_readlink("$libdir/libunversionedsymbols.so.1"), + '/run/host/opt/libunversionedsymbols.so.1.0.0', + "should take provider's version by default if name is the same"); + is(tolerant_readlink("$libdir/libversionedabibreak.so.1"), + '/run/host/opt/libversionedabibreak.so.1.0.0', + "should take provider's version by default if name is the same"); + is(tolerant_readlink("$libdir/libversionedsymbols.so.1"), + '/run/host/opt/libversionedsymbols.so.1.0.0', + "should take provider's version by default if name is the same"); + is(tolerant_readlink("$libdir/libversionedupgrade.so.1"), + '/run/host/opt/libversionedupgrade.so.1.0.0', + "should take provider's version by default if name is the same"); + is(tolerant_readlink("$libdir/libversionedlikeglibc.so.1"), + '/run/host/opt/libversionedlikeglibc.so.1.0.0', + "should take provider's version by default if name is the same"); + ok(! -l "$libdir/libunversionednumber.so.1", + "should not take provider's version when container's numeric tail is newer"); + ok(! -l "$libdir/libversionedlikedbus.so.1", + "should not take provider's version when container's numeric tail is newer"); + ok(! -l "$libdir/libversionednumber.so.1", + "should not take provider's version when container's numeric tail is newer"); + + run_ok(['rm', '-fr', $libdir]); + mkdir($libdir); + $result = run_verbose([qw(bwrap --ro-bind / /), + '--tmpfs', $host, + bind_usr('/', $host), + '--tmpfs', $container, + bind_usr('/', $container), + '--ro-bind', $version1, "$host/opt", + '--ro-bind', $version2, "$container/opt", + '--ro-bind', "$test_tempdir/capture-libs.ini", + "$test_tempdir/capture-libs.ini", + '--bind', $libdir, $libdir, + qw(--dev-bind /dev /dev), + 'env', 'CAPSULE_DEBUG=all', + $CAPSULE_CAPTURE_LIBS_TOOL, '--link-target=/run/host', + "--dest=$libdir", "--provider=$host", + "--container=$container", + "--library-knowledge=$test_tempdir/capture-libs.ini", + 'path-match:/opt/lib*.so.1'], + '2>', \$stderr, '>&2'); + diag $stderr; + ok($result); + ok(! -l "$libdir/libversionedsymbols.so.1", + "should not take provider's version when told to look at container's symbols"); + ok(! -l "$libdir/libversionedlikedbus.so.1", + "should not take provider's version when forced to use container's"); + is(tolerant_readlink("$libdir/libversionedprivatenothidden.so.1"), + '/run/host/opt/libversionedprivatenothidden.so.1.1.1', + "should take provider's version by default when they have the same symbol versions"); +} + +done_testing; + +# vim:set sw=4 sts=4 et: diff --git a/subprojects/libcapsule/tests/gl-shim.pl b/subprojects/libcapsule/tests/gl-shim.pl new file mode 100755 index 0000000000000000000000000000000000000000..3ff45e451da56eb0e2b519d0e716c921e939b75c --- /dev/null +++ b/subprojects/libcapsule/tests/gl-shim.pl @@ -0,0 +1,115 @@ +#!/usr/bin/perl + +# Copyright © 2017 Collabora Ltd +# +# This file is part of libcapsule. +# +# libcapsule is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as +# published by the Free Software Foundation; either version 2.1 of the +# License, or (at your option) any later version. +# +# libcapsule 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 Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with libcapsule. If not, see <http://www.gnu.org/licenses/>. + +use autodie; +use warnings; +use strict; + +use File::Temp qw(); +use IPC::Run qw(run); +use Test::More; + +use FindBin; +use lib $FindBin::Bin; + +use CapsuleTest; + +skip_all_unless_nm; + +my $test_tempdir = File::Temp->newdir(); +diag "Working directory: $test_tempdir"; +chdir $test_tempdir; + +my ($output, $ignored, $error); + +my @libcapsule_environment; +if (length $ENV{CAPSULE_TESTS_UNINSTALLED}) { + # Make sure the shim can load the just-built libcapsule + push @libcapsule_environment, "LD_LIBRARY_PATH=$ENV{G_TEST_BUILDDIR}/.libs"; +} +delete $ENV{MAKEFLAGS}; + +my $examples = "$srcdir/examples"; + +my @sonames = qw(libEGL.so.1 libGL.so.1 libGLESv2.so.2 libGLX.so.0 + libOpenGL.so.0 libX11.so.6 libXext.so.6 libXi.so.6 libgbm.so.1 + libxcb-dri2.so.0 libxcb-glx.so.0 libxcb-present.so.0 libxcb-sync.so.1 + libxcb.so.1); + +my @configure_arguments = (); + +if (length $ENV{CAPSULE_TESTS_GNU_BUILD}) { + push @configure_arguments, '--build', $ENV{CAPSULE_TESTS_GNU_BUILD}; +} + +if (length $ENV{CAPSULE_TESTS_GNU_HOST}) { + push @configure_arguments, '--host', $ENV{CAPSULE_TESTS_GNU_HOST}; +} + +# Don't require running capsule-version - just use the major versions +sub use_major_version { + my $soname = shift; + if ($soname =~ m/.*\.([0-9]+)$/) { + return "$soname/$1"; + } + else { + die "$soname doesn't end with a major version"; + } +} + +run_ok([$CAPSULE_INIT_PROJECT_TOOL, + '--runtime-tree=/run/host', + '--set-version=1.0.0', + "--symbols-from=$examples/shim", + map { use_major_version($_) } @sonames, + ]); +# The default name for the output is taken from the first library, +# which in this case happens to be libEGL-proxy +run_ok([ + 'sh', '-euc', 'cd "$1"; shift; ./configure "$@"', + 'sh', "$test_tempdir/libEGL-proxy", + @configure_arguments, + ], '>&2'); +run_ok(['make', '-C', "$test_tempdir/libEGL-proxy", 'V=1'], '>&2'); + +foreach my $soname (@sonames) { + my $basename = $soname; + $basename =~ s/\.so\..*$//; + + ok(-e "$test_tempdir/libEGL-proxy/$basename.la"); + ok(-e "$test_tempdir/libEGL-proxy/.libs/$basename.so"); + ok(-e "$test_tempdir/libEGL-proxy/.libs/$soname"); + ok(-e "$test_tempdir/libEGL-proxy/.libs/$soname.0.0"); + + my @symbols_wanted; + open my $fh, "$examples/shim/$soname.symbols"; + while (defined(my $line = <$fh>)) { + chomp $line; + push @symbols_wanted, $line; + } + + my @symbols_produced = + get_symbols_with_nm("$test_tempdir/libEGL-proxy/.libs/$soname"); + is_deeply \@symbols_wanted, [grep {! m/^capsule_meta $/} @symbols_produced]; +} + +chdir '/'; +done_testing; + +# vim:set sw=4 sts=4 et: diff --git a/subprojects/libcapsule/tests/glib-tap.sh b/subprojects/libcapsule/tests/glib-tap.sh new file mode 100755 index 0000000000000000000000000000000000000000..ccb44b0435a160d4051cb0bc57df708d7dcc101e --- /dev/null +++ b/subprojects/libcapsule/tests/glib-tap.sh @@ -0,0 +1,14 @@ +#!/bin/sh +# Wrapper to make GTest tests output TAP syntax, because Automake's test +# drivers do not currently support passing the same command-line argument +# to each test executable. All GTest tests produce TAP output if invoked +# with the --tap option. +# +# Usage: "glib-tap.sh test-foo --verbose ..." is equivalent to +# "test-foo --tap --verbose ..." + +set -e +t="$1" +shift + +exec "$t" --tap "$@" diff --git a/subprojects/libcapsule/tests/init-project.pl b/subprojects/libcapsule/tests/init-project.pl new file mode 100755 index 0000000000000000000000000000000000000000..6781c8e90fb8af202e47cdcc47fbaad7f2cc931f --- /dev/null +++ b/subprojects/libcapsule/tests/init-project.pl @@ -0,0 +1,237 @@ +#!/usr/bin/perl + +# Copyright © 2017 Collabora Ltd +# +# This file is part of libcapsule. +# +# libcapsule is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as +# published by the Free Software Foundation; either version 2.1 of the +# License, or (at your option) any later version. +# +# libcapsule 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 Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with libcapsule. If not, see <http://www.gnu.org/licenses/>. + +use autodie; +use warnings; +use strict; + +use File::Temp qw(); +use IPC::Run qw(run); +use Test::More; + +use FindBin; +use lib $FindBin::Bin; + +use CapsuleTest; + +skip_all_unless_nm; + +my $test_tempdir = File::Temp->newdir(); +diag "Working directory: $test_tempdir"; +chdir $test_tempdir; + +my ($output, $ignored, $error); + +my @libcapsule_environment; +if (length $ENV{CAPSULE_TESTS_UNINSTALLED}) { + # Make sure the shim can load the just-built libcapsule + push @libcapsule_environment, "LD_LIBRARY_PATH=$ENV{G_TEST_BUILDDIR}/.libs"; +} +delete $ENV{MAKEFLAGS}; + +my @configure_arguments = (); + +if (length $ENV{CAPSULE_TESTS_GNU_BUILD}) { + push @configure_arguments, '--build', $ENV{CAPSULE_TESTS_GNU_BUILD}; +} + +if (length $ENV{CAPSULE_TESTS_GNU_HOST}) { + push @configure_arguments, '--host', $ENV{CAPSULE_TESTS_GNU_HOST}; +} + +run_ok([$CAPSULE_INIT_PROJECT_TOOL, + '--search-tree=/', + '--runtime-tree=/run/host', + 'libz.so.1/1.2.3', + 'libxml2.so.2']); +run_ok([ + 'sh', '-euc', 'cd "$1"; shift; ./configure "$@"', + 'sh', "$test_tempdir/libz-proxy", + @configure_arguments, + '--with-search-tree=/', + '--with-runtime-tree=/host', + ], '>&2'); +run_ok(['make', '-C', "$test_tempdir/libz-proxy", 'V=1'], '>&2'); +ok(-e "$test_tempdir/libz-proxy/libz.la"); +ok(-e "$test_tempdir/libz-proxy/.libs/libz.so"); +ok(-e "$test_tempdir/libz-proxy/.libs/libz.so.1"); +ok(-e "$test_tempdir/libz-proxy/.libs/libz.so.1.2.3"); +ok(-e "$test_tempdir/libz-proxy/libxml2.la"); +ok(-e "$test_tempdir/libz-proxy/.libs/libxml2.so"); +ok(-e "$test_tempdir/libz-proxy/.libs/libxml2.so.2"); +ok(-e "$test_tempdir/libz-proxy/.libs/libxml2.so.2.0.0"); +{ + local $/ = undef; # read entire file in one go + open(my $fh, '<', "$test_tempdir/libz-proxy/shim/libz.so.1.c"); + my $source = <$fh>; + like($source, qr{\.default_prefix\s*=\s*"/host"}, + 'Configure-time runtime tree takes precedence'); + unlike($source, qr{\.default_prefix\s*=\s*"/run/host"}, + 'Init-time runtime tree is not used'); + unlike($source, qr{\.default_prefix\s*=\s*"/"}, + 'Search tree is not used at runtime'); + close $fh; +} +{ + local $/ = undef; # read entire file in one go + open(my $fh, '<', "$test_tempdir/libz-proxy/shim/libxml2.so.2.c"); + my $source = <$fh>; + like($source, qr{\.default_prefix\s*=\s*"/host"}, + 'Configure-time runtime tree takes precedence'); + unlike($source, qr{\.default_prefix\s*=\s*"/run/host"}, + 'Init-time runtime tree is not used'); + unlike($source, qr{\.default_prefix\s*=\s*"/"}, + 'Search tree is not used at runtime'); + close $fh; +} + +run_ok([$CAPSULE_SYMBOLS_TOOL, 'libz.so.1'], + '>', \$output); +my @symbols_wanted = sort(split /\n/, $output); +foreach my $sym (@symbols_wanted) { + diag "- $sym"; +} + +sub uniq { + my @unique; + my %seen; + foreach my $item (@_) { + push @unique, $item unless $seen{$item}; + $seen{$item} = 1; + } + return @unique; +} + +my @symbols_produced = + grep { !/^ZLIB_\d+\./ } + get_symbols_with_nm("$test_tempdir/libz-proxy/.libs/libz.so.1"); +is_deeply \@symbols_wanted, [grep {! m/^capsule_meta $/} @symbols_produced]; + +# Do the same thing with capsule-symbols. +# We can't load the library unless we let it load its real +# implementation, and it's hardwired to get that from /host, which +# probably doesn't exist... so cheat by overriding it. +run_ok(['env', "CAPSULE_PREFIX=/", @libcapsule_environment, + $CAPSULE_SYMBOLS_TOOL, "$test_tempdir/libz-proxy/.libs/libz.so.1"], + '>', \$output); +@symbols_produced = grep { !/capsule_meta\b/ } sort(split /\n/, $output); +foreach my $sym (@symbols_produced) { + diag "- $sym"; +} + +if ($symbols_produced[0] eq $symbols_produced[1]) { + TODO: { + local $TODO = 'Each symbol shows up twice on some OSs'; + is_deeply \@symbols_wanted, \@symbols_produced; + } +} + +is_deeply \@symbols_wanted, [uniq @symbols_produced]; + +# Make sure the symbols list appears older than the shared list. +# Exclude one symbol, effectively behaving as though we'd used an +# older version of zlib that didn't have it. +{ + open(my $fh, '>', "$test_tempdir/libz-proxy/shim/libz.so.1.symbols"); + foreach my $symbol (@symbols_wanted) { + print $fh "$symbol\n" unless $symbol =~ m/^zlibCompileFlags/; + } + close($fh); +} +run_ok(['touch', '-d', '1980-01-01 00:00', + "$test_tempdir/libz-proxy/shim/libz.so.1.symbols"], '>&2'); +# This doesn't fail or regenerate the symbols list +run_ok(['make', '-C', "$test_tempdir/libz-proxy", 'V=1'], '>&2'); +{ + local $/ = undef; # read entire file in one go + open(my $fh, '<', "$test_tempdir/libz-proxy/shim/libz.so.1.symbols"); + my $symbols = <$fh>; + unlike($symbols, qr{^zlibCompileFlags}m); + close $fh; +} + +# Make the shared list have actually differing contents +run_ok(['touch', '-d', '1980-01-01 00:00', + "$test_tempdir/libz-proxy/shim/libz.so.1.symbols"], '>&2'); +rename("$test_tempdir/libz-proxy/shim/libz.so.1.shared", + "$test_tempdir/libz-proxy/shim/libz.so.1.shared.good"); +{ + open(my $fh, '>', "$test_tempdir/libz-proxy/shim/libz.so.1.shared"); + print $fh "libfoo.so.2\n"; + print $fh "libbar.so.3\n"; + close($fh); +} +# This does fail +run(['make', '-C', "$test_tempdir/libz-proxy", 'V=1'], + '>', \$ignored, '2>', \$error); +diag_multiline $error; +like($error, qr{ERROR.*make maintainer-update-capsule-symbols}s); +# ... and does not fix the problem +run(['make', '-C', "$test_tempdir/libz-proxy", 'V=1'], + '>', \$ignored, '2>', \$error); +diag_multiline $error; +like($error, qr{ERROR.*make maintainer-update-capsule-symbols}s); + +# Pretend we had listed the symbols of libfoo and libbar, and go back +# to just zlib +rename("$test_tempdir/libz-proxy/shim/libz.so.1.shared", + "$test_tempdir/libz-proxy/shim/libz.so.1.symbols.updated-for"); +rename("$test_tempdir/libz-proxy/shim/libz.so.1.shared.good", + "$test_tempdir/libz-proxy/shim/libz.so.1.shared"); +{ + open(my $fh, '>', "$test_tempdir/libz-proxy/shim/libz.so.1.symbols"); + close($fh); +} +run_ok(['make', '-C', "$test_tempdir/libz-proxy", 'V=1', + 'maintainer-update-capsule-symbols'], '>&2'); +# The file has been updated with the full list of symbols +{ + local $/ = undef; # read entire file in one go + open(my $fh, '<', "$test_tempdir/libz-proxy/shim/libz.so.1.symbols"); + my $symbols = <$fh>; + like($symbols, qr{^zlibCompileFlags}m); + close $fh; +} +run_ok(['make', '-C', "$test_tempdir/libz-proxy", 'V=1'], '>&2'); + +@symbols_produced = + grep { !/^ZLIB_\d+\./ } + get_symbols_with_nm("$test_tempdir/libz-proxy/.libs/libz.so.1"); +is_deeply \@symbols_wanted, [grep {! m/^capsule_meta $/} @symbols_produced]; + +run_ok(['env', "CAPSULE_PREFIX=/", @libcapsule_environment, + $CAPSULE_SYMBOLS_TOOL, "$test_tempdir/libz-proxy/.libs/libz.so.1"], + '>', \$output); +@symbols_produced = grep { !/^capsule_meta\b/ } sort(split /\n/, $output); +foreach my $sym (@symbols_produced) { + diag "- $sym"; +} + +if ($symbols_produced[0] eq $symbols_produced[1]) { + TODO: { + local $TODO = 'Each symbol shows up twice on some OSs'; + is_deeply \@symbols_wanted, \@symbols_produced; + } +} +is_deeply \@symbols_wanted, [uniq @symbols_produced]; + +chdir '/'; +done_testing; + +# vim:set sw=4 sts=4 et: diff --git a/subprojects/libcapsule/tests/libversioned.map b/subprojects/libcapsule/tests/libversioned.map new file mode 100644 index 0000000000000000000000000000000000000000..72eb3014514adb75ee9bab73e98f34ddbb231c5d --- /dev/null +++ b/subprojects/libcapsule/tests/libversioned.map @@ -0,0 +1,4 @@ +LIBVERSIONED1 { + global: + *; +}; diff --git a/subprojects/libcapsule/tests/manual/gl.pl b/subprojects/libcapsule/tests/manual/gl.pl new file mode 100755 index 0000000000000000000000000000000000000000..2310d6e626b77b4bedf65f11849bf2746008bcef --- /dev/null +++ b/subprojects/libcapsule/tests/manual/gl.pl @@ -0,0 +1,1052 @@ +#!/usr/bin/env perl + +# gl.pl — exercise libGL + bwrap, with or without libcapsule +# +# Copyright © 2017 Collabora Ltd +# +# This file is part of libcapsule. +# +# libcapsule is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as +# published by the Free Software Foundation; either version 2.1 of the +# License, or (at your option) any later version. +# +# libcapsule 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 Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with libcapsule. If not, see <http://www.gnu.org/licenses/>. + +use warnings; +use strict; + +use autodie; +use Cwd qw(realpath); +use File::Path qw(make_path); +use File::Spec qw(); +use File::Temp qw(); +use Getopt::Long; +use IPC::Run qw(run); +use Sort::Versions; +use Test::More; + +use FindBin; +use lib ($FindBin::Bin, "$FindBin::Bin/.."); +use CapsuleTest; + +=encoding utf8 + +=head1 NAME + +gl.pl - exercise libGL + bwrap, with or without libcapsule + +=head1 SYNOPSIS + +B<gl.pl> [I<OPTIONS>] [I<COMMAND> [I<ARGUMENTS...>]] + +Run C<I<COMMAND> I<ARGUMENTS...>> in the container, or if no command +is given, run B<glxinfo>(1) and make some assertions about its output. + +The GL stack is made available in the container as F</overrides/lib/*>. +This can either be the host system's GL stack (provided as symbolic +links into F</run/host/{usr/,}lib*>), a different sysroot's GL stack +(provided as symbolic links into F</run/host/some-sysroot/{usr/,}lib*>), a +standalone GL stack (for example proprietary binaries), or a libcapsule +shim that will load the real GL stack from F</run/host> or +F</run/host/some-sysroot>. + +F</usr>, F</bin>, F</sbin>, F</lib*>, F</etc/alternatives> and +F</etc/ld.so.cache> from the host system or a sysroot are always mounted +at F</run/host> or F</run/host/some-sysroot>. + +By default, glibc is taken from either the GL provider or the container, +whichever has the newer version. It can also be taken from a third tree. +If the version in use is not the container's, F</overrides/lib/*> is +populated with symbolic links into F</run/host/.../lib*>. + +Similarly, libcapsule and libelf are taken from either the GL provider +or the container, or can be taken from a third tree. + +Other core graphics libraries (libX11, libxcb, etc.) are taken from either +the GL provider, the container, or another tree. + +An application "payload" is mounted on F</app>, as if for Flatpak. + +The B<LD_LIBRARY_PATH> for the child process is made to include +library directories from F</app> and F</overrides>. + +=head1 EXAMPLES + +=over + +=item Basic use + + $ gl.pl + +Run B<glxinfo>(1), non-interactively. The container is a Debian stretch +Flatpak runtime; get it using the instructions from +L<https://gitlab.collabora.com/smcv/flatdeb/blob/master/README>, +and rsync ~/.cache/flatdeb/repo onto a separate test machine if necessary. + +The OpenGL implementation and graphics stack are provided by the host +system. Core libraries (glibc, libcapsule, libX11) and +supporting libraries for the graphics stack (libstdc++, libpciaccess, +libXext) are provided by either the container or the host system, +whichever has the newer version, preferring the host system if the +versions appear to be the same. + +=item Using the Steam Runtime + + $ gl.pl \ + --flatpak-runtime=com.valvesoftware.SteamRuntime.Platform/x86_64/scout_beta \ + --flatpak-app=org.debian.packages.mesa_utils/x86_64/scout_beta + +Run B<glxinfo>(1) in the Steam Runtime. The container is a Steam Runtime +scout_beta Flatpak runtime; get it using the instructions from +L<https://gitlab.collabora.com/smcv/flatdeb-steam/blob/master/README>, +and rsync ~/.cache/flatdeb/repo onto a separate test machine if necessary. + +=item Running glxgears + + $ gl.pl glxgears + +Run B<glxgears>(1) instead of B<glxinfo>(1). Press B<Escape> to exit. + +=item Running OpenArena + + $ gl.pl \ + --flatpak-app=org.debian.packages.openarena/x86_64/master \ + -- \ + openarena +set com_hunkmegs 512 +set timedemo 1 +demo demo088-test1 + +Run B<openarena>(6) instead of B<glxinfo>(1), and run a demo at the maximum +possible frame rate. Type B<\quit> into the terminal, or +B<Shift+Escape \quit> into the GUI window, when the demo has finished. + +=item Using a sysroot instead of a Flatpak runtime + + $ sudo debootstrap --include=(many packages) stretch \ + ~/stretch-sysroot http://deb.debian.org/debian + $ gl.pl --container ~/stretch-sysroot + +The container can be a complete sysroot (with a F</usr>) instead of just +a Flatpak runtime or a merged F</usr>. + +=item Using NVIDIA proprietary drivers + + $ gl.pl --proposed-nvidia + +This uses the host NVIDIA drivers, without libcapsule. + +=item Using Mesa not via libcapsule + + $ gl.pl --full-mesa + +This uses the host Mesa drivers, without libcapsule. We need to pull in +lots of libraries from the host system. + +=item Using Mesa via libcapsule + + $ gl.pl --proposed-mesa + +This uses the host Mesa drivers, with libcapsule. + +=item Using a non-host GL stack + + $ sudo debootstrap --include=(many packages) sid \ + ~/sid-sysroot http://deb.debian.org/debian + $ gl.pl --gl-provider ~/sid-sysroot --container ~/stretch-sysroot + +The GL provider can be a complete sysroot (with a F</usr>), or a +merged F</usr>, instead of the host system. + +If the GL provider uses B<GLVND> and the host system's X11 display does +not have the B<GLX_EXT_libglvnd> extension, then either the GL provider +must have a symbolic link B<libGLX_indirect.so.0> to the correct +vendor GLX library, or B<__GLX_FORCE_VENDOR_LIBRARY_0> or +B<__GLX_VENDOR_LIBRARY_NAME> must be set to the correct vendor name +(typically B<mesa> or B<nvidia>). + +=item Using libcapsule + + $ gl.pl --libcapsule + # which is just a shortcut for... + $ gl.pl --gl-stack=/usr/lib/libcapsule/shims + +Actually use libcapsule. This currently works with a SteamOS brewmaster +(Debian jessie-based) host, a Steam Runtime or Debian stretch container, +Mesa graphics, and glxinfo, glxgears or OpenArena as the app, but crashes +when the graphics driver is NVIDIA. + +=back + +=head1 OPTIONS + +=over + +=item --app=I<APP> + +Mount I<APP> on F</app>. See B<--flatpak-app> for the default. + +=item --container=I<TREE> + +A complete sysroot or a merged F</usr> for the container. Most libraries +will come from here. See B<--flatpak-runtime> for the default. + +=item --flatpak-app=I<ID>/I<ARCH>/I<BRANCH> + +Assume that I<ID>/I<ARCH>/I<BRANCH> has been installed per-user with +C<flatpak --user install ...>, and use it as the B<--app>. +The default is F<org.debian.packages.mesa_utils/x86_64/scout_beta>. + +=item --flatpak-runtime=I<ID>/I<ARCH>/I<BRANCH> + +Assume that I<ID>/I<ARCH>/I<BRANCH> has been installed per-user with +C<flatpak --user install ...>, and use it as the B<--container>. +The default is F<net.debian.flatpak.Games.Platform/x86_64/stretch>. +Another interesting value is +F<com.valvesoftware.SteamRuntime.Platform/x86_64/scout_beta>. + +=item --gl-provider=I<TREE> + +A complete sysroot or a merged F</usr> for the OpenGL provider. +The OpenGL implementation will come from here unless overridden +by B<--gl-stack>, and whatever supporting libraries are required will +come from here unless overridden by B<--libc-provider>, +B<--libcapsule-provider>, B<--x11-provider>. It will be mounted under +F</run/host>. The default is F</>, meaning we use the host system +as the OpenGL provider. + +=item --gl-stack=I<TREE> + +A pre-prepared OpenGL stack, with F<lib/I<TUPLE>> subdirectories +containing F<libGLX.so.1> and related libraries. It will be mounted +under F</run/host>, and F</overrides> will be populated with symlinks +to it and added to the B<LD_LIBRARY_PATH>. + +=item --multiarch=I<TUPLE>[,I<TUPLE>...] + +A list of Debian multiarch tuples to enable. The default is +C<x86_64-linux-gnu>. + +=item --libc-provider=B<auto>|B<container>|B<gl-provider>|I<USR>|I<SYSROOT> + +Get glibc (libc, libm, libpthread etc.) from here. B<container> uses the +B<--container> (or B<--flatpak-runtime>). B<gl-provider> uses the +B<--gl-provider>. B<auto> uses whichever of those appears to have newer +libraries, mixing versions from both if necessary. Alternatively, +a complete sysroot or a merged F</usr> can be given. +The default is B<auto>. + +=item --x11-provider=B<auto>|B<container>|B<gl-provider>|I<USR>|I<SYSROOT> + +The same as for B<--libc-provider>, but for libX11 and related libraries. +The default is B<auto>. + +=item --libcapsule-provider=B<auto>|B<container>|B<gl-provider>|I<USR>|I<SYSROOT> + +The same as for B<--libc-provider>, but for libcapsule and libelf. +The default is B<container>, but B<--libcapsule> overrides this to +B</>. + +=item --libcapsule + +A shortcut for B<--gl-stack=/usr/lib/libcapsule/shims> and +B<--libcapsule-provider=/>. +This uses the libcapsule libGL, etc. shims from the host system. + +=item --proposed-mesa + +Use behaviour proposed as potentially appropriate for the Mesa graphics +stack: B<--gl-provider=/> B<--libcapsule> B<--libc-provider=auto> +B<--x11-provider=auto>. + +=item --proposed-nvidia + +Use behaviour proposed as potentially appropriate for the NVIDIA graphics +stack, or other binary blobs with conservative dependencies: +B<--gl-provider=/> B<--libc-provider=container> +B<--x11-provider=container>. + +=back + +=cut + +my $ansi_bright = ""; +my $ansi_cyan = ""; +my $ansi_green = ""; +my $ansi_magenta = ""; +my $ansi_reset = ""; + +if (-t STDOUT) { + # Mnemonic: cyan is the container, green is the GL provider, magenta + # is miscellaneous + $ansi_bright = "\e[1m"; + $ansi_cyan = "\e[36m"; + $ansi_green = "\e[32m"; + $ansi_magenta = "\e[35m"; + $ansi_reset = "\e[0m"; +} + +my %CAPSULE_VERSION_TOOLS; +my %CAPSULE_CAPTURE_LIBS_TOOLS; + +=head1 FUNCTIONS + +=over + +=item run_in_container(I<TREE>, I<ARGV>, ...) + +Use B<bwrap>(1) to run the command I<ARGV> (an array reference) in I<TREE>, +which may either be a complete sysroot or just the contents of a merged +F</usr> (see C<bind_usr>). The command is assumed not to need write +access to anything in the tree. Remaining arguments are passed to +C<IPC::Run::run> after the I<ARGV>. + +=cut + +sub run_in_container { + my $tree = shift; + my $argv = shift; + my @run_params = @_; + my @bwrap; + + @bwrap = qw(bwrap --dev-bind /dev /dev --bind /proc /proc --bind /sys /sys); + push @bwrap, bind_usr($tree); + + foreach my $subdir (qw(etc var)) { + if (-d "$tree/$subdir") { + push @bwrap, '--ro-bind', "$tree/$subdir", "/$subdir"; + } + } + + return run([@bwrap, @$argv], @run_params); +} + +=item multiarch_tuple_to_ldso(I<TUPLE>) + +Return the B<ld.so>(8) implementation for a Debian multiarch tuple. +For example, +C<multiarch_tuple_to_ldso('x86_64-linux-gnu')> returns +F</lib64/ld-linux-x86-64.so.2>. + +=cut + +my %multiarch_tuple_to_ldso; + +sub multiarch_tuple_to_ldso { + my $tuple = shift; + my $stdout; + + if (defined $multiarch_tuple_to_ldso{$tuple}) { + return $multiarch_tuple_to_ldso{$tuple}; + } + + assert_run_verbose([ + $CAPSULE_CAPTURE_LIBS_TOOLS{$tuple}, '--print-ld.so' + ], '>', \$stdout); + chomp $stdout; + $multiarch_tuple_to_ldso{$tuple} = $stdout; + return $stdout; +} + +=item real_path_of_ldso(I<TREE>, I<TUPLE>, I<WORKSPACE>) + +Return the path to the B<ld.so>(8) implementation for a Debian multiarch +tuple in I<TREE>, relative to I<TREE> itself. For example, +C<real_path_of_ldso('/', 'x86_64-linux-gnu', $workspace)> might +return F</lib/x86_64-linux-gnu/ld-2.25.so>, while +C<real_path_of_ldso('/chroots/stretch', 'i386-linux-gnu', $workspace)> +might return F</lib/i386-linux-gnu/ld-2.24.so>. + +I<WORKSPACE> is a temporary directory used internally. + +=cut + +sub real_path_of_ldso { + my $tree = shift; + my $tuple = shift; + my $workspace = shift; + my $stdout; + $tree = '/' unless defined $tree; + + assert_run_verbose([ + 'bwrap', + '--ro-bind', '/', '/', + '--tmpfs', $workspace, + bind_usr($tree, $workspace), + $CAPSULE_CAPTURE_LIBS_TOOLS{$tuple}, + "--resolve-ld.so=$workspace", + ], '>', \$stdout); + chomp $stdout; + diag "-> $stdout"; + return $stdout; +} + +=item use_ldso(I<LIBC_PROVIDER>, I<CONTAINER>, I<TUPLES>, I<WORKSPACE>) + +Return the necessary B<bwrap>(1) arguments to override the container's +B<ld.so>(1) with the one from the libc provider. + +=cut + +sub use_ldso { + my $libc_provider_tree = shift; + my $container_tree = shift; + my @multiarch_tuples = @{ shift() }; + my $workspace = shift; + my @bwrap; + + foreach my $tuple (@multiarch_tuples) { + my $resolved = real_path_of_ldso($libc_provider_tree, $tuple, + $workspace); + my $ldso = real_path_of_ldso($container_tree, $tuple, $workspace); + + if (-d "$container_tree/usr" || $ldso =~ m{^/usr/}) { + diag "Adding bind-mount of $libc_provider_tree$resolved over $ldso"; + push @bwrap, '--ro-bind', "$libc_provider_tree$resolved", $ldso; + } + else { + diag "Adding bind-mount of $libc_provider_tree$resolved over /usr$ldso because /lib{,64} will be symlinks"; + push @bwrap, '--ro-bind', "$libc_provider_tree$resolved", "/usr$ldso"; + } + } + + return @bwrap; +} + +=item capture_libs(I<PROVIDER>, I<CONTAINER>, I<TUPLES>, I<DEST>, I<LIBS>, I<WORKSPACE>, I<MOUNT>) + +Populate I<DEST>F</lib/>I<TUPLE> with symbolic links to the libraries +in I<PROVIDER>, assuming that I<PROVIDER> will be mounted on +I<MOUNT>, such that I<DEST>F</lib/>I<TUPLE> can be added +to the B<LD_LIBRARY_PATH>. Use I<WORKSPACE> as a temporary work area. + +Libraries that appear to be strictly newer in I<CONTAINER> are skipped. + +I<TUPLES> is a reference to an array of Debian multiarch tuples. + +I<LIBS> is a list of library patterns as for B<capsule-capture-libs>(1). + +=cut + +sub capture_libs { + my $provider_tree = shift; + my $container_tree = shift; + my @multiarch_tuples = @{ shift() }; + my $dest = shift; + my @libs = @{ shift() }; + my $workspace = shift; + my $mount = shift; + my @maybe_container; + + + foreach my $tuple (@multiarch_tuples) { + make_path("$dest/lib/$tuple", verbose => 1); + + assert_run_verbose([ + 'bwrap', + '--ro-bind', '/', '/', + '--bind', $dest, $dest, + '--tmpfs', $workspace, + bind_usr($container_tree, $workspace), + $CAPSULE_CAPTURE_LIBS_TOOLS{$tuple}, + "--container=$workspace", + "--link-target=$mount", + "--dest=$dest/lib/$tuple", + "--provider=$provider_tree", + @libs, + ], '>&2'); + } +} + +=back + +=cut + +skip_all_unless_bwrap; + +my $stdout; + +my $data_home = $ENV{XDG_DATA_HOME}; +$data_home = "$ENV{HOME}/.local/share" unless defined $data_home; +my $gl_provider_tree = "/"; +my $container_tree; +my $flatpak_app; +my $flatpak_runtime; +my @multiarch_tuples = qw(x86_64-linux-gnu); +my $app; +my $gl_stack = undef; +my $arch = 'x86_64'; +my $libc_provider_tree = 'auto'; +my $libcapsule_tree = 'container'; +my $x11_provider_tree = 'auto'; +my $mesa_drivers = 1; +my $mesa_driver_deps = 1; +my $nvidia_only = 0; + +GetOptions( + 'app=s' => \$app, + 'container=s' => \$container_tree, + 'flatpak-app=s' => \$flatpak_app, + 'flatpak-runtime=s' => \$flatpak_runtime, + 'gl-provider=s' => \$gl_provider_tree, + 'gl-stack=s' => \$gl_stack, + 'libcapsule-provider=s' => \$libcapsule_tree, + 'libcapsule' => sub { + $gl_stack = '/usr/lib/libcapsule/shims'; + $libcapsule_tree = '/'; + $mesa_driver_deps = 0; + }, + 'libc-provider=s' => \$libc_provider_tree, + 'mesa-drivers' => \$mesa_drivers, + 'no-mesa-drivers' => sub { $mesa_drivers = 0; }, + 'mesa-driver-deps' => \$mesa_driver_deps, + 'no-mesa-driver-deps' => sub { $mesa_driver_deps = 0; }, + 'nvidia-only' => \$nvidia_only, + 'x11-provider=s' => \$x11_provider_tree, + 'multiarch=s' => sub { + @multiarch_tuples = split /[\s,]+/, $_[1]; + }, + 'full-mesa' => sub { + $gl_provider_tree = '/'; + undef $gl_stack; + $libcapsule_tree = 'container'; + $libc_provider_tree = 'auto'; + $mesa_drivers = 1; + $mesa_driver_deps = 1; + $nvidia_only = 0; + $x11_provider_tree = 'auto'; + }, + 'proposed-mesa' => sub { + $gl_provider_tree = '/'; + $gl_stack = '/usr/lib/libcapsule/shims'; + $libcapsule_tree = '/'; + $libc_provider_tree = 'auto'; + $mesa_drivers = 1; + $mesa_driver_deps = 0; + $nvidia_only = 0; + $x11_provider_tree = 'auto'; + }, + 'proposed-nvidia' => sub { + $gl_provider_tree = '/'; + undef $gl_stack; + $libcapsule_tree = 'container'; + $libc_provider_tree = 'container'; + $mesa_drivers = 0; + $mesa_driver_deps = 0; + $nvidia_only = 1; + $x11_provider_tree = 'container'; + }, + help => sub { + print <<EOF; +Usage: $0 [OPTIONS] [-- COMMAND] +Options: + --app=DIRECTORY Mount DIRECTORY at /app + --flatpak-app=NAME/ARCH/BRANCH Use a `flatpak --user` app for --app + [….mesa_utils/$arch/master] + --container=USR|SYSROOT Populate /usr from USR or SYSROOT + --flatpak-runtime=NAME/ARCH/BRANCH Use a `flatpak --user` runtime + for --container + [….Games.Platform/$arch/stretch] + --gl-provider=USR|SYSROOT Get libGL etc. from USR or SYSROOT + [/] + --gl-stack=STACK Get libGL etc. from STACK/lib/MULTIARCH + instead of --gl-provider, but + still use --gl-provider libc, libX11, + libcapsule if appropriate. + [Same as --gl-provider] + --x11-provider=auto|container|gl-provider|USR|SYSROOT + Use libX11 etc. from here + [auto] + --libc-provider=auto|container|gl-provider|USR|SYSROOT + Use glibc from here + [auto] + --libcapsule-provider=auto|container|gl-provider|USR|SYSROOT + Use libcapsule, libelf from here + [container] + --libcapsule Shortcut for: + --gl-stack=/usr/lib/libcapsule/shims + --libcapsule-provider=/ + --multiarch=TUPLE[,TUPLE…] Enable architecture(s) by Debian + multiarch tuple + [x86_64-linux-gnu] + --nvidia-only Only capture NVIDIA proprietary + libraries (and possibly GLVND + wrappers), not the rest of the + libGL stack + --full-mesa Use short-term handling for Mesa: + --gl-provider=/ + --libcapsule + --libc-provider=auto + --mesa-drivers + --mesa-driver-deps + --x11-provider=auto + --libcapsule-provider=auto + --proposed-mesa Use proposed handling for Mesa: + --gl-provider=/ + --libcapsule + --libc-provider=auto + --mesa-drivers + --no-mesa-driver-deps + --x11-provider=auto + --libcapsule-provider=auto + --proposed-nvidia Use proposed handling for binary + blob drivers like NVIDIA: + --gl-provider=/ + --x11-provider=container + --libc-provider=container + --libcapsule-provider=container + --no-mesa-drivers + --no-mesa-driver-deps + --nvidia-only +EOF + exit 0; + }, +) or die "Error parsing command-line options"; + +if (defined $flatpak_app) { + die "--flatpak-app and --app are mutually exclusive" if defined $app; + $app = "$data_home/flatpak/app/$flatpak_app/active/files"; +} + +if (defined $flatpak_runtime) { + die "--flatpak-runtime and --container are mutually exclusive" + if defined $container_tree; + $container_tree = "$data_home/flatpak/runtime/$flatpak_runtime/active/files"; +} + +foreach my $tuple (@multiarch_tuples) { + assert_run_verbose([ + 'env', "PKG_CONFIG_PATH=/usr/lib/$tuple/pkgconfig", + 'pkg-config', '--variable=CAPSULE_VERSION_TOOL', 'libcapsule-tools', + ], '>', \$stdout); + chomp $stdout; + $CAPSULE_VERSION_TOOLS{$tuple} = $stdout; + assert_run_verbose([ + 'env', "PKG_CONFIG_PATH=/usr/lib/$tuple/pkgconfig", + 'pkg-config', '--variable=CAPSULE_CAPTURE_LIBS_TOOL', + 'libcapsule-tools', + ], '>', \$stdout); + chomp $stdout; + $CAPSULE_CAPTURE_LIBS_TOOLS{$tuple} = $stdout; +} + +$app = "$data_home/flatpak/app/org.debian.packages.mesa_utils/$arch/master/active/files" + unless defined $app; +$container_tree = "$data_home/flatpak/runtime/net.debian.flatpak.Games.Platform/$arch/stretch/active/files" + unless defined $container_tree; + +assert_run_verbose(['glxinfo'], '>', \$stdout); +diag_multiline($stdout); + +if ($stdout =~ /Error: couldn't find RGB GLX visual/) { + plan skip_all => 'Cannot run glxinfo here'; +} + +if ($stdout !~ /direct rendering: Yes/) { + plan skip_all => 'glxinfo reports no direct rendering available'; +} + +my $tmpdir = File::Temp->newdir( + TMPDIR => 1, TEMPLATE => 'capsule-test-temp-XXXXXXXX'); + +my @bwrap = qw( + bwrap + --unshare-user + --unshare-pid + --unshare-uts --hostname bwrap + --dev-bind /dev /dev + --bind /dev/shm /dev/shm + --proc /proc + --ro-bind /sys /sys + --tmpfs /run + --tmpfs /tmp + --tmpfs /var + --tmpfs /var/tmp + --symlink ../run /var/run +); +push @bwrap, '--bind', $ENV{HOME}, $ENV{HOME}; + +foreach my $mutable (qw(/etc /var/cache /var/lib)) { + if (-d "$container_tree$mutable") { + opendir(my $dir, "$container_tree$mutable"); + + while (defined(my $member = readdir $dir)) { + next if $member eq '.' || $member eq '..'; + next if "$mutable/$member" eq '/etc/group'; + next if "$mutable/$member" eq '/etc/host.conf'; + next if "$mutable/$member" eq '/etc/hosts'; + next if "$mutable/$member" eq '/etc/localtime'; + next if "$mutable/$member" eq '/etc/machine-id'; + next if "$mutable/$member" eq '/etc/passwd'; + next if "$mutable/$member" eq '/etc/resolv.conf'; + next if "$mutable/$member" eq '/var/lib/dbus'; + next if "$mutable/$member" eq '/var/lib/dhcp'; + next if "$mutable/$member" eq '/var/lib/sudo'; + next if "$mutable/$member" eq '/var/lib/urandom'; + + no autodie 'readlink'; + my $target = readlink "$container_tree$mutable/$member"; + + if (defined $target) { + push @bwrap, '--symlink', "$target", "$mutable/$member"; + } + else { + push @bwrap, '--ro-bind', "$container_tree$mutable/$member", "$mutable/$member"; + } + } + + closedir($dir); + } +} + +if (-e '/etc/machine-id') { + push @bwrap, '--ro-bind', '/etc/machine-id', '/etc/machine-id'; + push @bwrap, '--symlink', '/etc/machine-id', '/var/lib/dbus/machine-id'; +} +elsif (-e '/var/lib/dbus/machine-id') { + push @bwrap, '--ro-bind', '/var/lib/dbus/machine-id', '/etc/machine-id'; + push @bwrap, '--symlink', '/etc/machine-id', '/var/lib/dbus/machine-id'; +} + +if (-e '/etc/localtime') { + no autodie 'readlink'; + my $target = readlink '/etc/localtime'; + + if (defined $target && $target =~ m{^/usr/}) { + push @bwrap, '--symlink', $target, '/etc/localtime'; + } + else { + push @bwrap, '--ro-bind', '/etc/localtime', '/etc/localtime'; + } +} + +foreach my $file (qw(resolv.conf host.conf hosts passwd group)) { + if (-e "/etc/$file") { + push @bwrap, '--ro-bind', "/etc/$file", "/etc/$file"; + } +} + +push @bwrap, bind_usr($container_tree); +$gl_provider_tree = realpath($gl_provider_tree); +my $capsule_prefix = "/run/host$gl_provider_tree"; +$capsule_prefix =~ s,/*$,,; +push @bwrap, bind_usr($gl_provider_tree, $capsule_prefix); +push @bwrap, '--setenv', 'CAPSULE_PREFIX', $capsule_prefix; + +my @ld_path; + +if (defined $app) { + foreach my $tuple (@multiarch_tuples) { + push @ld_path, "/app/lib/$tuple"; + } + + push @ld_path, '/app/lib'; + push @bwrap, '--ro-bind', $app, '/app'; + push @bwrap, '--setenv', 'PATH', '/app/bin:/app/sbin:/usr/bin:/bin:/usr/sbin:/sbin'; +} + +my $overrides_tree = "$tmpdir/overrides"; +make_path("$tmpdir/scratch", verbose => 1); +make_path("$tmpdir/overrides", verbose => 1); + +foreach my $tuple (@multiarch_tuples) { + push @ld_path, "/overrides/lib/$tuple"; + make_path("$overrides_tree/lib/$tuple", verbose => 1); +} + +if ($libc_provider_tree eq 'auto') { + diag "Choosing GL provider's libc or container's libc automatically"; + capture_libs($gl_provider_tree, $container_tree, \@multiarch_tuples, + $overrides_tree, ['soname:libc.so.6'], "$tmpdir/scratch", $capsule_prefix); + + # See what we've got + foreach my $tuple (@multiarch_tuples) { + assert_run_verbose([ + 'ls', '-l', "$tmpdir/overrides/lib/$tuple", + ], '>', \$stdout); + diag_multiline $stdout; + } + + if (-l "$overrides_tree/lib/$multiarch_tuples[0]/libc.so.6") { + diag "${ansi_bright}${ansi_green}GL provider's libc appears ". + "newer${ansi_reset}"; + push @bwrap, use_ldso($gl_provider_tree, $container_tree, + \@multiarch_tuples, "$tmpdir/scratch"); + } + else { + diag "${ansi_bright}${ansi_cyan}Container's libc appears ". + "newer${ansi_reset}"; + } +} +elsif ($libc_provider_tree =~ m/^(?:gl-provider|host)$/) { + capture_libs($gl_provider_tree, $container_tree, \@multiarch_tuples, + $overrides_tree, ['even-if-older:soname:libc.so.6'], + "$tmpdir/scratch", $capsule_prefix); + diag "${ansi_bright}${ansi_green}Adding GL provider's libc via /overrides ". + "as requested${ansi_reset}"; + push @bwrap, use_ldso($gl_provider_tree, $container_tree, + \@multiarch_tuples, "$tmpdir/scratch"); +} +elsif ($libc_provider_tree eq 'container') { + diag "${ansi_bright}${ansi_cyan}Using container's libc as ". + "requested${ansi_reset}"; +} +else { + diag "${ansi_bright}${ansi_magenta}Adding ${libc_provider_tree} libc ". + "via /overrides as requested${ansi_reset}"; + $libc_provider_tree = realpath($libc_provider_tree); + capture_libs($libc_provider_tree, $container_tree, \@multiarch_tuples, + $overrides_tree, ['even-if-older:soname:libc.so.6'], + "$tmpdir/scratch", "/run/host$libc_provider_tree"); + push @bwrap, use_ldso($libc_provider_tree, $container_tree, + \@multiarch_tuples, "$tmpdir/scratch"); + push @bwrap, bind_usr($libc_provider_tree, "/run/host$libc_provider_tree"); +} + +my @dri_path; + +if (defined $gl_stack) { + diag "Using standalone GL stack $gl_stack"; + $gl_stack = realpath($gl_stack); + + push @bwrap, '--ro-bind', $gl_stack, "/run/host$gl_stack"; + + foreach my $tuple (@multiarch_tuples) { + next unless -d "$gl_stack/lib/$tuple"; + opendir(my $dir, "$gl_stack/lib/$tuple"); + + while(defined(my $member = readdir $dir)) { + next if $member eq '.' || $member eq '..'; + my $target = realpath("$gl_stack/lib/$tuple/$member"); + symlink( + "/run/host$target", + "$overrides_tree/lib/$tuple/$member"); + } + + closedir($dir); + } +} +elsif ($nvidia_only) { + diag "Using NVIDIA-only GL stack from $gl_provider_tree"; + capture_libs($gl_provider_tree, $container_tree, + \@multiarch_tuples, $overrides_tree, ['no-dependencies:nvidia:'], + "$tmpdir/scratch", $capsule_prefix); +} +else { + diag "Using GL stack from $gl_provider_tree"; + capture_libs($gl_provider_tree, $container_tree, + \@multiarch_tuples, $overrides_tree, ['gl:'], + "$tmpdir/scratch", $capsule_prefix); + + # If we're using Mesa, we need the DRI drivers too + if ($mesa_drivers || $mesa_driver_deps) { + foreach my $tuple (@multiarch_tuples) { + my $ldso = multiarch_tuple_to_ldso($tuple); + my $libdir; + + my @search_path = ("/lib/$tuple", "/usr/lib/$tuple"); + + if (defined $ldso) { + # Add /lib64 and /usr/lib64 if applicable + (undef, $libdir, undef) = File::Spec->splitpath($ldso); + push @search_path, $libdir, "/usr$libdir"; + } + + foreach my $search (@search_path) { + if (-d "$gl_provider_tree$search/dri") { + diag "Using GL provider's $search/dri"; + + if ($mesa_drivers) { + symlink("$capsule_prefix$search/dri", + "$overrides_tree/lib/$tuple/dri"); + push @dri_path, "/overrides/lib/$tuple/dri"; + } + + if ($mesa_driver_deps) { + capture_libs($gl_provider_tree, $container_tree, + \@multiarch_tuples, $overrides_tree, + ["only-dependencies:path-match:$search/dri/*"], + "$tmpdir/scratch", $capsule_prefix); + } + } + } + } + } +} + +# These are libraries that can't have more than one instance in use. +# TODO: We don't currently exclude libelf from being encapsulated. +# Should we? Does it have global state? +if ($libcapsule_tree eq 'auto') { + diag "Using libcapsule from $gl_provider_tree if newer"; + capture_libs($gl_provider_tree, $container_tree, + \@multiarch_tuples, $overrides_tree, ['soname:libcapsule.so.0'], + "$tmpdir/scratch", $capsule_prefix); +} +elsif ($libcapsule_tree eq 'container') { + diag "${ansi_cyan}Using libcapsule from container as requested${ansi_reset}"; +} +elsif ($libcapsule_tree =~ m/^(?:gl-provider|host)$/) { + diag "${ansi_green}Using libcapsule from GL provider as requested${ansi_reset}"; + capture_libs($gl_provider_tree, $container_tree, + \@multiarch_tuples, $overrides_tree, + ['even-if-older:soname:libcapsule.so.0'], + "$tmpdir/scratch", $capsule_prefix); +} +else { + diag "${ansi_magenta}Using libcapsule from $libcapsule_tree${ansi_reset}"; + $libcapsule_tree = realpath($libcapsule_tree); + capture_libs($libcapsule_tree, $container_tree, + \@multiarch_tuples, $overrides_tree, + ['even-if-older:soname:libcapsule.so.0'], + "$tmpdir/scratch", "/run/host$libcapsule_tree"); + push @bwrap, bind_usr($libcapsule_tree, "/run/host$libcapsule_tree"); +} + +# These are more libraries that can't have more than one instance in use. +# They also need to appear in libGL.so.1.excluded, so that the capsule +# won't load the GL-provider copy if the container copy is newer. +diag 'Adding singleton libraries from GL provider, if newer...'; + +# TODO: We don't currently exclude libX11-xcb, libxcb-dri3, +# libxcb-xfixes, libXau, libXdamage, libXdmcp, libXfixes, libXxf86vm +# from being encapsulated. We should be consistent one way or the other +my @XLIBS = qw(X11 X11-xcb xcb xcb-dri2 xcb-dri3 xcb-glx + xcb-present xcb-sync xcb-xfixes + Xau Xdamage Xdmcp Xext Xfixes Xxf86vm + ); + +if ($x11_provider_tree eq 'auto') { + diag "Using X libraries from $gl_provider_tree if newer"; + capture_libs($gl_provider_tree, $container_tree, + \@multiarch_tuples, $overrides_tree, + [map { "soname-match:lib$_.so.*" } @XLIBS], + "$tmpdir/scratch", $capsule_prefix); +} +elsif ($x11_provider_tree eq 'container') { + diag "${ansi_cyan}Using X libraries from container as requested${ansi_reset}"; +} +elsif ($x11_provider_tree =~ m/^(?:gl-provider|host)$/) { + diag "${ansi_green}Using X libraries from GL provider as requested${ansi_reset}"; + capture_libs($gl_provider_tree, $container_tree, + \@multiarch_tuples, $overrides_tree, + [map { "even-if-older:soname-match:lib$_.so.*" } @XLIBS], + "$tmpdir/scratch", $capsule_prefix); +} +else { + diag "${ansi_magenta}Using X libraries from $x11_provider_tree as requested${ansi_reset}"; + $x11_provider_tree = realpath($x11_provider_tree); + capture_libs($x11_provider_tree, $container_tree, + \@multiarch_tuples, $overrides_tree, + [map { "even-if-older:soname-match:lib$_.so.*" } @XLIBS], + "$tmpdir/scratch", "/run/host$x11_provider_tree", $ansi_magenta); + push @bwrap, bind_usr($libcapsule_tree, "/run/host$x11_provider_tree") +} + +foreach my $tuple (@multiarch_tuples) { + push @ld_path, "/overrides/lib/$tuple"; +} + +push @bwrap, '--ro-bind', $overrides_tree, '/overrides'; + +if (@ld_path) { + push @bwrap, '--setenv', 'LD_LIBRARY_PATH', join(':', @ld_path); +} + +if (@dri_path) { + push @bwrap, '--setenv', 'LIBGL_DRIVERS_PATH', join(':', @dri_path); +} + +push @bwrap, '--setenv', 'XDG_RUNTIME_DIR', "/run/user/$<"; + +# Simplified version of flatpak run --socket=X11 +push @bwrap, '--bind', '/tmp/.X11-unix', '/tmp/.X11-unix'; +push @bwrap, '--setenv', 'DISPLAY', $ENV{DISPLAY}; + +foreach my $xauth ($ENV{XAUTHORITY}, "$ENV{HOME}/.Xauthority") { + if (defined $xauth && -e $xauth) { + push @bwrap, '--ro-bind', $xauth, "/run/user/$</Xauthority"; + push @bwrap, '--setenv', 'XAUTHORITY', "/run/user/$</Xauthority"; + last; + } +} + +# Simplified version of flatpak run --socket=wayland +my $wayland_display = $ENV{WAYLAND_DISPLAY}; +$wayland_display = 'wayland-0' unless defined $wayland_display; +if (exists $ENV{XDG_RUNTIME_DIR} && + -S "$ENV{XDG_RUNTIME_DIR}/$wayland_display") { + push @bwrap, '--bind', "$ENV{XDG_RUNTIME_DIR}/$wayland_display", + "/run/user/$</$wayland_display"; +} + +# Simplified version of flatpak run --socket=pulseaudio, modified to +# support the SteamOS system-wide PulseAudio instance +if (exists $ENV{XDG_RUNTIME_DIR} && + -S "$ENV{XDG_RUNTIME_DIR}/pulse/native") { + push @bwrap, '--bind', "$ENV{XDG_RUNTIME_DIR}/pulse/native", + "/run/user/$</pulse/native"; + push @bwrap, '--setenv', 'PULSE_SERVER', "unix:/run/user/$</pulse/native"; +} +elsif (-S "/var/run/pulse/native") { + push @bwrap, '--bind', "/var/run/pulse/native", + "/run/user/$</pulse/native"; + push @bwrap, '--setenv', 'PULSE_SERVER', "unix:/run/user/$</pulse/native"; +} + +# Simplified version of flatpak run --socket=system-bus +if (exists $ENV{DBUS_SYSTEM_BUS_ADDRESS} && + $ENV{DBUS_SYSTEM_BUS_ADDRESS} =~ m/^unix:path=([^,;]*)/ && + -S $1) { + push @bwrap, '--bind', $1, '/var/run/dbus/system_bus_socket'; + push @bwrap, '--unsetenv', 'DBUS_SYSTEM_BUS_ADDRESS'; +} +elsif (-S '/var/run/dbus/system_bus_socket') { + push @bwrap, '--bind', '/var/run/dbus/system_bus_socket', + '/run/dbus/system_bus_socket'; +} + +# Simplified version of flatpak run --socket=session-bus +if (exists $ENV{DBUS_SESSION_BUS_ADDRESS} && + $ENV{DBUS_SESSION_BUS_ADDRESS} =~ m/^unix:path=([^,;]*)/ && + -S $1) { + push @bwrap, '--bind', $1, "/run/user/$</bus"; + push @bwrap, '--setenv', 'DBUS_SESSION_BUS_ADDRESS', + "unix:path=/run/user/$</bus"; +} +elsif (exists $ENV{XDG_RUNTIME_DIR} && + -S "$ENV{XDG_RUNTIME_DIR}/bus") { + push @bwrap, '--bind', "$ENV{XDG_RUNTIME_DIR}/bus", "/run/user/$</bus"; + push @bwrap, '--setenv', 'DBUS_SESSION_BUS_ADDRESS', + "unix:path=/run/user/$</bus"; +} +# ... else hope it's unix:abstract=... which will work because we don't +# unshare networking + +# See what we've got +foreach my $tuple (@multiarch_tuples) { + foreach my $dir ('overrides') { + next unless -d "$tmpdir/$dir/lib/$tuple"; + assert_run_verbose([ + 'ls', '-l', "$tmpdir/$dir/lib/$tuple", + ], '>', \$stdout); + diag_multiline $stdout; + } +} + +if (@ARGV) { + run_ok([@bwrap, @ARGV]); +} +else { + run_ok([@bwrap, 'glxinfo'], '>', \$stdout); + diag_multiline $stdout; + like($stdout, qr{direct rendering: Yes}); +} + +done_testing; + +# vim:set sw=4 sts=4 et: diff --git a/subprojects/libcapsule/tests/notgl-dlopener.c b/subprojects/libcapsule/tests/notgl-dlopener.c new file mode 100644 index 0000000000000000000000000000000000000000..6e8160d3cf4886de02eb4c4f2f28a200048dd474 --- /dev/null +++ b/subprojects/libcapsule/tests/notgl-dlopener.c @@ -0,0 +1,174 @@ +// Copyright © 2017 Collabora Ltd + +// This file is part of libcapsule. +// +// libcapsule is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as +// published by the Free Software Foundation; either version 2.1 of the +// License, or (at your option) any later version. +// +// libcapsule 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with libcapsule. If not, see <http://www.gnu.org/licenses/>. + +#include <assert.h> +#include <dlfcn.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> + +#include "notgl.h" +#include "notgles.h" +#include "notgl-helper.h" + +static void die( const char *format, ... ) + __attribute__((noreturn)) + __attribute__((format(printf, 1, 2))); + +static void +die( const char *format, ... ) +{ + va_list ap; + + va_start( ap, format ); + vfprintf( stderr, format, ap ); + va_end( ap ); + abort(); +} + +static void * +xdlopen( const char *filename, int flags ) +{ + void *handle = dlopen( filename, flags ); + + if( handle == NULL ) + die( "dlopen(\"%s\", %d): %s", filename, flags, dlerror() ); + + return handle; +} + +static void * +xdlsym( void *handle, const char *symbol ) +{ + void *value = dlsym( handle, symbol ); + + if( value == NULL ) + die( "dlsym(%p, \"%s\"): %s", handle, symbol, dlerror() ); + + return value; +} + +static void +xdlclose( void *handle ) +{ + if( dlclose( handle ) != 0 ) + die( "dlclose(%p): %s", handle, dlerror() ); +} + +static notgl_extension_function +get_extension( void *handle, + const char *name ) +{ + return dlsym( handle, name ); +} + +int +main ( int argc, + char **argv ) +{ + void *gl; + void *gles; + notgl_extension_function f; + + // Select line-buffering for output, in case we crash + setvbuf( stdout, NULL, _IOLBF, 0 ); + + gl = xdlopen( "libnotgl.so.0", RTLD_LAZY|RTLD_GLOBAL ); + gles = xdlopen( "libnotgles.so.1", RTLD_NOW|RTLD_LOCAL ); + + f = xdlsym( gl, "notgl_get_implementation" ); + printf( "NotGL implementation: %s\n", f() ); + f = xdlsym( gl, "notgl_use_helper" ); + printf( "NotGL helper implementation: %s\n", f() ); + + f = get_extension( RTLD_DEFAULT, "notgl_extension_both" ); + + if( f ) + printf( "notgl_extension_both: %s\n", f() ); + else + printf( "notgl_extension_both: (not found)\n" ); + + f = get_extension( RTLD_DEFAULT, "notgl_extension_red" ); + + if( f ) + printf( "notgl_extension_red: %s\n", f() ); + else + printf( "notgl_extension_red: (not found)\n" ); + + f = get_extension( RTLD_DEFAULT, "notgl_extension_green" ); + + if( f ) + printf( "notgl_extension_green: %s\n", f() ); + else + printf( "notgl_extension_green: (not found)\n" ); + + f = xdlsym( gles, "notgles_get_implementation" ); + printf( "NotGLES implementation: %s\n", f() ); + f = xdlsym( gles, "notgles_use_helper" ); + printf( "NotGLES helper implementation: %s\n", f() ); + + f = get_extension( gles, "notgles_extension_both" ); + + if( f ) + printf( "notgles_extension_both: %s\n", f() ); + else + printf( "notgles_extension_both: (not found)\n" ); + + f = get_extension( gles, "notgles_extension_red" ); + + if( f ) + printf( "notgles_extension_red: %s\n", f() ); + else + printf( "notgles_extension_red: (not found)\n" ); + + f = get_extension( gles, "notgles_extension_green" ); + + if( f ) + printf( "notgles_extension_green: %s\n", f() ); + else + printf( "notgles_extension_green: (not found)\n" ); + + xdlclose( gl ); + xdlclose( gles ); + + // Check that we can dlopen and dlclose repeatedly without crashing + + for( int i = 1; i < 10; i++ ) + { + printf( "dlopening and dlclosing %d times...\n", i ); + // dlopen'd handles are refcounted; take i references + for( int j = 0; j < i; j++ ) + { + // Arbitrary flags that happen to be oppositely paired + // compared with how we opened the libraries above + gl = xdlopen( "libnotgl.so.0", RTLD_LAZY|RTLD_LOCAL ); + gles = xdlopen( "libnotgles.so.1", RTLD_NOW|RTLD_GLOBAL ); + xdlsym( gl, "notgl_extension_both" ); + xdlsym( gles, "notgles_extension_both" ); + xdlsym( RTLD_DEFAULT, "notgles_extension_both" ); + } + + // Release all i references + for( int j = 0; j < i; j++ ) + { + xdlclose( gl ); + xdlclose( gles ); + } + } + + return 0; +} diff --git a/subprojects/libcapsule/tests/notgl-green.c b/subprojects/libcapsule/tests/notgl-green.c new file mode 100644 index 0000000000000000000000000000000000000000..ec7d1bb1edb017370e0e0b82dde7754d04379a4c --- /dev/null +++ b/subprojects/libcapsule/tests/notgl-green.c @@ -0,0 +1,49 @@ +// Copyright © 2017 Collabora Ltd + +// notgl-green — one of two implementations of libnotgl, taking the +// role of the NVIDIA implementation of OpenGL + +// This file is part of libcapsule. +// +// libcapsule is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as +// published by the Free Software Foundation; either version 2.1 of the +// License, or (at your option) any later version. +// +// libcapsule 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with libcapsule. If not, see <http://www.gnu.org/licenses/>. + +#include "notgl.h" + +#include <dlfcn.h> + +#include "notgl-helper.h" + +const char * +notgl_get_implementation( void ) +{ + return "green"; +} + +const char * +notgl_extension_both( void ) +{ + return "green implementation of common extension"; +} + +const char * +notgl_extension_green( void ) +{ + return "green-only extension"; +} + +const char * +notgl_use_helper( void ) +{ + return helper_get_implementation(); +} diff --git a/subprojects/libcapsule/tests/notgl-helper-green.c b/subprojects/libcapsule/tests/notgl-helper-green.c new file mode 100644 index 0000000000000000000000000000000000000000..0cfd2c99adb7d9319bb7ba8e0572f1d768c2322d --- /dev/null +++ b/subprojects/libcapsule/tests/notgl-helper-green.c @@ -0,0 +1,27 @@ +// Copyright © 2017 Collabora Ltd + +// notgl-helper-green — An implementation of libnotgl-helper in the +// host, taking the role of the host's libstdc++ + +// This file is part of libcapsule. +// +// libcapsule is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as +// published by the Free Software Foundation; either version 2.1 of the +// License, or (at your option) any later version. +// +// libcapsule 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with libcapsule. If not, see <http://www.gnu.org/licenses/>. + +#include "notgl-helper.h" + +const char * +helper_get_implementation( void ) +{ + return "host (green)"; +} diff --git a/subprojects/libcapsule/tests/notgl-helper-red.c b/subprojects/libcapsule/tests/notgl-helper-red.c new file mode 100644 index 0000000000000000000000000000000000000000..c0da1ee910ddb3bc9bbe3e0f70368bc2bd8aeef4 --- /dev/null +++ b/subprojects/libcapsule/tests/notgl-helper-red.c @@ -0,0 +1,27 @@ +// Copyright © 2017 Collabora Ltd + +// notgl-helper-red — An implementation of libnotgl-helper in the +// host, taking the role of the host's libstdc++ + +// This file is part of libcapsule. +// +// libcapsule is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as +// published by the Free Software Foundation; either version 2.1 of the +// License, or (at your option) any later version. +// +// libcapsule 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with libcapsule. If not, see <http://www.gnu.org/licenses/>. + +#include "notgl-helper.h" + +const char * +helper_get_implementation( void ) +{ + return "host (red)"; +} diff --git a/subprojects/libcapsule/tests/notgl-helper-ref.c b/subprojects/libcapsule/tests/notgl-helper-ref.c new file mode 100644 index 0000000000000000000000000000000000000000..29557c6c31e1cb72a0d90643352657f2cec37f6e --- /dev/null +++ b/subprojects/libcapsule/tests/notgl-helper-ref.c @@ -0,0 +1,27 @@ +// Copyright © 2017 Collabora Ltd + +// notgl-helper-ref — The implementation of libnotgl-helper in the +// container, taking the role of the container's libstdc++ + +// This file is part of libcapsule. +// +// libcapsule is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as +// published by the Free Software Foundation; either version 2.1 of the +// License, or (at your option) any later version. +// +// libcapsule 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with libcapsule. If not, see <http://www.gnu.org/licenses/>. + +#include "notgl-helper.h" + +const char * +helper_get_implementation( void ) +{ + return "container (reference)"; +} diff --git a/subprojects/libcapsule/tests/notgl-helper-user.c b/subprojects/libcapsule/tests/notgl-helper-user.c new file mode 100644 index 0000000000000000000000000000000000000000..81a8e78fd0878d86705a8d2ddd84937d667a31ac --- /dev/null +++ b/subprojects/libcapsule/tests/notgl-helper-user.c @@ -0,0 +1,92 @@ +// Copyright © 2017 Collabora Ltd + +// This file is part of libcapsule. +// +// libcapsule is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as +// published by the Free Software Foundation; either version 2.1 of the +// License, or (at your option) any later version. +// +// libcapsule 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with libcapsule. If not, see <http://www.gnu.org/licenses/>. + +#include <dlfcn.h> +#include <stdio.h> +#include <stdlib.h> + +#include "notgl.h" +#include "notgles.h" +#include "notgl-helper.h" + +static notgl_extension_function +get_extension( const char *name ) +{ + return dlsym( RTLD_DEFAULT, name ); +} + +int +main ( int argc, + char **argv ) +{ + notgl_extension_function f; + + // Select line-buffering for output, in case we crash + setvbuf( stdout, NULL, _IOLBF, 0 ); + + printf( "NotGL implementation: %s\n", notgl_get_implementation() ); + printf( "NotGL helper implementation: %s\n", notgl_use_helper() ); + + f = get_extension( "notgl_extension_both" ); + + if( f ) + printf( "notgl_extension_both: %s\n", f() ); + else + printf( "notgl_extension_both: (not found)\n" ); + + f = get_extension( "notgl_extension_red" ); + + if( f ) + printf( "notgl_extension_red: %s\n", f() ); + else + printf( "notgl_extension_red: (not found)\n" ); + + f = get_extension( "notgl_extension_green" ); + + if( f ) + printf( "notgl_extension_green: %s\n", f() ); + else + printf( "notgl_extension_green: (not found)\n" ); + + printf( "NotGLES implementation: %s\n", notgles_get_implementation() ); + printf( "NotGLES helper implementation: %s\n", notgles_use_helper() ); + + f = get_extension( "notgles_extension_both" ); + + if( f ) + printf( "notgles_extension_both: %s\n", f() ); + else + printf( "notgles_extension_both: (not found)\n" ); + + f = get_extension( "notgles_extension_red" ); + + if( f ) + printf( "notgles_extension_red: %s\n", f() ); + else + printf( "notgles_extension_red: (not found)\n" ); + + f = get_extension( "notgles_extension_green" ); + + if( f ) + printf( "notgles_extension_green: %s\n", f() ); + else + printf( "notgles_extension_green: (not found)\n" ); + + printf( "NotGL helper implementation as seen by executable: %s\n", helper_get_implementation () ); + + return 0; +} diff --git a/subprojects/libcapsule/tests/notgl-helper.h b/subprojects/libcapsule/tests/notgl-helper.h new file mode 100644 index 0000000000000000000000000000000000000000..2c622dc457a45235c5abc02f336f80eba51dad31 --- /dev/null +++ b/subprojects/libcapsule/tests/notgl-helper.h @@ -0,0 +1,26 @@ +// Copyright © 2017 Collabora Ltd + +// libnotgl-helper — A library used by the OpenGL driver, taking the +// role of libstdc++ + +// This file is part of libcapsule. +// +// libcapsule is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as +// published by the Free Software Foundation; either version 2.1 of the +// License, or (at your option) any later version. +// +// libcapsule 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with libcapsule. If not, see <http://www.gnu.org/licenses/>. + +#pragma once + +#define HELPER_PUBLIC __attribute__((visibility("default"))) + +HELPER_PUBLIC +const char *helper_get_implementation( void ); diff --git a/subprojects/libcapsule/tests/notgl-red.c b/subprojects/libcapsule/tests/notgl-red.c new file mode 100644 index 0000000000000000000000000000000000000000..090f7187510ce3b4d8a337e1f4138b7bbd6dba1c --- /dev/null +++ b/subprojects/libcapsule/tests/notgl-red.c @@ -0,0 +1,49 @@ +// Copyright © 2017 Collabora Ltd + +// notgl-red — one of two implementations of libnotgl, taking the +// role of the AMD implementation of OpenGL + +// This file is part of libcapsule. +// +// libcapsule is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as +// published by the Free Software Foundation; either version 2.1 of the +// License, or (at your option) any later version. +// +// libcapsule 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with libcapsule. If not, see <http://www.gnu.org/licenses/>. + +#include "notgl.h" + +#include <dlfcn.h> + +#include "notgl-helper.h" + +const char * +notgl_get_implementation( void ) +{ + return "red"; +} + +const char * +notgl_extension_both( void ) +{ + return "red implementation of common extension"; +} + +const char * +notgl_extension_red( void ) +{ + return "red-only extension"; +} + +const char * +notgl_use_helper( void ) +{ + return helper_get_implementation (); +} diff --git a/subprojects/libcapsule/tests/notgl-ref.c b/subprojects/libcapsule/tests/notgl-ref.c new file mode 100644 index 0000000000000000000000000000000000000000..a1c7993bed9d2663caf6d6e7be26a19e79e7fc05 --- /dev/null +++ b/subprojects/libcapsule/tests/notgl-ref.c @@ -0,0 +1,43 @@ +// Copyright © 2017 Collabora Ltd + +// notgl-ref — reference implementation of libnotgl, taking the +// role of the (possibly outdated) implementation of OpenGL in the container + +// This file is part of libcapsule. +// +// libcapsule is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as +// published by the Free Software Foundation; either version 2.1 of the +// License, or (at your option) any later version. +// +// libcapsule 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with libcapsule. If not, see <http://www.gnu.org/licenses/>. + +#include "notgl.h" + +#include <dlfcn.h> + +#include "notgl-helper.h" + +const char * +notgl_get_implementation( void ) +{ + return "reference"; +} + +const char * +notgl_extension_both( void ) +{ + return "reference implementation of common extension"; +} + +const char * +notgl_use_helper( void ) +{ + return helper_get_implementation (); +} diff --git a/subprojects/libcapsule/tests/notgl-user.c b/subprojects/libcapsule/tests/notgl-user.c new file mode 100644 index 0000000000000000000000000000000000000000..40f08e101a8a33171d8378d7a08c360c5219eea3 --- /dev/null +++ b/subprojects/libcapsule/tests/notgl-user.c @@ -0,0 +1,64 @@ +// Copyright © 2017 Collabora Ltd + +// This file is part of libcapsule. +// +// libcapsule is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as +// published by the Free Software Foundation; either version 2.1 of the +// License, or (at your option) any later version. +// +// libcapsule 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with libcapsule. If not, see <http://www.gnu.org/licenses/>. + +#include <dlfcn.h> +#include <stdio.h> +#include <stdlib.h> + +#include "notgl.h" + +static notgl_extension_function +get_extension( const char *name ) +{ + return dlsym( RTLD_DEFAULT, name ); +} + +int +main ( int argc, + char **argv ) +{ + notgl_extension_function f; + + // Select line-buffering for output, in case we crash + setvbuf( stdout, NULL, _IOLBF, 0 ); + + printf( "NotGL implementation: %s\n", notgl_get_implementation() ); + printf( "NotGL helper implementation: %s\n", notgl_use_helper() ); + + f = get_extension( "notgl_extension_both" ); + + if( f ) + printf( "notgl_extension_both: %s\n", f() ); + else + printf( "notgl_extension_both: (not found)\n" ); + + f = get_extension( "notgl_extension_red" ); + + if( f ) + printf( "notgl_extension_red: %s\n", f() ); + else + printf( "notgl_extension_red: (not found)\n" ); + + f = get_extension( "notgl_extension_green" ); + + if( f ) + printf( "notgl_extension_green: %s\n", f() ); + else + printf( "notgl_extension_green: (not found)\n" ); + + return 0; +} diff --git a/subprojects/libcapsule/tests/notgl.h b/subprojects/libcapsule/tests/notgl.h new file mode 100644 index 0000000000000000000000000000000000000000..42b84d5f196ad66fd830b62ee48e03c50a5b8855 --- /dev/null +++ b/subprojects/libcapsule/tests/notgl.h @@ -0,0 +1,35 @@ +// Copyright © 2017 Collabora Ltd + +// notgl — a library not entirely unlike OpenGL + +// This file is part of libcapsule. +// +// libcapsule is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as +// published by the Free Software Foundation; either version 2.1 of the +// License, or (at your option) any later version. +// +// libcapsule 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with libcapsule. If not, see <http://www.gnu.org/licenses/>. + +#pragma once + +#define NOTGL_PUBLIC __attribute__((visibility("default"))) + +typedef const char *(*notgl_extension_function)( void ); + +NOTGL_PUBLIC +const char *notgl_get_implementation( void ); +NOTGL_PUBLIC +const char *notgl_extension_both( void ); +NOTGL_PUBLIC +const char *notgl_extension_red( void ); +NOTGL_PUBLIC +const char *notgl_extension_green( void ); +NOTGL_PUBLIC +const char *notgl_use_helper( void ); diff --git a/subprojects/libcapsule/tests/notgl.pl b/subprojects/libcapsule/tests/notgl.pl new file mode 100755 index 0000000000000000000000000000000000000000..4dd14f6a2ffa52c61787557c22f7bc161325a443 --- /dev/null +++ b/subprojects/libcapsule/tests/notgl.pl @@ -0,0 +1,321 @@ +#!/usr/bin/env perl + +# notgl.pl — exercise libcapsule using an imitation of libGLX +# +# Copyright © 2017 Collabora Ltd +# +# This file is part of libcapsule. +# +# libcapsule is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as +# published by the Free Software Foundation; either version 2.1 of the +# License, or (at your option) any later version. +# +# libcapsule 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 Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with libcapsule. If not, see <http://www.gnu.org/licenses/>. + +use autodie; +use warnings; +use strict; + +use Cwd qw(realpath); +use File::Temp qw(); +use IPC::Run qw(run); +use Test::More; + +use FindBin; +use lib $FindBin::Bin; + +use CapsuleTest; + +skip_all_unless_bwrap; + +my $temp = File::Temp->newdir(); +my $capsule_prefix = "$temp/prefix"; +mkdir $capsule_prefix; + +my $libs = ''; +my $notgl_user = "$builddir/tests/notgl-user"; +my $notgl_helper_user = "$builddir/tests/notgl-helper-user"; +my $notgl_dlopener = "$builddir/tests/notgl-dlopener"; +my $stdout; + +if (exists $ENV{CAPSULE_TESTS_UNINSTALLED}) { + diag 'Running uninstalled: yes'; + # We need to bypass libtool because it plays around with + # LD_LIBRARY_PATH, and so do we. + $libs = '/.libs'; + run_ok([ + "$builddir/libtool", qw(--mode=execute ls -1), + "$builddir/tests/notgl-user" + ], '>', \$notgl_user) or BAIL_OUT 'Cannot find notgl-user: $?'; + chomp $notgl_user; + run_ok([ + "$builddir/libtool", qw(--mode=execute ls -1), + "$builddir/tests/notgl-helper-user" + ], '>', \$notgl_helper_user) or BAIL_OUT 'Cannot find notgl-helper-user: $?'; + chomp $notgl_helper_user; + run_ok([ + "$builddir/libtool", qw(--mode=execute ls -1), + "$builddir/tests/notgl-dlopener" + ], '>', \$notgl_dlopener) or BAIL_OUT 'Cannot find notgl-dlopener: $?'; + chomp $notgl_dlopener; +} +else { + diag 'Running uninstalled: no'; +} + +diag 'Without special measures:'; +run_ok(['env', + 'LD_LIBRARY_PATH='.join(':', realpath("$builddir/tests/lib$libs"), + realpath("$builddir/tests/helper$libs")), + $notgl_user], + '>', \$stdout); +diag_multiline $stdout; +like($stdout, qr/^NotGL implementation: reference$/m); +like($stdout, qr/^NotGL helper implementation: container \(reference\)$/m); +like($stdout, + qr/^notgl_extension_both: reference implementation of common extension$/m); +like($stdout, qr/^notgl_extension_red: \(not found\)$/m); +like($stdout, qr/^notgl_extension_green: \(not found\)$/m); + +diag 'Without special measures (linked to libhelper):'; +run_ok(['env', + 'LD_LIBRARY_PATH='.join(':', realpath("$builddir/tests/lib$libs"), + realpath("$builddir/tests/helper$libs")), + $notgl_helper_user], + '>', \$stdout); +diag_multiline $stdout; +like($stdout, qr/^NotGL implementation: reference$/m); +like($stdout, qr/^NotGL helper implementation: container \(reference\)$/m); +like($stdout, + qr/^notgl_extension_both: reference implementation of common extension$/m); +like($stdout, qr/^notgl_extension_red: \(not found\)$/m); +like($stdout, qr/^notgl_extension_green: \(not found\)$/m); +like($stdout, + qr/^NotGL helper implementation as seen by executable: container \(reference\)$/m); +like($stdout, qr/^NotGLES implementation: reference$/m); +like($stdout, qr/^NotGLES helper implementation: container \(reference\)$/m); +like($stdout, + qr/^notgles_extension_both: reference implementation of common extension$/m); +like($stdout, qr/^notgles_extension_red: \(not found\)$/m); +like($stdout, qr/^notgles_extension_green: \(not found\)$/m); +like($stdout, + qr/^NotGL helper implementation as seen by executable: container \(reference\)$/m); + +diag 'Without special measures (using dlopen):'; +run_ok(['env', + 'LD_LIBRARY_PATH='.join(':', realpath("$builddir/tests/lib$libs"), + realpath("$builddir/tests/helper$libs")), + $notgl_dlopener], + '>', \$stdout); +diag_multiline $stdout; +like($stdout, qr/^NotGL implementation: reference$/m); +like($stdout, qr/^NotGL helper implementation: container \(reference\)$/m); +like($stdout, + qr/^notgl_extension_both: reference implementation of common extension$/m); +like($stdout, qr/^notgl_extension_red: \(not found\)$/m); +like($stdout, qr/^notgl_extension_green: \(not found\)$/m); +like($stdout, qr/^NotGLES implementation: reference$/m); +like($stdout, qr/^NotGLES helper implementation: container \(reference\)$/m); +like($stdout, + qr/^notgles_extension_both: reference implementation of common extension$/m); +like($stdout, qr/^notgles_extension_red: \(not found\)$/m); +like($stdout, qr/^notgles_extension_green: \(not found\)$/m); + +diag 'With libcapsule loading red implementation:'; +# We mount the "host system" on $capsule_prefix. +# +# In the "container", the shim libnotgl is picked up from tests/shim because +# it was prepended to the LD_LIBRARY_PATH. The helper library used by +# libnotgl, libhelper, is picked up from tests/helper as usual, and +# we mount a tmpfs over tests/lib to avoid libnotgl being picked up +# from the RPATH (in older toolchains that produce those instead +# of RUNPATH, and cannot be overridden by LD_LIBRARY_PATH): +# +# emulated container on / +# $builddir/ +# tests/ +# helper/ (contains reference libhelper) +# lib/ (empty) +# shim/ (contains shim libnotgl) +# +# In the "host system", there is a tmpfs over the build or installation +# directory to avoid tests/shim being found *again*, and the "red" +# implementations of libnotgl and libhelper are mounted over tests/lib +# and tests/helper: +# +# emulated host filesystem on $capsule_prefix +# $builddir/ +# tests/ +# helper/ (contains red libnotgl and libhelper) +# lib/ (contains red libnotgl and libhelper) +# shim/ (does not exist) +run_ok([qw(bwrap + --ro-bind / / + --dev-bind /dev /dev + --ro-bind /), $capsule_prefix, + '--tmpfs', realpath("$builddir/tests/lib$libs"), + '--tmpfs', $capsule_prefix.realpath($builddir), + '--ro-bind', realpath("$builddir/tests/red"), + $capsule_prefix.realpath("$builddir/tests/helper"), + '--ro-bind', realpath("$builddir/tests/red"), + $capsule_prefix.realpath("$builddir/tests/lib"), + '--setenv', 'CAPSULE_PREFIX', $capsule_prefix, + '--setenv', 'LD_LIBRARY_PATH', join(':', + realpath("$builddir/tests/shim$libs"), + realpath("$builddir/tests/helper$libs"), + realpath("$builddir/tests/lib$libs"), + ), + $notgl_user], + '>', \$stdout); +diag_multiline $stdout; + +# Functions from libnotgl get dispatched through the shim to the "red" +# implementation from the "host system". This mirrors functions from libGL +# being dispatched through the shim to the AMD implementation of libGL. +like($stdout, qr/^NotGL implementation: red$/m); + +# When the "red" implementation of libnotgl calls functions from libhelper, +# implementation from the "host system". This mirrors functions from +# libstdc++ that are called by the host libGL ending up in the host libstdc++. +like($stdout, qr/^NotGL helper implementation: host \(red\)$/m); + +# We can dlsym() for an implemementation of an extension that is part of +# the ABI of the shim and the reference implementation. +like($stdout, + qr/^notgl_extension_both: red implementation of common extension$/m); + +# We can also dlsym() for an implemementation of an extension that is only +# available in the "red" implementation. +like($stdout, qr/^notgl_extension_red: red-only extension$/m); +like($stdout, qr/^notgl_extension_green: \(not found\)$/m); + +diag 'With libcapsule loading green implementation:'; +# Similar to the above, but now the host system is using the "green" +# implementation of libnotgl, mirroring the NVIDIA implementation of +# libGL; and the binary is linked to libhelper, so we can demonstrate +# that libcapsule gives us the necessary cognitive dissonance to have +# two copies of libhelper loaded simultaneously. +run_ok([qw(bwrap + --ro-bind / / + --dev-bind /dev /dev + --ro-bind /), $capsule_prefix, + '--tmpfs', realpath("$builddir/tests/lib$libs"), + '--tmpfs', $capsule_prefix.realpath($builddir), + '--ro-bind', realpath("$builddir/tests/green"), + $capsule_prefix.realpath("$builddir/tests/lib"), + '--setenv', 'CAPSULE_PREFIX', $capsule_prefix, + '--setenv', 'LD_LIBRARY_PATH', join(':', + realpath("$builddir/tests/shim$libs"), + realpath("$builddir/tests/helper$libs"), + realpath("$builddir/tests/lib$libs"), + ), + $notgl_helper_user], + '>', \$stdout); +diag_multiline $stdout; +like($stdout, qr/^NotGL implementation: green$/m); +like($stdout, qr/^NotGL helper implementation: host \(green\)$/m); +like($stdout, + qr/^notgl_extension_both: green implementation of common extension$/m); +like($stdout, qr/^notgl_extension_red: \(not found\)$/m); +like($stdout, qr/^notgl_extension_green: green-only extension$/m); +like($stdout, qr/^NotGLES implementation: green$/m); +like($stdout, qr/^NotGLES helper implementation: host \(green\)$/m); +like($stdout, + qr/^notgles_extension_both: green implementation of common extension$/m); +like($stdout, qr/^notgles_extension_red: \(not found\)$/m); +like($stdout, qr/^notgles_extension_green: green-only extension$/m); + +# Also, this program is linked directly to libhelper, mirroring a program +# that is linked directly to libstdc++ in the libGL case. It sees the +# container's libhelper, not the host's - even though libnotgl sees the +# host's libhelper when it looks up the same symbol. (This is the point +# of libcapsule.) +like($stdout, + qr/^NotGL helper implementation as seen by executable: container \(reference\)$/m); + +# Using dlopen also works. +diag 'With libcapsule loading red implementation, via dlopen:'; +run_ok([qw(bwrap + --ro-bind / / + --dev-bind /dev /dev + --ro-bind /), $capsule_prefix, + '--tmpfs', realpath("$builddir/tests/lib$libs"), + '--tmpfs', $capsule_prefix.realpath($builddir), + '--ro-bind', realpath("$builddir/tests/red"), + $capsule_prefix.realpath("$builddir/tests/lib"), + '--setenv', 'CAPSULE_PREFIX', $capsule_prefix, + '--setenv', 'LD_LIBRARY_PATH', join(':', + realpath("$builddir/tests/shim$libs"), + realpath("$builddir/tests/helper$libs"), + realpath("$builddir/tests/lib$libs"), + ), + $notgl_dlopener], + '>', \$stdout); +diag_multiline $stdout; +like($stdout, qr/^NotGL implementation: red$/m); +like($stdout, qr/^NotGL helper implementation: host \(red\)$/m); +like($stdout, + qr/^notgl_extension_both: red implementation of common extension$/m); +like($stdout, qr/^notgl_extension_red: red-only extension$/m); +like($stdout, qr/^notgl_extension_green: \(not found\)$/m); +like($stdout, qr/^NotGLES implementation: red$/m); +like($stdout, qr/^NotGLES helper implementation: host \(red\)$/m); +like($stdout, + qr/^notgles_extension_both: red implementation of common extension$/m); +like($stdout, qr/^notgles_extension_red: red-only extension$/m); +like($stdout, qr/^notgles_extension_green: \(not found\)$/m); + +# We can use separate prefixes for different encapsulated libraries. +my $red_capsule_prefix = "$temp/red"; +mkdir $red_capsule_prefix; +my $green_capsule_prefix = "$temp/green"; +mkdir $green_capsule_prefix; + +diag 'With libcapsule loading disparate implementations, via dlopen:'; +run_ok([qw(bwrap + --ro-bind / / + --dev-bind /dev /dev + --ro-bind /), $red_capsule_prefix, + qw(--ro-bind /), $green_capsule_prefix, + '--tmpfs', realpath("$builddir/tests/lib$libs"), + '--tmpfs', $red_capsule_prefix.realpath($builddir), + '--tmpfs', $green_capsule_prefix.realpath($builddir), + '--ro-bind', realpath("$builddir/tests/red"), + $red_capsule_prefix.realpath("$builddir/tests/lib"), + '--ro-bind', realpath("$builddir/tests/green"), + $green_capsule_prefix.realpath("$builddir/tests/lib"), + '--setenv', 'CAPSULE_LIBNOTGL_SO_0_PREFIX', $red_capsule_prefix, + # We don't specify CAPSULE_LIBNOTGLES_SO_1_PREFIX, so the + # generic version gets used. + '--setenv', 'CAPSULE_PREFIX', $green_capsule_prefix, + '--setenv', 'LD_LIBRARY_PATH', join(':', + realpath("$builddir/tests/shim$libs"), + realpath("$builddir/tests/helper$libs"), + realpath("$builddir/tests/lib$libs"), + ), + $notgl_dlopener], + '>', \$stdout); +diag_multiline $stdout; +like($stdout, qr/^NotGL implementation: red$/m); +like($stdout, qr/^NotGL helper implementation: host \(red\)$/m); +like($stdout, + qr/^notgl_extension_both: red implementation of common extension$/m); +like($stdout, qr/^notgl_extension_red: red-only extension$/m); +like($stdout, qr/^notgl_extension_green: \(not found\)$/m); +like($stdout, qr/^NotGLES implementation: green$/m); +like($stdout, qr/^NotGLES helper implementation: host \(green\)$/m); +like($stdout, + qr/^notgles_extension_both: green implementation of common extension$/m); +like($stdout, qr/^notgles_extension_red: \(not found\)$/m); +like($stdout, qr/^notgles_extension_green: green-only extension$/m); + +done_testing; + +# vim:set sw=4 sts=4 et: diff --git a/subprojects/libcapsule/tests/notgles-green.c b/subprojects/libcapsule/tests/notgles-green.c new file mode 100644 index 0000000000000000000000000000000000000000..1b2f735284ee305cc7ee91a5598b46419b830b03 --- /dev/null +++ b/subprojects/libcapsule/tests/notgles-green.c @@ -0,0 +1,49 @@ +// Copyright © 2017 Collabora Ltd + +// notgles-green — one of two implementations of libnotgles, taking the +// role of the NVIDIA implementation of GLES + +// This file is part of libcapsule. +// +// libcapsule is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as +// published by the Free Software Foundation; either version 2.1 of the +// License, or (at your option) any later version. +// +// libcapsule 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with libcapsule. If not, see <http://www.gnu.org/licenses/>. + +#include "notgles.h" + +#include <dlfcn.h> + +#include "notgl-helper.h" + +const char * +notgles_get_implementation( void ) +{ + return "green"; +} + +const char * +notgles_extension_both( void ) +{ + return "green implementation of common extension"; +} + +const char * +notgles_extension_green( void ) +{ + return "green-only extension"; +} + +const char * +notgles_use_helper( void ) +{ + return helper_get_implementation(); +} diff --git a/subprojects/libcapsule/tests/notgles-red.c b/subprojects/libcapsule/tests/notgles-red.c new file mode 100644 index 0000000000000000000000000000000000000000..a56dd1fb10b8a9d070f41cce009951a8422155dc --- /dev/null +++ b/subprojects/libcapsule/tests/notgles-red.c @@ -0,0 +1,49 @@ +// Copyright © 2017 Collabora Ltd + +// notgles-red — one of two implementations of libnotgles, taking the +// role of the AMD implementation of GLES + +// This file is part of libcapsule. +// +// libcapsule is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as +// published by the Free Software Foundation; either version 2.1 of the +// License, or (at your option) any later version. +// +// libcapsule 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with libcapsule. If not, see <http://www.gnu.org/licenses/>. + +#include "notgles.h" + +#include <dlfcn.h> + +#include "notgl-helper.h" + +const char * +notgles_get_implementation( void ) +{ + return "red"; +} + +const char * +notgles_extension_both( void ) +{ + return "red implementation of common extension"; +} + +const char * +notgles_extension_red( void ) +{ + return "red-only extension"; +} + +const char * +notgles_use_helper( void ) +{ + return helper_get_implementation (); +} diff --git a/subprojects/libcapsule/tests/notgles-ref.c b/subprojects/libcapsule/tests/notgles-ref.c new file mode 100644 index 0000000000000000000000000000000000000000..df8a6bf1d52ed6c95aeb0ec4881323dfd1c9fd79 --- /dev/null +++ b/subprojects/libcapsule/tests/notgles-ref.c @@ -0,0 +1,43 @@ +// Copyright © 2017 Collabora Ltd + +// notgles-ref — reference implementation of libnotgles, taking the +// role of the (possibly outdated) implementation of GLES in the container + +// This file is part of libcapsule. +// +// libcapsule is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as +// published by the Free Software Foundation; either version 2.1 of the +// License, or (at your option) any later version. +// +// libcapsule 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with libcapsule. If not, see <http://www.gnu.org/licenses/>. + +#include "notgles.h" + +#include <dlfcn.h> + +#include "notgl-helper.h" + +const char * +notgles_get_implementation( void ) +{ + return "reference"; +} + +const char * +notgles_extension_both( void ) +{ + return "reference implementation of common extension"; +} + +const char * +notgles_use_helper( void ) +{ + return helper_get_implementation (); +} diff --git a/subprojects/libcapsule/tests/notgles.h b/subprojects/libcapsule/tests/notgles.h new file mode 100644 index 0000000000000000000000000000000000000000..ad5fe26db2ab74d24f1095485f0ccac95b63be4f --- /dev/null +++ b/subprojects/libcapsule/tests/notgles.h @@ -0,0 +1,35 @@ +// Copyright © 2017 Collabora Ltd + +// notgles — a library not entirely unlike GLES + +// This file is part of libcapsule. +// +// libcapsule is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as +// published by the Free Software Foundation; either version 2.1 of the +// License, or (at your option) any later version. +// +// libcapsule 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with libcapsule. If not, see <http://www.gnu.org/licenses/>. + +#pragma once + +#define NOTGLES_PUBLIC __attribute__((visibility("default"))) + +typedef const char *(*notgles_extension_function)( void ); + +NOTGLES_PUBLIC +const char *notgles_get_implementation( void ); +NOTGLES_PUBLIC +const char *notgles_extension_both( void ); +NOTGLES_PUBLIC +const char *notgles_extension_red( void ); +NOTGLES_PUBLIC +const char *notgles_extension_green( void ); +NOTGLES_PUBLIC +const char *notgles_use_helper( void ); diff --git a/subprojects/libcapsule/tests/shim/libnotgl.so.0.excluded b/subprojects/libcapsule/tests/shim/libnotgl.so.0.excluded new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/subprojects/libcapsule/tests/shim/libnotgl.so.0.shared b/subprojects/libcapsule/tests/shim/libnotgl.so.0.shared new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/subprojects/libcapsule/tests/shim/libnotgl.so.0.symbols b/subprojects/libcapsule/tests/shim/libnotgl.so.0.symbols new file mode 100644 index 0000000000000000000000000000000000000000..338681e486b56f67aff9b164aa9fb254081217e3 --- /dev/null +++ b/subprojects/libcapsule/tests/shim/libnotgl.so.0.symbols @@ -0,0 +1,2 @@ +notgl_get_implementation +notgl_use_helper diff --git a/subprojects/libcapsule/tests/shim/libnotgles.so.1.excluded b/subprojects/libcapsule/tests/shim/libnotgles.so.1.excluded new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/subprojects/libcapsule/tests/shim/libnotgles.so.1.shared b/subprojects/libcapsule/tests/shim/libnotgles.so.1.shared new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/subprojects/libcapsule/tests/shim/libnotgles.so.1.symbols b/subprojects/libcapsule/tests/shim/libnotgles.so.1.symbols new file mode 100644 index 0000000000000000000000000000000000000000..8662801fadc045c0ca359a5273b13a7320e0efcd --- /dev/null +++ b/subprojects/libcapsule/tests/shim/libnotgles.so.1.symbols @@ -0,0 +1,2 @@ +notgles_get_implementation +notgles_use_helper diff --git a/subprojects/libcapsule/tests/symbols.pl b/subprojects/libcapsule/tests/symbols.pl new file mode 100755 index 0000000000000000000000000000000000000000..3d19896cee2551a4c5abb4c2538938c650a8582d --- /dev/null +++ b/subprojects/libcapsule/tests/symbols.pl @@ -0,0 +1,90 @@ +#!/usr/bin/perl + +# Copyright © 2017 Collabora Ltd +# +# This file is part of libcapsule. +# +# libcapsule is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as +# published by the Free Software Foundation; either version 2.1 of the +# License, or (at your option) any later version. +# +# libcapsule 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 Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with libcapsule. If not, see <http://www.gnu.org/licenses/>. + +use autodie; +use warnings; +use strict; + +use File::Temp qw(); +use IPC::Run qw(run); +use Test::More; + +use FindBin; +use lib $FindBin::Bin; + +use CapsuleTest; + +skip_all_unless_bwrap; + +my $test_tempdir = File::Temp->newdir(); +diag "Working directory: $test_tempdir"; +my $host = "${test_tempdir}/host"; +mkdir($host); + +if (length $ENV{CAPSULE_TESTS_UNINSTALLED}) { + my $path = $ENV{LD_LIBRARY_PATH}; + + if (length $path) { + $path = "$ENV{G_TEST_BUILDDIR}/.libs:$path"; + } + else { + $path = $ENV{G_TEST_BUILDDIR}; + } + + $ENV{LD_LIBRARY_PATH} = $path; + + TODO: { + local $TODO = "capsule-symbols doesn't respect LD_LIBRARY_PATH"; + fail 'Re-enable this later'; + } +} +else { + # We need a well-behaved library with a simple ABI to inspect. Let's use + # libcapsule itself :-) + my $output; + run_ok([qw(bwrap --ro-bind / / --ro-bind /), $host, + qw(--dev-bind /dev /dev), + $CAPSULE_SYMBOLS_TOOL, 'libcapsule.so.0', $host], '>', \$output); + diag_multiline $output; + return unless $output; + + like($output, qr{^capsule_init $}m, "capsule_init is an unversioned symbol"); + unlike($output, qr{^(?!capsule)}m, "all of libcapsule's ABI matches /^capsule/"); + + # Try the same thing without a prefix + run_ok([$CAPSULE_SYMBOLS_TOOL, 'libcapsule.so.0', '/'], '>', \$output); + diag_multiline $output; + + like($output, qr{^capsule_init $}m, "capsule_init is an unversioned symbol"); + unlike($output, qr{^(?!capsule)}m, "all of libcapsule's ABI matches /^capsule/"); +} + +# How about versioned symbols? +my $output; +run_ok([$CAPSULE_SYMBOLS_TOOL, 'libxml2.so.2', '/'], '>', \$output); +diag_multiline $output; + +like($output, qr{^xmlFree \@\@LIBXML2_2\.4\.30$}m, + "xmlFree is a versioned symbol"); +unlike($output, qr{^xmlFree (?!\@\@LIBXML2_2\.4\.30)}m, + "xmlFree does not appear unversioned"); + +done_testing; + +# vim:set sw=4 sts=4 et: diff --git a/subprojects/libcapsule/tests/test-helpers.c b/subprojects/libcapsule/tests/test-helpers.c new file mode 100644 index 0000000000000000000000000000000000000000..3281b71f8907e4d924d465a1a0e4f0e9a5e20fa8 --- /dev/null +++ b/subprojects/libcapsule/tests/test-helpers.c @@ -0,0 +1,50 @@ +/* + * Copyright © 2019-2020 Collabora Ltd. + * + * SPDX-License-Identifier: MIT + * + * 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. + */ + +#include "test-helpers.h" + +#include <ftw.h> +#include <stdio.h> + +static int +ftw_remove (const char *path, + const struct stat *sb, + int typeflags, + struct FTW *ftwbuf) +{ + if (remove (path) < 0) + return -1; + + return 0; +} + +gboolean +rm_rf (const char *path) +{ + if (nftw (path, ftw_remove, 10, FTW_DEPTH|FTW_MOUNT|FTW_PHYS) < 0) + return FALSE; + + return TRUE; +} diff --git a/subprojects/libcapsule/tests/test-helpers.h b/subprojects/libcapsule/tests/test-helpers.h new file mode 100644 index 0000000000000000000000000000000000000000..b19be776608b6c59ce13cc61125bbc661752e761 --- /dev/null +++ b/subprojects/libcapsule/tests/test-helpers.h @@ -0,0 +1,68 @@ +/* + * Copyright © 2019 Collabora Ltd. + * + * SPDX-License-Identifier: MIT + * + * 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. + */ + +#pragma once + +#include <glib.h> + +gboolean rm_rf (const char *path); + +/* + * Simplified implementations of some of the GLib test assertion macros, + * for use with older GLib versions. + */ + +#ifndef g_assert_true +#define g_assert_true(x) g_assert ((x)) +#endif + +#ifndef g_assert_false +#define g_assert_false(x) g_assert (!(x)) +#endif + +#ifndef g_assert_cmpint +#define g_assert_cmpint(a, op, b) g_assert ((a) op (b)) +#endif + +#ifndef g_assert_cmpmem +#define g_assert_cmpmem(m1, l1, m2, l2) \ + g_assert (l1 == l2 && memcmp (m1, m2, l1) == 0) +#endif + +#ifndef g_assert_cmpstr +#define g_assert_cmpstr(a, op, b) g_assert (g_strcmp0 ((a), (b)) op 0) +#endif + +#ifndef g_assert_nonnull +#define g_assert_nonnull(x) g_assert ((x) != NULL) +#endif + +#ifndef g_assert_null +#define g_assert_null(x) g_assert ((x) == NULL) +#endif + +#if !GLIB_CHECK_VERSION(2, 38, 0) +#define g_test_skip(msg) g_test_message ("SKIP: %s", msg) +#endif diff --git a/subprojects/libcapsule/tests/utils.c b/subprojects/libcapsule/tests/utils.c new file mode 100644 index 0000000000000000000000000000000000000000..deb0e03ae3bda36302dd2633e0c793f636ddc312 --- /dev/null +++ b/subprojects/libcapsule/tests/utils.c @@ -0,0 +1,820 @@ +// Copyright © 2017 Collabora Ltd + +// This file is part of libcapsule. + +// libcapsule is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as +// published by the Free Software Foundation; either version 2.1 of the +// License, or (at your option) any later version. + +// libcapsule 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 Lesser General Public License for more details. + +// You should have received a copy of the GNU Lesser General Public +// License along with libcapsule. If not, see <http://www.gnu.org/licenses/>. + +#include <errno.h> +#include <string.h> +#include <unistd.h> + +#include <glib.h> +#include <glib/gstdio.h> + +#include "tests/test-helpers.h" +#include "utils/library-cmp.h" +#include "utils/utils.h" + +static const char *argv0; + +#define assert_with_errno(expr) \ + do { \ + errno = 0; \ + \ + if (!(expr)) \ + g_error ("Assertion failed: %s: %s", #expr, g_strerror (errno)); \ + } while (0) + +static void +touch (const char *path) +{ + FILE *fh; + + assert_with_errno((fh = fopen (path, "w")) != NULL); + fclose (fh); +} + +typedef struct +{ + gchar *srcdir; + gchar *builddir; + gboolean uninstalled; +} Fixture; + +static void +setup (Fixture *f, + gconstpointer data) +{ + f->srcdir = g_strdup (g_getenv ("G_TEST_SRCDIR")); + f->builddir = g_strdup (g_getenv ("G_TEST_BUILDDIR")); + + if (f->srcdir == NULL) + f->srcdir = g_path_get_dirname (argv0); + + if (f->builddir == NULL) + f->builddir = g_path_get_dirname (argv0); + + f->uninstalled = (g_getenv ("CAPSULE_TESTS_UNINSTALLED") != NULL); +} + +static struct +{ + const char * const parts[3]; + const char *expected; +} filename_tests[] = +{ + { { "/host", "/usr/lib", "libc.so.6" }, "/host/usr/lib/libc.so.6" }, + { { "/usr/lib", "/libc.so.6", NULL }, "/usr/lib/libc.so.6" }, + { { "", "/usr/lib", "libc.so.6" }, "/usr/lib/libc.so.6" }, + { { "/", "usr/lib", "libc.so.6" }, "/usr/lib/libc.so.6" }, + { { "///host///", "///usr/lib///", "///libc.so.6" }, + "/host/usr/lib/libc.so.6" }, + { { NULL, "xxxxxxxxxxxxxxxx", NULL }, "" }, + { { "", NULL }, "" }, + { { "", "/etc/ld.so.cache", NULL }, "/etc/ld.so.cache" }, + { { "", "etc/ld.so.cache", NULL }, "/etc/ld.so.cache" }, + { { "/", "/etc/ld.so.cache", NULL }, "/etc/ld.so.cache" }, + { { "/", "etc/ld.so.cache", NULL }, "/etc/ld.so.cache" }, + { { "foo", "/bar", NULL }, "foo/bar" }, + { { "foo", "bar", NULL }, "foo/bar" }, +}; + +static void +test_build_filename (Fixture *f, + gconstpointer data) +{ + unsigned i; + + for (i = 0; i < G_N_ELEMENTS (filename_tests); i++) + { + const char * const *parts = filename_tests[i].parts; + const char *expected = filename_tests[i].expected; + const size_t allocated = strlen (expected) + 5; + gchar *buf = g_malloc (allocated); + char *str = NULL; + size_t used; + size_t len = allocated; + + if (parts[1] == NULL) + str = build_filename_alloc (parts[0], NULL); + else if (parts[2] == NULL) + str = build_filename_alloc (parts[0], parts[1], NULL); + else + str = build_filename_alloc (parts[0], parts[1], parts[2], NULL); + + g_assert_cmpstr (str, ==, expected); + + do + { + size_t j; + + memset (buf, '\xaa', allocated); + + if (parts[1] == NULL) + { + used = build_filename (buf, len, parts[0], NULL); + } + else if (parts[2] == NULL) + { + used = build_filename (buf, len, parts[0], parts[1], NULL); + } + else + { + used = build_filename (buf, len, parts[0], parts[1], parts[2], + NULL); + } + + g_test_message ("\"%s\", \"%s\", \"%s\" -> %zu \"%s\"", + parts[0], parts[1], parts[2], used, + len == 0 ? NULL : buf); + + g_assert_cmpuint (used, ==, strlen (expected)); + + if (len == 0) + { + /* Stupid corner case: we can't write anything into the buffer */ + } + else if (used >= len) + { + gchar *truncated = g_strndup (expected, len - 1); + + g_assert_cmpstr (buf, ==, truncated); + g_free (truncated); + } + else + { + g_assert_cmpstr (buf, ==, expected); + } + + /* The rest of the buffer is untouched (we didn't overflow) */ + for (j = len; j < allocated; j++) + { + g_assert_cmpint (buf[j], ==, '\xaa'); + } + } + while (--len > 0); + + g_free (buf); + free (str); + } +} + +typedef struct +{ + const char *soname; + const char *in_container; + char cmp; + const char *in_provider; +} CmpByNameTest; + +static const CmpByNameTest cmp_by_name_tests[] = +{ + { "libdbus-1.so.3", "libdbus-1.so.3.1", '<', "libdbus-1.so.3.2" }, + { "libdbus-1.so.3", "libdbus-1.so.3.1.2", '>', "libdbus-1.so.3.1.1" }, + { "libdbus-1.so.3", "libdbus-1.so.3.1", '=', "libdbus-1.so.3.1" }, + { "libc.so.6", "libc-2.19.so", '<', "libc-2.22.so" }, + { "libgcc_s.so.1", "libgcc_s-20200703.so.1", '>', "libgcc_s-20120401.so.1" }, + { "libgcc_s.so.1", "libgcc_s-20200703.so.1", '=', NULL }, + { "libgcc_s.so.1", NULL, '=', "libgcc_s-20200703.so.1" }, + { "libgcc_s.so.1", NULL, '=', NULL }, +}; + +static void +test_library_cmp_by_name (Fixture *f, + gconstpointer data) +{ + GError *error = NULL; + gchar *tmpdir = g_dir_make_tmp ("libcapsule.XXXXXX", &error); + gchar *container; + gchar *provider; + gsize i; + library_cmp_function *comparators; + + g_assert_no_error (error); + g_assert_nonnull (tmpdir); + + comparators = library_cmp_list_from_string ("name", ",", NULL, NULL); + g_assert_nonnull (comparators); + + container = g_build_filename (tmpdir, "c", NULL); + provider = g_build_filename (tmpdir, "p", NULL); + + assert_with_errno (g_mkdir (container, 0755) == 0); + assert_with_errno (g_mkdir (provider, 0755) == 0); + + for (i = 0; i < G_N_ELEMENTS (cmp_by_name_tests); i++) + { + const CmpByNameTest *test = &cmp_by_name_tests[i]; + gchar *container_file = NULL; + gchar *container_lib = g_build_filename (container, test->soname, NULL); + gchar *provider_file = NULL; + gchar *provider_lib = g_build_filename (provider, test->soname, NULL); + gchar *soname_copy = g_strdup (test->soname); + int result; + const library_details comparator_details = { + .name = soname_copy, + .comparators = comparators, + .public_symbol_versions = NULL, + .public_symbols = NULL, + }; + + assert_with_errno (g_unlink (container_lib) == 0 || errno == ENOENT); + assert_with_errno (g_unlink (provider_lib) == 0 || errno == ENOENT); + + if (test->in_container == NULL) + { + touch (container_lib); + } + else + { + container_file = g_build_filename (container, test->in_container, NULL); + assert_with_errno (g_unlink (container_file) == 0 || errno == ENOENT); + touch (container_file); + assert_with_errno (symlink (test->in_container, container_lib) == 0); + } + + if (test->in_provider == NULL) + { + touch (provider_lib); + } + else + { + provider_file = g_build_filename (provider, test->in_provider, NULL); + assert_with_errno (g_unlink (provider_file) == 0 || errno == ENOENT); + touch (provider_file); + assert_with_errno (symlink (test->in_provider, provider_lib) == 0); + } + + result = library_cmp_list_iterate (&comparator_details, + container_lib, container, + provider_lib, provider); + + switch (test->cmp) + { + case '<': + if (result >= 0) + g_error ("Expected %s (%s) < %s (%s), got %d", + container_lib, + container_file ? container_file : "regular file", + provider_lib, + provider_file ? provider_file : "regular file", + result); + break; + + case '>': + if (result <= 0) + g_error ("Expected %s (%s) > %s (%s), got %d", + container_lib, + container_file ? container_file : "regular file", + provider_lib, + provider_file ? provider_file : "regular file", + result); + break; + + case '=': + if (result != 0) + g_error ("Expected %s (%s) == %s (%s), got %d", + container_lib, + container_file ? container_file : "regular file", + provider_lib, + provider_file ? provider_file : "regular file", + result); + break; + + default: + g_assert_not_reached (); + } + + g_free (container_file); + g_free (container_lib); + g_free (provider_file); + g_free (provider_lib); + g_free (soname_copy); + } + + assert_with_errno (rm_rf (tmpdir)); + g_free (container); + g_free (provider); + g_free (tmpdir); + free (comparators); +} + +typedef struct +{ + const char *soname; + const char *spec; + const char *public_symbol_versions[8]; + const char *public_symbols[8]; + /* It compares such that "version1 cmp version2" is true, except for + * the special case that "c" and "p" mean we choose the container version + * and provider version respectively */ + char cmp; +} CmpTest; + +#ifdef ENABLE_SHARED +static const CmpTest cmp_tests[] = +{ + /* BY SYMBOLS */ + + /* This adds one symbol and removes one symbol, so we can't tell which + * was meant to be newer */ + { "libunversionedabibreak.so.1", "symbols", {}, {}, '=' }, + { "libversionedabibreak.so.1", "symbols", {}, {}, '=' }, + + /* The only difference here is the tail of the filename, which this + * comparator doesn't look at */ + { "libunversionednumber.so.1", "symbols", {}, {}, '=' }, + { "libversionednumber.so.1", "symbols", {}, {}, '=' }, + + /* This is the situation this comparator handles */ + { "libunversionedsymbols.so.1", "symbols", {}, {}, '<' }, + { "libversionedsymbols.so.1", "symbols", {}, {}, '<' }, + { "libversionedupgrade.so.1", "symbols", {}, {}, '<' }, + { "libversionedlikeglibc.so.1", "symbols", {}, {}, '<' }, + { "libversionedprivatenothidden.so.1", "symbols", {}, {}, '<' }, + + /* We can't currently tell which one is newer because the private symbols + * confuse us */ + { "libversionedlikedbus.so.1", "symbols", {}, {}, '=' }, + + /* After filtering the private symbols, they have the same symbols */ + { "libversionedprivatenothidden.so.1", "symbols", + {}, { "!_private*", "!LIBVERSIONEDPRIVATE*", "*", NULL }, '=' }, + + /* Same as before, but specify the exact symbols name */ + { "libversionedprivatenothidden.so.1", "symbols", + {}, + { "!_private1", "!_private2", "!LIBVERSIONEDPRIVATE_1_4_0", "!LIBVERSIONEDPRIVATE_2_1_0", "*", NULL }, + '=' }, + + /* Same as above, but test listing public symbol versions */ + { "libversionedprivatenothidden.so.1", "symbols", + {}, { "symbol*", "!*", NULL }, '=' }, + + /* Same as above, but use the "after this point is just a guess" special pattern */ + { "libversionedprivatenothidden.so.1", "symbols", + {}, { "symbol*", "!", "!*", NULL }, '=' }, + + /* BY VERSION DEFINITIONS */ + + /* All of these have no symbol-versioning, so we can't tell a + * difference with this comparator */ + { "libunversionedabibreak.so.1", "versions", {}, {}, '=' }, + { "libunversionednumber.so.1", "versions", {}, {}, '=' }, + { "libunversionedsymbols.so.1", "versions", {}, {}, '=' }, + + /* This adds one verdef and removes one verdef, so we can't tell which + * was meant to be newer */ + { "libversionedabibreak.so.1", "versions", {}, {}, '=' }, + + /* The only difference here is the tail of the filename, which this + * comparator doesn't look at */ + { "libversionednumber.so.1", "versions", {}, {}, '=' }, + + /* This is simple "version ~= SONAME" symbol-versioning, like in libtiff + * and libpng, so this comparator can't tell any difference */ + { "libversionedsymbols.so.1", "versions", {}, {}, '=' }, + + /* This one has version-specific verdefs like libmount, libgcab, OpenSSL, + * telepathy-glib etc., so we can tell it's an upgrade */ + { "libversionedupgrade.so.1", "versions", {}, {}, '<' }, + + /* This one has the same symbol listed in more than one verdef, + * like glibc - we can tell this is an upgrade */ + { "libversionedlikeglibc.so.1", "versions", {}, {}, '<' }, + + /* We can't currently tell which one is newer because the private verdefs + * confuse us */ + { "libversionedlikedbus.so.1", "versions", {}, {}, '=' }, + + /* This one has more private verdefs, but we don't know that they are + private */ + { "libversionedprivatenothidden.so.1", "versions", {}, {}, '<' }, + + /* After filtering the private verdefs, they have the same public verdefs */ + { "libversionedprivatenothidden.so.1", "versions", + { "!LIBVERSIONEDPRIVATE*", "*", NULL }, {}, '=' }, + + /* Same as above, but test using multiple private symbol versions filters */ + { "libversionedprivatenothidden.so.1", "versions", + { "!LIBVERSIONEDPRIVATE_1_4_0", "!LIBVERSIONEDPRIVATE_2_1_0", "!MISSING*", "*", NULL }, + {}, '=' }, + + /* Same as above, but test listing public symbol versions */ + { "libversionedprivatenothidden.so.1", "versions", + { "LIBVERSIONED1*", "!*", NULL }, {}, '=' }, + + /* After partially filtering the private verdefs, we think it's an upgrade + * because it has more verdefs */ + { "libversionedprivatenothidden.so.1", "versions", + { "!LIBVERSIONEDPRIVATE_1*", "*", NULL }, {}, '<' }, + + /* BY NAME */ + + /* These have the version number in the filename. */ + { "libunversionednumber.so.1", "name", {}, {}, '<' }, + { "libversionedlikedbus.so.1", "name", {}, {}, '<' }, + { "libversionednumber.so.1", "name", {}, {}, '<' }, + + /* These have the same filename in both versions, so we can't tell. */ + { "libunversionedabibreak.so.1", "name", {}, {}, '=' }, + { "libunversionedsymbols.so.1", "name", {}, {}, '=' }, + { "libversionedabibreak.so.1", "name", {}, {}, '=' }, + { "libversionedsymbols.so.1", "name", {}, {}, '=' }, + { "libversionedupgrade.so.1", "name", {}, {}, '=' }, + { "libversionedlikeglibc.so.1", "name", {}, {}, '=' }, + + /* BY MORE THAN ONE FACTOR */ + + { "libversionedlikeglibc.so.1", "name,versions", {}, {}, '<' }, + { "libversionedlikeglibc.so.1", "name,container", {}, {}, 'c' }, + { "libversionedlikeglibc.so.1", "name,container,provider", {}, {}, 'c' }, + { "libversionedlikeglibc.so.1", "name,provider", {}, {}, 'p' }, + { "libversionedlikeglibc.so.1", "name,provider,container", {}, {}, 'p' }, + { "libversionedlikeglibc.so.1", "versions,name", {}, {}, '<' }, + { "libversionedlikeglibc.so.1", "versions,container", {}, {}, '<' }, + { "libversionedlikeglibc.so.1", "versions,provider", {}, {}, '<' }, + { "libversionedlikeglibc.so.1", "name,symbols", {}, {}, '<' }, + { "libversionedlikeglibc.so.1", "symbols,provider", {}, {}, '<' }, + { "libversionedprivatenothidden.so.1", "versions,symbols", + { "!LIBVERSIONEDPRIVATE*", "*", NULL }, + { "!_private*", "!LIBVERSIONED*", "*", NULL }, '=' }, + + /* This one is a stand-in for libgcc_s.so.1 */ + { "libversionedupgrade.so.1", "name", {}, {}, '=' }, + { "libversionedupgrade.so.1", "versions,name,symbols", {}, {}, '<' }, + { "libversionedupgrade.so.1", "name,versions", {}, {}, '<' }, + + /* These are obviously silly - just assert that they don't crash. */ + { "libversionedupgrade.so.1", "", {}, {}, '=' }, + { "libversionedupgrade.so.1", ",,,,,,", {}, {}, '=' }, + { "libversionedupgrade.so.1", "name,,,,,,name,,,,,,,,,,,,,,,,,,,", {}, {}, '=' }, + { "libversionedupgrade.so.1", + "name,name,name,name,name,name,name,name,name,name,name,name,name,name", + {}, {}, '=' }, +}; + +static void +test_library_cmp (Fixture *f, + gconstpointer data) +{ + gsize i; + + for (i = 0; i < G_N_ELEMENTS (cmp_tests); i++) + { + const CmpTest *test = &cmp_tests[i]; + library_cmp_function *comparators; + gchar *v1; + gchar *v1_lib; + gchar *v2; + gchar *v2_lib; + gchar *soname_copy = g_strdup (test->soname); + int result; + + comparators = library_cmp_list_from_string (test->spec, ",", NULL, NULL); + g_assert_nonnull (comparators); + + const library_details comparator_details = { + .name = soname_copy, + .comparators = comparators, + .public_symbol_versions = test->public_symbol_versions[0] != NULL ? (char **) test->public_symbol_versions: NULL, + .public_symbols = test->public_symbols[0] != NULL ? (char **) test->public_symbols: NULL, + }; + + v1 = g_build_filename (f->builddir, "tests", "version1", NULL); + v1_lib = g_build_filename (v1, f->uninstalled ? ".libs" : ".", + test->soname, NULL); + v2 = g_build_filename (f->builddir, "tests", "version2", NULL); + v2_lib = g_build_filename (v2, f->uninstalled ? ".libs" : ".", + test->soname, NULL); + + result = library_cmp_list_iterate (&comparator_details, + v1_lib, v1, + v2_lib, v2); + + switch (test->cmp) + { + case '<': // v1 < v2 + case 'p': // choose provider, i.e. v2 here + if (result >= 0) + g_error ("Expected %s < %s by (%s), got %d", + v1_lib, v2_lib, test->spec, result); + break; + + case '>': // v1 > v2 + case 'c': // choose container, i.e. v1 here + if (result <= 0) + g_error ("Expected %s > %s by (%s), got %d", + v1_lib, v2_lib, test->spec, result); + break; + + case '=': + if (result != 0) + g_error ("Expected %s == %s by (%s), got %d", + v1_lib, v2_lib, test->spec, result); + break; + + default: + g_assert_not_reached (); + } + + /* We get the reverse result when we do it the other way round - + * unless the expected result is "c" or "p", in which case we + * get the same result, because those results are determined by + * the position of the arguments rather than by their content. */ + result = library_cmp_list_iterate (&comparator_details, + v2_lib, v2, + v1_lib, v1); + + switch (test->cmp) + { + case '>': // v1 > v2, i.e. v2 < v1 + case 'p': // choose provider, i.e. v1 here + if (result >= 0) + g_error ("Expected %s < %s by (%s), got %d", + v2_lib, v1_lib, test->spec, result); + break; + + case '<': // v1 < v2, i.e. v2 > v1 + case 'c': // choose container, i.e. v2 here + if (result <= 0) + g_error ("Expected %s > %s by (%s), got %d", + v2_lib, v1_lib, test->spec, result); + break; + + case '=': + if (result != 0) + g_error ("Expected %s == %s by (%s), got %d", + v2_lib, v1_lib, test->spec, result); + break; + + default: + g_assert_not_reached (); + } + + + g_free (v1); + g_free (v1_lib); + g_free (v2); + g_free (v2_lib); + g_free (soname_copy); + free (comparators); + } +} +#endif + +char library_ini_part_1[] = +"# Configuration for capsule-capture-libs\n" +"\n" +"[Library libc.so.6]\n" +"CompareBy=versions;symbols;name;\n" +"\n" +"[Library libgcc_s.so.1]\n" +"CompareBy=versions;\n" +"\n" +"[Library libwhatsmynameagain.so.0]\n" +"CompareBy=name;\n" +"\n" +"[Library libcountmysymbols.so.0]\n" +"CompareBy=symbols;\n" +"\n" +"[Not a library]\n" +"# Ignore this\n" +"CompareBy=By guesswork; by magic; randomly\n" +"whatever=Whatever\n" +"\n" +"[Library libdbus-1.so.3]\n" +"\n" +"[Library libdbus-1.so.3]\n" +"CompareBy=provider;container;\n" +""; + +char library_ini_part_2[] = +"[Library libglib-2.0.so.0]\n" +"\n" +"[Library libdbus-1.so.3]\n" +"CompareBy=versions;name;symbols;\n" +"private-symbols=_*;dbus_internal_do_not_use_*;\n" +"private-versions=LIBDBUS_PRIVATE_*;\n" +""; + +static void +test_library_knowledge_good (Fixture *f, + gconstpointer data) +{ + library_knowledge knowledge = LIBRARY_KNOWLEDGE_INIT; + const library_details *libc; + const library_details *libgcc; + const library_details *name; + const library_details *symbols; + const library_details *libdbus; + const library_details *glib; + const library_details *nope; + int code = -1; + char *message = NULL; + FILE *fh; + + assert_with_errno ((fh = fmemopen ((char *) library_ini_part_1, + sizeof (library_ini_part_1), "r")) != NULL); + + if (!library_knowledge_load_from_stream (&knowledge, fh, "library.ini.1", + &code, &message)) + g_error ("%s", message); + + g_assert_cmpstr (message, ==, NULL); + g_assert_cmpuint (code, ==, -1); + assert_with_errno (fclose (fh) == 0); + + assert_with_errno ((fh = fmemopen ((char *) library_ini_part_2, + sizeof (library_ini_part_2), "r")) != NULL); + + if (!library_knowledge_load_from_stream (&knowledge, fh, "library.ini.2", + &code, &message)) + g_error ("%s", message); + + g_assert_cmpstr (message, ==, NULL); + g_assert_cmpuint (code, ==, -1); + assert_with_errno (fclose (fh) == 0); + + libc = library_knowledge_lookup (&knowledge, "libc.so.6"); + g_assert_nonnull (libc); + g_assert_cmpstr (libc->name, ==, "libc.so.6"); + libgcc = library_knowledge_lookup (&knowledge, "libgcc_s.so.1"); + g_assert_nonnull (libgcc); + g_assert_cmpstr (libgcc->name, ==, "libgcc_s.so.1"); + name = library_knowledge_lookup (&knowledge, "libwhatsmynameagain.so.0"); + g_assert_nonnull (name); + g_assert_cmpstr (name->name, ==, "libwhatsmynameagain.so.0"); + symbols = library_knowledge_lookup (&knowledge, "libcountmysymbols.so.0"); + g_assert_nonnull (symbols); + g_assert_cmpstr (symbols->name, ==, "libcountmysymbols.so.0"); + libdbus = library_knowledge_lookup (&knowledge, "libdbus-1.so.3"); + g_assert_nonnull (libdbus); + g_assert_cmpstr (libdbus->name, ==, "libdbus-1.so.3"); + glib = library_knowledge_lookup (&knowledge, "libglib-2.0.so.0"); + g_assert_nonnull (glib); + g_assert_cmpstr (libdbus->name, ==, "libdbus-1.so.3"); + nope = library_knowledge_lookup (&knowledge, "libdbus-glib-1.so.2"); + g_assert_null (nope); + + g_assert_null (glib->comparators); + g_assert_nonnull (name->comparators); + g_assert_nonnull (name->comparators[0]); + g_assert_null (name->comparators[1]); + g_assert_nonnull (symbols->comparators); + g_assert_nonnull (symbols->comparators[0]); + g_assert_null (symbols->comparators[1]); + g_assert_nonnull (libgcc->comparators); + g_assert_nonnull (libgcc->comparators[0]); + g_assert_null (libgcc->comparators[1]); + + g_assert_nonnull (libc->comparators); + g_assert_true (libc->comparators[0] == libgcc->comparators[0]); + g_assert_true (libc->comparators[1] == symbols->comparators[0]); + g_assert_true (libc->comparators[2] == name->comparators[0]); + g_assert_null (libc->comparators[3]); + + g_assert_nonnull (libdbus->comparators); + g_assert_true (libdbus->comparators[0] == libgcc->comparators[0]); + g_assert_true (libdbus->comparators[1] == name->comparators[0]); + g_assert_true (libdbus->comparators[2] == symbols->comparators[0]); + g_assert_null (libdbus->comparators[3]); + + library_knowledge_clear (&knowledge); +} + +static const char * const library_knowledge_bad_strings[] = +{ + "nope", + "[nope", + "foo=bar", + "[nope]\n[no", +}; + +static void +test_library_knowledge_bad (Fixture *f, + gconstpointer data) +{ + gsize i; + library_knowledge knowledge = LIBRARY_KNOWLEDGE_INIT; + + for (i = 0; i < G_N_ELEMENTS (library_knowledge_bad_strings); i++) + { + const char *s = library_knowledge_bad_strings[i]; + int code = -1; + char *message = NULL; + FILE *fh; + gchar *description; + bool ok; + + description = g_strescape (s, NULL); + assert_with_errno ((fh = fmemopen ((char *) s, strlen (s), "r")) != NULL); + + ok = library_knowledge_load_from_stream (&knowledge, fh, description, + &code, &message); + g_assert_cmpstr (message, !=, NULL); + g_assert_cmpuint (code, !=, -1); + g_assert_false (ok); + g_test_message ("code %d: %s", code, message); + free (message); + assert_with_errno (fclose (fh) == 0); + library_knowledge_clear (&knowledge); + + assert_with_errno ((fh = fmemopen ((char *) s, strlen (s), "r")) != NULL); + ok = library_knowledge_load_from_stream (&knowledge, fh, description, + NULL, NULL); + g_assert_false (ok); + assert_with_errno (fclose (fh) == 0); + library_knowledge_clear (&knowledge); + + g_free (description); + } +} + +static void +test_ptr_list (Fixture *f, + gconstpointer data) +{ + size_t n; + ptr_list *list; + void **array; + + list = ptr_list_alloc (0); + ptr_list_push_ptr (list, (char *) "hello"); + ptr_list_add_ptr (list, (char *) "world", g_str_equal); + ptr_list_add_ptr (list, (char *) "hello", g_str_equal); // duplicate, not added + ptr_list_add_ptr (list, (char *) "world", g_str_equal); // duplicate, not added + ptr_list_push_ptr (list, (char *) "hello"); + ptr_list_push_ptr (list, NULL); + ptr_list_push_addr (list, 23); + + g_assert_cmpstr (ptr_list_nth_ptr (list, 0), ==, "hello"); + g_assert_cmpstr (ptr_list_nth_ptr (list, 1), ==, "world"); + g_assert_cmpstr (ptr_list_nth_ptr (list, 2), ==, "hello"); + g_assert_cmpstr (ptr_list_nth_ptr (list, 3), ==, NULL); + g_assert_cmpuint (GPOINTER_TO_SIZE (ptr_list_nth_ptr (list, 4)), ==, 23); + g_assert_cmpstr (ptr_list_nth_ptr (list, 5), ==, NULL); + g_assert_cmpstr (ptr_list_nth_ptr (list, 47), ==, NULL); + g_assert_true (ptr_list_contains (list, 23)); + g_assert_true (ptr_list_contains (list, (ElfW(Addr)) ptr_list_nth_ptr (list, 1))); + g_assert_false (ptr_list_contains (list, 1)); + + array = ptr_list_free_to_array (list, &n); + g_assert_cmpint (n, ==, 5); + g_assert_cmpstr (array[0], ==, "hello"); + g_assert_cmpstr (array[1], ==, "world"); + g_assert_cmpstr (array[2], ==, "hello"); + g_assert_cmpstr (array[3], ==, NULL); + g_assert_cmpuint (GPOINTER_TO_SIZE (array[4]), ==, 23); + g_assert_cmpstr (array[5], ==, NULL); + free (array); + + list = ptr_list_alloc (0); + ptr_list_free (list); +} + +static void +teardown (Fixture *f, + gconstpointer data) +{ + g_free (f->srcdir); + g_free (f->builddir); +} + +int +main (int argc, + char **argv) +{ + argv0 = argv[0]; + g_test_init (&argc, &argv, NULL); + g_test_set_nonfatal_assertions (); + + set_debug_flags (g_getenv("CAPSULE_DEBUG")); + + g_test_add ("/build-filename", Fixture, NULL, setup, + test_build_filename, teardown); +#ifdef ENABLE_SHARED + g_test_add ("/library-cmp/configurable", Fixture, NULL, + setup, test_library_cmp, teardown); +#endif + g_test_add ("/library-cmp/name", Fixture, NULL, + setup, test_library_cmp_by_name, teardown); + g_test_add ("/library-knowledge/bad", Fixture, NULL, + setup, test_library_knowledge_bad, teardown); + g_test_add ("/library-knowledge/good", Fixture, NULL, + setup, test_library_knowledge_good, teardown); + g_test_add ("/ptr-list", Fixture, NULL, setup, test_ptr_list, teardown); + + return g_test_run (); +} diff --git a/subprojects/libcapsule/tests/version.pl b/subprojects/libcapsule/tests/version.pl new file mode 100755 index 0000000000000000000000000000000000000000..100be93828ff86d51b8710f86567753e2d8789fb --- /dev/null +++ b/subprojects/libcapsule/tests/version.pl @@ -0,0 +1,118 @@ +#!/usr/bin/perl + +# Copyright © 2017 Collabora Ltd +# +# This file is part of libcapsule. +# +# libcapsule is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as +# published by the Free Software Foundation; either version 2.1 of the +# License, or (at your option) any later version. +# +# libcapsule 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 Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with libcapsule. If not, see <http://www.gnu.org/licenses/>. + +use autodie; +use warnings; +use strict; + +use File::Temp qw(); +use IPC::Run qw(run); +use Test::More; + +use FindBin; +use lib $FindBin::Bin; + +use CapsuleTest; + +my $LIBDIR = qr{/(?:usr/(?:\w+-(?:\w+-)?linux-gnu\w*/)?)?lib(?:32|64|x32)?(?:/\w+-linux-gnu\w*)?(?:/i686)?(?:/cmov)?}; + +skip_all_unless_bwrap; + +my $test_tempdir = File::Temp->newdir(); +diag "Working directory: $test_tempdir"; +my $host = "${test_tempdir}/host"; +mkdir($host); + +my $output; +my ($root, $soname, $version, $path); +run_ok([qw(bwrap --ro-bind / / --ro-bind /), $host, + qw(--dev-bind /dev /dev), $CAPSULE_VERSION_TOOL, 'libz.so.1', + $host], '>', \$output); +diag("→ $output"); +like($output, qr{^(\S+)\s+(\S+)\s+(\S+)\s+(\S+)$}); +$output =~ m{^(\S+)\s+(\S+)\s+(\S+)\s+(\S+)$}; +$root = $1; +$soname = $2; +$version = $3; +$path = $4; +is($root, $host); +is($soname, 'libz.so.1'); +like($version, qr{^1\.[0-9]+\.[0-9]+$}); +like($path, qr{^\Q$host\E($LIBDIR/libz\.so\.\Q$version\E)$}); +$path =~ s/^\Q$host\E//; +ok(-e $path); + +# Try the same thing without a prefix +run_ok([$CAPSULE_VERSION_TOOL, 'libz.so.1', '/'], '>', \$output); +diag("→ $output"); +like($output, qr{^(\S+)\s+(\S+)\s+(\S+)\s+(\S+)$}); +$output =~ m{^(\S+)\s+(\S+)\s+(\S+)\s+(\S+)$}; +$root = $1; +$soname = $2; +$version = $3; +$path = $4; +is($root, '/'); +is($soname, 'libz.so.1'); +TODO: { + local $TODO = 'returns 1 for some reason'; + like($version, qr{^1\.[0-9]+\.[0-9]+$}); +} +like($path, qr{^($LIBDIR/libz\.so\.\Q$version\E)$}); +ok(-e $1); + +# A different way +run_ok([$CAPSULE_VERSION_TOOL, 'libz.so.1', ''], '>', \$output); +diag("→ $output"); +like($output, qr{^(\S*)\s+(\S+)\s+(\S+)\s+(\S+)$}); +$output =~ m{^(\S*)\s+(\S+)\s+(\S+)\s+(\S+)$}; +$root = $1; +$soname = $2; +$version = $3; +$path = $4; +like($root, qr{/?}); +is($soname, 'libz.so.1'); +TODO: { + local $TODO = 'returns 1 for some reason'; + like($version, qr{^1\.[0-9]+\.[0-9]+$}); +} +like($path, qr{^($LIBDIR/libz\.so\.\Q$version\E)$}); +ok(-e $path); + +# Another different way +run_ok([$CAPSULE_VERSION_TOOL, 'libz.so.1'], '>', \$output); +diag("→ $output"); +like($output, qr{^(\S+)\s+(\S+)\s+(\S+)\s+(\S+)$}); +$output =~ m{^(\S+)\s+(\S+)\s+(\S+)\s+(\S+)$}; +$root = $1; +$soname = $2; +$version = $3; +$path = $4; +# TODO: This should probably not try to print NULL +unlike($root, qr{^/.+$}); # actually it's (null) +is($soname, 'libz.so.1'); +TODO: { + local $TODO = 'returns 1 for some reason'; + like($version, qr{^1\.[0-9]+\.[0-9]+$}); +} +like($path, qr{^($LIBDIR/libz\.so\.\Q$3\E)$}); +ok(-e $path); + +done_testing; + +# vim:set sw=4 sts=4 et: diff --git a/subprojects/libcapsule/tests/version1/libunversionedabibreak.c b/subprojects/libcapsule/tests/version1/libunversionedabibreak.c new file mode 100644 index 0000000000000000000000000000000000000000..6ab82e7e9129e60447a3678f618fb7ab40ea0931 --- /dev/null +++ b/subprojects/libcapsule/tests/version1/libunversionedabibreak.c @@ -0,0 +1,4 @@ +__attribute__((__visibility__("default"))) int symbol1(int); +int symbol1(int x) { return x; } +__attribute__((__visibility__("default"))) int symbol2(int); +int symbol2(int x) { return x; } diff --git a/subprojects/libcapsule/tests/version1/libunversionednumber.c b/subprojects/libcapsule/tests/version1/libunversionednumber.c new file mode 100644 index 0000000000000000000000000000000000000000..6ab82e7e9129e60447a3678f618fb7ab40ea0931 --- /dev/null +++ b/subprojects/libcapsule/tests/version1/libunversionednumber.c @@ -0,0 +1,4 @@ +__attribute__((__visibility__("default"))) int symbol1(int); +int symbol1(int x) { return x; } +__attribute__((__visibility__("default"))) int symbol2(int); +int symbol2(int x) { return x; } diff --git a/subprojects/libcapsule/tests/version1/libunversionedsymbols.c b/subprojects/libcapsule/tests/version1/libunversionedsymbols.c new file mode 100644 index 0000000000000000000000000000000000000000..6ab82e7e9129e60447a3678f618fb7ab40ea0931 --- /dev/null +++ b/subprojects/libcapsule/tests/version1/libunversionedsymbols.c @@ -0,0 +1,4 @@ +__attribute__((__visibility__("default"))) int symbol1(int); +int symbol1(int x) { return x; } +__attribute__((__visibility__("default"))) int symbol2(int); +int symbol2(int x) { return x; } diff --git a/subprojects/libcapsule/tests/version1/libversionedabibreak.c b/subprojects/libcapsule/tests/version1/libversionedabibreak.c new file mode 100644 index 0000000000000000000000000000000000000000..6ab82e7e9129e60447a3678f618fb7ab40ea0931 --- /dev/null +++ b/subprojects/libcapsule/tests/version1/libversionedabibreak.c @@ -0,0 +1,4 @@ +__attribute__((__visibility__("default"))) int symbol1(int); +int symbol1(int x) { return x; } +__attribute__((__visibility__("default"))) int symbol2(int); +int symbol2(int x) { return x; } diff --git a/subprojects/libcapsule/tests/version1/libversionedabibreak.map b/subprojects/libcapsule/tests/version1/libversionedabibreak.map new file mode 100644 index 0000000000000000000000000000000000000000..72eb3014514adb75ee9bab73e98f34ddbb231c5d --- /dev/null +++ b/subprojects/libcapsule/tests/version1/libversionedabibreak.map @@ -0,0 +1,4 @@ +LIBVERSIONED1 { + global: + *; +}; diff --git a/subprojects/libcapsule/tests/version1/libversionedlikedbus.c b/subprojects/libcapsule/tests/version1/libversionedlikedbus.c new file mode 100644 index 0000000000000000000000000000000000000000..96302ccbf2ec8e2cadb6311b019808ab6b86ae31 --- /dev/null +++ b/subprojects/libcapsule/tests/version1/libversionedlikedbus.c @@ -0,0 +1,6 @@ +__attribute__((__visibility__("default"))) int symbol1(int); +int symbol1(int x) { return x; } +__attribute__((__visibility__("default"))) int symbol2(int); +int symbol2(int x) { return x; } +__attribute__((__visibility__("default"))) int _private1(int); +int _private1(int x) { return x; } diff --git a/subprojects/libcapsule/tests/version1/libversionedlikedbus.map b/subprojects/libcapsule/tests/version1/libversionedlikedbus.map new file mode 100644 index 0000000000000000000000000000000000000000..f96c71c41988f3d8dfcc71dd3c4b85a1a36a4daf --- /dev/null +++ b/subprojects/libcapsule/tests/version1/libversionedlikedbus.map @@ -0,0 +1,8 @@ +LIBVERSIONED1 { + global: + symbol*; +}; +LIBVERSIONEDPRIVATE_1_0_0 { + global: + _private*; +}; diff --git a/subprojects/libcapsule/tests/version1/libversionedlikeglibc.c b/subprojects/libcapsule/tests/version1/libversionedlikeglibc.c new file mode 100644 index 0000000000000000000000000000000000000000..6ab82e7e9129e60447a3678f618fb7ab40ea0931 --- /dev/null +++ b/subprojects/libcapsule/tests/version1/libversionedlikeglibc.c @@ -0,0 +1,4 @@ +__attribute__((__visibility__("default"))) int symbol1(int); +int symbol1(int x) { return x; } +__attribute__((__visibility__("default"))) int symbol2(int); +int symbol2(int x) { return x; } diff --git a/subprojects/libcapsule/tests/version1/libversionedlikeglibc.map b/subprojects/libcapsule/tests/version1/libversionedlikeglibc.map new file mode 100644 index 0000000000000000000000000000000000000000..d38767289b0510130a989280532068d35ec4f187 --- /dev/null +++ b/subprojects/libcapsule/tests/version1/libversionedlikeglibc.map @@ -0,0 +1,4 @@ +LIBVERSIONED1 { + global: + symbol*; +}; diff --git a/subprojects/libcapsule/tests/version1/libversionednumber.c b/subprojects/libcapsule/tests/version1/libversionednumber.c new file mode 100644 index 0000000000000000000000000000000000000000..6ab82e7e9129e60447a3678f618fb7ab40ea0931 --- /dev/null +++ b/subprojects/libcapsule/tests/version1/libversionednumber.c @@ -0,0 +1,4 @@ +__attribute__((__visibility__("default"))) int symbol1(int); +int symbol1(int x) { return x; } +__attribute__((__visibility__("default"))) int symbol2(int); +int symbol2(int x) { return x; } diff --git a/subprojects/libcapsule/tests/version1/libversionedprivatenothidden.c b/subprojects/libcapsule/tests/version1/libversionedprivatenothidden.c new file mode 100644 index 0000000000000000000000000000000000000000..96302ccbf2ec8e2cadb6311b019808ab6b86ae31 --- /dev/null +++ b/subprojects/libcapsule/tests/version1/libversionedprivatenothidden.c @@ -0,0 +1,6 @@ +__attribute__((__visibility__("default"))) int symbol1(int); +int symbol1(int x) { return x; } +__attribute__((__visibility__("default"))) int symbol2(int); +int symbol2(int x) { return x; } +__attribute__((__visibility__("default"))) int _private1(int); +int _private1(int x) { return x; } diff --git a/subprojects/libcapsule/tests/version1/libversionedprivatenothidden.map b/subprojects/libcapsule/tests/version1/libversionedprivatenothidden.map new file mode 100644 index 0000000000000000000000000000000000000000..86acd233702104a6992a6c3994182eeb10b72745 --- /dev/null +++ b/subprojects/libcapsule/tests/version1/libversionedprivatenothidden.map @@ -0,0 +1,8 @@ +LIBVERSIONED1 { + global: + symbol*; +}; +LIBVERSIONEDPRIVATE_1_4_0 { + global: + _private1; +}; diff --git a/subprojects/libcapsule/tests/version1/libversionedsymbols.c b/subprojects/libcapsule/tests/version1/libversionedsymbols.c new file mode 100644 index 0000000000000000000000000000000000000000..6ab82e7e9129e60447a3678f618fb7ab40ea0931 --- /dev/null +++ b/subprojects/libcapsule/tests/version1/libversionedsymbols.c @@ -0,0 +1,4 @@ +__attribute__((__visibility__("default"))) int symbol1(int); +int symbol1(int x) { return x; } +__attribute__((__visibility__("default"))) int symbol2(int); +int symbol2(int x) { return x; } diff --git a/subprojects/libcapsule/tests/version1/libversionedupgrade.c b/subprojects/libcapsule/tests/version1/libversionedupgrade.c new file mode 100644 index 0000000000000000000000000000000000000000..6ab82e7e9129e60447a3678f618fb7ab40ea0931 --- /dev/null +++ b/subprojects/libcapsule/tests/version1/libversionedupgrade.c @@ -0,0 +1,4 @@ +__attribute__((__visibility__("default"))) int symbol1(int); +int symbol1(int x) { return x; } +__attribute__((__visibility__("default"))) int symbol2(int); +int symbol2(int x) { return x; } diff --git a/subprojects/libcapsule/tests/version1/libversionedupgrade.map b/subprojects/libcapsule/tests/version1/libversionedupgrade.map new file mode 100644 index 0000000000000000000000000000000000000000..72eb3014514adb75ee9bab73e98f34ddbb231c5d --- /dev/null +++ b/subprojects/libcapsule/tests/version1/libversionedupgrade.map @@ -0,0 +1,4 @@ +LIBVERSIONED1 { + global: + *; +}; diff --git a/subprojects/libcapsule/tests/version2/libunversionedabibreak.c b/subprojects/libcapsule/tests/version2/libunversionedabibreak.c new file mode 100644 index 0000000000000000000000000000000000000000..72d6338c811c80669c64f87c6fdea0ccd004c6f6 --- /dev/null +++ b/subprojects/libcapsule/tests/version2/libunversionedabibreak.c @@ -0,0 +1,4 @@ +__attribute__((__visibility__("default"))) int symbol1(int); +int symbol1(int x) { return x; } +__attribute__((__visibility__("default"))) int symbol3(int); +int symbol3(int x) { return x; } diff --git a/subprojects/libcapsule/tests/version2/libunversionednumber.c b/subprojects/libcapsule/tests/version2/libunversionednumber.c new file mode 100644 index 0000000000000000000000000000000000000000..6ab82e7e9129e60447a3678f618fb7ab40ea0931 --- /dev/null +++ b/subprojects/libcapsule/tests/version2/libunversionednumber.c @@ -0,0 +1,4 @@ +__attribute__((__visibility__("default"))) int symbol1(int); +int symbol1(int x) { return x; } +__attribute__((__visibility__("default"))) int symbol2(int); +int symbol2(int x) { return x; } diff --git a/subprojects/libcapsule/tests/version2/libunversionedsymbols.c b/subprojects/libcapsule/tests/version2/libunversionedsymbols.c new file mode 100644 index 0000000000000000000000000000000000000000..903b505e9097c255b121368dd85e20f7c28de760 --- /dev/null +++ b/subprojects/libcapsule/tests/version2/libunversionedsymbols.c @@ -0,0 +1,6 @@ +__attribute__((__visibility__("default"))) int symbol1(int); +int symbol1(int x) { return x; } +__attribute__((__visibility__("default"))) int symbol2(int); +int symbol2(int x) { return x; } +__attribute__((__visibility__("default"))) int symbol3(int); +int symbol3(int x) { return x; } diff --git a/subprojects/libcapsule/tests/version2/libversionedabibreak.c b/subprojects/libcapsule/tests/version2/libversionedabibreak.c new file mode 100644 index 0000000000000000000000000000000000000000..6ab82e7e9129e60447a3678f618fb7ab40ea0931 --- /dev/null +++ b/subprojects/libcapsule/tests/version2/libversionedabibreak.c @@ -0,0 +1,4 @@ +__attribute__((__visibility__("default"))) int symbol1(int); +int symbol1(int x) { return x; } +__attribute__((__visibility__("default"))) int symbol2(int); +int symbol2(int x) { return x; } diff --git a/subprojects/libcapsule/tests/version2/libversionedabibreak.map b/subprojects/libcapsule/tests/version2/libversionedabibreak.map new file mode 100644 index 0000000000000000000000000000000000000000..5d4b411c3c4c176681befc453cae1e6547cd4963 --- /dev/null +++ b/subprojects/libcapsule/tests/version2/libversionedabibreak.map @@ -0,0 +1,4 @@ +LIBVERSIONED2 { + global: + *; +}; diff --git a/subprojects/libcapsule/tests/version2/libversionedlikedbus.c b/subprojects/libcapsule/tests/version2/libversionedlikedbus.c new file mode 100644 index 0000000000000000000000000000000000000000..c47e5ee3a5e21cc9740f7f82c2dca2fa6782c374 --- /dev/null +++ b/subprojects/libcapsule/tests/version2/libversionedlikedbus.c @@ -0,0 +1,8 @@ +__attribute__((__visibility__("default"))) int symbol1(int); +int symbol1(int x) { return x; } +__attribute__((__visibility__("default"))) int symbol2(int); +int symbol2(int x) { return x; } +__attribute__((__visibility__("default"))) int symbol3(int); +int symbol3(int x) { return x; } +__attribute__((__visibility__("default"))) int _private1(int); +int _private1(int x) { return x; } diff --git a/subprojects/libcapsule/tests/version2/libversionedlikedbus.map b/subprojects/libcapsule/tests/version2/libversionedlikedbus.map new file mode 100644 index 0000000000000000000000000000000000000000..c8fd609ca7dda89f2eaf4d0ba6ea7e9d58d1047b --- /dev/null +++ b/subprojects/libcapsule/tests/version2/libversionedlikedbus.map @@ -0,0 +1,8 @@ +LIBVERSIONED1 { + global: + symbol*; +}; +LIBVERSIONEDPRIVATE_1_2_0 { + global: + _private*; +}; diff --git a/subprojects/libcapsule/tests/version2/libversionedlikeglibc.c b/subprojects/libcapsule/tests/version2/libversionedlikeglibc.c new file mode 100644 index 0000000000000000000000000000000000000000..24c1e42a69935d2c7de867ed6163b7402ce7b1a5 --- /dev/null +++ b/subprojects/libcapsule/tests/version2/libversionedlikeglibc.c @@ -0,0 +1,8 @@ +__attribute__((__visibility__("default"))) int symbol1(int); +int symbol1(int x) { return x; } +__asm__(".symver _original_symbol2,symbol2@LIBVERSIONED1"); +__attribute__((__visibility__("default"))) int _original_symbol2(int); +int _original_symbol2(int x) { return x; } +__asm__(".symver _new_symbol2,symbol2@@LIBVERSIONED2"); +__attribute__((__visibility__("default"))) int _new_symbol2(int); +int _new_symbol2(int x) { return x + 1; } diff --git a/subprojects/libcapsule/tests/version2/libversionedlikeglibc.map b/subprojects/libcapsule/tests/version2/libversionedlikeglibc.map new file mode 100644 index 0000000000000000000000000000000000000000..a80ed396df8211c191680bc8c170abb48c84b75e --- /dev/null +++ b/subprojects/libcapsule/tests/version2/libversionedlikeglibc.map @@ -0,0 +1,6 @@ +LIBVERSIONED1 { + global: + symbol*; +}; +LIBVERSIONED2 { +} LIBVERSIONED1; diff --git a/subprojects/libcapsule/tests/version2/libversionednumber.c b/subprojects/libcapsule/tests/version2/libversionednumber.c new file mode 100644 index 0000000000000000000000000000000000000000..6ab82e7e9129e60447a3678f618fb7ab40ea0931 --- /dev/null +++ b/subprojects/libcapsule/tests/version2/libversionednumber.c @@ -0,0 +1,4 @@ +__attribute__((__visibility__("default"))) int symbol1(int); +int symbol1(int x) { return x; } +__attribute__((__visibility__("default"))) int symbol2(int); +int symbol2(int x) { return x; } diff --git a/subprojects/libcapsule/tests/version2/libversionedprivatenothidden.c b/subprojects/libcapsule/tests/version2/libversionedprivatenothidden.c new file mode 100644 index 0000000000000000000000000000000000000000..ee67245af166757da0a139208a208a6735ca0940 --- /dev/null +++ b/subprojects/libcapsule/tests/version2/libversionedprivatenothidden.c @@ -0,0 +1,8 @@ +__attribute__((__visibility__("default"))) int symbol1(int); +int symbol1(int x) { return x; } +__attribute__((__visibility__("default"))) int symbol2(int); +int symbol2(int x) { return x; } +__attribute__((__visibility__("default"))) int _private1(int); +int _private1(int x) { return x; } +__attribute__((__visibility__("default"))) int _private2(int); +int _private2(int x) { return x; } diff --git a/subprojects/libcapsule/tests/version2/libversionedprivatenothidden.map b/subprojects/libcapsule/tests/version2/libversionedprivatenothidden.map new file mode 100644 index 0000000000000000000000000000000000000000..4ef4780458e0a5bfc37251b9ac5133f412ee8564 --- /dev/null +++ b/subprojects/libcapsule/tests/version2/libversionedprivatenothidden.map @@ -0,0 +1,12 @@ +LIBVERSIONED1 { + global: + symbol*; +}; +LIBVERSIONEDPRIVATE_1_4_0 { + global: + _private1; +}; +LIBVERSIONEDPRIVATE_2_1_0 { + global: + _private2; +}; diff --git a/subprojects/libcapsule/tests/version2/libversionedsymbols.c b/subprojects/libcapsule/tests/version2/libversionedsymbols.c new file mode 100644 index 0000000000000000000000000000000000000000..903b505e9097c255b121368dd85e20f7c28de760 --- /dev/null +++ b/subprojects/libcapsule/tests/version2/libversionedsymbols.c @@ -0,0 +1,6 @@ +__attribute__((__visibility__("default"))) int symbol1(int); +int symbol1(int x) { return x; } +__attribute__((__visibility__("default"))) int symbol2(int); +int symbol2(int x) { return x; } +__attribute__((__visibility__("default"))) int symbol3(int); +int symbol3(int x) { return x; } diff --git a/subprojects/libcapsule/tests/version2/libversionedupgrade.c b/subprojects/libcapsule/tests/version2/libversionedupgrade.c new file mode 100644 index 0000000000000000000000000000000000000000..903b505e9097c255b121368dd85e20f7c28de760 --- /dev/null +++ b/subprojects/libcapsule/tests/version2/libversionedupgrade.c @@ -0,0 +1,6 @@ +__attribute__((__visibility__("default"))) int symbol1(int); +int symbol1(int x) { return x; } +__attribute__((__visibility__("default"))) int symbol2(int); +int symbol2(int x) { return x; } +__attribute__((__visibility__("default"))) int symbol3(int); +int symbol3(int x) { return x; } diff --git a/subprojects/libcapsule/tests/version2/libversionedupgrade.map b/subprojects/libcapsule/tests/version2/libversionedupgrade.map new file mode 100644 index 0000000000000000000000000000000000000000..787c3302548ea0f6cd340a35e2f6de79cfed529f --- /dev/null +++ b/subprojects/libcapsule/tests/version2/libversionedupgrade.map @@ -0,0 +1,9 @@ +LIBVERSIONED1 { + global: + symbol1; + symbol2; +}; +LIBVERSIONED2 { + global: + symbol3; +} LIBVERSIONED1; diff --git a/subprojects/libcapsule/utils/capture-libs.c b/subprojects/libcapsule/utils/capture-libs.c new file mode 100644 index 0000000000000000000000000000000000000000..2de227364017d868024d7b69627d98e03f603bfd --- /dev/null +++ b/subprojects/libcapsule/utils/capture-libs.c @@ -0,0 +1,1371 @@ +// Copyright © 2017 Collabora Ltd +// +// This file is part of libcapsule. +// +// libcapsule is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as +// published by the Free Software Foundation; either version 2.1 of the +// License, or (at your option) any later version. +// +// libcapsule 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with libcapsule. If not, see <http://www.gnu.org/licenses/>. + +#include <assert.h> +#include <err.h> +#include <errno.h> +#include <fcntl.h> +#include <fnmatch.h> +#include <getopt.h> +#include <glob.h> +#include <stdbool.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> + +#include "debug.h" +#include "ld-libs.h" +#include "library-cmp.h" +#include "tools.h" +#include "utils.h" + +// We only really care about x86 here because that's the only thing +// libcapsule supports, but we might as well be a bit more complete. +// See https://sourceware.org/glibc/wiki/ABIList +#if defined(__x86_64__) && defined(__ILP32__) +# define LD_SO "/libx32/ld-linux-x32.so.2" +#elif defined(__x86_64__) +# define LD_SO "/lib64/ld-linux-x86-64.so.2" +#elif defined(__sparc__) && defined(__arch64__) +# define LD_SO "/lib64/ld-linux.so.2" +#elif defined(__i386__) || defined(__alpha__) || defined(__sh__) || \ + defined(__sparc__) +# define LD_SO "/lib/ld-linux.so.2" +#elif defined(__aarch64__) && __BYTE_ORDER == __BIG_ENDIAN +# define LD_SO "/lib/ld-linux-aarch64_be.so.1" +#elif defined(__aarch64__) +# define LD_SO "/lib/ld-linux-aarch64.so.1" +#elif defined(__arm__) && defined(__ARM_EABI__) && defined(_ARM_PCS_VFP) +# define LD_SO "/lib/ld-linux-armhf.so.3" +#elif defined(__arm__) && defined(__ARM_EABI__) +# define LD_SO "/lib/ld-linux.so.3" +#elif defined(__hppa__) || defined(__m68k__) || defined(__powerpc__) || \ + defined(__s390__) +// microblaze is also /lib/ld.so.1 +// mips classic NaN, o32 is also /lib/ld.so.1 +# define LD_SO "/lib/ld.so.1" +#elif defined(__powerpc64__) && __BYTE_ORDER == __LITTLE_ENDIAN +# define LD_SO "/lib/ld64.so.2" +#elif defined(__s390x__) || defined(__powerpc64__) +# define LD_SO "/lib/ld64.so.1" +// Others not supported here because we don't know which predefined macros +// can be used to detect them: +// C-SKY hardfloat: /lib/ld-linux-cskyv2-hf.so.1 +// C-SKY softfloat: /lib/ld-linux-cskyv2.so.1 +// ia64: /lib/ld-linux-ia64.so.2 +// mips classic NaN n32: /lib32/ld.so.1 +// mips classic NaN n64: /lib64/ld.so.1 +// mips NaN2008: as for classic NaN but basename is ld-linux-mipsn8.so.1 +// nios2: /lib/ld-linux-nios2.so.1 +// riscv64 softfloat: /lib/ld-linux-riscv64-lp64.so.1 +// riscv64 hardfloat: /lib/ld-linux-riscv64-lp64d.so.1 +#else +# error Unsupported architecture: we do not know where ld.so is +#endif + +static const char * const libc_patterns[] = { + "soname:libBrokenLocale.so.1", + "soname:libanl.so.1", + "soname:libc.so.6", + "soname:libcidn.so.1", + "soname:libcrypt.so.1", + "soname:libdl.so.2", + "soname:libm.so.6", + "soname:libmemusage.so", + "soname:libmvec.so.1", + "soname:libnsl.so.1", + "soname:libpcprofile.so", + "soname:libpthread.so.0", + "soname:libresolv.so.2", + "soname:librt.so.1", + "soname:libthread_db.so.1", + "soname:libutil.so.1", + NULL +}; + +enum +{ + OPTION_COMPARE_BY, + OPTION_CONTAINER, + OPTION_DEST, + OPTION_LIBRARY_KNOWLEDGE, + OPTION_LINK_TARGET, + OPTION_NO_GLIBC, + OPTION_PRINT_LD_SO, + OPTION_PROVIDER, + OPTION_REMAP_LINK_PREFIX, + OPTION_RESOLVE_LD_SO, + OPTION_VERSION, +}; + +typedef struct { + char *from; + const char *to; +} remap_tuple; + +static const char * const *arg_patterns = NULL; +static const char *option_container = "/"; +static const char *option_dest = "."; +static const char *option_provider = "/"; +static const char *option_link_target = NULL; +static remap_tuple *remap_prefix = NULL; +static bool option_glibc = true; + +static struct option long_options[] = +{ + { "compare-by", required_argument, NULL, OPTION_COMPARE_BY }, + { "container", required_argument, NULL, OPTION_CONTAINER }, + { "dest", required_argument, NULL, OPTION_DEST }, + { "help", no_argument, NULL, 'h' }, + { "library-knowledge", required_argument, NULL, OPTION_LIBRARY_KNOWLEDGE }, + { "link-target", required_argument, NULL, OPTION_LINK_TARGET }, + { "no-glibc", no_argument, NULL, OPTION_NO_GLIBC }, + { "print-ld.so", no_argument, NULL, OPTION_PRINT_LD_SO }, + { "provider", required_argument, NULL, OPTION_PROVIDER }, + { "remap-link-prefix", required_argument, NULL, OPTION_REMAP_LINK_PREFIX }, + { "resolve-ld.so", required_argument, NULL, OPTION_RESOLVE_LD_SO }, + { "version", no_argument, NULL, OPTION_VERSION }, + { NULL } +}; + +static int dest_fd = -1; + +static bool resolve_ld_so ( const char *prefix, + char path[PATH_MAX], + const char **within_prefix, + int *code, + char **message ) +{ + size_t prefix_len = strlen( prefix ); + + if( build_filename( path, PATH_MAX, prefix, LD_SO, + NULL ) >= PATH_MAX ) + { + _capsule_set_error( code, message, E2BIG, + "prefix \"%s\" is too long", + prefix ); + return false; + } + + DEBUG( DEBUG_TOOL, "Starting with %s", path ); + + while( resolve_link( prefix, path ) ) + { + DEBUG( DEBUG_TOOL, "-> %s", path ); + } + + if( strcmp( prefix, "/" ) == 0 ) + { + prefix_len = 0; + } + + if( ( prefix_len > 0 && + strncmp( path, prefix, prefix_len ) != 0 ) || + path[prefix_len] != '/' ) + { + _capsule_set_error( code, message, EXDEV, + "\"%s\" is not within prefix \"%s\"", + path, prefix ); + return false; + } + + if( within_prefix != NULL) + *within_prefix = path + prefix_len; + + return true; +} + +static void usage (int code) __attribute__((noreturn)); +static void usage (int code) +{ + FILE *fh; + + if( code == 0 ) + { + fh = stdout; + } + else + { + fh = stderr; + // Assume we already printed a warning; make it stand out more + fprintf( fh, "\n" ); + } + + fprintf( fh, "Usage:\n" + "%s [OPTIONS] PATTERN...\n", + program_invocation_short_name ); + fprintf( fh, "\tCreate symbolic links in LIBDIR that will make the\n" + "\tPATTERNs from PROVIDER available, assuming LIBDIR\n" + "\twill be added to the container's LD_LIBRARY_PATH.\n" ); + fprintf( fh, "\n" ); + fprintf( fh, "%s --print-ld.so\n", + program_invocation_short_name ); + fprintf( fh, "\tPrint the ld.so filename for this architecture and exit.\n" ); + fprintf( fh, "%s --resolve-ld.so=TREE\n", + program_invocation_short_name ); + fprintf( fh, "\tPrint the absolute path of the file that implements ld.so\n" + "\tin TREE.\n" ); + fprintf( fh, "\n" ); + fprintf( fh, "%s --help\n", + program_invocation_short_name ); + fprintf( fh, "\tShow this help.\n" ); + fprintf( fh, "\n" ); + fprintf( fh, "Options:\n" ); + fprintf( fh, "--compare-by=METHOD[,METHOD2...]\n" + "\tUse METHOD by default to decide which library is newer.\n" + "\tIf unable to decide, use METHOD2, and so on.\n" + "\tIf unable to decide by any method, choose PROVIDER.\n" + "\tThe default is 'name,provider'.\n" + "\t\tname: Use library name: libfoo.so.1.0 < libfoo.so.1.2\n" + "\t\tversions: The one with a superset of DT_VERDEF is newer\n" + "\t\tsymbols: The one with a superset of symbols is newer\n" + "\t\tcontainer: The one in CONTAINER is newer\n" + "\t\tprovider: The one in PROVIDER is newer\n" ); + fprintf( fh, "--container=CONTAINER\n" + "\tAssume the container will look like CONTAINER when\n" + "\tdeciding which libraries are needed [default: /]\n" ); + fprintf( fh, "--dest=LIBDIR\n" + "\tCreate symlinks in LIBDIR [default: .]\n" ); + fprintf( fh, "--library-knowledge=FILE\n" + "\tLoad information about known libraries from a" + "\t.desktop-style file at FILE, overriding --compare-by.\n" ); + fprintf( fh, "--link-target=PATH\n" + "\tAssume PROVIDER will be mounted at PATH when the\n" + "\tcontainer is used [default: PROVIDER]\n" ); + fprintf( fh, "--provider=PROVIDER\n" + "\tFind libraries in PROVIDER [default: /]\n" ); + fprintf( fh, "--remap-link-prefix=FROM=TO\n" + "\tWhile in the process of creating symlinks, if their prefix\n" + "\twas supposed to be FROM, they will instead be changed with\n" + "\tTO\n" ); + fprintf( fh, "--no-glibc\n" + "\tDon't capture libraries that are part of glibc\n" ); + fprintf( fh, "\n" ); + fprintf( fh, "Each PATTERN is one of:\n" ); + fprintf( fh, "\n" ); + fprintf( fh, "from:FILE\n" + "\tRead PATTERNs from FILE, one per line.\n" ); + fprintf( fh, "soname:SONAME\n" + "\tCapture the library in ld.so.cache whose name is\n" + "\texactly SONAME\n" ); + fprintf( fh, "soname-match:GLOB\n" + "\tCapture every library in ld.so.cache that matches\n" + "\ta shell-style glob (which will usually need to be\n" + "\tquoted when using a shell)\n" ); + fprintf( fh, "only-dependencies:PATTERN\n" + "\tCapture the dependencies of each library matched by\n" + "\tPATTERN, but not the library matched by PATTERN itself\n" + "\t(unless a match for PATTERN depends on another match)\n" ); + fprintf( fh, "no-dependencies:PATTERN\n" + "\tCapture each library matched by PATTERN, but not\n" + "\ttheir dependencies\n" ); + fprintf( fh, "if-exists:PATTERN\n" + "\tCapture PATTERN, but don't fail if nothing matches\n" ); + fprintf( fh, "if-same-abi:PATTERN\n" + "\tCapture PATTERN, but don't fail if it points to a\n" + "\tlibrary with mismatched word size or architecture\n" ); + fprintf( fh, "even-if-older:PATTERN\n" + "\tCapture PATTERN, even if the version in CONTAINER\n" + "\tappears newer\n" ); + fprintf( fh, "gl:\n" + "\tShortcut for even-if-older:if-exists:soname:libGL.so.1,\n" + "\teven-if-older:if-exists:soname-match:libGLX_*.so.0, and\n" + "\tvarious other GL-related libraries\n" ); + fprintf( fh, "path:ABS-PATH\n" + "\tResolve ABS-PATH as though chrooted into PROVIDER\n" + "\tand capture the result\n" ); + fprintf( fh, "path-match:GLOB\n" + "\tResolve GLOB as though chrooted into PROVIDER\n" + "\tand capture any results that are of the right ABI\n" ); + fprintf( fh, "an absolute path with no '?', '*', '['\n" + "\tSame as path:PATTERN\n" ); + fprintf( fh, "a glob pattern starting with '/'\n" + "\tSame as path-match:PATTERN\n" ); + fprintf( fh, "a glob pattern with no '/'\n" + "\tSame as soname-match:PATTERN\n" ); + fprintf( fh, "a bare SONAME with no '/', '?', '*', '['\n" + "\tSame as soname:PATTERN\n" ); + exit( code ); +} + +typedef enum +{ + CAPTURE_FLAG_NONE = 0, + CAPTURE_FLAG_EVEN_IF_OLDER = (1 << 0), + CAPTURE_FLAG_IF_EXISTS = (1 << 1), + CAPTURE_FLAG_LIBRARY_ITSELF = ( 1 << 2 ), + CAPTURE_FLAG_DEPENDENCIES = ( 1 << 3 ), + CAPTURE_FLAG_IF_SAME_ABI = ( 1 << 4 ), +} capture_flags; + +typedef struct +{ + capture_flags flags; + library_cmp_function *comparators; + library_knowledge knowledge; +} capture_options; + +static bool +init_with_target( ld_libs *ldlibs, const char *tree, const char *target, + int *code, char **message ) +{ + if( !ld_libs_init( ldlibs, NULL, tree, debug_flags, code, message ) ) + { + goto fail; + } + + if( !ld_libs_load_cache( ldlibs, code, message ) ) + { + goto fail; + } + + if( !ld_libs_set_target( ldlibs, target, code, message ) ) + { + goto fail; + } + + return true; + +fail: + ld_libs_finish( ldlibs ); + return false; +} + +static bool capture_pattern( const char *pattern, + const capture_options *options, + int *code, char **message ); + +static bool capture_patterns( const char * const *patterns, + const capture_options *options, + int *code, char **message ); + +static bool +capture_one( const char *soname, const capture_options *options, + int *code, char **message ) +{ + unsigned int i; + unsigned int j; + _capsule_cleanup(ld_libs_finish) ld_libs provider = {}; + int local_code = 0; + _capsule_autofree char *local_message = NULL; + + if( !init_with_target( &provider, option_provider, soname, + &local_code, &local_message ) ) + { + if( ( options->flags & CAPTURE_FLAG_IF_EXISTS ) && local_code == ENOENT ) + { + DEBUG( DEBUG_TOOL, "%s not found, ignoring", soname ); + _capsule_clear( &local_message ); + return true; + } + + if( ( options->flags & CAPTURE_FLAG_IF_SAME_ABI ) && local_code == ENOEXEC ) + { + DEBUG( DEBUG_TOOL, "%s is a different ABI: %s", soname, local_message ); + _capsule_clear( &local_message ); + return true; + } + + _capsule_propagate_error( code, message, local_code, + _capsule_steal_pointer( &local_message ) ); + return false; + } + + if( !ld_libs_find_dependencies( &provider, &local_code, &local_message ) ) + { + if( ( options->flags & CAPTURE_FLAG_IF_EXISTS ) && local_code == ENOENT ) + { + DEBUG( DEBUG_TOOL, + "Some of the dependencies for %s have not been found, ignoring", + soname ); + _capsule_clear( &local_message ); + return true; + } + + _capsule_propagate_error( code, message, local_code, + _capsule_steal_pointer( &local_message ) ); + return false; + } + + for( i = 0; i < N_ELEMENTS( provider.needed ); i++ ) + { + _capsule_autofree char *target = NULL; + struct stat statbuf; + const char *needed_name = provider.needed[i].name; + const char *needed_path_in_provider = provider.needed[i].path; + const char *needed_basename; + bool remapped_prefix = false; + + if( !needed_name ) + { + continue; + } + + if( i == 0 && !( options->flags & CAPTURE_FLAG_LIBRARY_ITSELF ) ) + { + DEBUG( DEBUG_TOOL, "Not capturing \"%s\" itself as requested", + needed_name ); + continue; + } + + if( i > 0 && !( options->flags & CAPTURE_FLAG_DEPENDENCIES ) ) + { + DEBUG( DEBUG_TOOL, + "Not capturing dependencies of \"%s\" as requested", + soname ); + break; + } + + needed_basename = _capsule_basename( needed_name ); + + if( !option_glibc ) + { + bool capture = true; + + for( j = 0; j < N_ELEMENTS( libc_patterns ); j++ ) + { + if( libc_patterns[j] == NULL ) + break; + + assert( strstarts( libc_patterns[j], "soname:" ) ); + + if( strcmp( libc_patterns[j] + strlen( "soname:" ), + needed_basename ) == 0 ) + { + DEBUG( DEBUG_TOOL, + "Not capturing \"%s\" because it is part of glibc", + needed_name ); + capture = false; + break; + } + } + + if( !capture ) + continue; + } + + if( fstatat( dest_fd, needed_basename, &statbuf, + AT_SYMLINK_NOFOLLOW ) == 0 ) + { + /* We already created a symlink for this library. No further + * action required (but keep going through its dependencies + * in case we need to symlink those into place) */ + DEBUG( DEBUG_TOOL, "We already have a symlink for %s", + needed_name ); + continue; + } + + /* For the library we were originally looking for, we don't + * compare with the container if we have EVEN_IF_OLDER flag. + * For its dependencies, we ignore that flag. */ + if( option_container == NULL ) + { + DEBUG( DEBUG_TOOL, + "Container unknown, cannot compare version with " + "\"%s\": assuming provider version is newer", + needed_path_in_provider ); + } + else if( i == 0 && ( options->flags & CAPTURE_FLAG_EVEN_IF_OLDER ) ) + { + DEBUG( DEBUG_TOOL, + "Explicitly requested %s from %s even if older: \"%s\"", + needed_name, option_provider, + needed_path_in_provider ); + } + else + { + _capsule_cleanup(ld_libs_finish) ld_libs container = {}; + if( init_with_target( &container, option_container, + needed_name, + &local_code, &local_message ) ) + { + const char *needed_path_in_container = container.needed[0].path; + int decision; + library_details details = {}; + const library_details *known = NULL; + + known = library_knowledge_lookup( &options->knowledge, + needed_name ); + + if( known != NULL ) + { + DEBUG( DEBUG_TOOL, "Found library-specific details for \"%s\"", + needed_name ); + details = *known; + } + + /* Not const-correct, but we don't actually modify or free it */ + details.name = (char *) needed_name; + + if( details.comparators == NULL ) + details.comparators = options->comparators; + + decision = library_cmp_list_iterate( &details, + needed_path_in_container, + option_container, + needed_path_in_provider, + option_provider ); + + if( decision > 0 ) + { + /* Version in container is strictly newer: don't + * symlink in the one from the provider */ + DEBUG( DEBUG_TOOL, + "Choosing %s from container", + needed_name ); + continue; + } + else if( decision < 0 ) + { + DEBUG( DEBUG_TOOL, + "Choosing %s from provider", + needed_name ); + } + else + { + /* If equal, we prefer the provider over the container + * (this is equivalent to having "...,provider" at the + * end of the comparison specification) */ + DEBUG( DEBUG_TOOL, + "Falling back to choosing %s from provider", + needed_name ); + } + } + else if( local_code == ENOENT ) + { + /* else assume it's absent from the container, which is + * just like it being newer in the provider */ + DEBUG( DEBUG_TOOL, "%s is not in the container", + needed_name ); + _capsule_clear( &local_message ); + } + else + { + _capsule_propagate_error( code, message, local_code, + _capsule_steal_pointer( &local_message ) ); + return false; + } + } + + /* By this point we've decided we want the version from the + * provider, not the version from the container */ + + if( option_link_target != NULL || remap_prefix != NULL ) + { + char path[PATH_MAX]; + size_t prefix_len = strlen( option_provider ); + + // We need to take the realpath() inside the container, + // because if we're using LD_LIBRARY_PATH rather than + // libcapsule, we have to follow the chain of + // $libdir/libGL.so.1 -> /etc/alternatives/whatever -> ... + // within that prefix. + + safe_strncpy( path, needed_path_in_provider, sizeof(path) ); + + DEBUG( DEBUG_TOOL, "Link target initially: \"%s\"", path ); + + while( resolve_link( option_provider, path ) ) + { + DEBUG( DEBUG_TOOL, "Link target pursued to: \"%s\"", path ); + } + + if( strcmp( option_provider, "/" ) == 0 ) + { + prefix_len = 0; + } + + if( strncmp( path, option_provider, prefix_len ) != 0 || + path[prefix_len] != '/' ) + { + warnx( "warning: \"%s\" is not within prefix \"%s\"", + path, option_provider ); + continue; + } + + target = build_filename_alloc( option_link_target ? option_link_target : "/", + path + prefix_len, + NULL ); + + for( j = 0; remap_prefix != NULL && remap_prefix[j].from != NULL; j++ ) + { + if( strstarts( target, remap_prefix[j].from ) ) + { + char *tmp; + assert( remap_prefix[j].to != NULL ); + DEBUG( DEBUG_TOOL, "Remapping \"%s\" to \"%s\" in \"%s\"", + remap_prefix[j].from, remap_prefix[j].to, target ); + xasprintf( &tmp, "%s%s", remap_prefix[j].to, + target + strlen( remap_prefix[j].from ) ); + free( target ); + target = tmp; + remapped_prefix = true; + } + } + } + + // If we don't have the link target option and we didn't remap the + // prefix, we just set the target to the needed path in provider + // without following the eventual link chain + if( !remapped_prefix && option_link_target == NULL ) + { + if( target != NULL ) + free( target ); + target = xstrdup( needed_path_in_provider ); + } + + assert( target != NULL ); + + DEBUG( DEBUG_TOOL, "Creating symlink %s/%s -> %s", + option_dest, needed_basename, target ); + + if( symlinkat( target, dest_fd, needed_basename ) < 0 ) + { + warn( "warning: cannot create symlink %s/%s", + option_dest, needed_basename ); + } + + if( strcmp( needed_basename, "libc.so.6" ) == 0 ) + { + capture_options new_options; + + /* Having captured libc, we need to capture the rest of + * the related libraries from the same place */ + DEBUG( DEBUG_TOOL, + "Capturing the rest of glibc to go with %s", + needed_name ); + + new_options = *options; + new_options.flags |= CAPTURE_FLAG_IF_EXISTS; + new_options.flags |= CAPTURE_FLAG_EVEN_IF_OLDER; + + if( !capture_patterns( libc_patterns, &new_options, + code, message ) ) + { + return false; + } + } + } + + return true; +} + +typedef struct +{ + const char *pattern; + const capture_options *options; + bool found; + ld_cache cache; + int *code; + char **message; +} cache_foreach_context; + +static intptr_t +cache_foreach_cb (const char *name, int flag, unsigned int osv, + uint64_t hwcap, const char *path, void *data) +{ + cache_foreach_context *ctx = data; + + if( !name || !*name ) + { + warnx( "warning: empty name found in ld.so.cache" ); + return 0; + } + + // We don't really care about whether the library matches our class, + // machine, hwcaps etc. - if we can't dlopen a library of this name, + // we'll just skip it. + if( fnmatch( ctx->pattern, name, 0 ) == 0 ) + { + capture_options new_options; + + DEBUG( DEBUG_TOOL, "%s matches %s", name, ctx->pattern ); + + ctx->found = true; + + new_options = *ctx->options; + new_options.flags |= CAPTURE_FLAG_IF_EXISTS; + + if( !capture_one( name, &new_options, ctx->code, ctx->message ) ) + return 1; + } + + return 0; // continue iteration +} + +static bool +capture_soname_match( const char *pattern, const capture_options *options, + int *code, char **message ) +{ + cache_foreach_context ctx = { + .pattern = pattern, + .options = options, + .cache = { .is_open = 0 }, + .found = false, + .code = code, + .message = message, + }; + bool ret = false; + size_t i; + + DEBUG( DEBUG_TOOL, "%s", pattern ); + + for( i = 0; ld_cache_filenames[i] != NULL; i++ ) + { + _capsule_autofree char *cache_path = NULL; + + if( message != NULL ) + _capsule_clear( message ); + + cache_path = build_filename_alloc( option_provider, + ld_cache_filenames[i], + NULL ); + + if( ld_cache_open( &ctx.cache, cache_path, code, message ) ) + break; + else + assert( !ctx.cache.is_open ); + } + + if( !ctx.cache.is_open ) + goto out; + + if( ld_cache_foreach( &ctx.cache, cache_foreach_cb, &ctx ) != 0 ) + goto out; + + if( !ctx.found && !( options->flags & CAPTURE_FLAG_IF_EXISTS ) ) + { + _capsule_set_error( code, message, ENOENT, + "no matches found for glob pattern \"%s\" " + "in ld.so.cache", + pattern ); + goto out; + } + + ret = true; + +out: + if( ctx.cache.is_open ) + ld_cache_close( &ctx.cache ); + + return ret; +} + +static bool +capture_path_match( const char *pattern, const capture_options *options, + int *code, char **message ) +{ + char *abs_path = NULL; + int res; + bool ret = false; + glob_t buffer; + size_t i; + + DEBUG( DEBUG_TOOL, "%s", pattern ); + + abs_path = build_filename_alloc( option_provider, pattern, NULL ); + res = glob( abs_path, 0, NULL, &buffer ); + + switch( res ) + { + case 0: + ret = true; + for( i = 0; i < buffer.gl_pathc; i++) + { + const char *path = buffer.gl_pathv[i]; + capture_options new_options; + + if( option_provider != NULL && + strcmp( option_provider, "/" ) != 0 && + ( !strstarts( path, option_provider ) || + path[strlen( option_provider )] != '/' ) ) + { + _capsule_set_error( code, message, EXDEV, + "path pattern \"%s\" matches \"%s\"" + "which is not in \"%s\"", + pattern, path, option_provider ); + globfree( &buffer ); + return false; + } + + new_options = *options; + new_options.flags |= CAPTURE_FLAG_IF_SAME_ABI; + + if( !capture_one( path + strlen( option_provider ), + &new_options, code, message ) ) + { + ret = false; + break; + } + } + globfree( &buffer ); + break; + + case GLOB_NOMATCH: + if( options->flags & CAPTURE_FLAG_IF_EXISTS ) + { + ret = true; + } + else + { + _capsule_set_error( code, message, ENOENT, + "no matches found for glob pattern \"%s\" " + "in \"%s\"", + pattern, option_provider ); + } + break; + + case GLOB_NOSPACE: + _capsule_set_error( code, message, ENOMEM, + "unable to match glob pattern \"%s\" " + "in \"%s\"", + pattern, option_provider ); + break; + + default: + case GLOB_ABORTED: + _capsule_set_error( code, message, EIO, + "unable to match glob pattern \"%s\" " + "in \"%s\"", + pattern, option_provider ); + break; + } + + free( abs_path ); + return ret; +} + +static bool +capture_lines( const char *filename, const capture_options *options, + int *code, char **message ) +{ + FILE *fp = NULL; + bool ret = true; + char *line = NULL; + size_t len = 0; + ssize_t chars; + + if( strcmp( filename, "-" ) != 0 ) + { + fp = fopen( filename, "re" ); + + if( fp == NULL ) + { + _capsule_set_error( code, message, errno, + "Unable to open \"%s\": %s", + filename, strerror( errno ) ); + return false; + } + } + + while( ( chars = getline( &line, &len, fp ? fp : stdin ) ) > -1 ) + { + /* Ignore blank lines and shell-style comments (which must + * currently be at the beginning of the line) */ + if( chars == 0 || line[0] == '\0' || line[0] == '\n' || line[0] == '#' ) + continue; + + if( line[chars - 1] == '\n' ) + line[chars - 1] = '\0'; + + if( !capture_pattern( line, options, code, message ) ) + { + ret = false; + break; + } + } + + free( line ); + + if( fp != NULL ) + fclose( fp ); + + return ret; +} + +static bool +capture_pattern( const char *pattern, const capture_options *options, + int *code, char **message ) +{ + DEBUG( DEBUG_TOOL, "%s", pattern ); + + if ( ( options->flags & ( CAPTURE_FLAG_LIBRARY_ITSELF | + CAPTURE_FLAG_DEPENDENCIES ) ) == 0 ) + { + _capsule_set_error( code, message, EINVAL, + "combining no-dependencies: with " + "only-dependencies: is meaningless, " + "so \"%s\" is invalid", + pattern ); + return false; + } + + if( strstarts( pattern, "path:" ) ) + { + if( !strstarts( pattern, "path:/" ) ) + { + _capsule_set_error( code, message, EINVAL, + "path: requires an absolute path as " + "argument, not \"%s\"", + pattern ); + return false; + } + + return capture_one( pattern + strlen( "path:" ), + options, code, message ); + } + + if( strstarts( pattern, "soname:" ) ) + { + return capture_one( pattern + strlen( "soname:" ), + options, code, message ); + } + + if( strstarts( pattern, "soname-match:" ) ) + { + return capture_soname_match( pattern + strlen( "soname-match:" ), + options, code, message ); + } + + if( strstarts( pattern, "path-match:" ) ) + { + return capture_path_match( pattern + strlen( "path-match:" ), + options, code, message ); + } + + if( strstarts( pattern, "if-exists:" ) ) + { + capture_options new_options = *options; + + new_options.flags |= CAPTURE_FLAG_IF_EXISTS; + return capture_pattern( pattern + strlen( "if-exists:" ), + &new_options, code, message ); + } + + if( strstarts( pattern, "if-same-abi:" ) ) + { + capture_options new_options = *options; + + new_options.flags |= CAPTURE_FLAG_IF_SAME_ABI; + return capture_pattern( pattern + strlen( "if-same-abi:" ), + &new_options, code, message ); + } + + if( strstarts( pattern, "even-if-older:" ) ) + { + capture_options new_options = *options; + + new_options.flags |= CAPTURE_FLAG_EVEN_IF_OLDER; + return capture_pattern( pattern + strlen( "even-if-older:" ), + &new_options, code, message ); + } + + if( strstarts( pattern, "only-dependencies:" ) ) + { + capture_options new_options = *options; + + new_options.flags &= ~CAPTURE_FLAG_LIBRARY_ITSELF; + return capture_pattern( pattern + strlen( "only-dependencies:" ), + &new_options, code, message ); + } + + if( strstarts( pattern, "no-dependencies:" ) ) + { + capture_options new_options = *options; + + new_options.flags &= ~CAPTURE_FLAG_DEPENDENCIES; + return capture_pattern( pattern + strlen( "no-dependencies:" ), + &new_options, code, message ); + } + + if( strcmp( pattern, "gl:" ) == 0 ) + { + // Useful information: + // https://devtalk.nvidia.com/default/topic/915640/multiple-glx-client-libraries-in-the-nvidia-linux-driver-installer-package/ + static const char * const gl_patterns[] = { + "soname:libEGL.so.1", + // Vendor ICDs for libEGL.so.1 + // (Registered via JSON in /usr/share/glvnd/egl_vendor.d) + "soname-match:libEGL_*.so.*", + + "soname:libGL.so.1", + + "soname:libGLESv1_CM.so.1", + // Vendor ICDs for libGLESv1_CM.so.1 + "soname-match:libGLESv1_CM_*.so.*", + + "soname:libGLESv2.so.2", + // Vendor ICDs for libGLESv2.so.2 + "soname:libGLESv2_*.so.*", + + "soname:libGLX.so.0", + // Vendor ICDs for libGL.so.1 and/or libGLX.so.0 + "soname-match:libGLX_*.so.*", + // This one looks redundant, but because it's usually a + // symlink to someone else's implementation, we can't find + // it in the ld.so cache under its own name: its SONAME is + // libGLX_mesa.so.0 or libGLX_nvidia.so.0. So we can't find + // it by wildcard-matching and have to look it up explicitly + // instead. + "soname:libGLX_indirect.so.0", + + // This is an implementation detail of GLVND, but it had + // better match the GLVND dispatchers or bad things will + // happen + "soname-match:libGLdispatch.so.*", + + "soname:libOpenGL.so.0", + + // Mostly used by Mesa, but apps/games are also allowed to + // use it directly + "soname:libgbm.so.1", + + // Mesa libraries should have DT_NEEDED for this, but some + // historical versions didn't, so it wouldn't be picked up + // by recursive dependency resolution + "soname:libglapi.so.0", + + // Some libraries are not explicitly mentioned here: + // For NVIDIA, we also need libnvidia-glcore.so.$VERSION, + // but it will be pulled in by dependencies, so we don't + // need to list it explicitly. + // For NVIDIA, we also need libnvidia-tls.so.$VERSION, + // either the TLS or non-TLS version as appropriate; but + // again it will be pulled in via dependencies. + NULL + }; + capture_options new_options = *options; + + new_options.flags |= CAPTURE_FLAG_IF_EXISTS; + /* We usually want to capture the host GL stack even if it + * appears older than what's in the container. */ + new_options.flags |= CAPTURE_FLAG_EVEN_IF_OLDER; + + return capture_patterns( gl_patterns, &new_options, code, message ); + } + + if( strcmp( pattern, "nvidia:" ) == 0 ) + { + static const char * const gl_patterns[] = { + "soname:libEGL.so.1", + "soname-match:libEGL_nvidia.so.*", + + "soname:libGL.so.1", + + "soname:libGLESv1_CM.so.1", + "soname-match:libGLESv1_CM_nvidia.so.*", + + "soname:libGLESv2.so.2", + "soname-match:libGLESv2_nvidia.so.*", + + "soname:libGLX.so.0", + "soname-match:libGLX_nvidia.so.*", + "soname:libGLX_indirect.so.0", + + "soname-match:libGLdispatch.so.*", + + "soname:libOpenGL.so.0", + + "soname-match:libcuda.so.*", + "soname-match:libglx.so.*", + "soname-match:libnvcuvid.so.*", + "soname-match:libnvidia-*.so.*", + "soname-match:libOpenCL.so.*", + "soname-match:libvdpau_nvidia.so.*", + NULL + }; + capture_options new_options = *options; + + new_options.flags |= CAPTURE_FLAG_IF_EXISTS; + /* We certainly want to capture the host GL stack even if it + * appears older than what's in the container: the NVIDIA + * proprietary drivers have to be in lockstep with the kernel. */ + new_options.flags |= CAPTURE_FLAG_EVEN_IF_OLDER; + + return capture_patterns( gl_patterns, &new_options, code, message ); + } + + if( strstarts( pattern, "from:" ) ) + { + return capture_lines( pattern + strlen( "from:" ), + options, code, message ); + } + + if( strchr( pattern, ':' ) != NULL ) + { + _capsule_set_error( code, message, EINVAL, + "patterns containing ':' must match a known " + "mode, not \"%s\" (use soname: or path: to " + "take patterns containing ':' literally, if " + "necessary)", + pattern ); + return false; + } + + if( pattern[0] == '/' ) + { + if( strchr( pattern, '*' ) != NULL || + strchr( pattern, '?' ) != NULL || + strchr( pattern, '[' ) != NULL ) + { + // Interpret as if path-match: + return capture_path_match( pattern, options, code, message ); + } + else + { + return capture_one( pattern, options, code, message ); + } + } + + if( strchr( pattern, '/' ) != NULL ) + { + _capsule_set_error( code, message, EINVAL, + "path arguments must be absolute, not \"%s\"", + pattern ); + return false; + } + + if( strchr( pattern, '*' ) != NULL || + strchr( pattern, '?' ) != NULL || + strchr( pattern, '[' ) != NULL ) + { + // Interpret as if soname-match: + return capture_soname_match( pattern, options, code, message ); + } + + // Default: interpret as if soname: + return capture_one( pattern, options, code, message ); +} + +static bool +capture_patterns( const char * const *patterns, + const capture_options *options, + int *code, char **message ) +{ + unsigned int i; + + for( i = 0; patterns[i] != NULL; i++ ) + { + if( !capture_pattern( patterns[i], options, code, message ) ) + return false; + } + + return true; +} + +int +main (int argc, char **argv) +{ + capture_options options = { + .comparators = NULL, + .flags = (CAPTURE_FLAG_LIBRARY_ITSELF | + CAPTURE_FLAG_DEPENDENCIES ), + .knowledge = LIBRARY_KNOWLEDGE_INIT, + }; + const char *option_compare_by = "name,provider"; + const char *option_library_knowledge = NULL; + int code = 0; + char *message = NULL; + // Arbitrary initialization size + ptr_list *remap_list = ptr_list_alloc( 4 ); + + set_debug_flags( getenv("CAPSULE_DEBUG") ); + + while( 1 ) + { + int opt = getopt_long( argc, argv, "h", long_options, NULL ); + + if( opt == -1 ) + { + break; + } + + switch( opt ) + { + case '?': + default: + usage( 2 ); + break; // not reached + + case 'h': + usage( 0 ); + break; // not reached + + case OPTION_COMPARE_BY: + option_compare_by = optarg; + break; + + case OPTION_CONTAINER: + option_container = optarg; + break; + + case OPTION_DEST: + option_dest = optarg; + break; + + case OPTION_LIBRARY_KNOWLEDGE: + if( option_library_knowledge != NULL ) + errx( 1, "--library-knowledge can only be used once" ); + + option_library_knowledge = optarg; + break; + + case OPTION_LINK_TARGET: + option_link_target = optarg; + break; + + case OPTION_PRINT_LD_SO: + puts( LD_SO ); + goto out; + + case OPTION_PROVIDER: + option_provider = optarg; + break; + + case OPTION_NO_GLIBC: + option_glibc = false; + break; + + case OPTION_VERSION: + _capsule_tools_print_version( "capsule-capture-libs" ); + goto out; + + case OPTION_REMAP_LINK_PREFIX: + if( strchr( optarg, '=' ) == NULL ) + errx( 1, "--remap-link-prefix value must follow the FROM=TO pattern" ); + + ptr_list_push_ptr( remap_list, xstrdup( optarg ) ); + break; + + case OPTION_RESOLVE_LD_SO: + { + char path[PATH_MAX] = { 0 }; + const char *within_prefix = NULL; + + if( !resolve_ld_so( optarg, path, &within_prefix, + &code, &message ) ) + { + errx( 1, "code %d: %s", code, message ); + } + + puts( within_prefix ); + goto out; + } + break; + } + } + + if( optind >= argc ) + { + warnx( "One or more patterns must be provided" ); + usage( 2 ); + } + + if (remap_list->next > 0) + { + size_t n = 0; + char **remap_array = (char **) ptr_list_free_to_array( _capsule_steal_pointer( &remap_list ), &n ); + remap_prefix = xcalloc( n + 1, sizeof( remap_tuple ) ); + for( size_t i = 0; remap_array[i] != NULL; i++ ) + { + char *buf = xstrdup( remap_array[i] ); + char *equals = strchr( buf, '=' ); + if( equals == NULL || equals == buf || *(equals + 1) == '\0' ) + { + free( buf ); + free_strv_full( remap_array ); + errx( 1, "--remap-link-prefix value must follow the FROM=TO pattern" ); + } + *equals = '\0'; + remap_prefix[i].from = buf; + remap_prefix[i].to = equals + 1; + } + free_strv_full( remap_array ); + } + + arg_patterns = (const char * const *) argv + optind; + assert( arg_patterns[argc - optind] == NULL ); + + if( strcmp( option_dest, "." ) != 0 && + mkdir( option_dest, 0755 ) < 0 && + errno != EEXIST ) + { + err( 1, "creating \"%s\"", option_dest ); + } + + options.comparators = library_cmp_list_from_string( option_compare_by, ",", + &code, &message ); + + if( options.comparators == NULL ) + { + errx( 1, "code %d: %s", code, message ); + } + + if( option_library_knowledge != NULL ) + { + FILE *fh = fopen( option_library_knowledge, "re" ); + + if( fh == NULL) + { + err( 1, "opening \"%s\"", option_library_knowledge ); + } + + if( !library_knowledge_load_from_stream( &options.knowledge, + fh, option_library_knowledge, + &code, &message ) ) + { + errx( 1, "code %d: %s", code, message ); + } + + fclose( fh ); + } + + dest_fd = open( option_dest, O_RDWR|O_DIRECTORY|O_CLOEXEC|O_PATH ); + + if( dest_fd < 0 ) + { + err( 1, "opening \"%s\"", option_dest ); + } + + if( !capture_patterns( arg_patterns, &options, &code, &message ) ) + { + errx( 1, "code %d: %s", code, message ); + } + + close( dest_fd ); + free( options.comparators ); + library_knowledge_clear( &options.knowledge ); + + for( size_t i = 0; remap_prefix != NULL && remap_prefix[i].from != NULL; i++ ) + { + free( remap_prefix[i].from ); + remap_prefix[i].from = NULL; + remap_prefix[i].to = NULL; + } + +out: + if( remap_list != NULL ) + { + char **tmp_to_free = (char **) ptr_list_free_to_array( _capsule_steal_pointer( &remap_list ), NULL ); + free_strv_full( tmp_to_free ); + } + + return 0; +} diff --git a/subprojects/libcapsule/utils/debug.h b/subprojects/libcapsule/utils/debug.h new file mode 100644 index 0000000000000000000000000000000000000000..2eed3df89e18bedf560159d6bd8777b9ca62aa1c --- /dev/null +++ b/subprojects/libcapsule/utils/debug.h @@ -0,0 +1,45 @@ +// Copyright © 2017 Collabora Ltd + +// This file is part of libcapsule. + +// libcapsule is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as +// published by the Free Software Foundation; either version 2.1 of the +// License, or (at your option) any later version. + +// libcapsule 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 Lesser General Public License for more details. + +// You should have received a copy of the GNU Lesser General Public +// License along with libcapsule. If not, see <http://www.gnu.org/licenses/>. + +#pragma once + +enum +{ + DEBUG_NONE = 0, + DEBUG_PATH = 0x1, + DEBUG_SEARCH = 0x1 << 1, + DEBUG_LDCACHE = 0x1 << 2, + DEBUG_CAPSULE = 0x1 << 3, + DEBUG_MPROTECT = 0x1 << 4, + DEBUG_WRAPPERS = 0x1 << 5, + DEBUG_RELOCS = 0x1 << 6, + DEBUG_ELF = 0x1 << 7, + DEBUG_DLFUNC = 0x1 << 8, + DEBUG_TOOL = 0x1 << 9, + DEBUG_ALL = 0xffff, +}; + +#define LDLIB_DEBUG(ldl, flags, fmt, args...) \ + if( ldl->debug && (ldl->debug & (flags)) ) \ + fprintf( stderr, "%s:" fmt "\n", __PRETTY_FUNCTION__, ##args ) + +#define DEBUG(flags, fmt, args...) \ + if( debug_flags && (debug_flags & (flags)) ) \ + fprintf( stderr, "%s:" fmt "\n", __PRETTY_FUNCTION__, ##args ) + +extern unsigned long debug_flags; +void set_debug_flags (const char *control); diff --git a/subprojects/libcapsule/utils/dump.c b/subprojects/libcapsule/utils/dump.c new file mode 100644 index 0000000000000000000000000000000000000000..12f6aea53f28e8b55a6eb1d75339ec84c8d801ea --- /dev/null +++ b/subprojects/libcapsule/utils/dump.c @@ -0,0 +1,1140 @@ +// Copyright © 2017 Collabora Ltd + +// This file is part of libcapsule. + +// libcapsule is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as +// published by the Free Software Foundation; either version 2.1 of the +// License, or (at your option) any later version. + +// libcapsule 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 Lesser General Public License for more details. + +// You should have received a copy of the GNU Lesser General Public +// License along with libcapsule. If not, see <http://www.gnu.org/licenses/>. + +#include <assert.h> +#include <dlfcn.h> +#include <stdlib.h> +#include <stdio.h> + +#include <string.h> + +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <errno.h> +#include <limits.h> + +#include <link.h> +#include "dump.h" +#include "utils.h" + +// this file is a home for the various ways of parsing the ELF layout +// of a program or library from within the executable itself, ie +// it allows the different parts of the in-memory ELF layout to be +// extracted in some useful way. not all these sections are relevant +// or useful for our eventual goals, but as this knowledge is hard +// to come by it is [literally] codified here + +// these macros are secretly the same for elf32 & elf64: +#define ELFW_ST_TYPE(a) ELF32_ST_TYPE(a) +#define ELFW_ST_BIND(a) ELF32_ST_BIND(a) +#define ELFW_ST_VISIBILITY(a) ELF32_ST_VISIBILITY(a) + +typedef enum +{ + TTYPE_VAL, + TTYPE_PTR, + TTYPE_STR, +} ttype; + +static const char * +pt_flags (int x) +{ + static char flags[80]; + + flags[sizeof(flags) - 1] = '\0'; + snprintf( flags, sizeof(flags) - 1, + "%c%c%c [%x %x]", + (x & PF_R) ? 'R' : '-', + (x & PF_W) ? 'W' : '-', + (x & PF_X) ? 'X' : '-', + x & PF_MASKOS , + x & PF_MASKPROC ); + + return flags; +} + +static const char * +pt_type (int x) +{ + switch (x) + { + case PT_NULL: return "NULL"; + case PT_LOAD: return "LOAD"; + case PT_DYNAMIC: return "DYNAMIC"; + case PT_INTERP: return "INTERP"; + case PT_NOTE: return "NOTE"; + case PT_SHLIB: return "SHLIB"; + case PT_PHDR: return "PHDR"; + case PT_TLS: return "TLS"; + case PT_NUM: return "NUM"; + case PT_LOOS: return "LOOS"; + case PT_GNU_EH_FRAME: return "GNU_EH_FRAME"; + case PT_GNU_STACK: return "GNU_STACK"; + case PT_GNU_RELRO: return "GNU_RELRO"; + case PT_SUNWBSS: return "SUNWBSS"; + case PT_SUNWSTACK: return "SUNWSTACK"; + case PT_HISUNW: return "HISUNW"; + case PT_LOPROC: return "LOPROC"; + case PT_HIPROC: return "HIPROC"; + default: return "-unknown-"; + }; +} + +static const char * +d_tag (unsigned long tag, int *tag_type) +{ + static char label[80]; + const char *rval = label; + + label[0] = '\0'; + *tag_type = TTYPE_VAL; + + switch( tag ) + { + case DT_NULL: // Marks end of dynamic section + rval = "NULL"; + break; + + case DT_NEEDED: // Name of needed library + *tag_type = TTYPE_STR; + rval = "NEEDED"; + break; + + case DT_PLTRELSZ: // Size in bytes of PLT relocs + rval = "PLTRELSZ"; + break; + + case DT_PLTGOT: // Processor defined value + *tag_type = TTYPE_PTR; + rval = "PLTGOT"; + break; + + case DT_HASH: // Address of symbol hash table + *tag_type = TTYPE_PTR; + rval = "HASH"; + break; + + case DT_STRTAB: // Address of string table + *tag_type = TTYPE_PTR; + rval = "STRTAB"; + break; + + case DT_SYMTAB: // Address of symbol table + *tag_type = TTYPE_PTR; + rval = "SYMTAB"; + break; + + case DT_RELA: // Address of Rela relocs + *tag_type = TTYPE_PTR; + rval = "RELA"; + break; + + case DT_RELASZ: // Total size of Rela relocs + rval = "RELASZ"; + break; + + case DT_RELAENT: // Size of one Rela reloc + rval = "RELAENT"; + break; + + case DT_STRSZ: // Size of string table + rval = "STRSZ"; + break; + + case DT_SYMENT: // Size of one symbol table entry + rval = "SYMENT"; + break; + + case DT_INIT: // Address of init function + *tag_type = TTYPE_PTR; + rval = "INIT"; + break; + + case DT_FINI: // Address of termination function + *tag_type = TTYPE_PTR; + rval = "FINI"; + break; + + case DT_SONAME: // Name of shared object + *tag_type = TTYPE_STR; + rval = "SONAME"; + break; + + case DT_RPATH: // Library search path (deprecated) + *tag_type = TTYPE_STR; + rval = "RPATH (deprecated)"; + break; + + case DT_SYMBOLIC: // Start symbol search here + *tag_type = TTYPE_PTR; + rval = "SYMBOLIC"; + break; + + case DT_REL: // Address of Rel relocs + *tag_type = TTYPE_PTR; + rval = "REL"; + break; + + case DT_RELSZ: // Total size of Rel relocs + rval = "RELSZ"; + break; + + case DT_RELENT: // Size of one Rel reloc + rval = "RELENT"; + break; + + case DT_PLTREL: // Type of reloc in PLT + rval = "PLTREL"; + break; + + case DT_DEBUG: // For debugging; unspecified + rval = "DEBUG"; + break; + + case DT_TEXTREL: // Reloc might modify .text + rval = "TEXTREL"; + break; + + case DT_JMPREL: // Address of PLT relocs + *tag_type = TTYPE_PTR; + rval = "JMPREL"; + break; + + case DT_BIND_NOW: // Process relocations of object + rval = "BIND_NOW"; + break; + + case DT_INIT_ARRAY: // Array with addresses of init fct + *tag_type = TTYPE_PTR; + rval = "INIT_ARRAY"; + break; + + case DT_FINI_ARRAY: // Array with addresses of fini fct + *tag_type = TTYPE_PTR; + rval = "FINI_ARRAY"; + break; + + case DT_INIT_ARRAYSZ: // Size in bytes of DT_INIT_ARRAY + rval = "INIT_ARRAYSZ"; + break; + + case DT_FINI_ARRAYSZ: // Size in bytes of DT_FINI_ARRAY + rval = "FINI_ARRAYSZ"; + break; + + case DT_RUNPATH: // Library search path + *tag_type = TTYPE_PTR; + rval = "RUNPATH"; + break; + + case DT_FLAGS: // Flags for the object being loaded + rval = "FLAGS"; + break; + + case DT_PREINIT_ARRAY: // Array with addresses of preinit fct + *tag_type = TTYPE_PTR; + rval = "PREINIT_ARRAY"; + break; + + case DT_PREINIT_ARRAYSZ: // size in bytes of DT_PREINIT_ARRAY + rval = "PREINIT_ARRAYSZ"; + break; + + case DT_NUM: // Number used + rval = "NUM"; + break; + + case DT_GNU_PRELINKED: + rval = "GNU_PRELINKED"; // Prelinking timestamp + break; + + case DT_GNU_CONFLICTSZ: + rval = "GNU_CONFLICTSZ"; // Size of conflict section + break; + + case DT_GNU_LIBLISTSZ: + rval = "GNU_LIBLISTSZ"; // Size of library list + break; + + case DT_CHECKSUM: + rval = "CHECKSUM"; // Feature selection (DTF_*). + break; + + case DT_POSFLAG_1: + rval = "POSFLAG_1"; // Flags for DT_* entries, effecting the following DT_* entry. + break; + + case DT_SYMINSZ: + rval = "SYMINSZ"; // Size of syminfo table (in bytes) + break; + + case DT_SYMINENT: + rval = "SYMINENT"; // Entry size of syminfo + break; + + case DT_GNU_HASH: + rval = "GNU_HASH"; // GNU-style hash table. + break; + + case DT_TLSDESC_PLT: + rval = "TLSDESC_PLT"; // Start of conflict section + break; + + case DT_GNU_LIBLIST: + rval = "GNU_LIBLIST"; // Library list + break; + + case DT_CONFIG: + rval = "CONFIG"; // Configuration information. + break; + + case DT_DEPAUDIT: + rval = "DEPAUDIT"; // Dependency auditing. + break; + + case DT_AUDIT: + rval = "AUDIT"; // Object auditing. + break; + + case DT_PLTPAD: + rval = "PLTPAD"; // PLT padding. + break; + + case DT_MOVETAB: + rval = "MOVETAB"; // Move table. + break; + + case DT_SYMINFO: + rval = "SYMINFO"; + break; + + case DT_VERSYM: + rval = "DT_VERSYM"; + *tag_type = TTYPE_PTR; + break; + + case DT_RELACOUNT: + rval = "RELACOUNT"; + break; + + case DT_RELCOUNT: + rval = "DT_RELCOUNT"; + break; + + case DT_FLAGS_1: /* State flags, see DF_1_* below. */ + rval = "DT_FLAGS_1"; + break; + + case DT_VERDEF: /* Address of version definition table */ + rval = "DT_VERDEF"; + break; + + case DT_VERDEFNUM: /* Number of version definitions */ + rval = "VERDEFNUM"; + break; + + case DT_VERNEED: /* Address of table with needed versions */ + *tag_type = TTYPE_PTR; + rval = "DT_VERNEED"; + break; + + case DT_VERNEEDNUM: + rval = "DT_VERNEEDNUM"; + break; + + default: + label[sizeof(label) - 1] = '\0'; + if( tag >= DT_LOOS && tag <= DT_HIOS ) + snprintf( label, sizeof(label) - 1, "OS_SPECIFIC:%lx", tag ); + else if ( tag >= DT_LOPROC && tag <= DT_HIPROC ) + snprintf( label, sizeof(label) - 1, "PROC_SPECIFIC:%lx", tag ); + else + snprintf( label, sizeof(label) - 1, "-unknown-:%lx", tag ); + rval = &label[0]; + } + + if ( tag >= DT_ADDRRNGLO && tag <= DT_ADDRRNGHI ) + *tag_type = TTYPE_PTR; + + return rval; +} + +static const char * +st_bind(int bind) +{ + switch( bind ) + { + case STB_LOCAL: return "LOCAL" ; // Local symbol + case STB_GLOBAL: return "GLOBAL" ; // Global symbol + case STB_WEAK: return "WEAK" ; // Weak symbol + case STB_NUM: return "NUM" ; // Number of defined types. + case STB_LOOS: return "LOOS/GNU_UNIQUE"; // Start of OS-specific + case STB_HIOS: return "HIOS" ; // End of OS-specific + case STB_LOPROC: return "LOPROC" ; // Start of processor-specific + case STB_HIPROC: return "HIPROC" ; // End of processor-specific + default: return "???"; + } +} + +static const char * +st_type(int type) +{ + switch( type ) + { + case STT_NOTYPE: return "NOTYPE" ; // Symbol type is unspecified + case STT_OBJECT: return "OBJECT" ; // Symbol is a data object + case STT_FUNC: return "FUNC" ; // Symbol is a code object + case STT_SECTION: return "SECTION" ; // associated with a section + case STT_FILE: return "FILE" ; // Symbol's name is file name + case STT_COMMON: return "COMMON" ; // common data object + case STT_TLS: return "TLS" ; // thread-local data object + case STT_NUM: return "NUM" ; // Number of defined types. + case STT_LOOS: return "LOOS/GNU_IFUNC"; // Start of OS-specific + case STT_HIOS: return "HIOS" ; // End of OS-specific + case STT_LOPROC: return "LOPROC" ; // Start of processor-specific + case STT_HIPROC: return "HIPROC" ; // End of processor-specific + default: return "???"; + } +} + +static void +symbol_version ( const ElfW(Sym) *symbol, + int i, + const char *strtab, + const ElfW(Versym) *versym, + const void *verdef, + const int verdefnum, + int *default_version, + const char **version, + ElfW(Versym) *vs) +{ + *default_version = 0; + *version = NULL; + + if( versym == NULL ) + return; + + switch( symbol->st_shndx ) + { + case SHN_UNDEF: + case SHN_ABS: + case SHN_COMMON: + case SHN_BEFORE: + case SHN_AFTER: + case SHN_XINDEX: + // none of these are handled (and we're very unlikely to need to) + break; + + default: + if( symbol->st_shndx < SHN_LORESERVE ) + { + const char *vd = verdef; + *vs = *(versym + i); + + for( int x = 0; x < verdefnum; x++ ) + { + ElfW(Verdef) *entry = (ElfW(Verdef) *) vd; + + if( entry->vd_ndx == (*vs & 0x7fff) ) + { + const char *au; + + if( entry->vd_flags & VER_FLG_BASE ) + break; + + au = vd + entry->vd_aux; + ElfW(Verdaux) *aux = (ElfW(Verdaux) *) au; + *version = strtab + aux->vda_name; + *default_version = (*vs & 0x8000) ? 0 : 1; + } + + vd = vd + entry->vd_next; + } + } + } +} + +static void +dump_symtab (const char *indent, + const ElfW(Sym) *start, + size_t symsz, + const char *strtab, + size_t strsz, + const ElfW(Versym) *versym, + const void *verdef, + const int verdefnum) +{ + int x = 0; + const ElfW(Sym) *entry; + + for( entry = start; + (entry - start + 1) * sizeof(*entry) <= symsz; + entry++ ) + { + int defversym = 0; + const char *version = NULL; + ElfW(Versym) vs = 0; + + symbol_version( entry, x, strtab, versym, verdef, verdefnum, + &defversym, &version, &vs ); + + fprintf(stderr, "%s // %02d [%-8s %-8s] size:%2" FMT_WORD " (%d)%s%s%s%s %04x %p\n", + indent, + x, + st_type( ELFW_ST_TYPE(entry->st_info) ), + st_bind( ELFW_ST_BIND(entry->st_info) ), + // ELFW_ST_VISIBILITY(entry->st_other), + entry->st_size , + entry->st_shndx , + strtab + entry->st_name , + version ? "@" : "", + defversym ? "@" : "", + version ? version : "", + vs, + (void *)entry->st_value ); + x++; + } +} + +static void +dump_interp (const char *indent, void *start, size_t size, ElfW(Addr) base) +{ + char interpreter[PATH_MAX + 1] = ""; + interpreter[PATH_MAX] = '\0'; + + strncpy( &interpreter[0], start + base, PATH_MAX ); + + fprintf( stderr, "%s// %p %"FMT_SIZE" %p %s", indent, start, size, (void *)base, + interpreter ); + return; +} + +static void * +addr (ElfW(Addr) base, ElfW(Addr) ptr) +{ + if( ptr > base ) + return (void *)ptr; + else + return (void *)(base + ptr); +} + +static const char * +reloc_type (int rtype) +{ + switch( __ELF_NATIVE_CLASS ) + { + case 64: + switch( rtype ) + { + case R_X86_64_NONE: return "NONE"; // No reloc + case R_X86_64_64: return "64"; // Direct 64 bit + case R_X86_64_PC32: return "PC32"; // PC rel 32 bit signed + case R_X86_64_GOT32: return "GOT32"; // 32 bit GOT entry + case R_X86_64_PLT32: return "PLT32"; // 32 bit PLT address + case R_X86_64_COPY: return "COPY"; // Copy symbol at runtime + case R_X86_64_GLOB_DAT: return "GLOB_DAT"; // Create GOT entry + case R_X86_64_JUMP_SLOT: return "JUMP_SLOT"; // Create PLT entry + case R_X86_64_RELATIVE: return "RELATIVE"; // Adjust by program base + case R_X86_64_GOTPCREL: return "GOTPCREL"; // 32 bit GOT PC ofset + case R_X86_64_32: return "32"; // Direct 32 bit zero + case R_X86_64_32S: return "32S"; // Direct 32 bit sign + case R_X86_64_16: return "16"; // Direct 16 bit zero + case R_X86_64_PC16: return "PC16"; // 16 bit extended pc + case R_X86_64_8: return "8"; // Direct 8 bit extended + case R_X86_64_PC8: return "PC8"; // 8 bit sign extended pc relative + case R_X86_64_DTPMOD64: return "DTPMOD64"; // ID of containing symbol module + case R_X86_64_DTPOFF64: return "DTPOFF64"; // Offset in module's TLS + case R_X86_64_TPOFF64: return "TPOFF64"; // Offset in initial TLS + case R_X86_64_TLSGD: return "TLSGD"; // 32 bit PC relative offset to two GOT entries for GD symbol + case R_X86_64_TLSLD: return "TLSLD"; // 32 bit PC relative offset to two GOT entries for LD symbol + case R_X86_64_DTPOFF32: return "DTPOFF32"; // Offset in TLS block + case R_X86_64_GOTTPOFF: return "GOTTPOFF"; // 32 bit PC relative offset to GOT entry for IE symbol + case R_X86_64_TPOFF32: return "TPOFF32"; // Offset in initial TLS + default: break; + } + break; + case 32: + switch( rtype ) + { + case R_386_NONE: return "NONE"; // No reloc + case R_386_32: return "32"; // Direct 32 bit + case R_386_PC32: return "PC32"; // PC relative 32 bit + case R_386_GOT32: return "GOT32"; // 32 bit GOT entry + case R_386_PLT32: return "PLT32"; // 32 bit PLT address + case R_386_COPY: return "COPY"; // Copy symbol at runtime + case R_386_GLOB_DAT: return "GLOB_DAT"; // Create GOT entry + case R_386_JMP_SLOT: return "JMP_SLOT"; // Create PLT entry + case R_386_RELATIVE: return "RELATIVE"; // Adjust by program base + case R_386_GOTOFF: return "GOTOFF"; // 32 bit offset to GOT + case R_386_GOTPC: return "GOTPC"; // 32 bit PC relative offset to GOT + case R_386_32PLT: return "32PLT"; + case R_386_TLS_TPOFF: return "TLS_TPOFF"; // Offset in static TLS block + case R_386_TLS_IE: return "TLS_IE"; // Address of GOT entry for static TLS block offset + case R_386_TLS_GOTIE: return "TLS_GOTIE"; // GOT entry for static TLS block offset + case R_386_TLS_LE: return "TLS_LE"; // Offset relative to static TLS block + case R_386_TLS_GD: return "TLS_GD"; // Direct 32 bit for GNU version of general dynamic thread local data + case R_386_TLS_LDM: return "TLS_LDM"; // Direct 32 bit for GNU version of local dynamic thread local data in LE code + case R_386_16: return "16"; + case R_386_PC16: return "PC16"; + case R_386_8: return "8"; + case R_386_PC8: return "PC8"; + case R_386_TLS_GD_32: return "TLS_GD_32"; // Direct 32 bit for general dynamic thread local data + case R_386_TLS_GD_PUSH: return "TLS_GD_PUSH"; // Tag for pushl in GD TLS code + case R_386_TLS_GD_CALL: return "TLS_GD_CALL"; // Relocation for call to __tls_get_addr() + case R_386_TLS_GD_POP: return "TLS_GD_POP"; // Tag for popl in GD TLS code + case R_386_TLS_LDM_32: return "TLS_LDM_32"; // Direct 32 bit for local dynamic thread local data in LE code + case R_386_TLS_LDM_PUSH: return "TLS_LDM_PUSH"; // Tag for pushl in LDM TLS code + case R_386_TLS_LDM_CALL: return "TLS_LDM_CALL"; // Relocation for call to __tls_get_addr() in LDM code + case R_386_TLS_LDM_POP: return "TLS_LDM_POP"; // Tag for popl in LDM TLS code + case R_386_TLS_LDO_32: return "TLS_LDO_32"; // Offset relative to TLS block + case R_386_TLS_IE_32: return "TLS_IE_32"; // GOT entry for negated static TLS block offset + case R_386_TLS_LE_32: return "TLS_LE_32"; // Negated offset relative to static TLS block + case R_386_TLS_DTPMOD32: return "TLS_DTPMOD32"; // ID of module containing symbol + case R_386_TLS_DTPOFF32: return "TLS_DTPOFF32"; // Offset in TLS block + case R_386_TLS_TPOFF32: return "TLS_TPOFF32"; // Negated offset in static TLS block + default: break; + } + break; + default: + fprintf( stderr, "Unknown __ELF_NATIVE_CLASS\n" ); + abort(); + break; + } + + return "???"; +} + +static void +dump_rel (const char *indent, + const void *start, + size_t relsz, + const char *strtab, + size_t strsz, + const void *symtab, + size_t symsz) +{ + int x = 0; + ElfW(Rel) *entry; + + for( entry = (ElfW(Rel) *)start; + entry < (ElfW(Rel) *)(start + relsz); + entry++ ) + { + int sym; + int chr; + const char *name = NULL; + + const ElfW(Sym) *symbol; + +#if __ELF_NATIVE_CLASS == 32 + sym = ELF32_R_SYM (entry->r_info); + chr = ELF32_R_TYPE(entry->r_info); +#elif __ELF_NATIVE_CLASS == 64 + sym = ELF64_R_SYM (entry->r_info); + chr = ELF64_R_TYPE(entry->r_info); +#else + fprintf( stderr, + "__ELF_NATIVE_CLASS is NOT 32 or 64:" + " What the actual hell\n" ); + exit( 22 ); +#endif + + symbol = find_symbol( sym, symtab, symsz, strtab, strsz, &name ); + if( symbol ) + fprintf( stderr, "%s // [%03d] %16s off: %"FMT_XADDR"; %s;\n", + indent, + x++, + reloc_type(chr), + entry->r_offset, + name ); + else + fprintf( stderr, "%s // [%03d] %16s off: %"FMT_XADDR"; sym[%d];\n", + indent, + x++, + reloc_type(chr), + entry->r_offset, + sym ); + } +} + +static void +dump_rela (const char *indent, + const void *start, + size_t relasz, + const char *strtab, + size_t strsz, + const void *symtab, + size_t symsz, + ElfW(Addr) base) +{ + int x = 0; + ElfW(Rela) *entry; + + for( entry = (ElfW(Rela) *)start; + entry < (ElfW(Rela) *)(start + relasz); + entry++ ) + { + int sym; + int chr; + const char *name = NULL; + + const ElfW(Sym) *symbol; + +#if __ELF_NATIVE_CLASS == 32 + sym = ELF32_R_SYM (entry->r_info); + chr = ELF32_R_TYPE(entry->r_info); +#elif __ELF_NATIVE_CLASS == 64 + sym = ELF64_R_SYM (entry->r_info); + chr = ELF64_R_TYPE(entry->r_info); +#else + fprintf( stderr, + "__ELF_NATIVE_CLASS is NOT 32 or 64:" + " What the actual hell\n" ); + exit( 22 ); +#endif + + symbol = find_symbol( sym, symtab, symsz, strtab, strsz, &name ); + if( symbol ) + fprintf( stderr, "%s // [%03d] %16s off: %"FMT_XADDR"; %s; add: 0x%"FMT_XADDR"\n", + indent, + x++, + reloc_type(chr), + entry->r_offset, + name, + entry->r_addend ); + else + fprintf( stderr, "%s // [%03d] %16s off: %"FMT_XADDR"; sym: %d; add: %"FMT_XADDR"\n", + indent, + x++, + reloc_type(chr), + entry->r_offset, + sym, + entry->r_addend ); + + switch( chr ) + { + ElfW(Addr) *reloc_addr; + case R_X86_64_JUMP_SLOT: + reloc_addr = addr( base, entry->r_offset ); + fprintf( stderr, + "%s // JUMP_SLOT relocation for %s is at %p " + "and contains %p\n", + indent, + (symbol && name) ? name : "*unnamed*", + (void *)reloc_addr, + (void *)*((ElfW(Addr) *) reloc_addr) ); + break; + default: + break; + } + } +} + +static void +dump_verneed(const char *indent, + const void *start, + int entries, + const char *strtab, + ElfW(Addr) base) +{ + const char *vn = start; + + for( int x = 0; x < entries; x++ ) + { + ElfW(Verneed) *entry = (ElfW(Verneed) *) vn; + + fprintf( stderr, "%s // %02d %-15s : %d entries (aux %d)\n", + indent, x, + strtab + entry->vn_file, + entries, + // entry->vn_cnt, + entry->vn_aux ); + + const char *au = vn + entry->vn_aux; + + for( int y = 0; y < entry->vn_cnt; y++ ) + { + ElfW(Vernaux) *aux = (ElfW(Vernaux) *) au; + fprintf( stderr, "%s // %s %s\n", + indent, + strtab + aux->vna_name, + (aux->vna_flags & VER_FLG_WEAK) ? "(weak symbol)" : "" ); + au = au + aux->vna_next; + } + + vn = vn + entry->vn_next; + } +} + +static void +dump_verdef(const char *indent, + const void *start, + int entries, + const char *strtab, + ElfW(Addr) base) +{ + const char *vd = start; + + for( int x = 0; x < entries; x++ ) + { + ElfW(Verdef) *entry = (ElfW(Verdef) *) vd; + + fprintf( stderr, "%s // %02d/%02d [%d]; ndx: %0x; flags: %0x\n", + indent, x, entries, + entry->vd_cnt , + entry->vd_ndx , + entry->vd_flags); + + const char *au = vd + entry->vd_aux; + + for( int y = 0; y < entry->vd_cnt; y++ ) + { + ElfW(Verdaux) *aux = (ElfW(Verdaux) *) au; + fprintf( stderr, "%s // %s\n", + indent, + strtab + aux->vda_name ); + au = au + aux->vda_next; + } + + vd = vd + entry->vd_next; + } +} + + +static void +dump_dynamic (const char *indent, void *start, size_t size, ElfW(Addr) base) +{ + int x = 0; + const char *tag; + ElfW(Dyn) *entry; + + size_t strsiz = -1; + size_t relasz = -1; + size_t relsz = -1; + size_t jmprelsz = -1; + int verneednum = -1; + int verdefnum = -1; + int jmpreltype = DT_NULL; + size_t symsz; + + const void *symtab = NULL; + const void *versym = NULL; + const void *verdef = NULL; + const char *strtab = dynamic_section_find_strtab( start + base, (const void *) base, &strsiz ); + int tag_type = TTYPE_VAL; + + fprintf( stderr, "%s{\n", indent ); + + for( entry = start + base; + (entry->d_tag != DT_NULL) && ((void *)entry < (start + base + size)); + entry++ ) + { + if( entry->d_tag == DT_SYMTAB ) + { + symtab = fix_addr( (void *) base, entry->d_un.d_ptr ); + } + } + + if( strtab ) + { + fprintf( stderr, "%s string table at %p, %zu bytes\n", + indent, strtab, strsiz ); + + /* XXX Apparently the only way to find out the size of the dynamic + symbol section is to assume that the string table follows right + afterwards... —glibc elf/dl-fptr.c */ + assert( strtab >= (const char *) symtab ); + symsz = strtab - (const char *) symtab; + + fprintf( stderr, "%s symbol table at %p, %zu bytes\n", + indent, symtab, symsz ); + } + else + { + fprintf( stderr, "%s no string table?!\n", indent ); + symsz = 0; + } + + for( entry = start + base; + (entry->d_tag != DT_NULL) && ((void *)entry < (start + base + size)); + entry++ ) + { + tag = d_tag( entry->d_tag, &tag_type ); + + switch( tag_type ) + { + case TTYPE_VAL: + fprintf( stderr, "%s { #%03d %20s(%"FMT_SWORD") = val:%"FMT_WORD" }\n", + indent, + x++, + tag, + entry->d_tag, + entry->d_un.d_val ); + break; + case TTYPE_PTR: + fprintf( stderr, "%s { #%03d %20s(%"FMT_SWORD") = ptr:%p }\n", + indent, x++, + tag, + entry->d_tag, + (const void *) entry->d_un.d_ptr ); + break; + case TTYPE_STR: + fprintf( stderr, "%s { #%03d %20s(%"FMT_SWORD") = str:%s }\n", + indent, + x++, + tag, + entry->d_tag, + strtab + entry->d_un.d_ptr ); + break; + default: + fprintf( stderr, "%s { #%03d %20s(%"FMT_SWORD") = ???:%p }\n", + indent, + x++, + tag, + entry->d_tag, + (void *)entry->d_un.d_ptr ); + break; + } + + switch( entry->d_tag ) + { + case DT_PLTRELSZ: + jmprelsz = entry->d_un.d_val; + break; + + case DT_SYMTAB: + if( versym == NULL ) + versym = (const void *) find_ptr( base, start, DT_VERSYM ); + if( verdef == NULL ) + verdef = (const void *) find_ptr( base, start, DT_VERDEF ); + if( verdefnum == -1 ) + verdefnum = find_value( base, start, DT_VERDEFNUM ); + symtab = fix_addr( (void *) base, entry->d_un.d_ptr ); + dump_symtab( indent, symtab, symsz, strtab, strsiz, versym, verdef, verdefnum ); + break; + + case DT_RELA: + if( relasz == (size_t) -1 ) + relasz = find_value( base, start, DT_RELASZ ); + dump_rela( indent, (const void *) entry->d_un.d_ptr, relasz, + strtab, strsiz, symtab, symsz, base ); + break; + + case DT_RELASZ: + relasz = entry->d_un.d_val; + break; + + case DT_RELSZ: + relsz = entry->d_un.d_val; + break; + + case DT_REL: + if( relsz == (size_t) -1 ) + relsz = find_value( base, start, DT_RELSZ ); + dump_rel( indent, (const void *) entry->d_un.d_ptr, relsz, + strtab, strsiz, symtab, symsz ); + break; + + case DT_PLTREL: + jmpreltype = entry->d_un.d_val; + break; + + case DT_JMPREL: + if( jmprelsz == (size_t) -1 ) + jmprelsz = find_value( base, start, DT_PLTRELSZ ); + if( jmpreltype == DT_NULL ) + jmpreltype = find_value( base, start, DT_PLTREL ); + + switch( jmpreltype ) + { + int unused; + + case DT_REL: + dump_rel( indent, (const void *) entry->d_un.d_ptr, jmprelsz, + strtab, strsiz, symtab, symsz ); + break; + + case DT_RELA: + dump_rela( indent, (const void *) entry->d_un.d_ptr, jmprelsz, + strtab, strsiz, symtab, symsz, base ); + break; + + default: + fprintf( stderr, "%s // unknown DT_PLTREL value: %d (%s)\n", + indent, jmpreltype, d_tag( jmpreltype, &unused ) ); + fprintf( stderr, "%s // expected DT_REL or DT_RELA\n", indent ); + break; + } + break; + + case DT_VERNEEDNUM: + verneednum = entry->d_un.d_val; + break; + + case DT_VERNEED: + if( verneednum == -1 ) + verneednum = find_value( base, start, DT_VERNEEDNUM ); + dump_verneed( indent, + fix_addr( (const void *) base, entry->d_un.d_ptr ), + verneednum, + strtab, base ); + break; + + case DT_VERDEFNUM: + verdefnum = entry->d_un.d_val; + break; + + case DT_VERDEF: + if( verdefnum == -1 ) + verdefnum = find_value( base, start, DT_VERDEFNUM ); + verdef = fix_addr( (const void *) base, entry->d_un.d_ptr ); + dump_verdef( indent, verdef, verdefnum, strtab, base ); + break; + + case DT_VERSYM: + if( versym == NULL ) + versym = (const void *) entry->d_un.d_ptr; + break; + + default: + break; + } + } + fprintf(stderr, "%s}\n", indent); + return; +} + +static int +phdr_cb (struct dl_phdr_info *info, size_t size, void *data) +{ + int j; + + if( data != NULL && strstr( info->dlpi_name, (const char *)data ) == NULL ) + return 0; + + fprintf(stderr, "\n\ +===============================================================================\n\ +struct dl_phdr_info \n\ +{ \n\ + ElfW(Addr) dlpi_addr; %p \n\ + const char *dlpi_name; \"%s\" \n\ + ElfW(Half) dlpi_phnum; %d \n\ + const ElfW(Phdr) *dlpi_phdr; %p \n", + (void *)info->dlpi_addr , + info->dlpi_name , + info->dlpi_phnum , + (void *)info->dlpi_phdr ); + + for( j = 0; j < info->dlpi_phnum; j++ ) + { + fprintf(stderr, " { // #%03d @ ( %p + %p ) %p\n\ + ElfW(Word) p_type; %d %s \n\ + ElfW(Off) p_offset; %"FMT_OFF"\n\ + ElfW(Addr) p_vaddr; %p \n\ + ElfW(Addr) p_paddr; %p \n\ + ElfW(Word) p_filesz; %"FMT_WORD" \n\ + ElfW(Word) p_memsz; %"FMT_WORD" \n\ + ElfW(Word) p_flags; 0x%x %s \n\ + ElfW(Word) p_align; %"FMT_WORD" \n", + j, + (void *)info->dlpi_addr , + (void *)info->dlpi_phdr[j].p_vaddr , + (void *) ( info->dlpi_addr + info->dlpi_phdr[j].p_vaddr ), + info->dlpi_phdr[j].p_type , + pt_type( info->dlpi_phdr[j].p_type ), + info->dlpi_phdr[j].p_offset , + (void *)info->dlpi_phdr[j].p_vaddr , + (void *)info->dlpi_phdr[j].p_paddr , + info->dlpi_phdr[j].p_filesz , + info->dlpi_phdr[j].p_memsz , + info->dlpi_phdr[j].p_flags , + pt_flags( info->dlpi_phdr[j].p_flags ), + info->dlpi_phdr[j].p_align ); + switch(info->dlpi_phdr[j].p_type) + { + case PT_DYNAMIC: + dump_dynamic(" ", + (void *) info->dlpi_phdr[j].p_vaddr, + info->dlpi_phdr[j].p_memsz, + info->dlpi_addr); + break; + case PT_INTERP: + dump_interp(" ", + (void *) info->dlpi_phdr[j].p_vaddr, + info->dlpi_phdr[j].p_memsz, + info->dlpi_addr); + default: + break; + } + fprintf(stderr, "\n }\n"); + } + + fprintf(stderr, "};\n"); + return 0; +} + +void +dump_elf_data (const char *soname) +{ + dl_iterate_phdr( phdr_cb, (void *) soname ); +} + +static int +dlhdr_cb (struct dl_phdr_info *info, size_t size, void *data) +{ + ElfW(Dyn) *dyn = NULL; + + for( int j = 0; j < info->dlpi_phnum; j++ ) + if( info->dlpi_phdr[j].p_type == PT_DYNAMIC ) + dyn = (ElfW(Dyn) *)info->dlpi_phdr[j].p_vaddr; + + if( dyn ) + fprintf(stderr, "+ dl_phdr: %p | %50s | addr: %p | dyn: %p |\n", + info, + info->dlpi_name, + (void *)info->dlpi_addr, + dyn + info->dlpi_addr ); + else + fprintf(stderr, "+ dl_phdr: %p | %50s | addr: %p | dyn: NOT-FOUND |\n", + info, + info->dlpi_name, + (void *)info->dlpi_addr ); + + return 0; +} + +void debug_link_maps (void) +{ + struct link_map *map; + void *handle; + + handle = dlopen( NULL, RTLD_LAZY ); + dlinfo( handle, RTLD_DI_LINKMAP, &map ); + + if (map->l_prev) + for( struct link_map *m = map; m; m = m->l_prev ) + fprintf(stderr, "-link_map: %p | %50s | addr: %p | dyn: %p \n", + m, m->l_name, (void *)m->l_addr, m->l_ld); + + if (map->l_next) + for( struct link_map *m = map; m; m = m->l_next ) + fprintf(stderr, "+link_map: %p | %50s | addr: %p | dyn: %p |\n", + m, m->l_name, (void *)m->l_addr, m->l_ld); + + dl_iterate_phdr( dlhdr_cb, NULL ); +} diff --git a/subprojects/libcapsule/utils/dump.h b/subprojects/libcapsule/utils/dump.h new file mode 100644 index 0000000000000000000000000000000000000000..f3fb3046c543f23d450ecbdce280686b4b96149a --- /dev/null +++ b/subprojects/libcapsule/utils/dump.h @@ -0,0 +1,22 @@ +// Copyright © 2017 Collabora Ltd + +// This file is part of libcapsule. + +// libcapsule is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as +// published by the Free Software Foundation; either version 2.1 of the +// License, or (at your option) any later version. + +// libcapsule 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 Lesser General Public License for more details. + +// You should have received a copy of the GNU Lesser General Public +// License along with libcapsule. If not, see <http://www.gnu.org/licenses/>. + +#pragma once + +void dump_elf_data (const char *soname); + +void debug_link_maps (void); diff --git a/subprojects/libcapsule/utils/elf-dump.c b/subprojects/libcapsule/utils/elf-dump.c new file mode 100644 index 0000000000000000000000000000000000000000..9acf2cdc755beeba3fb8cef84bf85746151dcf10 --- /dev/null +++ b/subprojects/libcapsule/utils/elf-dump.c @@ -0,0 +1,117 @@ +// Copyright © 2017 Collabora Ltd + +// This file is part of libcapsule. + +// libcapsule is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as +// published by the Free Software Foundation; either version 2.1 of the +// License, or (at your option) any later version. + +// libcapsule 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 Lesser General Public License for more details. + +// You should have received a copy of the GNU Lesser General Public +// License along with libcapsule. If not, see <http://www.gnu.org/licenses/>. + +#include <dlfcn.h> +#include <errno.h> +#include <getopt.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "debug.h" +#include "dump.h" +#include "tools.h" + +static void usage (int code) __attribute__((noreturn)); +static void usage (int code) +{ + FILE *fh; + + if( code == 0 ) + { + fh = stdout; + } + else + { + fh = stderr; + } + + fprintf( fh, "Usage: %s SONAME\n", + program_invocation_short_name ); + fprintf( fh, "SONAME is the machine-readable name of a shared library,\n" + "for example 'libz.so.1'.\n" ); + exit( code ); +} + +enum +{ + OPTION_HELP, + OPTION_VERSION, +}; + +static struct option long_options[] = +{ + { "help", no_argument, NULL, OPTION_HELP }, + { "version", no_argument, NULL, OPTION_VERSION }, + { NULL } +}; + +int main (int argc, char **argv) +{ + void *handle; + const char *libname; + + set_debug_flags( getenv("CAPSULE_DEBUG") ); + + while( 1 ) + { + int opt = getopt_long( argc, argv, "", long_options, NULL ); + + if( opt == -1 ) + { + break; + } + + switch( opt ) + { + case '?': + default: + usage( 2 ); + break; // not reached + + case OPTION_HELP: + usage( 0 ); + break; // not reached + + case OPTION_VERSION: + _capsule_tools_print_version( "capsule-elf-dump" ); + return 0; + } + } + + if( argc != optind + 1 ) + usage( 1 ); + + handle = dlopen( argv[optind], RTLD_LAZY ); + + if( !handle ) + { + int e = errno; + const char *err = dlerror(); + fprintf( stderr, "%s: dlopen failed (%s)\n", program_invocation_short_name, err ); + return e ? e : ENOENT; + } + + if( (libname = strrchr( argv[optind], '/' )) ) + libname = libname + 1; + else + libname = argv[optind]; + + dump_elf_data( libname ); + + return 0; +} diff --git a/subprojects/libcapsule/utils/ld-cache.c b/subprojects/libcapsule/utils/ld-cache.c new file mode 100644 index 0000000000000000000000000000000000000000..600f2d3e69f6735ac38928f51864e62c63609e2e --- /dev/null +++ b/subprojects/libcapsule/utils/ld-cache.c @@ -0,0 +1,370 @@ +// Copyright © 1999-2017 Free Software Foundation, Inc. +// Copyright © 2017 Collabora Ltd + +// This file is part of libcapsule. + +// libcapsule is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as +// published by the Free Software Foundation; either version 2.1 of the +// License, or (at your option) any later version. + +// libcapsule 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 Lesser General Public License for more details. + +// You should have received a copy of the GNU Lesser General Public +// License along with libcapsule. If not, see <http://www.gnu.org/licenses/>. + +#include <errno.h> +#include <stdio.h> +#include <sys/mman.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <fcntl.h> +#include <string.h> + +#include "ld-cache.h" +#include "utils.h" + +// ========================================================================== +// this is stolen from the ld.so config (dl-cache.h) since we need +// to use a slightly re-brained version of the linker to do our +// filthy, filthy business (and these details are not in a shipped header) + +#define FLAG_ANY -1 +#define FLAG_TYPE_MASK 0x00ff +#define FLAG_LIBC4 0x0000 +#define FLAG_ELF 0x0001 +#define FLAG_ELF_LIBC5 0x0002 +#define FLAG_ELF_LIBC6 0x0003 +#define FLAG_REQUIRED_MASK 0xff00 +#define FLAG_SPARC_LIB64 0x0100 +#define FLAG_IA64_LIB64 0x0200 +#define FLAG_X8664_LIB64 0x0300 +#define FLAG_S390_LIB64 0x0400 +#define FLAG_POWERPC_LIB64 0x0500 +#define FLAG_MIPS64_LIBN32 0x0600 +#define FLAG_MIPS64_LIBN64 0x0700 +#define FLAG_X8664_LIBX32 0x0800 +#define FLAG_ARM_LIBHF 0x0900 +#define FLAG_AARCH64_LIB64 0x0a00 +#define FLAG_ARM_LIBSF 0x0b00 +#define FLAG_MIPS_LIB32_NAN2008 0x0c00 +#define FLAG_MIPS64_LIBN32_NAN2008 0x0d00 +#define FLAG_MIPS64_LIBN64_NAN2008 0x0e00 + +static const char *flag_descr[] = { "libc4", "ELF", "libc5", "libc6"}; + +static const char *const abi_tag_os[] = +{ + [0] = "Linux", + [1] = "Hurd", + [2] = "Solaris", + [3] = "FreeBSD", + [4] = "kNetBSD", + [5] = "Syllable", + [6] = "Unknown OS" +}; +#define MAXTAG (N_ELEMENTS( abi_tag_os ) - 1) + +/* Used to align cache_file_new. */ +#define ALIGN_CACHE(addr) \ + (((addr) + __alignof__ (struct cache_file_new) -1) \ + & (~(__alignof__ (struct cache_file_new) - 1))) + +// end of stolen header structures +//////////////////////////////////////////////////////////////////////////// + +const char * const ld_cache_filenames[] = +{ +#ifdef __i386__ + "/etc/ld-i686-pc-linux-gnu.cache", /* Exherbo */ +#endif +#ifdef __x86_64__ + "/etc/ld-x86_64-pc-linux-gnu.cache", /* Exherbo */ +#endif + "/etc/ld.so.cache", /* interoperable */ + "/var/cache/ldconfig/ld.so.cache", /* Clear Linux */ + NULL +}; + +static void +ld_cache_reset (ld_cache *cache) +{ + cache->fd = -1; + cache->map_size = 0; + cache->data = NULL; + cache->file.old = NULL; + cache->type = CACHE_NONE; + cache->mmap = MAP_FAILED; + cache->is_open = 0; +} + +void +ld_cache_close (ld_cache *cache) +{ + // 0 is a valid fd, but is also the default value of the unset + // struct member, so we have to check if it's _really_ open: + if( cache->is_open ) + { + if( cache->fd >= 0 ) + close( cache->fd ); + } + + if( cache->mmap && cache->map_size ) + munmap( cache->mmap, cache->map_size ); + + ld_cache_reset( cache ); +} + +int +ld_cache_open (ld_cache *cache, const char *path, int *code, char **message) +{ + struct stat ldcache = {}; + + ld_cache_close( cache ); + + cache->fd = open( path, O_RDONLY ); + + if( cache->fd < 0 ) + { + int saved_errno = errno; + + _capsule_set_error( code, message, saved_errno, "open \"%s\": %s", + path, strerror( saved_errno ) ); + goto cleanup; + } + + // now we have a real file descriptor tag the + // cache as successfully opened: + cache->is_open = 1; + + fstat( cache->fd, &ldcache ); + + // cache file must be at least this big or it's invalid: + if( ldcache.st_size < (off_t) sizeof( struct cache_file ) ) + { + _capsule_set_error( code, message, ENODATA, + "\"%s\" too small (%jd < %zu bytes)", + path, (intmax_t) ldcache.st_size, + sizeof( struct cache_file ) ); + goto cleanup; + } + + cache->mmap = + mmap( NULL, ldcache.st_size, PROT_READ, MAP_PRIVATE, cache->fd, 0 ); + + if( cache->mmap == MAP_FAILED ) + { + int saved_errno = errno; + + _capsule_set_error( code, message, saved_errno, "mmap \"%s\": %s", + path, strerror( saved_errno ) ); + goto cleanup; + } + + cache->map_size = ldcache.st_size; + + // plain modern (circa 2016) cache map: + if( memcmp( cache->mmap->magic, CACHEMAGIC, sizeof(CACHEMAGIC) -1 ) ) + { + DEBUG( DEBUG_LDCACHE, "New format ld cache" ); + cache->file.new = (struct cache_file_new *)cache->mmap; + + // if the magic strings don't reside at the expected offsets, bail out: + if( memcmp( cache->file.new->magic, + CACHEMAGIC_NEW, sizeof(CACHEMAGIC_NEW) - 1 ) || + memcmp( cache->file.new->version, + CACHE_VERSION, sizeof(CACHE_VERSION) - 1 ) ) + { + _capsule_set_error( code, message, EINVAL, + "invalid cache, expected %s: %s", + CACHEMAGIC_NEW, CACHE_VERSION ); + goto cleanup; + } + + cache->data = (char *)cache->file.new; + cache->type = CACHE_NEW; + } + else + { + size_t header = sizeof( struct cache_file ); + size_t entry = sizeof( struct file_entry ); + size_t block = header + (cache->mmap->nlibs * entry); + size_t offset = ALIGN_CACHE( block ); + int nlibs = cache->mmap->nlibs; + + DEBUG( DEBUG_LDCACHE, "Old format ld cache" ); + + // it's an old-style cache, unless we successfully probe for a + // nested new cache inside it: + cache->type = CACHE_OLD; + + /* This is where the strings start in an old style cache */ + cache->data = (const char *) &cache->mmap->libs[ nlibs ]; + + if( cache->map_size > (offset + sizeof( struct cache_file_new )) ) + { + cache->file.new = (void *)cache->mmap + offset; + + // this is the probe: as in the pervious if block, except + // that if we don't find a new cache it's not an error, + // it just means we're in an old style cache: + if( memcmp( cache->file.new->magic, CACHEMAGIC_NEW, + sizeof(CACHEMAGIC_NEW) - 1 ) || + memcmp( cache->file.new->version, CACHE_VERSION, + sizeof(CACHE_VERSION) - 1) ) + { + // nope, no encapsulated new cache: + cache->file.old = cache->mmap; + } + else + { + DEBUG( DEBUG_LDCACHE, "... with a new style cache inside" ); + cache->type = CACHE_NEW; + cache->data = (char *)cache->file.new; + } + } + } + + DEBUG( DEBUG_LDCACHE, "Opened ld.cache at %s", path ); + return 1; + +cleanup: + DEBUG( DEBUG_LDCACHE, "Failed to open ld.cache at %s", path ); + ld_cache_close( cache ); + return 0; +} + +// iterate over the entries in the ld cache, invoking callback cb on each one +// until eithe the callback returns true or we run out of entries. +// if the callback terminates iteration by returning true, return that value, +// otherwise return false: +intptr_t +ld_cache_foreach (ld_cache *cache, ld_cache_entry_cb cb, void *data) +{ + int rval = 0; + const char *base = cache->data; + + switch( cache->type ) + { + case CACHE_OLD: + for (unsigned i = 0; !rval && (i < cache->file.old->nlibs); i++) + { + struct file_entry *f = &cache->file.old->libs[i]; + rval = cb( base + f->key, f->flags, 0, 0, base + f->value, data ); + } + break; + + case CACHE_NEW: + for (unsigned i = 0; !rval && (i < cache->file.new->nlibs); i++) + { + struct file_entry_new *f = &cache->file.new->libs[i]; + rval = cb( base + f->key, f->flags, + f->osversion, f->hwcap, base + f->value, data ); + } + break; + + case CACHE_NONE: + default: + fprintf( stderr, "Invalid ld cache type %d", cache->type ); + break; + } + + return rval; +} + +intptr_t +ld_entry_dump (const char *name, + int flag, + unsigned int osv, + uint64_t hwcap, + const char *path, + void *data) +{ + FILE *stream = (FILE *)data; + fprintf( stream, "%s → %s\n", name, path ); + + fputs( " type: ", stream ); + switch (flag & FLAG_TYPE_MASK) + { + case FLAG_LIBC4: + case FLAG_ELF: + case FLAG_ELF_LIBC5: + case FLAG_ELF_LIBC6: + fputs( flag_descr[flag & FLAG_TYPE_MASK], stream ); + break; + default: + fputs( "???", stdout ); + break; + } + fputs( "\n", stream ); + + fputs( " requires: ", stream ); + switch (flag & FLAG_REQUIRED_MASK) + { + case FLAG_SPARC_LIB64: + fputs( "Sparc 64bit", stream ); + break; + case FLAG_IA64_LIB64: + fputs( "IA-64", stream ); + break; + case FLAG_X8664_LIB64: + fputs( "x86-64", stream ); + break; + case FLAG_S390_LIB64: + fputs( "s390 64bit", stream ); + break; + case FLAG_POWERPC_LIB64: + fputs( "PPC 64bit", stream ); + break; + case FLAG_MIPS64_LIBN32: + fputs( "MIPS N32", stream ); + break; + case FLAG_MIPS64_LIBN64: + fputs( "MIPS 64bit", stream ); + break; + case FLAG_X8664_LIBX32: + fputs( "x32", stream ); + break; + case FLAG_ARM_LIBHF: + fputs( "Arm hard-float", stream ); + break; + case FLAG_AARCH64_LIB64: + fputs( "AArch64", stream ); + break; + /* Uses the ARM soft-float ABI. */ + case FLAG_ARM_LIBSF: + fputs( "Arm soft-float", stream ); + break; + case FLAG_MIPS_LIB32_NAN2008: + fputs( "MIPS nan2008", stream ); + break; + case FLAG_MIPS64_LIBN32_NAN2008: + fputs( "MIPS N32 nan2008", stream ); + break; + case FLAG_MIPS64_LIBN64_NAN2008: + fputs( "IPS 64bit nan2008", stream ); + break; + case 0: + break; + default: + fprintf (stream, "%0x", flag & FLAG_REQUIRED_MASK); + break; + } + fputs( "\n", stream ); + + unsigned int os = osv >> 24; + + fprintf( stream, " OS ABI: %s %d.%d.%d (%0x)\n", + abi_tag_os[ os > MAXTAG ? MAXTAG : os ], + (osv >> 16) & 0xff, + (osv >> 8 ) & 0xff, + osv & 0xff, + osv ); + + fprintf( stream, " hwcaps: %0"FMT_XU64"\n", hwcap ); + return 0; +} diff --git a/subprojects/libcapsule/utils/ld-cache.h b/subprojects/libcapsule/utils/ld-cache.h new file mode 100644 index 0000000000000000000000000000000000000000..869458af21347dacee68df602855bd125cac5db3 --- /dev/null +++ b/subprojects/libcapsule/utils/ld-cache.h @@ -0,0 +1,103 @@ +// Copyright © 1999-2017 Free Software Foundation, Inc. +// Copyright © 2017 Collabora Ltd + +// This file is part of libcapsule. + +// libcapsule is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as +// published by the Free Software Foundation; either version 2.1 of the +// License, or (at your option) any later version. + +// libcapsule 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 Lesser General Public License for more details. + +// You should have received a copy of the GNU Lesser General Public +// License along with libcapsule. If not, see <http://www.gnu.org/licenses/>. + +#pragma once + +#include <sys/types.h> +#include <stdint.h> + +typedef enum +{ + CACHE_NONE, + CACHE_NEW , + CACHE_OLD , +} cache_type; + + +// ========================================================================== +// this is stolen from the ld.so config (dl-cache.h) since we need +// to use a slightly re-brained version of the linker to do our +// filthy, filthy business (and these details are not in a shipped header) +#define CACHEMAGIC "ld.so-1.7.0" + +#define CACHEMAGIC_NEW "glibc-ld.so.cache" +#define CACHE_VERSION "1.1" +#define CACHEMAGIC_VERSION_NEW CACHEMAGIC_NEW CACHE_VERSION + +struct file_entry_new +{ + int32_t flags; /* This is 1 for an ELF library. */ + uint32_t key, value; /* String table indices. */ + uint32_t osversion; /* Required OS version. */ + uint64_t hwcap; /* Hwcap entry. */ +}; + +struct cache_file_new +{ + char magic[sizeof CACHEMAGIC_NEW - 1]; + char version[sizeof CACHE_VERSION - 1]; + uint32_t nlibs; /* Number of entries. */ + uint32_t len_strings; /* Size of string table. */ + uint32_t unused[5]; /* Leave space for future extensions + and align to 8 byte boundary. */ + struct file_entry_new libs[0]; /* Entries describing libraries. */ + /* After this the string table of size len_strings is found. */ +}; + +struct file_entry +{ + int flags; /* This is 1 for an ELF library. */ + unsigned int key, value; /* String table indices. */ +}; + +struct cache_file +{ + char magic[sizeof CACHEMAGIC - 1]; + unsigned int nlibs; + struct file_entry libs[0]; +}; + +typedef struct +{ + int fd; + size_t map_size; + struct cache_file *mmap; + const char *data; + union { struct cache_file *old; struct cache_file_new *new; } file; + cache_type type; + int is_open; +} ld_cache; + +// ========================================================================== + +extern const char * const ld_cache_filenames[]; + +typedef intptr_t (*ld_cache_entry_cb) (const char *name, + int flag, + unsigned int osv, + uint64_t hwcap, + const char *path, + void *data); + +int ld_cache_open (ld_cache *cache, const char *path, int *code, + char **message); +void ld_cache_close (ld_cache *cache); +intptr_t ld_cache_foreach (ld_cache *cache, ld_cache_entry_cb cb, void *data); + +intptr_t ld_entry_dump (const char *name, int flag, unsigned int osv, + uint64_t hwcap, const char *path, void *data); diff --git a/subprojects/libcapsule/utils/ld-libs.c b/subprojects/libcapsule/utils/ld-libs.c new file mode 100644 index 0000000000000000000000000000000000000000..2a0b9c87395db9838facf7b538d32f3aab06da62 --- /dev/null +++ b/subprojects/libcapsule/utils/ld-libs.c @@ -0,0 +1,1156 @@ +// Copyright © 2017 Collabora Ltd + +// This file is part of libcapsule. + +// libcapsule is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as +// published by the Free Software Foundation; either version 2.1 of the +// License, or (at your option) any later version. + +// libcapsule 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 Lesser General Public License for more details. + +// You should have received a copy of the GNU Lesser General Public +// License along with libcapsule. If not, see <http://www.gnu.org/licenses/>. + +#include <assert.h> +#include <string.h> +#include <dlfcn.h> +#include <link.h> +#include <errno.h> +#include <stdio.h> +#include <sys/param.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> +#include <stdlib.h> +#include <execinfo.h> + +#include "utils.h" +#include "ld-libs.h" + + +struct dso_cache_search +{ + int idx; + const char *name; + ld_libs *ldlibs; +}; + +static const struct stat *stat_caller (void); + +// Note that this is not re-entrant. In this context we don't care. +static const char * +_rtldstr(int flag) +{ + static char flags[160] = { 0 }; + char *f = &flags[0]; + + if( !flag) + return "LOCAL"; + +#define RTLDFLAGSTR(x) \ + if( x & flag ) f += snprintf(f, &flags[80] - f, " %s", & #x [5]) + + RTLDFLAGSTR(RTLD_LAZY); + RTLDFLAGSTR(RTLD_NOW); + RTLDFLAGSTR(RTLD_NOLOAD); + RTLDFLAGSTR(RTLD_DEEPBIND); + RTLDFLAGSTR(RTLD_GLOBAL); + RTLDFLAGSTR(RTLD_NODELETE); + + return ( flags[0] == ' ' ) ? &flags[1] : &flags[0]; +} + +// as we are pulling in files from a non '/' prefix ('/host' by default) +// we need to compensate for this when resolving symlinks. +// this will keep following the path at entry i in ldlibs until +// it finds something that is not a symlink. +static void resolve_symlink_prefixed (ld_libs *ldlibs, int i) +{ + int count = 0; + char resolved[PATH_MAX]; + + assert( ldlibs->prefix.path[ ldlibs->prefix.len ] == '\0' ); + // prefix is unset or is /, nothing to do here (we can rely on + // libc's built-in symlink following if there's no prefix): + if( ldlibs->prefix.len == 0 || + (ldlibs->prefix.path[0] == '/' && ldlibs->prefix.path[1] == '\0') ) + return; + + LDLIB_DEBUG( ldlibs, DEBUG_PATH, + "resolving (un)prefixed link in %s", ldlibs->needed[i].path ); + + // set the resolved path to the current needed path as a starting point: + safe_strncpy( resolved, ldlibs->needed[i].path, PATH_MAX ); + + // now keep poking resolve_link (resolved will be updated each time) + // until it returns false: + while( resolve_link(ldlibs->prefix.path, resolved) ) + { + LDLIB_DEBUG( ldlibs, DEBUG_PATH, " resolved to: %s", resolved ); + + if( ++count > MAXSYMLINKS ) + { + fprintf( stderr, "%s: MAXSYMLINKS (%d) exceeded resolving %s\n", + __PRETTY_FUNCTION__, MAXSYMLINKS, + ldlibs->needed[i].path ); + break; + } + } + + // if the path changed, copy `resolved' back into needed[].path: + if( count ) + safe_strncpy( ldlibs->needed[i].path, resolved, PATH_MAX ); +} + + +// set the ldlibs elf class and machine based on the link map entry +// passed to us if possible: if we found values for these, return 1, +// otherwise return 0: +static int +find_elf_constraints(ld_libs *ldlibs, struct link_map *m) +{ + int fd = -1; + Elf *dso = NULL; + GElf_Ehdr ehdr = { }; + + // absolute path or it's a "fake" link map entry which we can't use + // as there's no actual file to open and inspect: + if( !m || !m->l_name || (m->l_name[0] != '/')) + return 0; + + // if we can't open the DSO pointed to by the link map, bail: + fd = open( m->l_name, O_RDONLY ); + + if( fd < 0 ) + return 0; + + dso = elf_begin( fd, ELF_C_READ_MMAP, NULL ); + + if( dso && gelf_getehdr( dso, &ehdr ) ) + { + ldlibs->elf_class = gelf_getclass( dso ); + ldlibs->elf_machine = ehdr.e_machine; + DEBUG( DEBUG_SEARCH|DEBUG_CAPSULE, + "elf class: %d; elf machine: %d; set from: %s", + ldlibs->elf_class, ldlibs->elf_machine, m->l_name ); + } + + if( dso != NULL ) + elf_end( dso ); + + if( fd >= 0 ) + close( fd ); + + return ( ldlibs->elf_class != ELFCLASSNONE ); +} + +// record the class & machine of the start of the link chain +// so that we can only consider matching libraries later +// this matters on multi-arch systems so we don't pick an +// i386 or x32 DSO to statisfy a DT_NEEDED from an x86-64 one. +// return true if we found a valid DSO, false (can't happen?) otherwise +static int +set_elf_constraints (ld_libs *ldlibs) +{ + void *handle; + struct link_map *map; + struct link_map *m; + + static int elf_class = ELFCLASSNONE; + static Elf64_Half elf_machine = EM_NONE; + + if( elf_class != ELFCLASSNONE ) + { + ldlibs->elf_class = elf_class; + ldlibs->elf_machine = elf_machine; + } + else if( (handle = dlmopen( LM_ID_BASE, NULL, RTLD_LAZY|RTLD_NOLOAD )) && + (dlinfo( handle, RTLD_DI_LINKMAP, &map ) == 0) ) + { + // we're not guaranteed to be at the start of the link map chain: + while( map->l_prev ) + map = map->l_prev; + + // check link maps until we find one we can fill in + // our constraints from: + for( m = map; m; m = m->l_next ) + if( find_elf_constraints( ldlibs, m ) ) + break; + } + else + { + // this would be frankly beyond bizarre: + fprintf(stderr, "dlopen/dlinfo on self failed: %s\n", dlerror() ); + } + + return ( ( ldlibs->elf_class != ELFCLASSNONE ) && + ( ldlibs->elf_machine |= EM_NONE ) ); +} + +// check that the currently opened DSO at offset idx in the needed array +// matches the class & architecture of the DSO we started with: +// return true on a match, false otherwise +static int +check_elf_constraints (ld_libs *ldlibs, int idx, int *code, char **message) +{ + GElf_Ehdr ehdr = {}; + int eclass; + + // bogus ELF DSO - no ehdr available? + if( !gelf_getehdr( ldlibs->needed[ idx ].dso, &ehdr ) ) + { + // FIXME: elf_errno() isn't actually in the same domain as errno + _capsule_set_error( code, message, elf_errno(), "gelf_getehdr(%s): %s", + ldlibs->needed[ idx ].path, + elf_errmsg( elf_errno() ) ); + return 0; + } + + eclass = gelf_getclass( ldlibs->needed[ idx ].dso ); + + // check class (32 vs 64 bit) + if( ldlibs->elf_class != eclass ) + { + _capsule_set_error( code, message, ENOEXEC, + "gelf_getclass(%s): expected %d, found %d", + ldlibs->needed[ idx ].path, ldlibs->elf_class, + eclass ); + return 0; + } + + // check target architecture (i386, x86-64) + // x32 ABI is class 32 but machine x86-64 + if( ldlibs->elf_machine != ehdr.e_machine ) + { + _capsule_set_error( code, message, ENOEXEC, + "ehdr.e_machine of %s: expected %d, found %d", + ldlibs->needed[ idx ].path, ldlibs->elf_machine, + ehdr.e_machine ); + return 0; + } + + DEBUG( DEBUG_ELF, "constraints: class %d; machine: %d;", + ldlibs->elf_class, ldlibs->elf_machine ); + DEBUG( DEBUG_ELF, "results : class %d; machine: %d;", + eclass, ehdr.e_machine ); + + // both the class (word size) and machine (architecture) match + return 1; +} + +// check that the library we're opening is NOT ourself (this is to stop +// a proxy libfoo.so.X from opening itself instead of the real target): +// In normal situations we won't find ourself by accident as the path +// prefix should place the real library ahead of us in the search order. +// However LD_PRELOAD or a weirdly configured system or a missing target +// library could result in the capsule library finding itself and +// inifinitely looping, so we try to avoid that with this test: +static int +target_is_ourself (ld_libs *ldlibs, int idx) +{ + const struct stat *cdso = stat_caller(); + struct stat xdso = { 0 }; + + // Can't check, no stat info for orginal caller. + // Assume things will mostly be ok, mostly: + if( cdso == NULL ) + return 0; + + // if stat says we have the same device and inode as + // the target we're considering ourselves as a target: + if( fstat( ldlibs->needed[idx].fd, &xdso ) == 0 ) + return ( cdso->st_dev == xdso.st_dev && + cdso->st_ino == xdso.st_ino ); + + // placeholder while this test is developed: + return 0; +} + +static void clear_needed (dso_needed_t *needed) +{ + elf_end( needed->dso ); + needed->dso = NULL; + + if( needed->fd >= 0 ) + close( needed->fd ); + needed->fd = -1; + + free( needed->name ); + needed->name = NULL; + + needed->depcount = 0; + + memset( needed->path, 0, PATH_MAX ); + memset( needed->requestors, 0, sizeof(int) * DSO_LIMIT ); +} + +// open the dso at offset i in the needed array, but only accept it +// if it matches the class & architecture of the starting DSO: +// return a true value only if we finish with a valid fd for the DSO +// +// will set up the needed entry at offset i correctly if we are +// successful, and clear it if we are not: +// +// designed to be called on a populated ldlib needed entry, 'name' +// is the original requested name (typically an absolute path to +// a DSO or a standard DT_NEEDED style specifier following 'libfoo.so.X') +// +// note that this expects the prefix to have already been prepended +// to the DSO path in the ldlibs->needed[i].path buffer if necessary +// this is to allow both prefixed and unprefixed DSOs to be handled +// here: +static int +ld_lib_open (ld_libs *ldlibs, const char *name, int i, int *code, char **message) +{ + + LDLIB_DEBUG( ldlibs, DEBUG_SEARCH, + "ldlib_open: target -: %s", ldlibs->needed[i].path ); + + // resolve the symlink manually if there's a prefix: + resolve_symlink_prefixed( ldlibs, i ); + + LDLIB_DEBUG( ldlibs, DEBUG_SEARCH, + "ldlib_open: target +: %s", ldlibs->needed[i].path ); + + ldlibs->needed[i].fd = open( ldlibs->needed[i].path, O_RDONLY ); + + if( ldlibs->needed[i].fd >= 0 ) + { + int acceptable = 0; + + ldlibs->needed[i].name = NULL; + ldlibs->needed[i].dso = + elf_begin( ldlibs->needed[i].fd, ELF_C_READ_MMAP, NULL ); + + acceptable = check_elf_constraints( ldlibs, i, code, message ); + + LDLIB_DEBUG( ldlibs, DEBUG_SEARCH, + "[%03d] %s on fd #%d; elf: %p; acceptable: %d", + i, + ldlibs->needed[i].path, + ldlibs->needed[i].fd , + ldlibs->needed[i].dso , + acceptable ); + + if( !acceptable ) + { + clear_needed( &ldlibs->needed[i] ); + return 0; + } + + acceptable = !target_is_ourself( ldlibs, i ); + + // either clean up the current entry so we can find a better DSO + // or (for a valid candidate) copy the original requested name in: + if( !acceptable ) + { + _capsule_set_error( code, message, EINVAL, + "\"%s\" appears to be the libcapsule shim", + ldlibs->needed[i].path ); + clear_needed( &ldlibs->needed[i] ); + return 0; + } + + ldlibs->needed[i].name = xstrdup( name ); + return 1; + } + else + { + int errsv = errno; + + _capsule_set_error( code, message, errsv, + "Cannot open \"%s\": %s", + ldlibs->needed[i].path, strerror( errsv ) ); + clear_needed( &ldlibs->needed[i] ); + return 0; + } +} + +// search callback for search_ldcache. see search_ldcache and ld_cache_foreach: +// returning a true value means we found (and set up) the DSO we wanted: +static intptr_t +search_ldcache_cb (const char *name, // name of the DSO in the ldcache + int flag, // 1 for an ELF DSO + unsigned int osv, // OS version. we don't use this + uint64_t hwcap, // HW caps. Ibid. + const char *path, // absolute path to DSO (may be a symlink) + void *data) +{ + struct dso_cache_search *target = data; + + // passed an empty query, just abort the whole search + if( !target->name || !(*target->name) ) + return 1; + + // what would this even mean? malformed cache entry? + // skip it and move on + if( !name || !*name ) + return 0; + + if( strcmp( name, target->name ) == 0 ) + { + const char *prefix = target->ldlibs->prefix.path; + int idx = target->idx; + char *lpath = target->ldlibs->needed[ idx ].path; + + LDLIB_DEBUG( target->ldlibs, DEBUG_SEARCH|DEBUG_LDCACHE, + "checking %s vs %s [%s]", + target->name, name, path ); + // copy in the prefix and append the DSO path to it + if( build_filename( lpath, PATH_MAX, prefix, path, NULL ) >= PATH_MAX ) + { + return 0; + } + + // try to open the DSO. This will finish setting up the + // needed[idx] slot if successful, and reset it ready for + // another attempt if it fails. + // TODO: Can we propagate error information? + if( !ld_lib_open( target->ldlibs, name, idx, NULL, NULL ) ) + { + // search_ldcache() relies on fd >= 0 iff we succeeded + assert( target->ldlibs->needed[idx].fd < 0 ); + assert( target->ldlibs->needed[idx].name == NULL ); + return 0; + } + + // search_ldcache() relies on fd >= 0 iff we succeeded + assert( target->ldlibs->needed[idx].fd >= 0 ); + assert( target->ldlibs->needed[idx].name != NULL ); + return 1; + } + + return 0; +} + +// search the ld.so.cache loaded into ldlibs for one matching `name' +// name should be unadorned: eg just libfoo.so.X - no path elements +// attached (as the cache lookup is for unadorned library names): +// +// if a match is found, the needed array entry at i will be populated +// and will contain a valid fd for the DSO. (and search_ldcache will +// return true). Otherwise the entry will be empty and we will return false: +// +// this function will respect any path prefix specified in ldlibs +static int +search_ldcache (const char *name, ld_libs *ldlibs, int i) +{ + struct dso_cache_search target; + + target.idx = i; + target.name = name; + target.ldlibs = ldlibs; + + if( !ldlibs->ldcache.is_open ) + { + if( !ld_libs_load_cache( ldlibs, NULL, NULL ) ) + { + // TODO: report error? + return 0; + } + } + + ld_cache_foreach( &ldlibs->ldcache, + search_ldcache_cb, &target ); + + return ldlibs->needed[i].fd >= 0; +} + +// search a : separated path (such as LD_LIBRARY_PATH from the environment) +// for a DSO matching the bare `name' (eg libfoo.so.X) +// +// if a match is found, the needed array entry at i will be populated +// and will contain a valid fd for the DSO. (and search_ldcache will +// return true). Otherwise the entry will be empty and we will return false: +// +// this function will respect any path prefix specified in ldlibs +static int +search_ldpath (const char *name, const char *ldpath, ld_libs *ldlibs, int i) +{ + const char *sp = ldpath; + const char *prefix = ldlibs->prefix.path; + size_t plen = ldlibs->prefix.len; + char search[PATH_MAX]; + char prefixed[PATH_MAX] = { '\0' }; + + assert( prefix[plen] == '\0' ); + + LDLIB_DEBUG( ldlibs, DEBUG_SEARCH, + "searching for %s in %s (prefix: %s)", + name, ldpath, plen ? prefix : "-none-" ); + + while( sp && *sp ) + { + size_t len; + const char *end; + + end = strchr( sp, ':' ); + if( end ) + len = MIN((size_t) (end - sp), sizeof(search) - 1); + else + len = MIN(strlen( sp ), sizeof(search) - 1); + + // If this gets truncated, then there will be no space left in + // prefixed either, so we'll skip it anyway + safe_strncpy( search, sp, len + 1 ); + + LDLIB_DEBUG( ldlibs, DEBUG_SEARCH, " searchpath element: %s", search ); + + if( build_filename( prefixed, sizeof(prefixed), prefix, search, + name, NULL ) >= sizeof(prefixed) ) + { + LDLIB_DEBUG( ldlibs, DEBUG_SEARCH, " (too long)" ); + } + else + { + LDLIB_DEBUG( ldlibs, DEBUG_SEARCH, "examining %s", prefixed ); + // if path resolution succeeds _and_ we can open an acceptable + // DSO at that location, we're good to go (ldlib_open will + // finish setting up or clearing the needed[] entry for us): + // FIXME: this is broken if the target is an absolute symlink + // TODO: Propagate error information from ld_lib_open()? + if( realpath( prefixed, ldlibs->needed[i].path ) && + ld_lib_open( ldlibs, name, i, NULL, NULL ) ) + return 1; + } + + // search the next path element if there is one + if( !end ) + break; + + sp = end + 1; + } + + return 0; +} + +// find a DSO using an algorithm that matches the one used by the +// normal dynamic linker and set up the needed array entry at offset i. +// +// Exceptions: +// we don't support DT_RPATH/DT_RUNPATH +// we don't handle ${ORIGIN} and similar +// we will respect any path prefix specified in ldlibs +// +// if a match is found, the needed array entry at i will be populated +// and will contain a valid fd for the DSO. (and search_ldcache will +// return true). Otherwise the entry will be empty and we will return false: +static int +dso_find (const char *name, ld_libs *ldlibs, int i, int *code, char **message) +{ + int found = 0; + const char *ldpath = NULL; + int absolute = (name && (name[0] == '/')); + + // 'name' is an absolute path, or relative to CWD: + // we may to need to do some path manipulation + if( strchr( name, '/' ) ) + { + char prefixed[PATH_MAX]; + const char *target; + + // we have a path prefix, so yes, we need to do some path manipulation: + if( ldlibs->prefix.len ) + { + assert( ldlibs->prefix.path[ ldlibs->prefix.len ] == '\0' ); + + if( build_filename( prefixed, sizeof(prefixed), + ldlibs->prefix.path, name, + NULL ) >= sizeof(prefixed) ) + { + _capsule_set_error( code, message, ENAMETOOLONG, + "path to DSO is too long: \"%s...\"", + prefixed ); + return 0; + } + else if( absolute ) + { + LDLIB_DEBUG( ldlibs, DEBUG_SEARCH|DEBUG_PATH, + "absolute path to DSO %s", prefixed ); + } + else + { // name is relative... this is probably wrong? + // I don't think this can ever really happen but + // worst case is we'll simply not open a DSO whose + // path we couldn't resolve, and then move on + // (if name is something like ./mylib.so then we'll + // have treated it as though it was $prefix/./mylib.so) + LDLIB_DEBUG( ldlibs, DEBUG_SEARCH|DEBUG_PATH, + "relative path to DSO %s", prefixed ); + } + + target = prefixed; + } + else + { // name is a standard bare 'libfoo.so.X' spec: + target = name; + } + + LDLIB_DEBUG( ldlibs, DEBUG_SEARCH|DEBUG_PATH, + "resolving path %s", target ); + // this will fail for a non-absolute path, but that's OK + // if realpath lookup succeeds needed[i].path will be set correctly: + if( realpath( target, ldlibs->needed[i].path ) ) + { + return ld_lib_open( ldlibs, name, i, code, message ); + } + else if( absolute ) + { + int saved_errno = errno; + + // path was absolute and we couldn't resolve it. give up: + _capsule_set_error( code, message, saved_errno, + "realpath(\"%s\"): %s", + ldlibs->needed[i].path, + strerror( saved_errno ) ); + return 0; + } + } + else + { + assert( !absolute ); + } + + LDLIB_DEBUG( ldlibs, DEBUG_SEARCH, "target DSO is %s", name ); + // now search LD_LIBRARY_PATH, the ld.so.cache, and the default locations + // in that order (similar algorithm to the linker, but with the RPATH and + // ${ORIGIN} support dropped) + if( (ldpath = getenv( "LD_LIBRARY_PATH" )) ) + if( (found = search_ldpath( name, ldpath, ldlibs, i )) ) + return found; + + if( (found = search_ldcache( name, ldlibs, i )) ) + return found; + + if( (found = search_ldpath( name, "/lib:/usr/lib", ldlibs, i )) ) + return found; + + if (ldpath) + _capsule_set_error( code, message, ENOENT, + "Could not find \"%s\" in LD_LIBRARY_PATH \"%s\", " + "ld.so.cache, /lib or /usr/lib", name, ldpath ); + else + _capsule_set_error( code, message, ENOENT, + "Could not find \"%s\" in LD_LIBRARY_PATH (unset), " + "ld.so.cache, /lib or /usr/lib", name ); + + return 0; +} + +// if a DSO has already been requested and found as a result of a DT_NEEDED +// entry we've seen before then it's already in the needed array - check +// for such pre-required entries and simply record the dependency instead +// of reopening the DSO (and return true to indicate that we already have the +// DSO) +// +// we have assumed that the root of the DSO chain can never be already-needed +// as this would indicate a circular dependency. +static int +already_needed (dso_needed_t *needed, int requesting_idx, const char *name) +{ + for( int i = DSO_LIMIT - 1; i > 0; i-- ) + { + if( needed[i].name && strcmp( needed[i].name, name ) == 0) + { + DEBUG( DEBUG_CAPSULE, "needed[%d] (%s) also needs needed[%d] (%s)", + requesting_idx, needed[requesting_idx].name, i, name ); + needed[i].requestors[requesting_idx] = 1; + return i; + } + } + + return 0; +} + +// we're getting to the meat of it: process a DSO at offset idx in the +// needed array, extract each SHT_DYNAMIC section, then make sure we +// can find a DSO to satisfy every DT_NEEDED sub-entry in the section. +// this function recurses into itself each time it finds a previously +// unseen DT_NEEDED value (but not if the DT_NEEDED value is for a DSO +// it has already found and recorded in the needed array) +// +// NOTE: you must use dso_find to seed the 0th entry in the needed array +// or the elf handle in needed[0].dso will not be set up and hilarity* +// will ensue. +static int +_dso_iterate_sections (ld_libs *ldlibs, int idx, int *code, char **error) +{ + int had_error = 0; + Elf_Scn *scn = NULL; + + //debug(" ldlibs: %p; idx: %d (%s)", ldlibs, idx, ldlibs->needed[idx].name); + + ldlibs->last_idx = idx; + + LDLIB_DEBUG( ldlibs, DEBUG_ELF, + "%03d: fd:%d dso:%p ↠%s", + idx, + ldlibs->needed[idx].fd, + ldlibs->needed[idx].dso, + ldlibs->needed[idx].path ); + + while((scn = elf_nextscn( ldlibs->needed[idx].dso, scn )) != NULL) + { + GElf_Shdr shdr = {}; + gelf_getshdr( scn, &shdr ); + + // SHT_DYNAMIC is the only section type we care about here: + if( shdr.sh_type == SHT_DYNAMIC ) + { + int i = 0; + GElf_Dyn dyn = {}; + Elf_Data *edata = NULL; + + edata = elf_getdata( scn, edata ); + + // process eaach DT_* entry in the SHT_DYNAMIC section: + while( !had_error && + gelf_getdyn( edata, i++, &dyn ) && + (dyn.d_tag != DT_NULL) ) + { + int skip = 0; + int next = ldlibs->last_idx; + dso_needed_t *needed = ldlibs->needed; + char *next_dso; // name of the dependency we're going to need + char *local_error = NULL; + + // we're only gathering DT_NEEDED (dependency) entries here: + if( dyn.d_tag != DT_NEEDED ) + continue; + + next_dso = + elf_strptr( needed[idx].dso, shdr.sh_link, dyn.d_un.d_val ); + + ////////////////////////////////////////////////// + // ignore the linker itself + if( strstr( next_dso, "ld-" ) == next_dso ) + continue; + + // ignore any DSOs we've been specifically told to leave out: + for( char **x = (char **)ldlibs->exclude; x && *x; x++ ) + { + if( strcmp( *x, next_dso ) == 0 ) + { + LDLIB_DEBUG( ldlibs, DEBUG_SEARCH|DEBUG_ELF, + "skipping %s / %s", next_dso, *x ); + skip = 1; + break; + } + } + + if( skip ) + continue; + + ////////////////////////////////////////////////// + // if we got this far, we have another dependency: + needed[idx].depcount++; + + // already on our list, no need to do anything else here: + if( already_needed( needed, idx, next_dso ) ) + continue; + + next++; + if( next >= DSO_LIMIT ) + { + had_error = 1; + _capsule_set_error_literal( code, error, ELIBMAX, + "Too many dependencies" ); + break; + } + + if( !dso_find( next_dso, ldlibs, next, code, &local_error ) ) + { + had_error = 1; + ldlibs->not_found[ ldlibs->last_not_found++ ] = + _capsule_steal_pointer( &local_error ); + // Avoid "piling up" errors which would be a memory + // leak + if( error != NULL && *error == NULL ) + *error = xstrdup( "Missing dependencies:" ); + } + else + { + LDLIB_DEBUG( ldlibs, DEBUG_CAPSULE, + "needed[%d] (%s) is the first to need needed[%d] (%s)", + idx, needed[idx].name, next, next_dso ); + // record which DSO requested the new library we found: + needed[next].requestors[idx] = 1; + // now find the dependencies of our newest dependency: + _dso_iterate_sections( ldlibs, next, code, error ); + } + } + } + } + + return !had_error; +} + +static void +_dso_iterator_format_error (ld_libs * ldlibs, char **message) +{ + size_t extra_space = 0; + + assert( message ); + assert( *message ); + + if( ! ldlibs->not_found[0] ) + return; + + for( int i = 0; (i < DSO_LIMIT) && ldlibs->not_found[i]; i++ ) + extra_space += strlen( ldlibs->not_found[i] ) + 1; + + if( extra_space ) + { + char *append_here; + char *end; + size_t prev_space = strlen( *message ); + + *message = xrealloc( *message, prev_space + extra_space + 2 ); + append_here = *message + prev_space; + end = *message + prev_space + extra_space + 1; + memset( append_here, 0, extra_space + 2 ); + + for( int i = 0; (i < DSO_LIMIT) && ldlibs->not_found[i]; i++ ) + { + append_here += + snprintf( append_here, end - append_here, + " %s", ldlibs->not_found[i] ); + free( ldlibs->not_found[i] ); + ldlibs->not_found[i] = NULL; + } + } +} + +// wrapper to format any accumulated errors and similar after +// invoking the actual dso iterator: returns true if we gathered +// all the needed info witout error, false otherwise: +static int +dso_iterate_sections (ld_libs *ldlibs, int idx, int *code, char **message) +{ + if( _dso_iterate_sections( ldlibs, idx, code, message ) ) + return 1; + + if( message ) + _dso_iterator_format_error( ldlibs, message ); + + return 0; +} + + +//////////////////////////////////////////////////////////////////////////// +// public API: +int +ld_libs_init (ld_libs *ldlibs, + const char **exclude, + const char *prefix, + unsigned long dbg, + int *errcode, + char **error) +{ + memset( ldlibs, 0, sizeof(ld_libs) ); + + ld_cache_close( &ldlibs->ldcache ); + + ldlibs->elf_class = ELFCLASSNONE; + ldlibs->elf_machine = EM_NONE; + ldlibs->exclude = exclude; + ldlibs->debug = dbg; + + if( errcode ) + *errcode = 0; + + if( error ) + *error = NULL; + + // super important, 0 is valid but is usually stdin, + // don't want to go stomping all over that by accident: + for( int x = 0; x < DSO_LIMIT; x++ ) + ldlibs->needed[x].fd = -1; + + if( elf_version(EV_CURRENT) == EV_NONE ) + { + // FIXME: elf_errno() isn't actually in the same domain as errno + _capsule_set_error( errcode, error, elf_errno(), + "elf_version(EV_CURRENT): %s", + elf_errmsg( elf_errno() ) ); + return 0; + } + + stat_caller(); + set_elf_constraints( ldlibs ); + // ================================================================== + // set up the path prefix at which we expect to find the encapsulated + // library and its ld.so.cache and dependencies and so forth: + if( prefix && prefix[0] != '\0' ) + { + size_t prefix_len = strlen( prefix ); + ssize_t space = PATH_MAX - prefix_len; + + if( prefix[0] != '/' ) + { + _capsule_set_error( errcode, error, EINVAL, + "An absolute path is required, not \"%s\"", + prefix ); + } + + // if we don't have at least this much space it's not + // going to work out: + if( (space - strlen( "/usr/lib/libx.so.x" )) <= 0 ) + { + _capsule_set_error_literal( errcode, error, ENAMETOOLONG, + "capsule_dlmopen: prefix is too large" ); + return 0; + } + + // This now can't get truncated + safe_strncpy( ldlibs->prefix.path, prefix, PATH_MAX ); + ldlibs->prefix.len = prefix_len; + } + else + { + ldlibs->prefix.path[0] = '\0'; + ldlibs->prefix.len = 0; + } + + return 1; +} + +// set up the starting point for our capsule load +// (ie the DSO we actually want): +// returns false on error: +int +ld_libs_set_target (ld_libs *ldlibs, const char *target, int *code, char **message) +{ + return dso_find( target, ldlibs, 0, code, message ); +} + +int +ld_libs_find_dependencies (ld_libs *ldlibs, int *code, char **message) +{ + return dso_iterate_sections( ldlibs, 0, code, message ); +} + +// And now we actually open everything we have found, in reverse +// dependency order (which prevents dlmopen from going and finding +// DT_NEEDED values from outside the capsule), which it will do +// if we don't work backwards: +void * +ld_libs_load (ld_libs *ldlibs, Lmid_t *namespace, int flag, int *error, + char **message) +{ + int go; + Lmid_t lm = (*namespace >= 0) ? *namespace : LM_ID_NEWLM; + void *ret = NULL; + + if( !flag ) + flag = RTLD_LAZY; + + do + { + go = 0; + + for( int j = 0; j < DSO_LIMIT; j++ ) + { + // reached the end of the list + if( !ldlibs->needed[j].name ) + continue; + + // library has no further dependencies which have not already + // been satisfied (except for the libc and linker DSOs), + // this means we can safely open it without dlmopen accidentally + // pulling in DSOs from outside the encapsulated tree: + if( ldlibs->needed[j].depcount == 0 ) + { + const char *path = ldlibs->needed[j].path; + go++; + + LDLIB_DEBUG( ldlibs, DEBUG_CAPSULE, + "DLMOPEN needed[%d]: %p %s %s", + j, (void *)lm, _rtldstr(flag), path ); + + // The actual dlmopen. If this was the first one, it may + // have created a new link map id, wich we record later on: + + // note that since we do the opens in reverse dependency order, + // the _last_ one we open will be the DSO we actually asked for + // so if we succeed, ret has to contain the right handle. + ret = dlmopen( lm, path, flag ); + + if( !ret ) + { + if (lm == LM_ID_NEWLM) + _capsule_set_error( error, message, EINVAL, + "dlmopen(LM_ID_NEWLM, \"%s\", %s): %s", + path, _rtldstr( flag ), dlerror() ); + else + _capsule_set_error( error, message, EINVAL, + "dlmopen(%p, \"%s\", %s): %s", + (void *) lm, path, + _rtldstr( flag ), dlerror() ); + + return NULL; + } + + // If this was the first dlmopen, record the new LM Id + // for return to our caller: + if( lm == LM_ID_NEWLM ) + { + dlinfo( ret, RTLD_DI_LMID, namespace ); + lm = *namespace; + LDLIB_DEBUG( ldlibs, DEBUG_CAPSULE, + "new Lmid_t handle %p\n", (void *)lm ); + } + + // go through the map of DSOs and reduce the dependency + // count for any DSOs which had the current DSO as a dep: + for( int k = 0; k < DSO_LIMIT; k++ ) + { + if( ldlibs->needed[j].requestors[k] ) + { + ldlibs->needed[k].depcount--; + LDLIB_DEBUG( ldlibs, DEBUG_CAPSULE, + "needed[%d] (%s) dependency on \"%s\" " + "satisfied, %d more libraries needed", + k, ldlibs->needed[k].name, path, + ldlibs->needed[k].depcount ); + } + } + + LDLIB_DEBUG( ldlibs, DEBUG_CAPSULE, + "Forgetting about needed[%d] (%s)", + j, ldlibs->needed[j].name ); + clear_needed( &ldlibs->needed[j] ); + } + } + } while (go); + + if( !ret ) + { + _capsule_set_error( error, message, EINVAL, + "How do we get here? go = 0" ); + return NULL; + } + + return ret; +} + +void +ld_libs_finish (ld_libs *ldlibs) +{ + for( int i = 0; i < DSO_LIMIT; i++ ) + clear_needed( &ldlibs->needed[i] ); + + for( int i = ldlibs->last_not_found; i >= 0; i-- ) + { + free( ldlibs->not_found[i] ); + ldlibs->not_found[i] = NULL; + } + + ldlibs->last_not_found = 0; + + ld_cache_close( &ldlibs->ldcache ); + + ldlibs->last_idx = 0; + ldlibs->elf_class = ELFCLASSNONE; + ldlibs->elf_machine = EM_NONE; + + ldlibs->prefix.len = 0; + ldlibs->prefix.path[0] = '\0'; +} + +// this walks backwards along (up? down?) the stack until it finds +// a DSO with a base address that differs from its own, then caches +// the stat info for said path. +// This is to support the libcapsule-proxies-will-not-reopen-themselves +// infinite loop prevention mechanism. +static const struct stat * +stat_caller (void) +{ + static struct stat cdso = { 0 }; + static int done = 0; + + Dl_info self = { 0 }; + char caller[PATH_MAX] = { '\0' }; + + if( !done && dladdr( ld_libs_init, &self ) ) + { + void *trace[16] = { NULL }; + Dl_info dso = { 0 }; + void *origin = self.dli_fbase; + int traced; + + traced = backtrace( trace, N_ELEMENTS( trace ) ); + + for( int x = 0; x < traced && caller[0] == '\0'; x++ ) + if( dladdr( trace[x], &dso ) ) + if( origin != dso.dli_fbase ) + safe_strncpy( caller, dso.dli_fname, PATH_MAX ); + + done = 1; + + if( caller[0] == '\0' ) + { + DEBUG( DEBUG_SEARCH|DEBUG_ELF, + "unable to test for infinitely-looped capsule, " + "proceeding anyway" ); + } + else + { + DEBUG( DEBUG_SEARCH|DEBUG_ELF, + "initial libcapsule user is %s", caller ); + if( soname_matches_path( "libc.so", caller ) ) + { + // if we get back to here we're looking at a normal + // library from one of the build helper tools and + // we don't care about loop protection: + DEBUG( DEBUG_SEARCH|DEBUG_ELF, + " … which is not a capsule: disabling loop protection" ); + } + else + { + stat( caller, &cdso ); + } + } + } + + return (cdso.st_size > 0) ? &cdso : NULL; +} + +// map the ld.so.cache for the system into memory so that we can search it +// for DSOs in the same way as the dynamic linker. +// +// returns true on success, false otherwise. +// +// this function respects any path prefix specified in ldlibs +int +ld_libs_load_cache (ld_libs *libs, int *code, char **message) +{ + char prefixed[PATH_MAX] = { '\0' }; + size_t i; + + for( i = 0; ld_cache_filenames[i] != NULL; i++ ) + { + if( message != NULL ) + _capsule_clear( message ); + + if( build_filename( prefixed, sizeof(prefixed), libs->prefix.path, + ld_cache_filenames[i], NULL ) >= sizeof(prefixed) ) + { + _capsule_set_error( code, message, ENAMETOOLONG, + "Cannot append \"%s\" to prefix \"%s\": too long", + ld_cache_filenames[i], libs->prefix.path ); + continue; + } + + if( ld_cache_open( &libs->ldcache, prefixed, code, message ) ) + return 1; + } + + // return the last error + return 0; +} diff --git a/subprojects/libcapsule/utils/ld-libs.h b/subprojects/libcapsule/utils/ld-libs.h new file mode 100644 index 0000000000000000000000000000000000000000..1fa7acfb8aeffe16c07436dbf4f6583a07b1acbb --- /dev/null +++ b/subprojects/libcapsule/utils/ld-libs.h @@ -0,0 +1,110 @@ +// Copyright © 2017 Collabora Ltd + +// This file is part of libcapsule. + +// libcapsule is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as +// published by the Free Software Foundation; either version 2.1 of the +// License, or (at your option) any later version. + +// libcapsule 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 Lesser General Public License for more details. + +// You should have received a copy of the GNU Lesser General Public +// License along with libcapsule. If not, see <http://www.gnu.org/licenses/>. + +#pragma once + +#include <limits.h> +#include <link.h> + +#include <libelf.h> +#include <gelf.h> + +#include "ld-cache.h" + +// we only handle up to this many library dependencies - +// yes, hardwired limits are bad but there's already enough complexity +// here - can revisit this decision if it ever becomes close to being +// an issue (shouldn't affect the api or abi): +#define DSO_LIMIT 256 + +/* + * dso_needed_t: + * @fd: @path opened for reading (only valid after ld_lib_open()) + * @name: The name we are looking for, either a bare SONAME or an + * absolute path (only valid after ld_lib_open()) + * @path: Absolute path to the library we need to load, including the + * %CAPSULE_PREFIX if required (must be set before ld_lib_open()) + * @requestors: `needed[i].requestors[j]` is 1 if `needed[j]` + * depends on `needed[i]` + * @depcount: the number of libraries that this one depends on, + * such that `needed[j].depcount` is the number of nonzero + * `needed[i].requestors[j]` for each value of *i* where `needed[i]` + * has not yet been loaded + * @dso: @fd opened for ELF inspection (only valid after ld_lib_open()) + * + * A library that we need to load. + */ +typedef struct +{ + int fd; + char *name; + char path[PATH_MAX]; + int requestors[DSO_LIMIT]; + int depcount; + Elf *dso; +} dso_needed_t; + +/** + * ld_libs: + * @ldcache: the runtime linker cache, or all-zeroes + * if ld_libs_load_cache() has not yet been called + * @last_idx: private, used internally by the ld-libs code + * @elf_class: the ELF class of the caller that initialized this + * @elf_machine: the ELF machine type of the caller that initialized this + * @prefix: the sysroot from which we will load encapsulated libraries + * @exclude: (array zero-terminated=1) (nullable): libraries to ignore + * @needed: private, used internally by the ld-libs code. + * needed[0] is the library we are looking for, and needed[1...] + * are the libraries in its recursive dependency tree. + * @not_found: (transfer full): private, used internally by the ld-libs + * code. Each item is a copy of the name of a missing dependency. + * @last_not_found: private, used internally by the ld-libs code. + * Number of items in @not_found used. + * @debug: The debug flags passed to ld_libs_init() + * + * Data structure representing the libraries used in a capsule. + */ +typedef struct +{ + ld_cache ldcache; + int last_idx; + int elf_class; + Elf64_Half elf_machine; + struct { char path[PATH_MAX]; size_t len; } prefix; + const char **exclude; + dso_needed_t needed[DSO_LIMIT]; + char *not_found[DSO_LIMIT]; + int last_not_found; + unsigned long debug; +} ld_libs; + +int ld_libs_init (ld_libs *ldlibs, + const char **exclude, + const char *prefix, + unsigned long dbg, + int *error, + char **message); + +int ld_libs_set_target (ld_libs *ldlibs, const char *target, + int *code, char **message); +int ld_libs_find_dependencies (ld_libs *ldlibs, int *code, char **message); +void ld_libs_finish (ld_libs *ldlibs); +int ld_libs_load_cache (ld_libs *libs, int *code, char **message); + +void *ld_libs_load (ld_libs *ldlibs, Lmid_t *namespace, int flag, int *error, + char **message); + diff --git a/subprojects/libcapsule/utils/library-cmp.c b/subprojects/libcapsule/utils/library-cmp.c new file mode 100644 index 0000000000000000000000000000000000000000..8aeed8024f291e235cfefccdb4214ac9ac2b4601 --- /dev/null +++ b/subprojects/libcapsule/utils/library-cmp.c @@ -0,0 +1,1618 @@ +// Copyright © 2020 Collabora Ltd + +// This file is part of libcapsule. + +// libcapsule is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as +// published by the Free Software Foundation; either version 2.1 of the +// License, or (at your option) any later version. + +// libcapsule 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 Lesser General Public License for more details. + +// You should have received a copy of the GNU Lesser General Public +// License along with libcapsule. If not, see <http://www.gnu.org/licenses/>. + +#include "library-cmp.h" + +#include <err.h> +#include <errno.h> +#include <fcntl.h> +#include <fnmatch.h> +#include <search.h> +#include <stdbool.h> +#include <stdio.h> +#include <string.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> + +#include <libelf.h> +#include <gelf.h> + +#include "debug.h" +#include "utils.h" + +// From binutils/include/elf/common.h (this doesn't appear to be documented +// anywhere else). +// +// /* This flag appears in a Versym structure. It means that the symbol +// is hidden, and is only visible with an explicit version number. +// This is a GNU extension. */ +// #define VERSYM_HIDDEN 0x8000 +// +// /* This is the mask for the rest of the Versym information. */ +// #define VERSYM_VERSION 0x7fff + +#define VERSYM_HIDDEN 0x8000 +#define VERSYM_VERSION 0x7fff + +static int +bsearch_strcmp_cb( const void *n, const void *ip ) +{ + const char *needle = n; + const char * const *item_p = ip; + return strcmp( needle, *item_p ); +} + +static int +qsort_strcmp_cb( const void* s1, const void* s2 ) +{ + const char * const *a = (const char* const*) s1; + const char* const *b = (const char* const*) s2; + return strcmp( *a, *b ); +} + +/* + * string_set_diff_flags: + * @STRING_SET_DIFF_ONLY_IN_FIRST: At least one element is in the first set but not the second + * @STRING_SET_DIFF_ONLY_IN_SECOND: At least one element is in the second set but not the first + * @STRING_SET_DIFF_NONE: All elements are equal + * + * The result of comparing two sets of strings. If each set + * contains elements that the other does not, then + * both @STRING_SET_DIFF_ONLY_IN_FIRST + * and @STRING_SET_DIFF_ONLY_IN_SECOND will be set. + */ +typedef enum +{ + STRING_SET_DIFF_ONLY_IN_FIRST = (1 << 0), + STRING_SET_DIFF_ONLY_IN_SECOND = (1 << 1), + STRING_SET_DIFF_NONE = 0 +} string_set_diff_flags; + +/* + * compare_string_sets: + * @first: the first set to compare + * @first_length: number of elements in the first set + * @second: the second set to compare + * @second_length: number of elements in the second set + * + * The two sets needs to be ordered because we will use a binary search to do + * the comparison. + */ +static string_set_diff_flags +compare_string_sets ( char **first, size_t first_length, + char **second, size_t second_length ) +{ + string_set_diff_flags result = STRING_SET_DIFF_NONE; + + assert( first != NULL ); + assert( second != NULL ); + + if( first_length > second_length ) + { + result |= STRING_SET_DIFF_ONLY_IN_FIRST; + } + else + { + for( size_t i = 0; i < first_length; i++ ) + { + char *found = bsearch( first[i], second, second_length, sizeof(char *), bsearch_strcmp_cb ); + if( found == NULL ) + { + result |= STRING_SET_DIFF_ONLY_IN_FIRST; + break; + } + } + } + + if( first_length < second_length ) + { + result |= STRING_SET_DIFF_ONLY_IN_SECOND; + } + else + { + for( size_t i = 0; i < second_length; i++ ) + { + char *found = bsearch( second[i], first, first_length, sizeof(char *), bsearch_strcmp_cb ); + if( found == NULL ) + { + result |= STRING_SET_DIFF_ONLY_IN_SECOND; + break; + } + } + } + return result; +} + +static void +close_elf (Elf **elfp, int *fdp) +{ + if (elfp != NULL && *elfp != NULL) + { + elf_end( *elfp ); + *elfp = NULL; + } + + if (fdp != NULL && *fdp >= 0) + { + close( *fdp ); + *fdp = -1; + } +} + +/* + * open_elf_library: + * @path: (type filename): The path where the library is located + * @fd: (out) (not optional): Used to return a file descriptor of the + * opened library + * @elf: (out) (not optional): Used to return an initialized elf of the + * library + * @code: (out) (optional): Used to return an error code on failure + * @message: (out) (optional) (nullable): Used to return an error message + * on failure + * + * Returns: %TRUE if the elf opening succeded, %FALSE otherwise. + */ +static bool +open_elf_library ( const char *path, int *fd, Elf **elf, + int *code, char **message) +{ + GElf_Ehdr ehdr; + bool result = true; + + assert( *elf == NULL ); + + if( elf_version(EV_CURRENT) == EV_NONE ) + { + _capsule_set_error( code, message, EINVAL, + "elf_version(EV_CURRENT): %s", + elf_errmsg( elf_errno() ) ); + result = false; + goto out; + } + + if( ( *fd = open( path, O_RDONLY | O_CLOEXEC, 0 ) ) < 0 ) + { + _capsule_set_error( code, message, EINVAL, + "failed to open %s", path ); + result = false; + goto out; + } + + if( ( *elf = elf_begin( *fd, ELF_C_READ, NULL ) ) == NULL ) + { + _capsule_set_error( code, message, EINVAL, + "elf_begin() failed: %s", + elf_errmsg( elf_errno() ) ); + result = false; + goto out; + } + + if( elf_kind( *elf ) != ELF_K_ELF ) + { + _capsule_set_error( code, message, EINVAL, + "%s is not in ELF format", path ); + result = false; + goto out; + } + + if( gelf_getehdr( *elf, &ehdr ) == NULL ) + { + _capsule_set_error( code, message, EINVAL, + "gelf_getehdr() failed: %s", + elf_errmsg( elf_errno() ) ); + result = false; + goto out; + } + if( ehdr.e_type != ET_DYN ) + { + _capsule_set_error( code, message, EINVAL, + "%s is not a shared library, elf type is %d", + path, ehdr.e_type ); + result = false; + goto out; + } + +out: + if( !result ) + close_elf( elf, fd ); + + return result; +} + +static void +print_debug_string_list( char **list, const char *begin_message ) +{ + if( list == NULL || list[0] == NULL ) + return; + + if( begin_message != NULL ) + DEBUG( DEBUG_ELF, "%s", begin_message ); + + for( size_t i = 0; list[i] != NULL; i++ ) + DEBUG( DEBUG_ELF, "%s", list[i] ); +} + +static int +library_details_cmp( const void *pa, const void *pb ) +{ + const library_details *a = pa; + const library_details *b = pb; + + return strcmp( a->name, b->name ); +} + +static void +library_details_free( library_details *self ) +{ + free_strv_full( self->public_symbol_versions ); + free_strv_full( self->public_symbols ); + free( self->comparators ); + free( self->name ); + free( self ); +} + +static void +library_details_free_generic( void *p ) +{ + library_details_free( p ); +} + +void +library_knowledge_clear( library_knowledge *self ) +{ + if( self->tree != NULL ) + tdestroy( self->tree, library_details_free_generic ); + + self->tree = NULL; +} + +/* + * library_knowledge_lookup: + * @self: Details of all known libraries + * @library: The name of a library, normally the SONAME + * + * Returns: Details of @library, or %NULL if nothing is known about it + */ +const library_details * +library_knowledge_lookup( const library_knowledge *self, + const char *library ) +{ + const library_details key = { (char *) library, NULL }; + library_details **node; + + node = tfind( &key, &self->tree, library_details_cmp ); + + if( node == NULL ) + return NULL; + + return *node; +} + +/* + * library_cmp_split_string_by_delimiters: + * @spec: A string of elements separated by @delimiters + * @delimiters: Any character from this set separates the elements in @spec + * + * Parse a string of elements into an array. + * + * Returns: (transfer full): An array of the elements from @spec + */ +static char ** +library_cmp_split_string_by_delimiters( const char *spec, + const char *delimiters ) +{ + char **ret = NULL; + char *buf = xstrdup( spec ); + char *saveptr = NULL; + char *token; + ptr_list *list; + + /* Start with an arbitrary size of 8 */ + list = ptr_list_alloc( 8 ); + + for( token = strtok_r( buf, delimiters, &saveptr ); + token != NULL; + token = strtok_r( NULL, delimiters, &saveptr ) ) + { + if( token[0] == '\0' ) + continue; + + ptr_list_push_ptr( list, xstrdup( token ) ); + } + + ret = (char **) ptr_list_free_to_array( _capsule_steal_pointer( &list ), NULL ); + + free( buf ); + return ret; +} + +/* + * library_knowledge_load_from_stream: + * @stream: A stream + * @name: The filename of @stream or a placeholder, for diagnostic messages + * @code: (out) (optional): Set to an `errno` value on failure + * @message: (out) (optional): Set to an error message on failure + * + * Load knowledge of libraries from a stream. It is merged with anything + * previously known by @self, with the new version preferred (so if there + * are multiple files containing library knowledge, they should be loaded + * in least-important-first order). + * + * Returns: true on success + */ +bool +library_knowledge_load_from_stream( library_knowledge *self, + FILE *stream, const char *name, + int *code, char **message ) +{ + library_details *current = NULL; + bool in_a_section = false; + bool ok = false; + int line_number = 0; + char *line = NULL; + size_t buf_len = 0; + ssize_t len; + + while( ( len = getline( &line, &buf_len, stream ) ) >= 0 ) + { + line_number++; + + if( len == 0 || *line == '\0' || *line == '\n' || *line == '#' ) + continue; + + if( line[len - 1] == '\n' ) + { + line[--len] = '\0'; + } + + if( *line == '[' ) + { + library_details **node; + + // new section + if( line[len - 1] != ']' ) + { + _capsule_set_error( code, message, EINVAL, + "%s:%d: Invalid section heading \"%s\"", + name, line_number, line ); + goto out; + } + + // Wipe out the ']' + line[--len] = '\0'; + + if( strstarts( line, "[Library " ) ) + { + current = xcalloc( 1, sizeof( library_details ) ); + current->name = xstrdup( line + strlen( "[Library " ) ); + current->comparators = NULL; + + node = tsearch( current, &self->tree, library_details_cmp ); + + if( node == NULL ) + { + oom(); + } + else if( *node != current ) + { + // we have seen this one before: keep the existing version + library_details_free( current ); + current = *node; + } + // else self->tree takes ownership of current + } + // TODO: Future expansion: we could have glob matches if we + // want them, for example [Match libGLX_*.so.0] + else + { + DEBUG( DEBUG_TOOL, "Ignoring unknown section heading \"%s\"", line + 1 ); + current = NULL; + } + + in_a_section = true; + } + else if( !in_a_section ) + { + _capsule_set_error( code, message, EINVAL, + "%s:%d: Unexpected line not in a section: \"%s\"", + name, line_number, line ); + goto out; + } + else if( current != NULL && strstarts( line, "CompareBy=" ) ) + { + char *values = line + strlen( "CompareBy=" ); + + free( current->comparators ); + current->comparators = library_cmp_list_from_string( values, ";", + code, message ); + + if( current->comparators == NULL ) + { + if( message != NULL ) + { + // Prepend file:line: before failing + char *tmp = *message; + + *message = NULL; + + if( asprintf( message, "%s:%d: %s", name, line_number, tmp ) < 0 ) + oom(); + + free( tmp ); + } + + goto out; + } + } + else if( current != NULL && strstarts( line, "PublicSymbolVersions=" ) ) + { + char *values = line + strlen( "PublicSymbolVersions=" ); + free_strv_full( current->public_symbol_versions ); + + current->public_symbol_versions = library_cmp_split_string_by_delimiters( values, + ";" ); + } + else if( current != NULL && strstarts( line, "PublicSymbols=" ) ) + { + char *values = line + strlen( "PublicSymbols=" ); + free_strv_full( current->public_symbols ); + + current->public_symbols = library_cmp_split_string_by_delimiters( values, + ";" ); + } + else if( strchr( line, '=' ) != NULL ) + { + DEBUG( DEBUG_TOOL, "%s:%d: Ignoring unknown key/value pair \"%s\"", + name, line_number, line ); + } + else + { + _capsule_set_error( code, message, EINVAL, + "%s:%d: Unexpected line not a key/value pair: \"%s\"", + name, line_number, line ); + goto out; + } + } + + ok = true; + +out: + free( line ); + + if( ok ) + { + DEBUG( DEBUG_TOOL, "Loaded library knowledge from \"%s\"", name ); + } + else + { + DEBUG( DEBUG_TOOL, "Failed to load library knowledge from \"%s\"", name ); + } + + return ok; +} + +/* + * get_versions: + * @elf: The object's elf of which we want to get the versions + * @versions_number: (out) (not optional): The number of versions found + * @code: (out) (optional): Used to return an error code on + * failure + * @message: (out) (optional) (nullable): Used to return an error message + * on failure + * + * Returns: (transfer full): The list of versions that the + * shared object has, on failure %NULL. + */ +static char ** +get_versions( Elf *elf, size_t *versions_number, int *code, char **message ) +{ + char **versions = NULL; + Elf_Scn *scn = NULL; + Elf_Data *data; + GElf_Shdr shdr_mem; + GElf_Shdr *shdr = NULL; + GElf_Verdef def_mem; + GElf_Verdef *def; + bool found_verdef = false; + uintptr_t verdef_ptr = 0; + size_t auxoffset; + size_t offset = 0; + size_t phnum; + size_t sh_entsize; + ptr_list *versions_list = NULL; + + assert( versions_number != NULL ); + + *versions_number = 0; + + if( elf_getphdrnum( elf, &phnum ) < 0 ) + { + _capsule_set_error( code, message, EINVAL, + "Unable to determine the number of program headers: %s", + elf_errmsg( elf_errno() ) ); + return versions; + } + + /* Get the dynamic section */ + for( size_t i = 0; i < phnum; i++ ) + { + GElf_Phdr phdr_mem; + GElf_Phdr *phdr = gelf_getphdr( elf, i, &phdr_mem ); + if( phdr != NULL && phdr->p_type == PT_DYNAMIC ) + { + scn = gelf_offscn( elf, phdr->p_offset ); + shdr = gelf_getshdr( scn, &shdr_mem ); + break; + } + } + + if( shdr == NULL ) + { + int err = elf_errno(); + if( err == 0 ) + { + _capsule_set_error( code, message, EINVAL, + "Unable to find the section header from the dynamic section" ); + } + else + { + _capsule_set_error( code, message, EINVAL, + "Unable to get the section header: %s", + elf_errmsg( err ) ); + } + + return versions; + } + + data = elf_getdata( scn, NULL ); + if( data == NULL ) + { + _capsule_set_error( code, message, EINVAL, + "Unable to get the dynamic section data: %s", + elf_errmsg( elf_errno() ) ); + return versions; + } + + sh_entsize = gelf_fsize( elf, ELF_T_DYN, 1, EV_CURRENT ); + for( size_t i = 0; i < shdr->sh_size / sh_entsize; i++ ) + { + GElf_Dyn dyn_mem; + GElf_Dyn *dyn = gelf_getdyn( data, i, &dyn_mem ); + if( dyn == NULL ) + break; + + if( dyn->d_tag == DT_VERDEF ) + { + verdef_ptr = dyn->d_un.d_ptr; + found_verdef = true; + break; + } + } + + if( !found_verdef ) + { + DEBUG( DEBUG_ELF, "The version definition table is not available" ); + versions = calloc( 1, sizeof(char *) ); + versions[0] = NULL; + return versions; + } + scn = gelf_offscn( elf, verdef_ptr ); + data = elf_getdata( scn, NULL ); + if( data == NULL ) + { + _capsule_set_error( code, message, EINVAL, + "Unable to get symbols data: %s", elf_errmsg( elf_errno() ) ); + return versions; + } + + def = gelf_getverdef( data, 0, &def_mem ); + if( def == NULL ) + { + DEBUG( DEBUG_ELF, "Verdef is not available: %s", elf_errmsg( elf_errno() ) ); + versions = calloc( 1, sizeof(char *) ); + versions[0] = NULL; + return versions; + } + + /* Arbitrarily start the list with 8 elements */ + versions_list = ptr_list_alloc( 8 ); + while( def != NULL ) + { + GElf_Verdaux aux_mem, *aux; + const char *version; + + auxoffset = offset + def->vd_aux; + offset += def->vd_next; + + /* The first Verdaux array must exist and it points to the version + * definition string that Verdef defines. Every possible additional + * Verdaux arrays are the dependencies of said version definition. + * In our case we don't need to list the dependencies, so we just + * get the first Verdaux of every Verdef. */ + aux = gelf_getverdaux( data, auxoffset, &aux_mem ); + if( aux == NULL ) + continue; + version = elf_strptr( elf, shdr->sh_link, aux->vda_name ); + if( version == NULL ) + continue; + + if( ( def->vd_flags & VER_FLG_BASE ) == 0 ) + ptr_list_push_ptr( versions_list, strdup( version ) ); + + if( def->vd_next == 0 ) + def = NULL; + else + def = gelf_getverdef( data, offset, &def_mem ); + + } + + versions = (char **) ptr_list_free_to_array ( versions_list, versions_number ); + qsort( versions, *versions_number, sizeof(char *), qsort_strcmp_cb ); + return versions; +} + +static const char * const ignore_symbols[] = +{ + /* Libraries on at least SteamOS 2 'brewmaster' sometimes have + * symbols that appear to have an empty name. */ + "", + + /* These symbols can appear in libraries witout actually being part + * of anyone's ABI. List taken from dpkg-gensymbols. */ + "__bss_end__", + "__bss_end", + "_bss_end__", + "__bss_start", + "__bss_start__", + "__data_start", + "__do_global_ctors_aux", + "__do_global_dtors_aux", + "__do_jv_register_classes", + "_DYNAMIC", + "_edata", + "_end", + "__end__", + "__exidx_end", + "__exidx_start", + "_fbss", + "_fdata", + "_fini", + "_ftext", + "_GLOBAL_OFFSET_TABLE_", + "__gmon_start__", + "__gnu_local_gp", + "_gp", + "_init", + "_PROCEDURE_LINKAGE_TABLE_", + "_SDA2_BASE_", + "_SDA_BASE_", +}; + +/* + * get_symbols: + * @elf: The object's elf of which we want to get the symbols + * @symbols_number: (out) (not optional): The number of symbols found + * @code: (out) (optional): Used to return an error code on + * failure + * @message: (out) (optional) (nullable): Used to return an error message + * on failure + * + * Returns: (transfer full): The list of symbols that the + * shared object has, on failure %NULL. + */ +static char ** +get_symbols ( Elf *elf, size_t *symbols_number, int *code, char **message ) +{ + char **symbols = NULL; + Elf_Scn *scn = NULL; + Elf_Scn *scn_sym = NULL; + Elf_Scn *scn_ver = NULL; + Elf_Scn *scn_verdef = NULL; + Elf_Data *data; + Elf_Data *sym_data; + Elf_Data *versym_data = NULL; + Elf_Data *verdef_data = NULL; + GElf_Ehdr ehdr; + GElf_Shdr shdr_mem; + GElf_Shdr *shdr = NULL; + bool found_symtab = false; + bool found_versym = false; + bool found_verdef = false; + uintptr_t symtab_ptr = 0; + uintptr_t versym_ptr = 0; + uintptr_t verdef_ptr = 0; + size_t elsize = 0; + size_t phnum; + size_t sh_entsize; + ptr_list *symbols_list = NULL; + + assert( symbols_number != NULL ); + + *symbols_number = 0; + + if( elf_getphdrnum( elf, &phnum ) < 0 ) + { + _capsule_set_error( code, message, EINVAL, + "Unable to determine the number of program headers: %s", + elf_errmsg( elf_errno() ) ); + return symbols; + } + + /* Get the dynamic section */ + for( size_t i = 0; i < phnum; i++ ) + { + GElf_Phdr phdr_mem; + GElf_Phdr *phdr = gelf_getphdr( elf, i, &phdr_mem ); + if( phdr != NULL && phdr->p_type == PT_DYNAMIC ) + { + scn = gelf_offscn( elf, phdr->p_offset ); + shdr = gelf_getshdr( scn, &shdr_mem ); + break; + } + } + + if( shdr == NULL ) + { + int err = elf_errno(); + if( err == 0 ) + { + _capsule_set_error( code, message, EINVAL, + "Unable to find the section header from the dynamic section" ); + } + else + { + _capsule_set_error( code, message, EINVAL, + "Unable to get the section header: %s", + elf_errmsg( err ) ); + } + + return symbols; + } + + data = elf_getdata( scn, NULL ); + if( data == NULL ) + { + _capsule_set_error( code, message, EINVAL, + "Unable to get dynamic section data: %s", elf_errmsg( elf_errno() ) ); + return symbols; + } + + sh_entsize = gelf_fsize( elf, ELF_T_DYN, 1, EV_CURRENT ); + + for( size_t i = 0; i < shdr->sh_size / sh_entsize; i++ ) + { + GElf_Dyn dyn_mem; + GElf_Dyn *dyn = gelf_getdyn (data, i, &dyn_mem); + if( dyn == NULL ) + break; + + switch( dyn->d_tag ) + { + case DT_SYMTAB: + symtab_ptr = dyn->d_un.d_ptr; + found_symtab = true; + break; + case DT_VERSYM: + versym_ptr = dyn->d_un.d_ptr; + found_versym = true; + break; + case DT_VERDEF: + verdef_ptr = dyn->d_un.d_ptr; + found_verdef = true; + break; + default: + break; + } + } + + + if( !found_symtab ) + { + _capsule_set_error( code, message, EINVAL, "Unable to find the symbols table" ); + return symbols; + } + scn_sym = gelf_offscn( elf, symtab_ptr ); + sym_data = elf_getdata( scn_sym, NULL ); + if( sym_data == NULL ) + { + _capsule_set_error( code, message, EINVAL, + "Unable to get symbols table data: %s", elf_errmsg( elf_errno() ) ); + return symbols; + } + + if( found_versym ) + { + scn_ver = gelf_offscn( elf, versym_ptr ); + versym_data = elf_getdata( scn_ver, NULL ); + if( versym_data == NULL ) + { + _capsule_set_error( code, message, EINVAL, + "Unable to get symbols version information data: %s", + elf_errmsg( elf_errno() ) ); + return symbols; + } + } + + if( found_verdef ) + { + scn_verdef = gelf_offscn( elf, verdef_ptr ); + verdef_data = elf_getdata( scn_verdef, NULL ); + if( verdef_data == NULL ) + { + _capsule_set_error( code, message, EINVAL, + "Unable to get symbols version definition data: %s", + elf_errmsg( elf_errno() ) ); + return symbols; + } + } + + if( gelf_getehdr( elf, &ehdr ) == NULL ) + { + _capsule_set_error( code, message, EINVAL, + "Unable to retrieve Ehdr header: %s", + elf_errmsg( elf_errno() ) ); + return symbols; + } + + elsize = gelf_fsize( elf, ELF_T_SYM, 1, ehdr.e_version ); + if( elsize == 0 ) + { + _capsule_set_error( code, message, EINVAL, + "Size of symbols in Ehdr array is zero: %s", + elf_errmsg( elf_errno() ) ); + return symbols; + } + + /* Arbitrarily start the list with 8 elements */ + symbols_list = ptr_list_alloc( 8 ); + + for( size_t index = 0; index < sym_data->d_size / elsize; index++ ) + { + GElf_Sym *sym; + GElf_Sym sym_mem; + const char *symbol; + GElf_Versym versym_mem; + GElf_Versym *versym; + GElf_Verdaux aux_mem; + GElf_Verdaux *aux = NULL; + GElf_Verdef def_mem; + GElf_Verdef *def = NULL; + bool interesting = true; + + sym = gelf_getsymshndx( sym_data, NULL, index, &sym_mem, NULL ); + + /* If the symbol info is not available, or undefined, we skip it */ + if( sym == NULL || sym->st_shndx == SHN_UNDEF ) + continue; + + symbol = elf_strptr( elf, shdr->sh_link, sym->st_name ); + if( symbol == NULL ) + break; + + for( size_t i = 0; i < N_ELEMENTS( ignore_symbols ); i++ ) + { + if( strcmp( symbol, ignore_symbols[i] ) == 0 ) + { + interesting = false; + break; + } + } + + if ( !interesting ) + { + DEBUG( DEBUG_ELF, "Symbol '%s' is uninteresting", symbol ); + continue; + } + + /* Search the version of the symbol */ + if( found_versym && found_verdef ) + { + versym = gelf_getversym( versym_data, index, &versym_mem ); + def = gelf_getverdef( verdef_data, 0, &def_mem ); + + size_t auxoffset; + size_t offset = 0; + while( def != NULL ) + { + auxoffset = offset + def->vd_aux; + offset += def->vd_next; + + /* The first Verdaux array must exist and it points to the version + * definition string that Verdef defines. Every possible additional + * Verdaux arrays are the dependencies of said version definition. + * In our case we don't need to list the dependencies, so we just + * get the first Verdaux of every Verdef. */ + aux = gelf_getverdaux( verdef_data, auxoffset, &aux_mem ); + if( aux == NULL ) + continue; + + /* Break if we have found the symbol's version */ + if( def->vd_ndx == ( *versym & VERSYM_VERSION ) ) + break; + + if( def->vd_next == 0 ) + def = NULL; + else + def = gelf_getverdef( verdef_data, offset, &def_mem ); + } + } + + /* If the symbol is versioned */ + if( def != NULL && aux != NULL ) + { + const char *version = elf_strptr( elf, shdr->sh_link, aux->vda_name ); + char *symbol_versioned; + xasprintf( &symbol_versioned, "%s@%s", symbol, version ); + ptr_list_push_ptr( symbols_list, symbol_versioned ); + } + else + { + ptr_list_push_ptr( symbols_list, strdup( symbol ) ); + } + } + + symbols = (char **) ptr_list_free_to_array ( symbols_list, symbols_number ); + qsort( symbols, *symbols_number, sizeof(char *), qsort_strcmp_cb ); + return symbols; +} + +/* + * library_cmp_by_name: + * @details: The library we are interested in and how to compare it + * @left_path: The path to the "left" instance of the library + * @left_from: Arbitrary description of the container/provider/sysroot + * where we found @left_path, used in debug logging + * @right_path: The path to the "right" instance of the library + * @right_from: Arbitrary description of the container/provider/sysroot + * where we found @right_path, used in debug logging + * + * Attempt to determine whether @left_path is older than, newer than or + * the same as than @right_path by inspecting their filenames. + * + * Return a strcmp-style result: negative if left < right, + * positive if left > right, zero if left == right or if left and right + * are non-comparable. + */ +static int +library_cmp_by_name( const library_details *details, + const char *left_path, + const char *left_from, + const char *right_path, + const char *right_from ) +{ + _capsule_autofree char *left_realpath = NULL; + _capsule_autofree char *right_realpath = NULL; + const char *left_basename; + const char *right_basename; + + // This might look redundant when our arguments come from the ld_libs, + // but resolve_symlink_prefixed() doesn't chase symlinks if the + // prefix is '/' or empty. + left_realpath = realpath( left_path, NULL ); + right_realpath = realpath( right_path, NULL ); + left_basename = _capsule_basename( left_realpath ); + right_basename = _capsule_basename( right_realpath ); + + DEBUG( DEBUG_TOOL, + "Comparing %s \"%s\" from \"%s\" with " + "\"%s\" from \"%s\"", + details->name, left_basename, left_from, right_basename, right_from ); + + if( strcmp( left_basename, right_basename ) == 0 ) + { + DEBUG( DEBUG_TOOL, + "Name of %s \"%s\" from \"%s\" compares the same as " + "\"%s\" from \"%s\"", + details->name, left_basename, left_from, right_basename, right_from ); + return 0; + } + + if( strcmp( details->name, left_basename ) == 0 ) + { + /* In some distributions (Debian, Ubuntu, Manjaro) + * libgcc_s.so.1 is a plain file, not a symlink to a + * version-suffixed version. We cannot know just from the name + * whether that's older or newer, so assume equal. The caller is + * responsible for figuring out which one to prefer. */ + DEBUG( DEBUG_TOOL, + "Unversioned %s \"%s\" from \"%s\" cannot be compared with " + "\"%s\" from \"%s\"", + details->name, left_basename, left_from, + right_basename, right_from ); + return 0; + } + + if( strcmp( details->name, right_basename ) == 0 ) + { + /* The same, but the other way round */ + DEBUG( DEBUG_TOOL, + "%s \"%s\" from \"%s\" cannot be compared with " + "unversioned \"%s\" from \"%s\"", + details->name, left_basename, left_from, + right_basename, right_from ); + return 0; + } + + return ( strverscmp( left_basename, right_basename ) ); +} + +/* + * library_cmp_filter_list: + * @filters: The list of patterns that will be checked against @list. + * Patterns that start with '!' are considered negated (privates), + * i.e. the elements in @list, that matches said pattern, will be removed. + * A pattern that is just '!' is used to separate what's known to the + * guessing. + * @list: (array zero-terminated=1): The list to filter + * @list_number: The number of elements in @list excluding the %NULL + * terminator + * @filtered_list_number_out: (out): Used to return the number of elements in + * the newly created filtered list excluding the %NULL terminator + * + * Creates a new filtered list starting from the given @list and applying the + * patterns in @filters. + * The patterns are evaluated in order. + * If there are elements that don't match any of the provided filters, or they + * match a filter after the special '!' pattern, a warning will be printed. + * The default filter behavior for elements that don't match any patterns + * is to exclude them (treat as private). However it is higly recommended + * to be explicit and end @filters with a wildcard allow everything "*", + * or reject everything "!*". + * + * Returns: (array zero-terminated=1) (transfer full): An array with + * the elements that matches the provided @filters. + */ +static char ** +library_cmp_filter_list( char ** const filters, + char ** const list, + size_t list_number, + size_t *filtered_list_number_out ) +{ + ptr_list *filtered_list; + size_t i = 0; + size_t j = 0; + bool guessing = false; + char *buf = NULL; + char *token; + filtered_list = ptr_list_alloc( list_number ); + + assert( filters != NULL ); + assert( list != NULL ); + assert( filtered_list_number_out != NULL ); + + *filtered_list_number_out = 0; + + for( i = 0; list[i] != NULL; i++ ) + { + char *saveptr = NULL; + guessing = false; + + free( buf ); + buf = xstrdup( list[i] ); + /* If we have a versioned symbol, like "symbol@version", remove the + * version part because the filters are just for the symbols name. */ + token = strtok_r( buf, "@", &saveptr ); + + for( j = 0; filters[j] != NULL; j++ ) + { + if( strcmp( filters[j], "!" ) == 0 ) + { + DEBUG( DEBUG_TOOL, "After this point we are just guessing" ); + guessing = true; + continue; + } + + if( filters[j][0] == '!' ) + { + if( fnmatch( filters[j] + 1, token, 0 ) == 0 ) + { + if( guessing ) + warnx( "warning: we are assuming \"%s\" to be private, but it's just a guess", + token ); + else + DEBUG( DEBUG_TOOL, + "Ignoring \"%s\" because it has been declared as private", + token ); + break; + } + } + else + { + if( fnmatch( filters[j], token, 0 ) == 0 ) + { + if( guessing ) + warnx( "warning: we are assuming \"%s\" to be public, but it's just a guess", + token ); + + ptr_list_push_ptr( filtered_list, xstrdup( list[i] ) ); + break; + } + } + } + /* If we checked all the patterns and didn't have a match */ + if( filters[j] == NULL ) + warnx( "warning: \"%s\" does not have a match in the given filters, treating it as private", + token ); + } + + free( buf ); + + return (char **) ptr_list_free_to_array( _capsule_steal_pointer( &filtered_list ), + filtered_list_number_out ); +} + +/* + * library_cmp_by_symbols: + * @details: The library we are interested in and how to compare it + * @container_path: (type filename): The path where the container's soname is + * located + * @provider_path: (type filename): The path where the provider's soname is + * located + * + * Attempt to determine whether soname is older, newer or + * the same in the container or the provider inspecting their + * symbols. + * + * Return a strcmp-style result: negative if container < provider, + * positive if container > provider, zero if container == provider + * or if container and provider are non-comparable. + */ +static int +library_cmp_by_symbols( const library_details *details, + const char *container_path, + const char *container_root, + const char *provider_path, + const char *provider_root ) +{ + string_set_diff_flags symbol_result = STRING_SET_DIFF_NONE; + int cmp_result = 0; + int container_fd = -1; + int provider_fd = -1; + Elf *container_elf = NULL; + Elf *provider_elf = NULL; + char **container_symbols = NULL; + char **provider_symbols = NULL; + size_t container_symbols_number = 0; + size_t provider_symbols_number = 0; + int code = 0; + char *message = NULL; + char *symbol_message = NULL; + + if( !open_elf_library( container_path, &container_fd, &container_elf, + &code, &message ) ) + { + DEBUG( DEBUG_TOOL, + "an error occurred while opening %s (%d): %s", + container_path, code, message ); + goto out; + } + + container_symbols = get_symbols( container_elf, &container_symbols_number, + &code, &message ); + + if( container_symbols == NULL ) + { + warnx( "failed to get container versions for %s (%d): %s", + details->name, code, message ); + goto out; + } + + if( details->public_symbols != NULL ) + { + char **container_symbols_filtered; + size_t container_symbols_number_filtered = 0; + container_symbols_filtered = library_cmp_filter_list( details->public_symbols, + container_symbols, + container_symbols_number, + &container_symbols_number_filtered ); + free_strv_full( container_symbols ); + + container_symbols = container_symbols_filtered; + container_symbols_number = container_symbols_number_filtered; + } + + xasprintf( &symbol_message, "Container Symbols of %s:", details->name ); + print_debug_string_list( container_symbols, symbol_message ); + _capsule_clear( &symbol_message ); + + if( !open_elf_library( provider_path, &provider_fd, &provider_elf, + &code, &message ) ) + { + DEBUG( DEBUG_TOOL, + "an error occurred while opening %s (%d): %s", + container_path, code, message ); + goto out; + } + + provider_symbols = get_symbols( provider_elf, &provider_symbols_number, + &code, &message ); + + if( provider_symbols == NULL ) + { + warnx( "failed to get provider versions for %s (%d): %s", + details->name, code, message ); + goto out; + } + + if( details->public_symbols != NULL ) + { + char **provider_symbols_filtered; + size_t provider_symbols_number_filtered = 0; + provider_symbols_filtered = library_cmp_filter_list( details->public_symbols, + provider_symbols, + provider_symbols_number, + &provider_symbols_number_filtered ); + free_strv_full( provider_symbols ); + + provider_symbols = provider_symbols_filtered; + provider_symbols_number = provider_symbols_number_filtered; + } + + xasprintf( &symbol_message, "Provider Symbols of %s:", details->name ); + print_debug_string_list( provider_symbols, symbol_message ); + _capsule_clear( &symbol_message ); + + symbol_result = compare_string_sets( container_symbols, container_symbols_number, + provider_symbols, provider_symbols_number ); + + /* In container we have strictly more symbols: don't symlink the one + * from the provider */ + if( symbol_result == STRING_SET_DIFF_ONLY_IN_FIRST ) + { + DEBUG( DEBUG_TOOL, + "%s in the container is newer because its symbols are a strict superset", + details->name ); + cmp_result = 1; + } + /* In provider we have strictly more symbols: create the symlink */ + else if( symbol_result == STRING_SET_DIFF_ONLY_IN_SECOND ) + { + DEBUG( DEBUG_TOOL, + "%s in the provider is newer because its symbols are a strict superset", + details->name ); + cmp_result = -1; + } + /* With the following two cases we are still unsure which library is newer, so we + * will choose the provider */ + else if( symbol_result == STRING_SET_DIFF_NONE ) + { + DEBUG( DEBUG_TOOL, + "%s in the container and the provider have the same symbols", + details->name ); + } + else + { + DEBUG( DEBUG_TOOL, + "%s in the container and the provider have different symbols and neither is a superset of the other", + details->name ); + } + +out: + _capsule_clear( &message ); + free_strv_full( container_symbols ); + free_strv_full( provider_symbols ); + close_elf( &container_elf, &container_fd ); + close_elf( &provider_elf, &provider_fd ); + return cmp_result; + +} + +/* + * library_cmp_by_versions: + * @details: The library we are interested in and how to compare it + * @container_path: (type filename): The path where the container's soname is + * located + * @provider_path: (type filename): The path where the provider's soname is + * located + * + * Attempt to determine whether soname is older, newer or + * the same in the container or the provider inspecting their + * symbols versions. + * + * Return a strcmp-style result: negative if container < provider, + * positive if container > provider, zero if container == provider + * or if container and provider are non-comparable. + */ +static int +library_cmp_by_versions( const library_details *details, + const char *container_path, + const char *container_root, + const char *provider_path, + const char *provider_root ) +{ + string_set_diff_flags version_result = STRING_SET_DIFF_NONE; + int cmp_result = 0; + int container_fd = -1; + int provider_fd = -1; + Elf *container_elf = NULL; + Elf *provider_elf = NULL; + char **container_versions = NULL; + char **provider_versions = NULL; + size_t container_versions_number = 0; + size_t provider_versions_number = 0; + int code = 0; + char *message = NULL; + char *version_message; + + if( !open_elf_library( container_path, &container_fd, &container_elf, + &code, &message ) ) + { + DEBUG( DEBUG_TOOL, + "an error occurred while opening %s (%d): %s", + container_path, code, message ); + goto out; + } + + container_versions = get_versions( container_elf, &container_versions_number, + &code, &message ); + + if( container_versions == NULL ) + { + warnx( "failed to get container versions for %s (%d): %s", + details->name, code, message ); + goto out; + } + + if( details->public_symbol_versions != NULL ) + { + char **container_versions_filtered; + size_t container_versions_number_filtered = 0; + container_versions_filtered = library_cmp_filter_list( details->public_symbol_versions, + container_versions, + container_versions_number, + &container_versions_number_filtered ); + free_strv_full( container_versions ); + + container_versions = container_versions_filtered; + container_versions_number = container_versions_number_filtered; + } + + xasprintf( &version_message, "Container versions of %s:", details->name); + print_debug_string_list( container_versions, version_message ); + _capsule_clear( &version_message ); + + if( !open_elf_library( provider_path, &provider_fd, &provider_elf, + &code, &message ) ) + { + DEBUG( DEBUG_TOOL, + "an error occurred while opening %s (%d): %s", + container_path, code, message ); + goto out; + } + + provider_versions = get_versions( provider_elf, &provider_versions_number, + &code, &message ); + + if( provider_versions == NULL ) + { + warnx( "failed to get provider versions for %s (%d): %s", + details->name, code, message ); + goto out; + } + + if( details->public_symbol_versions != NULL ) + { + char **provider_versions_filtered; + size_t provider_versions_number_filtered = 0; + provider_versions_filtered = library_cmp_filter_list( details->public_symbol_versions, + provider_versions, + provider_versions_number, + &provider_versions_number_filtered ); + free_strv_full( provider_versions ); + + provider_versions = provider_versions_filtered; + provider_versions_number = provider_versions_number_filtered; + } + + xasprintf( &version_message, "Provider versions of %s:", details->name); + print_debug_string_list( provider_versions, version_message ); + _capsule_clear( &version_message ); + + version_result = compare_string_sets( container_versions, container_versions_number, + provider_versions, provider_versions_number ); + + /* Version in container is strictly newer: don't symlink the one + * from the provider */ + if( version_result == STRING_SET_DIFF_ONLY_IN_FIRST ) + { + DEBUG( DEBUG_TOOL, + "%s in the container is newer because its version definitions are a strict superset", + details->name ); + cmp_result = 1; + } + /* Version in the provider is strictly newer: create the symlink */ + else if( version_result == STRING_SET_DIFF_ONLY_IN_SECOND ) + { + DEBUG( DEBUG_TOOL, + "%s in the provider is newer because its version definitions are a strict superset", + details->name ); + cmp_result = -1; + } + /* With the following two cases we are still unsure which library is newer */ + else if( version_result == STRING_SET_DIFF_NONE ) + { + DEBUG( DEBUG_TOOL, + "%s in the container and the provider have the same symbol versions", + details->name ); + } + else + { + DEBUG( DEBUG_TOOL, + "%s in the container and the provider have different symbol versions and neither is a superset of the other", + details->name ); + } + +out: + _capsule_clear( &message ); + free_strv_full( container_versions ); + free_strv_full( provider_versions ); + close_elf( &container_elf, &container_fd ); + close_elf( &provider_elf, &provider_fd ); + return cmp_result; +} + +static int +library_cmp_choose_container( const library_details *details, + const char *container_path, + const char *container_root, + const char *provider_path, + const char *provider_root ) +{ + DEBUG( DEBUG_TOOL, + "Choosing %s from container \"%s\", ignoring provider \"%s\"", + details->name, container_root, provider_root ); + return 1; +} + +static int +library_cmp_choose_provider( const library_details *details, + const char *container_path, + const char *container_root, + const char *provider_path, + const char *provider_root ) +{ + DEBUG( DEBUG_TOOL, + "Choosing %s from provider \"%s\", ignoring container \"%s\"", + details->name, provider_root, container_root ); + return -1; +} + +typedef struct +{ + const char *name; + library_cmp_function comparator; +} named_comparator; + +static const named_comparator named_comparators[] = +{ + { "name", library_cmp_by_name }, + { "symbols", library_cmp_by_symbols }, + { "versions", library_cmp_by_versions }, + { "container", library_cmp_choose_container }, + { "provider", library_cmp_choose_provider }, +}; + +/* + * library_cmp_list_from_string: + * @spec: A string of tokens separated by @delimiters + * @delimiters: Any character from this set separates tokens + * @code: (out) (optional): Set to an errno value on error + * @message: (out) (optional): Set to a string error message on error + * + * Parse a list of comparators into an array of functions. + * + * Returns: (free-function free): An array of comparators + */ +library_cmp_function * +library_cmp_list_from_string( const char *spec, + const char *delimiters, + int *code, + char **message ) +{ + library_cmp_function *ret = NULL; + char *buf = xstrdup( spec ); + char *saveptr = NULL; + char *token; + ptr_list *list; + + static_assert( sizeof( void * ) == sizeof( library_cmp_function ), + "Function pointer assumed to be same size as void *"); + static_assert( alignof( void * ) == alignof( library_cmp_function ), + "Function pointer assumed to be same alignment as void *"); + + list = ptr_list_alloc( N_ELEMENTS( named_comparators ) ); + + for( token = strtok_r( buf, delimiters, &saveptr ); + token != NULL; + token = strtok_r( NULL, delimiters, &saveptr ) ) + { + library_cmp_function comparator = NULL; + size_t i; + + if( token[0] == '\0' ) + continue; + + for( i = 0; i < N_ELEMENTS( named_comparators ); i++ ) + { + if( strcmp( token, named_comparators[i].name ) == 0 ) + { + comparator = named_comparators[i].comparator; + break; + } + } + + if( comparator == NULL ) + { + _capsule_set_error( code, message, + EINVAL, "Unknown library comparison mode \"%s\"", + token ); + goto out; + } + + ptr_list_push_ptr( list, (void *) comparator ); + } + + ret = (library_cmp_function *) ptr_list_free_to_array( _capsule_steal_pointer( &list ), NULL ); + +out: + if( list != NULL ) + ptr_list_free( list ); + + free( buf ); + return ret; +} + +/* + * library_cmp_list_iterate: + * @details: The library we are interersted in and how to compare it + * @container_path: The path to the library in the container + * @container_root: The path to the top-level directory of the container + * @provider_path: The path to the library in the provider + * @provider_root: The path to the top-level directory of the provider + * + * Iterate through a %NULL-terminated array of comparators from the given + * @details, highest-precedence first, calling each one in turn until one of + * them returns a nonzero value. If none of them return nonzero, return 0. + * + * Returns: Negative if the container version appears newer, zero if they + * appear the same or we cannot tell, or positive if the provider version + * appears newer. + */ +int +library_cmp_list_iterate( const library_details *details, + const char *container_path, + const char *container_root, + const char *provider_path, + const char *provider_root ) +{ + const library_cmp_function *iter; + + assert( details != NULL ); + + for( iter = details->comparators; iter != NULL && *iter != NULL; iter++ ) + { + int decision = (*iter)( details, + container_path, container_root, + provider_path, provider_root ); + + if( decision != 0 ) + return decision; + } + + return 0; +} diff --git a/subprojects/libcapsule/utils/library-cmp.h b/subprojects/libcapsule/utils/library-cmp.h new file mode 100644 index 0000000000000000000000000000000000000000..480de5bb15c848256da02c49c60d2bab8393ea71 --- /dev/null +++ b/subprojects/libcapsule/utils/library-cmp.h @@ -0,0 +1,95 @@ +// Copyright © 2020 Collabora Ltd + +// This file is part of libcapsule. + +// libcapsule is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as +// published by the Free Software Foundation; either version 2.1 of the +// License, or (at your option) any later version. + +// libcapsule 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 Lesser General Public License for more details. + +// You should have received a copy of the GNU Lesser General Public +// License along with libcapsule. If not, see <http://www.gnu.org/licenses/>. + +#pragma once + +#include <stdbool.h> +#include <stdio.h> + +typedef struct _library_details library_details; + +/* + * library_cmp_function: + * @details: The details about the library we are interested in and how + * to compare it + * @container_path: The path to the library in the container + * @container_root: The path to the top-level directory of the container + * @provider_path: The path to the library in the provider + * @provider_root: The path to the top-level directory of the provider + * + * Compare two libraries, returning a result with the same convention + * as strcmp(): informally, `cmp(a, b) < 0` if `a < b`, and the same + * for `>`. + * + * Returns: Negative if the container version appears newer, zero if they + * appear the same or we cannot tell, or positive if the provider version + * appears newer. + */ +typedef int ( *library_cmp_function ) ( const library_details *details, + const char *container_path, + const char *container_root, + const char *provider_path, + const char *provider_root ); + +/* + * library_details: + * @name: The name we look for the library under, normally a SONAME + * @comparators: Functions to use to compare versions, or %NULL for + * default behaviour + * @public_symbol_versions: An array of patterns to discriminate public + * versions from private versions + * @public_symbols: An array of patterns to discriminate public + * symbols from private symbols + */ +struct _library_details +{ + char *name; + library_cmp_function *comparators; + char **public_symbol_versions; + char **public_symbols; +}; + +library_cmp_function *library_cmp_list_from_string( const char *spec, + const char *delimiters, + int *code, + char **message ); +int library_cmp_list_iterate( const library_details *details, + const char *container_path, + const char *container_root, + const char *provider_path, + const char *provider_root ); + +/* + * library_knowledge: + * @tree: (element-type library_details): A tsearch(3) tree + * + * Details of all known libraries. + */ +typedef struct +{ + /*< private >*/ + void *tree; +} library_knowledge; + +#define LIBRARY_KNOWLEDGE_INIT { NULL } + +bool library_knowledge_load_from_stream( library_knowledge *self, + FILE *stream, const char *name, + int *code, char **message ); +const library_details *library_knowledge_lookup( const library_knowledge *self, + const char *library ); +void library_knowledge_clear( library_knowledge *self ); diff --git a/subprojects/libcapsule/utils/mmap-info.c b/subprojects/libcapsule/utils/mmap-info.c new file mode 100644 index 0000000000000000000000000000000000000000..8cdc10c6b4476db28af7924849bfac5ae992facd --- /dev/null +++ b/subprojects/libcapsule/utils/mmap-info.c @@ -0,0 +1,264 @@ +// Copyright © 2017 Collabora Ltd + +// This file is part of libcapsule. + +// libcapsule is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as +// published by the Free Software Foundation; either version 2.1 of the +// License, or (at your option) any later version. + +// libcapsule 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 Lesser General Public License for more details. + +// You should have received a copy of the GNU Lesser General Public +// License along with libcapsule. If not, see <http://www.gnu.org/licenses/>. + +#include <stdlib.h> +#include <stdio.h> +#include <fcntl.h> +#include <ctype.h> +#include <errno.h> +#include <string.h> +#include <link.h> + +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/mman.h> + +#include "mmap-info.h" +#include "utils.h" + +typedef enum +{ + MAPS_START , + MAPS_END , + MAPS_DATA , + MAPS_PROT_R , + MAPS_PROT_W , + MAPS_PROT_X , + MAPS_PROT_P , + MAPS_SKIP , + MAPS_PATH , +} map_parse_state; + +#define PROC_FILE "/proc/self/maps" +#define ERROR(s) \ + ({ if(err ) *err = errno; \ + if(errstr) *errstr = s; \ + return NULL; }) + +void +free_mmap_info(mmapinfo *ptr) +{ + if( ptr ) + free( ptr ); +} + +mmapinfo * +find_mmap_info(mmapinfo *maps, void *addr) +{ + if( !maps ) + return NULL; + + for( int i = 0; maps[i].start != MAP_FAILED; i++ ) + { + if( (ElfW(Addr)) maps[i].start > (ElfW(Addr)) addr ) + continue; + + if( (ElfW(Addr)) maps[i].end < (ElfW(Addr)) addr ) + continue; + + return &maps[i]; + } + + return NULL; +} + +int +add_mmap_protection(mmapinfo *mmap_info, unsigned int flags) +{ + ElfW(Addr) start = (ElfW(Addr)) mmap_info->start; + ElfW(Addr) end = (ElfW(Addr)) mmap_info->end; + size_t size = end - start; + unsigned int prot = mmap_info->protect | flags; + + return mprotect( (void *)start, size, prot ); +} + +int +reset_mmap_protection(mmapinfo *mmap_info) +{ + ElfW(Addr) start = (ElfW(Addr)) mmap_info->start; + ElfW(Addr) end = (ElfW(Addr)) mmap_info->end; + size_t size = end - start; + + return mprotect( (void *)start, size, mmap_info->protect ); +} + +mmapinfo * +load_mmap_info (int *err, const char **errstr) +{ + FILE *maps = fopen( PROC_FILE, "r" ); + char map_line[80 + PATH_MAX]; + int map_entries = 0; + mmapinfo *entries = NULL; + + if( !maps ) + ERROR("Warning: could not open " PROC_FILE); + + while( fgets(&map_line[0], sizeof(map_line), maps) ) + map_entries++; + + if( fseek(maps, 0, SEEK_SET) < 0 ) + ERROR("Warning: Unable to seek to start of " PROC_FILE); + + if( map_entries > 0 ) + entries = xcalloc( map_entries + 1, sizeof(mmapinfo) ); + else + ERROR("Warning: no mmap entries found in " PROC_FILE); + + int l = 0; + + while( fgets(&map_line[0], sizeof(map_line), maps) ) + { + size_t offs; + char *s = NULL; + int skip = 0; + map_parse_state p = MAPS_START; + map_parse_state item = MAPS_START; + int last_blank = 0; + char *path = NULL; + + for( offs = 0; offs < sizeof(map_line) && l < map_entries; offs++ ) + { + char *c = map_line + offs; + + // we've hit the end of the line: prematurely or not, clean up: + if( *c == '\n' ) + { + *c = '\0'; + if( path ) + strncpy( &entries[l].path[0], path, PATH_MAX ); + entries[l].path[PATH_MAX - 1] = '\0'; + l++; + break; + } + + switch (p) + { + case MAPS_START: + case MAPS_END: + if( isxdigit(*c) ) + { + s = c; + item = p; + p = MAPS_DATA; + } + break; + + case MAPS_DATA: + if( !isxdigit(*c) ) + { + char **addr = ( item == MAPS_START ? + &entries[l].start : + &entries[l].end ); + *c = '\0'; + *addr = (char *)strtoul( s, NULL, 16 ); + p = item == MAPS_START ? MAPS_END : MAPS_PROT_R; + } + break; + + case MAPS_PROT_R: + if( *c != 'r' && *c != '-' ) + continue; + + entries[l].protect = PROT_NONE; + + entries[l].protect |= (*c == 'r' ? PROT_READ : PROT_NONE); + p = MAPS_PROT_W; + break; + + case MAPS_PROT_W: + entries[l].protect |= (*c == 'w' ? PROT_WRITE : PROT_NONE); + p = MAPS_PROT_X; + break; + + case MAPS_PROT_X: + entries[l].protect |= (*c == 'x' ? PROT_EXEC : PROT_NONE); + p = MAPS_PROT_P; + break; + + case MAPS_PROT_P: + last_blank = isblank(*c); + skip = 7; + p = MAPS_SKIP; + break; + + case MAPS_SKIP: + if( isblank(*c) ) + { + if( last_blank == 0 ) + skip--; + } + else if( !isblank(*c) ) + { + if( last_blank == 1 ) + skip--; + } + + last_blank = isblank(*c); + + if( skip <= 0 ) + p = MAPS_PATH; + break; + + case MAPS_PATH: + if( !path && !isblank(*c) ) + path = c; + break; + + default: + // invalid state reached: flag and move on: + fprintf( stderr, "Cannot parse mmap info: %s\n", map_line ); + entries[l].invalid = 1; + entries[l].protect = PROT_NONE; + entries[l].path[PATH_MAX - 1] = '\0'; + break; + } + + // bogus/unparseable line, proceed to next entry: + if( entries[l].invalid ) + continue; + } + } + + entries[map_entries].start = MAP_FAILED; + entries[map_entries].end = MAP_FAILED; + entries[map_entries].protect = PROT_NONE; + entries[map_entries].path[0] = '\0'; + + fclose( maps ); + + return entries; +} + +int +mmap_entry_should_be_writable (mmapinfo *mmap_info) +{ + // malformed or unparseable entry - cannot handle: + if( mmap_info->invalid ) + return 0; + + // already has write permissions, don't care: + if( mmap_info->protect & PROT_WRITE ) + return 0; + + // or not a 'real' DSO, leave the hell alone: + if( strchr( mmap_info->path, '[' ) ) + return 0; + + return 1; +} + diff --git a/subprojects/libcapsule/utils/mmap-info.h b/subprojects/libcapsule/utils/mmap-info.h new file mode 100644 index 0000000000000000000000000000000000000000..265925e61bc7750b62efb30b23edb0c8c25ee641 --- /dev/null +++ b/subprojects/libcapsule/utils/mmap-info.h @@ -0,0 +1,40 @@ +// Copyright © 2017 Collabora Ltd + +// This file is part of libcapsule. + +// libcapsule is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as +// published by the Free Software Foundation; either version 2.1 of the +// License, or (at your option) any later version. + +// libcapsule 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 Lesser General Public License for more details. + +// You should have received a copy of the GNU Lesser General Public +// License along with libcapsule. If not, see <http://www.gnu.org/licenses/>. + +#pragma once + +#include <sys/param.h> +#include <sys/mman.h> + +typedef struct +{ + char path[PATH_MAX]; + char *start; + char *end; + unsigned int protect; + int invalid; +} mmapinfo; + +mmapinfo *load_mmap_info (int *err, const char **errstr); +mmapinfo *find_mmap_info (mmapinfo *maps, void *addr); +void free_mmap_info (mmapinfo *ptr); + +int add_mmap_protection (mmapinfo *mmap_info, unsigned int flags); +int reset_mmap_protection (mmapinfo *mmap_info); + +int mmap_entry_should_be_writable (mmapinfo *mmap_info); + diff --git a/subprojects/libcapsule/utils/print-libstubs.c b/subprojects/libcapsule/utils/print-libstubs.c new file mode 100644 index 0000000000000000000000000000000000000000..649e8521a08f401f3bc4868bae6e8496260799c1 --- /dev/null +++ b/subprojects/libcapsule/utils/print-libstubs.c @@ -0,0 +1,405 @@ +// Copyright © 2017 Collabora Ltd + +// This file is part of libcapsule. + +// libcapsule is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as +// published by the Free Software Foundation; either version 2.1 of the +// License, or (at your option) any later version. + +// libcapsule 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 Lesser General Public License for more details. + +// You should have received a copy of the GNU Lesser General Public +// License along with libcapsule. If not, see <http://www.gnu.org/licenses/>. + +#include <dlfcn.h> +#include <getopt.h> +#include <stdlib.h> +#include <stdio.h> + +#include <string.h> + +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/param.h> +#include <fcntl.h> +#include <errno.h> +#include <limits.h> + +#include <link.h> + +#include "tools.h" +#include "utils.h" +#include "ld-cache.h" +#include "ld-libs.h" + +static void usage (int code) __attribute__((noreturn)); +static void usage (int code) +{ + FILE *fh; + + if( code == 0 ) + { + fh = stdout; + } + else + { + fh = stderr; + } + + fprintf( fh, "Usage: %s SONAME [SYSROOT]\n", + program_invocation_short_name ); + fprintf( fh, "SONAME is the machine-readable name of a shared library,\n" + "for example 'libz.so.1'.\n" ); + fprintf( fh, "SYSROOT is the root directory where we look for SONAME.\n" ); + exit( code ); +} + +typedef struct +{ + size_t plen; + const char *prefix; + const char *target; +} dso_search_t; + +// these macros are secretly the same for elf32 & elf64: +#define ELFW_ST_TYPE(a) ELF32_ST_TYPE(a) +#define ELFW_ST_BIND(a) ELF32_ST_BIND(a) +#define ELFW_ST_VISIBILITY(a) ELF32_ST_VISIBILITY(a) + +// given a symbol in DT_SYMTAB at index ð’Š, jump to its +// entry in DT_VERSYM (also at offset ð’Š for an array of ElfW(Versym)) +// and extract its value ð’—ð’” (a number). The corresponding DT_VERDEF entry +// (ElfW(Verdef)) is the one whose vd_ndx member == ð’—ð’” & 0x7fff +// +// NOTE: if ð’—ð’” & 0x8000 then the version is the default or base version +// of the symbol, which should be used if the requestor has not specified +// a version for this symbol +// +// NOTE: in practice the vd_ndx member is the 1-based array position in +// the DT_VERDEF array, but the linker/elfutils code does not rely on +// this, so neither do we. +// +// next we check that the vd_flags member in the DT_VERDEF entry does not +// contain VER_FLG_BASE, as that is the DT_VERDEF entry for the entire DSO +// and must not be used as a symbol version (this should never happen: +// the spec does not allow it, but it's not physically impossible). +// +// if we have a valid DT_VERDEF entry the ElfW(Verdaux) array entry at offset +// vd_aux (from the address of the DT_VERDEF entry itself) will give +// the address of an ElfW(Verdaux) struct whose vda_name entry points +// to (ð‘“ð‘–ð‘›ð‘Žð‘™ð‘™ð‘¦) an offset into the DT_STRTAB which gives the version name. +// +// And that's how symbol version lookup works, as near as I can tell. +static void +symbol_version ( ElfW(Sym) *symbol, + int i, + const char *strtab, + const ElfW(Versym) *versym, + const void *verdef, + const int verdefnum, + int *default_version, + const char **version, + ElfW(Versym) *vs) +{ + *default_version = 0; + *version = NULL; + + if( versym == NULL ) + return; + + switch( symbol->st_shndx ) + { + case SHN_UNDEF: + case SHN_ABS: + case SHN_COMMON: + case SHN_BEFORE: + case SHN_AFTER: + case SHN_XINDEX: + // none of these are handled (and we're very unlikely to need to) + break; + + default: + if( symbol->st_shndx < SHN_LORESERVE ) + { + const char *vd = verdef; + *vs = *(versym + i); + + for( int x = 0; x < verdefnum; x++ ) + { + ElfW(Verdef) *entry = (ElfW(Verdef) *) vd; + + if( entry->vd_ndx == (*vs & 0x7fff) ) + { + const char *au; + + if( entry->vd_flags & VER_FLG_BASE ) + break; + + au = vd + entry->vd_aux; + ElfW(Verdaux) *aux = (ElfW(Verdaux) *) au; + *version = strtab + aux->vda_name; + *default_version = (*vs & 0x8000) ? 0 : 1; + } + + vd = vd + entry->vd_next; + } + } + } +} + + +static int +symbol_excluded (const char *name) +{ + if( !strcmp(name, "_init") || + !strcmp(name, "_fini") ) + return 1; + + return 0; +} + +static void +parse_symtab (const void *start, + const char *strtab, + const ElfW(Versym) *versym, + const void *verdef, + const int verdefnum) +{ + int x = 0; + ElfW(Sym) *entry; + + for( entry = (ElfW(Sym) *)start; + ( (ELFW_ST_TYPE(entry->st_info) < STT_NUM) && + (ELFW_ST_BIND(entry->st_info) < STB_NUM) ); + entry++, x++ ) + { + int defversym = 0; + const char *version = NULL; + ElfW(Versym) vs = 0; + + switch( ELFW_ST_TYPE(entry->st_info) ) + { + case STT_FUNC: + case STT_OBJECT: + symbol_version( entry, x, strtab, versym, verdef, verdefnum, + &defversym, &version, &vs ); + + if( !vs ) + continue; + + if( symbol_excluded(strtab + entry->st_name) ) + continue; + + fprintf( stdout, "%s %s%s%s\n", + strtab + entry->st_name , + version ? "@" : "" , + defversym ? "@" : "" , + version ? version : "" ); + break; + + default: + // Assumed to be uninteresting? + break; + } + } +} + +static void +parse_dynamic (ElfW(Addr) base, ElfW(Dyn) *dyn) +{ + + int verdefnum = -1; + void *start = NULL; + const void *symtab = NULL; + const void *versym = NULL; + const void *verdef = NULL; + const char *strtab = NULL; + ElfW(Dyn) *entry = NULL; + + start = (void *) ((ElfW(Addr)) dyn - base); + strtab = dynamic_section_find_strtab( dyn, (const void *) base, NULL ); + + for( entry = dyn; entry->d_tag != DT_NULL; entry++ ) + { + switch( entry->d_tag ) + { + case DT_SYMTAB: + if( versym == NULL ) + versym = (const void *) find_ptr( base, start, DT_VERSYM ); + if( verdef == NULL ) + verdef = (const void *) find_ptr( base, start, DT_VERDEF ); + if( verdefnum == -1 ) + verdefnum = find_value( base, start, DT_VERDEFNUM ); + + symtab = (const void *) entry->d_un.d_ptr; + parse_symtab( symtab, strtab, versym, verdef, verdefnum ); + break; + + case DT_VERDEFNUM: + verdefnum = entry->d_un.d_val; + break; + + case DT_VERDEF: + if( verdefnum == -1 ) + verdefnum = find_value( base, start, DT_VERDEFNUM ); + verdef = (const void *) entry->d_un.d_ptr; + // parse_verdef( verdef, strtab, verdefnum ); + break; + + case DT_VERSYM: + if( versym == NULL ) + versym = (const void *) entry->d_un.d_ptr; + break; + + default: + break; + } + } + + return; +} + +static int +dso_name_matches (const char *target, const char *maybe) +{ + const char *dir = strrchr( maybe, '/' ); + const size_t len = strlen( target ); + + // maybe contains a full path: + if( !dir ) + return 0; + + // maybe's filename is at least long enough to match target + if( strlen(++dir) < len ) + return 0; + + // /maybefilename matches /target + if( memcmp(dir, target, len) ) + return 0; + + // the last major/minor/etc number didn't actually match + // eg libfoo.so.1 vs libfoo.so.10 + if ( *(dir + len) != '.' && *(dir + len) != '\0' ) + return 0; + + return 1; +} + +static void +dump_symbols (void *handle, const char *libname) +{ + int dlcode = 0; + struct link_map *map; + + if( (dlcode = dlinfo( handle, RTLD_DI_LINKMAP, &map )) ) + { + fprintf( stderr, "cannot access symbols for %s via handle %p [%d]\n", + libname, handle, dlcode ); + exit( dlcode ); + } + + // find start of link map chain: + while( map->l_prev ) + map = map->l_prev; + + for( struct link_map *m = map; m; m = m->l_next ) + if( dso_name_matches( libname, m->l_name ) ) + parse_dynamic( m->l_addr, m->l_ld ); + +} + +enum +{ + OPTION_HELP, + OPTION_VERSION, +}; + +static struct option long_options[] = +{ + { "help", no_argument, NULL, OPTION_HELP }, + { "version", no_argument, NULL, OPTION_VERSION }, + { NULL } +}; + +int main (int argc, char **argv) +{ + const char *libname; + const char *prefix = NULL; + char *message = NULL; + ld_libs ldlibs = {}; + int error = 0; + Lmid_t ns = LM_ID_BASE; + void *handle; + + while( 1 ) + { + int opt = getopt_long( argc, argv, "", long_options, NULL ); + + if( opt == -1 ) + { + break; + } + + switch( opt ) + { + case '?': + default: + usage( 2 ); + break; // not reached + + case OPTION_HELP: + usage( 0 ); + break; // not reached + + case OPTION_VERSION: + _capsule_tools_print_version( "capsule-symbols" ); + return 0; + } + } + + if( argc < optind + 1 || argc > optind + 2) + usage( 1 ); + + if( argc > optind + 1 ) + prefix = argv[optind + 1]; + + set_debug_flags( getenv("CAPSULE_DEBUG") ); + + if( !ld_libs_init( &ldlibs, NULL, prefix, 0, &error, &message ) ) + { + fprintf( stderr, "%s: failed to initialize for prefix %s (%d: %s)\n", + program_invocation_short_name, prefix, error, message ); + exit( error ? error : ENOENT ); + } + + if( !ld_libs_set_target( &ldlibs, argv[optind], &error, &message ) || + !ld_libs_find_dependencies( &ldlibs, &error, &message ) ) + { + fprintf( stderr, "%s: failed to open [%s]%s (%d: %s)\n", + program_invocation_short_name, prefix, argv[optind], error, message ); + exit( error ? error : ENOENT ); + } + + if( ( handle = ld_libs_load( &ldlibs, &ns, 0, &error, &message ) ) ) + { + if( (libname = strrchr( argv[optind], '/' )) ) + libname = libname + 1; + else + libname = argv[optind]; + + // dl_iterate_phdr won't work with private dlmopen namespaces: + dump_symbols( handle, libname ); + } + else + { + fprintf( stderr, "%s: failed to open [%s]%s (%d: %s)\n", + program_invocation_short_name, prefix, argv[optind], error, message ); + exit(error ? error : ENOENT); + } + + exit(0); +} diff --git a/subprojects/libcapsule/utils/print-version.c b/subprojects/libcapsule/utils/print-version.c new file mode 100644 index 0000000000000000000000000000000000000000..adc08c2f0ae67b8b86dc2de98b8aeb49a1044c5c --- /dev/null +++ b/subprojects/libcapsule/utils/print-version.c @@ -0,0 +1,154 @@ +// Copyright © 2017 Collabora Ltd + +// This file is part of libcapsule. + +// libcapsule is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as +// published by the Free Software Foundation; either version 2.1 of the +// License, or (at your option) any later version. + +// libcapsule 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 Lesser General Public License for more details. + +// You should have received a copy of the GNU Lesser General Public +// License along with libcapsule. If not, see <http://www.gnu.org/licenses/>. + +#include <dlfcn.h> +#include <getopt.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <errno.h> + +#include "debug.h" +#include "ld-cache.h" +#include "ld-libs.h" +#include "tools.h" + +static void usage (int code) __attribute__((noreturn)); +static void usage (int code) +{ + FILE *fh; + + if( code == 0 ) + { + fh = stdout; + } + else + { + fh = stderr; + } + + fprintf( fh, "Usage: %s SONAME [SYSROOT]\n", + program_invocation_short_name ); + fprintf( fh, "SONAME is the machine-readable name of a shared library,\n" + "for example 'libz.so.1'.\n" ); + fprintf( fh, "SYSROOT is the root directory where we look for SONAME.\n" ); + exit( code ); +} + +enum +{ + OPTION_HELP, + OPTION_VERSION, +}; + +static struct option long_options[] = +{ + { "help", no_argument, NULL, OPTION_HELP }, + { "version", no_argument, NULL, OPTION_VERSION }, + { NULL } +}; + +int main (int argc, char **argv) +{ + const char *libname; + const char *prefix = NULL; + char *message = NULL; + ld_libs ldlibs = {}; + int error = 0; + int e = 0; + + set_debug_flags( getenv("CAPSULE_DEBUG") ); + + while( 1 ) + { + int opt = getopt_long( argc, argv, "", long_options, NULL ); + + if( opt == -1 ) + { + break; + } + + switch( opt ) + { + case '?': + default: + usage( 2 ); + break; // not reached + + case OPTION_HELP: + usage( 0 ); + break; // not reached + + case OPTION_VERSION: + _capsule_tools_print_version( "capsule-version" ); + return 0; + } + } + + if( argc < optind + 1 || argc > optind + 2) + usage( 1 ); + + if( argc > optind + 1 ) + prefix = argv[optind + 1]; + + if( !ld_libs_init( &ldlibs, NULL, prefix, 0, &error, &message ) ) + { + fprintf( stderr, "%s: failed to initialize for prefix %s (%d: %s)\n", + program_invocation_short_name, prefix, error, message ); + exit( error ? error : ENOENT ); + } + + if( ld_libs_set_target( &ldlibs, argv[optind], &error, &message ) ) + { + const char *path; + const char *buf; + + if( (libname = strrchr( argv[optind], '/' )) ) + libname = libname + 1; + else + libname = argv[optind]; + + path = &ldlibs.needed[0].path[0]; + + while( (buf = strstr( path + 1, libname )) ) + path = buf; + + if( path ) + path = strstr( path, ".so." ); + + if( path ) + path += 4; + + if( !path || !*path ) + if( (path = strstr( libname, ".so." )) ) + path += 4; + + fprintf( stdout, "%s %s %s %s\n", + prefix, libname, + (path && *path) ? path : "1", // wild guess if we failed + &ldlibs.needed[0].path[0] ); + } + else + { + fprintf( stderr, "%s: failed to open [%s]%s (%d: %s)\n", + program_invocation_short_name, prefix, argv[optind], error, message ); + exit( error ? error : ENOENT ); + } + + ld_libs_finish( &ldlibs ); + exit(e); +} diff --git a/subprojects/libcapsule/utils/process-pt-dynamic.c b/subprojects/libcapsule/utils/process-pt-dynamic.c new file mode 100644 index 0000000000000000000000000000000000000000..57dac19db72c2661fba95abd250943ca216d8067 --- /dev/null +++ b/subprojects/libcapsule/utils/process-pt-dynamic.c @@ -0,0 +1,785 @@ +// Copyright © 2017 Collabora Ltd + +// This file is part of libcapsule. + +// libcapsule is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as +// published by the Free Software Foundation; either version 2.1 of the +// License, or (at your option) any later version. + +// libcapsule 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 Lesser General Public License for more details. + +// You should have received a copy of the GNU Lesser General Public +// License along with libcapsule. If not, see <http://www.gnu.org/licenses/>. + +#include <assert.h> +#include <dlfcn.h> +#include <inttypes.h> +#include <stdlib.h> +#include <stdio.h> + +#include <string.h> + +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> + +#include <capsule/capsule.h> +#include "utils.h" +#include "process-pt-dynamic.h" +#include "mmap-info.h" + +static void * +#if __ELF_NATIVE_CLASS == 32 +addr (void *base, ElfW(Addr) offset, ElfW(Sword) addend) +#elif __ELF_NATIVE_CLASS == 64 +addr (void *base, ElfW(Addr) offset, ElfW(Sxword) addend) +#else +#error "Unsupported __ELF_NATIVE_CLASS size (not 32 or 64)" +#endif +{ + return base + offset + addend; +} + + +static int +try_relocation (ElfW(Addr) *reloc_addr, const char *name, void *data) +{ + capsule_item *map; + relocation_data *rdata = data; + + if( !name || !*name || !reloc_addr ) + return 0; + + for( map = rdata->relocs; map->name; map++ ) + { + if( strcmp( name, map->name ) ) + continue; + + DEBUG( DEBUG_RELOCS, + "relocation for %s (%p->{ %p }, %p, %p)", + name, reloc_addr, NULL, (void *)map->shim, (void *)map->real ); + + // we used to check for the shim address here but it's possible + // that we can't look it up if the proxy library was dlopen()ed + // in which case map->shim will be null. + // this turns out not to be a problem as we only need it when + // working around RELRO linking, which doesn't apply to dlopen() + + // sought after symbols is not available in the private namespace + if( !map->real ) + { + rdata->count.failure++; + DEBUG( DEBUG_RELOCS, "--failed" ); + + return 1; + } + + // our work here is already done, apparently + if( *reloc_addr == map->real ) + { + DEBUG( DEBUG_RELOCS, "==target %p already contains %p (%p)", + reloc_addr, (void *)*reloc_addr, (void *)map->real ); + return 0; + } + // ====================================================================== + // exegesis: + + // linking goes like this: we start with a PLT entry pointing at the + // 'trampoline' entry which patches up the relocations. The first + // time we call a function, we go to the PLT which sends us to the + // trampoline, which finds the shim (in the case of our proxy library) + // or the real address (in the case of a normal library) and pastes that + // address into the PLT. + + // This function scribbles over the trampoline address with the real + // address, thus bypassing the trampoline _and_ the shim permanently. + + /// IOW the 0th, 1st and second function calls normally look like this: + // 0: function-call → PLT → trampoline : (PLT ↠address) → address + // 1: function-call → PLT → address + // 2: ibid + + // If we are already pointing to the shim instead of the trampoline + // that indicates we have RELRO linking - the linker has already resolved + // the address to the shim (as it doesn't know about the real address + // which is hidden inside the capsule). + + // -1: linker → function-lookup : (PLT ↠address) + // 0: function-call → PLT → address + // 1: ibid + + // butâ° RELRO linking also mprotect()s the relevant pages to be read-only + // which prevents us from overwriting the address. + + // but¹ we are smarter than the average bear, and we tried to harvest + // the mprotect info: If we did, then we will already have toggled the + // write permission on everything that didn't have it and can proceed + // (we're also not savages, so we'll put those permissions back later) + + // however, if we don't have any mprotect into for this relocation entry, + // then we can't de-shim the RELROd PLT entry, and it's sad 🼠time. + // ====================================================================== + if( (*reloc_addr == map->shim) && + !find_mmap_info(rdata->mmap_info, reloc_addr) ) + { + DEBUG( DEBUG_RELOCS|DEBUG_MPROTECT, + " ERROR: cannot update relocation record for %s", name ); + return 1; // FIXME - already shimmed, can't seem to override? + } + + *reloc_addr = map->real; + rdata->count.success++; + DEBUG( DEBUG_RELOCS, "++relocated" ); + return 0; + } + + // nothing to relocate + return 0; +} + +#if defined(__x86_64__) + +static const char * +reloc_type_name (int type) +{ + switch (type) + { + // Please keep these in numerical order. + +#define CASE(x) \ + case x: \ + return #x; + + CASE( R_X86_64_NONE ) + CASE( R_X86_64_64 ) + CASE( R_X86_64_PC32 ) + CASE( R_X86_64_GOT32 ) + CASE( R_X86_64_PLT32 ) + CASE( R_X86_64_COPY ) + CASE( R_X86_64_GLOB_DAT ) + CASE( R_X86_64_JUMP_SLOT ) + CASE( R_X86_64_RELATIVE ) + CASE( R_X86_64_GOTPCREL ) + CASE( R_X86_64_32 ) + CASE( R_X86_64_32S ) + CASE( R_X86_64_16 ) + CASE( R_X86_64_PC16 ) + CASE( R_X86_64_8 ) + CASE( R_X86_64_PC8 ) + CASE( R_X86_64_DTPMOD64 ) + CASE( R_X86_64_DTPOFF64 ) + CASE( R_X86_64_TPOFF64 ) + CASE( R_X86_64_TLSGD ) + CASE( R_X86_64_TLSLD ) + CASE( R_X86_64_DTPOFF32 ) + CASE( R_X86_64_GOTTPOFF ) + CASE( R_X86_64_TPOFF32 ) + CASE( R_X86_64_PC64 ) + CASE( R_X86_64_GOTOFF64 ) + CASE( R_X86_64_GOTPC32 ) + CASE( R_X86_64_GOT64 ) + CASE( R_X86_64_GOTPCREL64 ) + CASE( R_X86_64_GOTPC64 ) + CASE( R_X86_64_GOTPLT64 ) + CASE( R_X86_64_PLTOFF64 ) + CASE( R_X86_64_SIZE32 ) + CASE( R_X86_64_SIZE64 ) + CASE( R_X86_64_GOTPC32_TLSDESC ) + CASE( R_X86_64_TLSDESC_CALL ) + CASE( R_X86_64_TLSDESC ) + CASE( R_X86_64_IRELATIVE ) + + // Entries below this point are new since glibc 2.19 + +#ifdef R_X86_64_RELATIVE64 + CASE( R_X86_64_RELATIVE64 ) +#endif +#ifdef R_X64_64_GOTPCRELX + CASE( R_X86_64_GOTPCRELX ) +#endif +#ifdef R_X64_64_REX_GOTPCRELX + CASE( R_X86_64_REX_GOTPCRELX ) +#endif + +#undef CASE + + default: + return "UNKNOWN"; + } +} + +#elif defined(__i386__) + +static const char * +reloc_type_name (int type) +{ + switch (type) + { + // Please keep these in numerical order. + +#define CASE(x) \ + case x: \ + return #x; + + CASE( R_386_NONE ) + CASE( R_386_32 ) + CASE( R_386_PC32 ) + CASE( R_386_GOT32 ) + CASE( R_386_PLT32 ) + CASE( R_386_COPY ) + CASE( R_386_GLOB_DAT ) + CASE( R_386_JMP_SLOT ) + CASE( R_386_RELATIVE ) + CASE( R_386_GOTOFF ) + CASE( R_386_GOTPC ) + CASE( R_386_32PLT ) + CASE( R_386_TLS_TPOFF ) + CASE( R_386_TLS_IE ) + CASE( R_386_TLS_GOTIE ) + CASE( R_386_TLS_LE ) + CASE( R_386_TLS_GD ) + CASE( R_386_TLS_LDM ) + CASE( R_386_16 ) + CASE( R_386_PC16 ) + CASE( R_386_8 ) + CASE( R_386_PC8 ) + CASE( R_386_TLS_GD_32 ) + CASE( R_386_TLS_GD_PUSH ) + CASE( R_386_TLS_GD_CALL ) + CASE( R_386_TLS_GD_POP ) + CASE( R_386_TLS_LDM_32 ) + CASE( R_386_TLS_LDM_PUSH ) + CASE( R_386_TLS_LDM_CALL ) + CASE( R_386_TLS_LDM_POP ) + CASE( R_386_TLS_LDO_32 ) + CASE( R_386_TLS_IE_32 ) + CASE( R_386_TLS_LE_32 ) + CASE( R_386_TLS_DTPMOD32 ) + CASE( R_386_TLS_DTPOFF32 ) + CASE( R_386_TLS_TPOFF32 ) +#ifdef R_386_SIZE32 + CASE( R_386_SIZE32 ) +#endif + CASE( R_386_TLS_GOTDESC ) + CASE( R_386_TLS_DESC_CALL ) + CASE( R_386_TLS_DESC ) + CASE( R_386_IRELATIVE ) + + // Entries below this point are new since glibc 2.19 + +#ifdef R_386_GOT32X + CASE( R_386_GOT32X ) +#endif + +#undef CASE + + default: + return "UNKNOWN"; + } +} + +#else + +#error Unsupported CPU architecture + +#endif + +int +process_dt_rela (const ElfW(Rela) *start, + size_t relasz, + const char *strtab, + size_t strsz, + const ElfW(Sym) *symtab, + size_t symsz, + void *base, + void *data) +{ + const ElfW(Rela) *entry; + + DEBUG( DEBUG_ELF, + "%zu RELA entries (%zu bytes) starting at %p", + relasz / sizeof(*entry), + relasz, + start ); + + if( relasz % sizeof(*entry) != 0 ) + DEBUG( DEBUG_ELF, "%zu bytes left over?!", relasz % sizeof(*entry) ); + + for( entry = start; entry < start + (relasz / sizeof(*entry)); entry++ ) + { + int sym; + int chr; + const char *name = NULL; + const ElfW(Sym) *symbol; + +#if __ELF_NATIVE_CLASS == 32 + sym = ELF32_R_SYM (entry->r_info); + chr = ELF32_R_TYPE(entry->r_info); +#elif __ELF_NATIVE_CLASS == 64 + sym = ELF64_R_SYM (entry->r_info); + chr = ELF64_R_TYPE(entry->r_info); +#else + fprintf( stderr, "__ELF_NATIVE_CLASS is neither 32 nor 64" ); + exit( 22 ); +#endif + + DEBUG( DEBUG_ELF, "RELA entry at %p", entry ); + + symbol = find_symbol( sym, symtab, symsz, strtab, strsz, &name ); + + DEBUG( DEBUG_ELF, + "symbol %p; name: %p:%s", symbol, name, name ? name : "-" ); + + if( !symbol || !name || !*name ) + continue; + +#if defined(__i386__) || defined(__x86_64__) + switch( chr ) + { + void *slot; + // details at: https://github.com/hjl-tools/x86-psABI/wiki/X86-psABI +#if defined(__i386__) + case R_386_32: + case R_386_GLOB_DAT: + case R_386_JMP_SLOT: +#elif defined(__x86_64__) + case R_X86_64_64: + case R_X86_64_GLOB_DAT: + case R_X86_64_JUMP_SLOT: +#else +#error Unsupported CPU architecture +#endif + slot = addr( base, entry->r_offset, entry->r_addend ); + DEBUG( DEBUG_ELF, + " %30s %30s: %p ↠{ offset: %"FMT_ADDR"; add: %"FMT_SIZE" }", + name, reloc_type_name( chr ), + slot, entry->r_offset, entry->r_addend ); + try_relocation( slot, name, data ); + break; + + default: + DEBUG( DEBUG_ELF, + "%s has slot type %s (%d), not doing anything special", + name, reloc_type_name( chr ), chr ); + break; + } +#else +#error Unsupported CPU architecture +#endif + } + + return 0; +} + +int +process_dt_rel (const ElfW(Rel) *start, + size_t relasz, + const char *strtab, + size_t strsz, + const ElfW(Sym) *symtab, + size_t symsz, + void *base, + void *data) +{ + const ElfW(Rel) *entry; + + DEBUG( DEBUG_ELF, + "%zu REL entries (%zu bytes) starting at %p", + relasz / sizeof(*entry), + relasz, + start ); + + if( relasz % sizeof(*entry) != 0 ) + DEBUG( DEBUG_ELF, "%zu bytes left over?!", relasz % sizeof(*entry) ); + + for( entry = start; entry < start + (relasz / sizeof(*entry)); entry++ ) + { + int sym; + int chr; + const char *name = NULL; + + const ElfW(Sym) *symbol; + +#if __ELF_NATIVE_CLASS == 32 + sym = ELF32_R_SYM (entry->r_info); + chr = ELF32_R_TYPE(entry->r_info); +#elif __ELF_NATIVE_CLASS == 64 + sym = ELF64_R_SYM (entry->r_info); + chr = ELF64_R_TYPE(entry->r_info); +#else +#error Unsupported CPU architecture +#endif + + symbol = find_symbol( sym, symtab, symsz, strtab, strsz, &name ); + + DEBUG( DEBUG_ELF, + "symbol %p; name: %p:%s", symbol, name, name ? name : "-" ); + + if( !symbol || !name || !*name ) + continue; + +#if defined(__i386__) || defined(__x86_64__) + switch( chr ) + { + void *slot; + // details at: https://github.com/hjl-tools/x86-psABI/wiki/X86-psABI +#if defined(__i386__) + case R_386_32: + case R_386_GLOB_DAT: + case R_386_JMP_SLOT: +#elif defined(__x86_64__) + case R_X86_64_64: + case R_X86_64_GLOB_DAT: + case R_X86_64_JUMP_SLOT: +#else +#error Unsupported CPU architecture +#endif + slot = addr( base, entry->r_offset, 0 ); + DEBUG( DEBUG_ELF, + " %30s %30s: %p ↠{ offset: %"FMT_ADDR"; addend: n/a }", + name, + reloc_type_name( chr ), + slot, entry->r_offset ); + try_relocation( slot, name, data ); + break; + + default: + DEBUG( DEBUG_ELF, + "%s has slot type %s (%d), not doing anything special", + name, reloc_type_name( chr ), chr ); + break; + } +#else +#error Unsupported CPU architecture +#endif + } + + return 0; +} + +/* + * process_pt_dynamic: + * @start: offset of dynamic section (an array of ElfW(Dyn) structures) + * relative to @base + * @size: size of dynamic section in bytes (not structs!), or 0 + * if the dynamic section is terminated by an entry with d_tag == DT_NULL + * @base: Starting address of the program header (the shared object) + * in memory. @start is relative to this. Addresses are normally + * relative to this, except for when they are absolute (see fix_addr()). + * @process_rela: called when we find the DT_RELA section + * @process_rel: called when we find the DT_REL section + * @data: arbitrary user data to be passed to both @process_rela + * and @process_rel + * + * Iterate over the PT_DYNAMIC entry in a shared library and perform + * relocations using the given callbacks. + */ +int +process_pt_dynamic (ElfW(Addr) start, + size_t size, + void *base, + relocate_rela_cb process_rela, + relocate_rel_cb process_rel, + void *data) +{ + int ret = 0; + const ElfW(Dyn) *entries; + const ElfW(Dyn) *entry; + size_t relasz = (size_t) -1; + size_t relsz = (size_t) -1; + size_t jmprelsz = (size_t) -1; + int jmpreltype = DT_NULL; + const ElfW(Sym) *symtab = NULL; + size_t strsz = (size_t) -1; + const char *strtab = dynamic_section_find_strtab( base + start, base, &strsz ); + size_t symsz; + + DEBUG( DEBUG_ELF, + "start: %#" PRIxPTR "; size: %" FMT_SIZE "; base: %p; handlers: %p %p; …", + start, size, base, process_rela, process_rel ); + entries = base + start; + DEBUG( DEBUG_ELF, "dyn entry: %p", entries ); + + DEBUG( DEBUG_ELF, + "strtab is at %p: %s", strtab, strtab ? "…" : ""); + + // Do a first pass to find the bits we'll need later + for( entry = entries; + (entry->d_tag != DT_NULL) && + ((size == 0) || ((void *)entry < (start + base + size))); + entry++ ) { + switch( entry->d_tag ) + { + case DT_PLTRELSZ: + jmprelsz = entry->d_un.d_val; + DEBUG( DEBUG_ELF, "jmprelsz is %zu", jmprelsz ); + break; + + case DT_SYMTAB: + symtab = fix_addr( base, entry->d_un.d_ptr ); + DEBUG( DEBUG_ELF, "symtab is %p", symtab ); + break; + + case DT_RELASZ: + relasz = entry->d_un.d_val; + DEBUG( DEBUG_ELF, "relasz is %zu", relasz ); + break; + + case DT_RELSZ: + relsz = entry->d_un.d_val; + DEBUG( DEBUG_ELF, "relsz is %zu", relsz ); + break; + + case DT_PLTREL: + jmpreltype = entry->d_un.d_val; + DEBUG( DEBUG_ELF, "jmpreltype is %d : %s", jmpreltype, + jmpreltype == DT_REL ? "DT_REL" : + jmpreltype == DT_RELA ? "DT_RELA" : "???" ); + break; + + default: + // We'll deal with this later + break; + } + } + + /* XXX Apparently the only way to find out the size of the dynamic + symbol section is to assume that the string table follows right + afterwards... —glibc elf/dl-fptr.c */ + assert( strtab >= (const char *) symtab ); + symsz = strtab - (const char *) symtab; + + DEBUG( DEBUG_ELF, + "%zu symbol table entries (%zu bytes) starting at %p", + symsz / sizeof(*symtab), + symsz, + symtab ); + + if( symsz % sizeof(*symtab) != 0 ) + DEBUG( DEBUG_ELF, "%zu bytes left over?!", symsz % sizeof(*symtab) ); + + for( entry = entries; + (entry->d_tag != DT_NULL) && + ((size == 0) || ((void *)entry < (start + base + size))); + entry++ ) { + switch( entry->d_tag ) + { + // Please keep the contents of this switch in numerical order. + + // IGNORE(x): Ignore a known d_tag that we believe we don't need + // to know about +#define IGNORE(x) \ + case x: \ + DEBUG( DEBUG_ELF, "ignoring %s (0x%zx): 0x%zx", \ + #x, (size_t) x, (size_t) entry->d_un.d_val); \ + break; + + // ALREADY_DID(x): Silently ignore a case we dealt with in + // the previous loop +#define ALREADY_DID(x) \ + case x: \ + break; + + IGNORE( DT_NEEDED ) + ALREADY_DID( DT_PLTRELSZ ) + IGNORE( DT_PLTGOT ) + IGNORE( DT_HASH ) + IGNORE( DT_STRTAB ) + ALREADY_DID( DT_SYMTAB ) + + case DT_RELA: + if( process_rela != NULL ) + { + const ElfW(Rela) *relstart; + + DEBUG( DEBUG_ELF, "processing DT_RELA section" ); + if( relasz == (size_t) -1 ) + { + fprintf( stderr, "libcapsule: DT_RELA section not accompanied by DT_RELASZ, ignoring" ); + break; + } + relstart = fix_addr( base, entry->d_un.d_ptr ); + process_rela( relstart, relasz, strtab, strsz, symtab, symsz, base, data ); + } + else + { + DEBUG( DEBUG_ELF, + "skipping DT_RELA section: no handler" ); + } + break; + + ALREADY_DID( DT_RELASZ ) + IGNORE( DT_RELAENT ) + IGNORE( DT_STRSZ ) + IGNORE( DT_SYMENT ) + IGNORE( DT_INIT ) + IGNORE( DT_FINI ) + IGNORE( DT_SONAME ) + IGNORE( DT_RPATH ) + IGNORE( DT_SYMBOLIC ) + + case DT_REL: + if( process_rel != NULL ) + { + const ElfW(Rel) *relstart; + + DEBUG( DEBUG_ELF, "processing DT_REL section" ); + if( relsz == (size_t) -1 ) + { + fprintf( stderr, "libcapsule: DT_REL section not accompanied by DT_RELSZ, ignoring" ); + break; + } + relstart = fix_addr( base, entry->d_un.d_ptr ); + process_rel( relstart, relsz, strtab, strsz, symtab, symsz, base, data ); + } + else + { + DEBUG( DEBUG_ELF, "skipping DT_REL section: no handler" ); + } + break; + + ALREADY_DID( DT_RELSZ ) + IGNORE( DT_RELENT ) + ALREADY_DID( DT_PLTREL ) + IGNORE( DT_DEBUG ) + IGNORE( DT_TEXTREL ) + + case DT_JMPREL: + if( jmprelsz == (size_t) -1 ) + { + fprintf( stderr, "libcapsule: DT_JMPREL section not accompanied by DT_PLTRELSZ, ignoring" ); + break; + } + + if( jmpreltype == DT_NULL ) + { + fprintf( stderr, "libcapsule: DT_JMPREL section not accompanied by DT_PLTREL, ignoring" ); + break; + } + + switch( jmpreltype ) + { + case DT_REL: + if( process_rel != NULL ) + { + const ElfW(Rel) *relstart; + + DEBUG( DEBUG_ELF, + "processing DT_JMPREL/DT_REL section" ); + relstart = fix_addr( base, entry->d_un.d_ptr ); + DEBUG( DEBUG_ELF, " -> REL entry #0 at %p", relstart ); + ret = process_rel( relstart, jmprelsz, strtab, strsz, + symtab, symsz, base, data ); + } + else + { + DEBUG( DEBUG_ELF, + "skipping DT_JMPREL/DT_REL section: no handler" ); + } + break; + + case DT_RELA: + if( process_rela != NULL ) + { + const ElfW(Rela) *relstart; + + DEBUG( DEBUG_ELF, + "processing DT_JMPREL/DT_RELA section" ); + relstart = fix_addr( base, entry->d_un.d_ptr ); + ret = process_rela( relstart, jmprelsz, strtab, strsz, + symtab, symsz, base, data ); + } + else + { + DEBUG( DEBUG_ELF, + "skipping DT_JMPREL/DT_RELA section: no handler" ); + } + break; + + default: + DEBUG( DEBUG_ELF, + "Unknown DT_PLTREL value: %d (expected %d or %d)", + jmpreltype, DT_REL, DT_RELA ); + ret = 1; + break; + } + break; + + IGNORE( DT_BIND_NOW ) + IGNORE( DT_INIT_ARRAY ) + IGNORE( DT_FINI_ARRAY ) + IGNORE( DT_INIT_ARRAYSZ ) + IGNORE( DT_FINI_ARRAYSZ ) + IGNORE( DT_RUNPATH ) + IGNORE( DT_FLAGS ) + // DT_ENCODING is numerically equal to DT_PREINIT_ARRAY + // so we can't separate them + case DT_ENCODING: + DEBUG( DEBUG_ELF, + "ignoring DT_ENCODING or DT_PREINIT_ARRAY (0x%zx): 0x%zx", + (size_t) DT_ENCODING, (size_t) entry->d_un.d_val); \ + break; + IGNORE( DT_PREINIT_ARRAYSZ ) + IGNORE( DT_NUM ) + + // OS-specifics, in range DT_LOOS to DT_HIOS + + // Uses Dyn.d_un.d_val + IGNORE( DT_GNU_PRELINKED ) + IGNORE( DT_GNU_CONFLICTSZ ) + IGNORE( DT_GNU_LIBLISTSZ ) + IGNORE( DT_CHECKSUM ) + IGNORE( DT_PLTPADSZ ) + IGNORE( DT_MOVEENT ) + IGNORE( DT_MOVESZ ) + IGNORE( DT_FEATURE_1 ) + IGNORE( DT_POSFLAG_1 ) + IGNORE( DT_SYMINSZ ) + IGNORE( DT_SYMINENT ) + + // Uses Dyn.d_un.d_ptr + IGNORE( DT_GNU_HASH ) + IGNORE( DT_TLSDESC_PLT ) + IGNORE( DT_TLSDESC_GOT ) + IGNORE( DT_GNU_CONFLICT ) + IGNORE( DT_GNU_LIBLIST ) + IGNORE( DT_CONFIG ) + IGNORE( DT_DEPAUDIT ) + IGNORE( DT_AUDIT ) + IGNORE( DT_PLTPAD ) + IGNORE( DT_MOVETAB ) + IGNORE( DT_SYMINFO ) + + IGNORE( DT_VERSYM ) + + IGNORE( DT_RELACOUNT ) + IGNORE( DT_RELCOUNT ) + + // Sun-compatible + IGNORE( DT_FLAGS_1 ) + IGNORE( DT_VERDEF ) + IGNORE( DT_VERDEFNUM ) + IGNORE( DT_VERNEED ) + IGNORE( DT_VERNEEDNUM ) + + // Sun-compatible + IGNORE( DT_AUXILIARY ) + IGNORE( DT_FILTER ) + +#undef IGNORE +#undef ALREADY_DID + + default: + DEBUG( DEBUG_ELF, "Ignoring unknown dynamic section entry tag 0x%zx", + (size_t) entry->d_tag ); + break; + } + } + + return ret; +} + diff --git a/subprojects/libcapsule/utils/process-pt-dynamic.h b/subprojects/libcapsule/utils/process-pt-dynamic.h new file mode 100644 index 0000000000000000000000000000000000000000..d2305d5c360a30d9b9c4f9ffac36e104695523cc --- /dev/null +++ b/subprojects/libcapsule/utils/process-pt-dynamic.h @@ -0,0 +1,116 @@ +// Copyright © 2017 Collabora Ltd + +// This file is part of libcapsule. + +// libcapsule is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as +// published by the Free Software Foundation; either version 2.1 of the +// License, or (at your option) any later version. + +// libcapsule 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 Lesser General Public License for more details. + +// You should have received a copy of the GNU Lesser General Public +// License along with libcapsule. If not, see <http://www.gnu.org/licenses/>. + +#pragma once + +#include <dlfcn.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <link.h> +#include "utils/utils.h" +#include "mmap-info.h" + +// these macros are secretly the same for elf32 & elf64: +#define ELFW_ST_TYPE(a) ELF32_ST_TYPE(a) +#define ELFW_ST_BIND(a) ELF32_ST_BIND(a) +#define ELFW_ST_VISIBILITY(a) ELF32_ST_VISIBILITY(a) + +typedef enum +{ + RELOCATION_FLAGS_NONE = 0, + RELOCATION_FLAGS_AVOID_LIBC = (1 << 0), +} relocation_flags; + +typedef struct +{ + capsule_item *relocs; + struct { int success; int failure; } count; + int debug; + char *error; + mmapinfo *mmap_info; + relocation_flags flags; + ptr_list *seen; +} relocation_data; + +/* + * relocate_rela_cb: + * @start: array of relocation entries + * @relasz: number of bytes (not number of structs!) following @start + * @strtab: string table, a series of 0-terminated strings concatenated + * @strsz: length of string table in bytes + * @symtab: symbol table, an array of ElfW(Sym) structs + * @symsz: length of symbol table in bytes + * @base: base address of the shared object + * @data: the same data that was passed to process_pt_dynamic() + * + * Callback used to iterate over relocations. + */ +typedef int (*relocate_rela_cb)(const ElfW(Rela) *start, + const size_t relasz, + const char *strtab, + const size_t strsz, + const ElfW(Sym) *symtab, + const size_t symsz, + void *base, + void *data); + +/* + * relocate_rel_cb: + * @start: beginning of an array of relocation entries + * @relasz: number of bytes (not number of structs!) following @start + * @strtab: string table, a series of 0-terminated strings concatenated + * @strsz: length of string table in bytes + * @symtab: symbol table, an array of ElfW(Sym) structs + * @symsz: length of symbol table in bytes + * @base: base address of the shared object + * @data: the same data that was passed to process_pt_dynamic() + * + * Callback used to iterate over relocations. + */ +typedef int (*relocate_rel_cb)(const ElfW(Rel) *start, + const size_t relasz, + const char *strtab, + const size_t strsz, + const ElfW(Sym) *symtab, + const size_t symsz, + void *base, + void *data); + +int process_dt_rela (const ElfW(Rela) *start, + const size_t relasz, + const char *strtab, + const size_t strsz, + const ElfW(Sym) *symtab, + const size_t symsz, + void *base, + void *data); + +int process_dt_rel (const ElfW(Rel) *start, + const size_t relasz, + const char *strtab, + const size_t strsz, + const ElfW(Sym) *symtab, + const size_t symsz, + void *base, + void *data); + +int process_pt_dynamic (ElfW(Addr) start, + size_t size, + void *base, + relocate_rela_cb process_rela, + relocate_rel_cb process_rel, + void *data); diff --git a/subprojects/libcapsule/utils/tools.c b/subprojects/libcapsule/utils/tools.c new file mode 100644 index 0000000000000000000000000000000000000000..32fcfcaa3b0d73fca391b4d8a25571f7b2c37d6c --- /dev/null +++ b/subprojects/libcapsule/utils/tools.c @@ -0,0 +1,31 @@ +// Copyright © 2017-2019 Collabora Ltd + +// This file is part of libcapsule. + +// libcapsule is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as +// published by the Free Software Foundation; either version 2.1 of the +// License, or (at your option) any later version. + +// libcapsule 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 Lesser General Public License for more details. + +// You should have received a copy of the GNU Lesser General Public +// License along with libcapsule. If not, see <http://www.gnu.org/licenses/>. + +#include "tools.h" + +#include <stdio.h> + +void +_capsule_tools_print_version( const char *command ) +{ + /* This is actually valid/parseable YAML, so that it's both + * human-readable and machine-readable. Inspired by `docker version` + * and `ostree --version`. */ + printf( "%s%s:\n", HOST_PREFIX, command ); + printf( " Package: %s\n", PACKAGE_TARNAME ); + printf( " Version: '%s'\n", PACKAGE_VERSION ); +} diff --git a/subprojects/libcapsule/utils/tools.h b/subprojects/libcapsule/utils/tools.h new file mode 100644 index 0000000000000000000000000000000000000000..ac8f4f75e7d7b915c44cd0df867a50d599e9a4d2 --- /dev/null +++ b/subprojects/libcapsule/utils/tools.h @@ -0,0 +1,20 @@ +// Copyright © 2017-2019 Collabora Ltd + +// This file is part of libcapsule. + +// libcapsule is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as +// published by the Free Software Foundation; either version 2.1 of the +// License, or (at your option) any later version. + +// libcapsule 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 Lesser General Public License for more details. + +// You should have received a copy of the GNU Lesser General Public +// License along with libcapsule. If not, see <http://www.gnu.org/licenses/>. + +#pragma once + +void _capsule_tools_print_version( const char *command ); diff --git a/subprojects/libcapsule/utils/utils.c b/subprojects/libcapsule/utils/utils.c new file mode 100644 index 0000000000000000000000000000000000000000..915836297378307e265065f7f4cfe255ce4c9d3c --- /dev/null +++ b/subprojects/libcapsule/utils/utils.c @@ -0,0 +1,726 @@ +// Copyright © 2017 Collabora Ltd + +// This file is part of libcapsule. + +// libcapsule is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as +// published by the Free Software Foundation; either version 2.1 of the +// License, or (at your option) any later version. + +// libcapsule 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 Lesser General Public License for more details. + +// You should have received a copy of the GNU Lesser General Public +// License along with libcapsule. If not, see <http://www.gnu.org/licenses/>. + +#include <assert.h> +#include <sys/param.h> +#include <unistd.h> +#include <fcntl.h> +#include <string.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include "debug.h" +#include "utils.h" + +unsigned long debug_flags; + +// ========================================================================== +// these are for finding entries in the dynamic section +// note that tha d_un.d_ptr member may be pre-offset by the +// linker, or we may beed to adjust it by the value of base ourselves: +// this is effectively private linker information and there's no +// hard and fast rule: +const void * +fix_addr (const void *base, ElfW(Addr) offset_or_addr) +{ + if (offset_or_addr < (ElfW(Addr)) base) + { + // Assume it's an offset, so an address relative to addr + return base + offset_or_addr; + } + else + { + // Assume it's an absolute address + return (const void *) offset_or_addr; + } +} + +const ElfW(Dyn) * +find_dyn (ElfW(Addr) base, void *start, int what) +{ + ElfW(Dyn) *entry = start + base; + + for( ; entry->d_tag != DT_NULL; entry++ ) + if( entry->d_tag == what ) + return entry; + + return NULL; +} + +size_t +find_value (ElfW(Addr) base, void *start, int what) +{ + const ElfW(Dyn) *entry = find_dyn( base, start, what ); + return entry ? entry->d_un.d_val : (size_t) -1; +} + +ElfW(Addr) +find_ptr (ElfW(Addr) base, void *start, int what) +{ + const ElfW(Dyn) *entry = find_dyn( base, start, what ); + + if( entry ) + { + if( entry->d_un.d_ptr < base ) + return base + entry->d_un.d_ptr; + else + return entry->d_un.d_ptr; + } + + return (ElfW(Addr)) NULL; +} + +/* + * dynamic_section_find_strtab: + * @start: Array of dynamic section entries + * @base: Starting address of the shared object in memory + * @siz: (out) (optional): Used to return the length of the string + * table + * + * Find the string table for the given dynamic section. + * + * Returns: (nullable): The string table, a series of concatenated + * 0-terminated strings whose total size is written through @siz if + * non-%NULL, or %NULL if not found + */ +const char * +dynamic_section_find_strtab (const ElfW(Dyn) *entries, const void *base, size_t *siz) +{ + const ElfW(Dyn) *entry; + ElfW(Addr) stab = 0; + + for( entry = entries; entry->d_tag != DT_NULL; entry++ ) + { + if( entry->d_tag == DT_STRTAB ) + { + stab = entry->d_un.d_ptr; + } + else if( entry->d_tag == DT_STRSZ ) + { + if( siz ) + *siz = entry->d_un.d_val; + } + } + + if (stab == 0) + { + return NULL; + } + else if (stab < (ElfW(Addr)) base) + { + return base + stab; + } + else + { + return (const char *) stab; + } +} + +/* + * find_symbol: + * @idx: The symbol index + * @stab: (array element-type=ElfW(Sym)): The symbol table + * @symsz: The length of the symbol table in bytes + * @str: The string table + * @strsz: The length of the string table in bytes + * @name: (out) (optional): Used to return the name, a pointer into @str + * + * If @idx is in-bounds for @stab, return a pointer to the @idx'th + * entry in @stab. Otherwise return %NULL. + */ +const ElfW(Sym) * +find_symbol (int idx, const ElfW(Sym) *stab, size_t symsz, const char *str, size_t strsz, const char **name) +{ + const ElfW(Sym) *target = stab + idx; + + if( idx < 0 ) + return NULL; + + // we could just accept the index as legitimate but then we'd + // run the risk of popping off into an unknown hyperspace coordinate + // this way we stop if the target is past the known end of the table: + if( (idx + 1) * sizeof(*target) > symsz ) + return NULL; + + if( name ) + *name = str + target->st_name; + + return target; +} + +// ========================================================================== + +// like strncpy except it guarantees the final byte is NUL in +// case we truncated the string. does not yet warn you in any +// way about truncation though, should probably fix that: +char *safe_strncpy (char *dest, const char *src, size_t n) +{ + char *rv = strncpy( dest, src, n - 1 ); + dest[ n - 1 ] = '\0'; + return rv; +} + +// prefix is the root of the external tree we're patching in +// with libcapsule, path is what we're trying to resolve if +// it is a symlink. path must have at least PATH_MAX chars allocated. +// +// Designed to be called repeatedly, starting with an ABSOLUTE +// path the first time. Will write the resolved link back into +// path each time and return true, until fed a path which is +// not a symlink, at which point it will leave path alone and +// return false: +int resolve_link(const char *prefix, char path[PATH_MAX]) +{ + int dfd; + char rl[PATH_MAX]; + char dir[PATH_MAX]; + char *end = NULL; + int rv = 0; + + safe_strncpy( dir, path, PATH_MAX ); + end = strrchr( dir, '/' ); + + if( end ) + *end = '\0'; + else + strcpy( dir, "." ); // not sure this is right, FIXME? + // but as long as the first call to us + // in any sequence was an absolute path + // this will never come up + dfd = open( dir, O_RDONLY ); + + if( dfd < 0 ) + return 0; + + rv = readlinkat( dfd, path, rl, sizeof(rl) ); + + if( rv == sizeof(rl) ) + { + // Either rl was truncated, or there is no longer space for '\0' + rv = -1; + goto out; + } + + if( rv >= 0 ) + { + rl[ rv ] = '\0'; + + if( rl[0] == '/' ) + { + if( build_filename( path, PATH_MAX, prefix, rl, NULL ) >= PATH_MAX ) + { + goto out; + } + } + else + { + if( build_filename( path, PATH_MAX, dir, rl, NULL ) >= PATH_MAX ) + { + goto out; + } + } + } + +out: + close( dfd ); + + return rv != -1; +} + +// todo - check this properly for actual word boundaries and +// make it warn about unknown debug keywords: +void set_debug_flags (const char *control) +{ + debug_flags = DEBUG_NONE; + + if ( !control ) + return; + + if( strstr( control, "path" ) ) debug_flags |= DEBUG_PATH; + if( strstr( control, "search" ) ) debug_flags |= DEBUG_SEARCH; + if( strstr( control, "ldcache" ) ) debug_flags |= DEBUG_LDCACHE; + if( strstr( control, "capsule" ) ) debug_flags |= DEBUG_CAPSULE; + if( strstr( control, "mprotect" ) ) debug_flags |= DEBUG_MPROTECT; + if( strstr( control, "wrappers" ) ) debug_flags |= DEBUG_WRAPPERS; + if( strstr( control, "reloc" ) ) debug_flags |= DEBUG_RELOCS; + if( strstr( control, "elf" ) ) debug_flags |= DEBUG_ELF; + if( strstr( control, "dlfunc" ) ) debug_flags |= DEBUG_DLFUNC; + if( strstr( control, "tool" ) ) debug_flags |= DEBUG_TOOL; + if( strstr( control, "all" ) ) debug_flags |= DEBUG_ALL; + + if( !debug_flags ) + return; + + fprintf(stderr, "capsule debug flags: \n" + " path : %c # path manipulation and translation" "\n" + " search : %c # searching for DSOs" "\n" + " ldcache : %c # loading/processing the ld cache" "\n" + " capsule : %c # setting up the proxy capsule" "\n" + " mprotect: %c # handling mprotect (for RELRO)" "\n" + " wrappers: %c # function wrappers installed in the capsule" "\n" + " reloc : %c # patching capsule symbols into external DSOs" "\n" + " dlfunc : %c # special handling of dlopen/dlsym calls" "\n" + " elf : %c # detailed ELF introspection logging" "\n" + " tool : %c # command-line tools" "\n", + (debug_flags & DEBUG_PATH ) ? 'Y' : 'n' , + (debug_flags & DEBUG_SEARCH ) ? 'Y' : 'n' , + (debug_flags & DEBUG_LDCACHE ) ? 'Y' : 'n' , + (debug_flags & DEBUG_CAPSULE ) ? 'Y' : 'n' , + (debug_flags & DEBUG_MPROTECT) ? 'Y' : 'n' , + (debug_flags & DEBUG_WRAPPERS) ? 'Y' : 'n' , + (debug_flags & DEBUG_RELOCS ) ? 'Y' : 'n' , + (debug_flags & DEBUG_DLFUNC ) ? 'Y' : 'n' , + (debug_flags & DEBUG_ELF ) ? 'Y' : 'n' , + (debug_flags & DEBUG_TOOL ) ? 'Y' : 'n' ); +} + +// soname: bare libfoo.so.X style name +// path: [possibly absolute] path to DSO +// return true if soname: libFOO.so.X matches +// path: /path/to/libFOO.so.X.Y or /path/to/libFOO.so.X +int soname_matches_path (const char *soname, const char *path) +{ + const char *path_soname = strrchr( path, '/' ); + const char *pattern = path_soname ? path_soname + 1: path; + const size_t slen = strlen( soname ); + + if( strncmp( soname, pattern, slen ) != 0 ) + return 0; + + const char *end = pattern + slen; + + return ( *end == '\0' || *end == '.' ); +} + +/* + * build_filename_va: + * @buf: a buffer + * @len: length of buffer + * @first_path: an absolute or relative path + * @ap: further path segments + * + * Fill @buf with a copy of @first_path, with subsequent path segments + * appended to it. + * + * Returns: The number of bytes that would have been used in buf, not + * including the '\0', if there was enough space. If this is >= len, + * then truncation has occurred. + */ +size_t +build_filename_va (char *buf, size_t len, const char *first_path, va_list ap) +{ + const char *path; + size_t used = 0; + int first = 1; + int need_separator = 0; + + + if( len > 0 ) + *buf = '\0'; + + for( path = first_path, first = 1; + path != NULL; + path = va_arg( ap, const char * ), first = 0) + { + size_t path_len; + + // Collapse any leading '//' to '/' + while( path[0] == '/' && path[1] == '/' ) + path++; + + // If this is not the first path segment, strip any leading '/' + if( path[0] == '/' && !first ) + path++; + + path_len = strlen( path ); + + // Collapse any trailing '/' to nothing, unless this is the + // first path segment, in which case collapse them to just '/' + while( path_len > (first ? 1 : 0) && path[path_len - 1] == '/' ) + { + path_len--; + } + + // Ensure there is a '/' before we append path if necessary + if( need_separator ) + { + used++; + if( used < len ) + { + buf[used - 1] = '/'; + buf[used] = '\0'; + } + } + + // If there's still any space left, try to append the path + if( used < len ) + { + strncpy( buf + used, path, MIN( len - used, path_len + 1 ) ); + buf[len - 1] = '\0'; + } + + used += path_len; + + // Next time, we need to append a separator, unless this was + // the first path segment and it was '/' + need_separator = (path_len == 0 || path[path_len - 1] != '/'); + } + + va_end( ap ); + return used; +} + +/* + * build_filename: + * @buf: a buffer + * @len: length of buffer + * @first_path: an absolute or relative path + * @...: further path segments + * + * Fill @buf with a copy of @first_path, with subsequent path segments + * appended to it. + * + * Returns: The number of bytes that would have been used in buf, not + * including the '\0', if there was enough space. If this is >= len, + * then truncation has occurred. + */ +size_t +build_filename (char *buf, size_t len, const char *first_path, ...) +{ + size_t used; + va_list ap; + + va_start( ap, first_path ); + used = build_filename_va( buf, len, first_path, ap ); + va_end( ap ); + + return used; +} + +/* + * build_filename_alloc: + * @first_path: an absolute or relative path + * @...: further path segments + * + * Allocate and return a string built from @first_path and subsequent + * segments. Abort if there is not enough memory. + * + * Returns: A string that can be freed with free() + */ +char * +build_filename_alloc (const char *first_path, ...) +{ + char *buf; + size_t allocate; + size_t len; + va_list ap; + + // Do a first pass to count how much space we're going to need + va_start( ap, first_path ); + // We need an extra byte for the "\0" which isn't included in the + // result, consistent with strlcpy() + allocate = build_filename_va( NULL, 0, first_path, ap ) + 1; + va_end( ap ); + + buf = xrealloc( NULL, allocate ); + + // Iterate over the arguments again to fill buf + va_start( ap, first_path ); + len = build_filename_va( buf, allocate, first_path, ap ); + va_end( ap ); + + // build_filename_va() returns the same thing every time. In + // particular this means truncation did not occur, because that + // would be indicated by len >= allocate. + assert( len + 1 == allocate ); + + return buf; +} + +ptr_list * +ptr_list_alloc(size_t size) +{ + ptr_list *list = xcalloc( 1, sizeof(ptr_list) ); + + list->loc = xcalloc( size, sizeof(ptr_item) ); + list->allocated = size; + list->next = 0; + return list; +} + +/* + * ptr_list_free_to_array: + * @list: (transfer full): list to free + * @n: (out) (optional): if not %NULL, used to return the number of + * items that were in @list + * + * Add an extra %NULL entry to @list, then return an array with the + * same contents as @list, freeing @list. If @n is not %NULL, set it + * to the number of elements in the returned array before the %NULL + * added by this function. + */ +void ** +ptr_list_free_to_array (ptr_list *list, size_t *n) +{ + void *ret; + + // Without these assertions, it's unsafe to assign a ptr_item * + // to a void ** + static_assert( alignof( void * ) == alignof( ptr_item ), + "union {void *, ElfW(Addr)} assumed to be same size " + "as void *"); + static_assert( sizeof( void * ) == sizeof( ptr_item ), + "union {void *, ElfW(Addr)} assumed to be same " + "alignment as void *"); + + if( n != NULL ) + *n = list->next; + + ptr_list_push_ptr( list, NULL ); + ret = list->loc; + list->loc = NULL; + ptr_list_free (list); + return ret; +} + +void +ptr_list_free (ptr_list *list) +{ + free( list->loc ); + list->loc = NULL; + list->allocated = 0; + list->next = 0; + free( list ); +} + +void +ptr_list_push_addr (ptr_list *list, ElfW(Addr) addr) +{ + if( list->next >= list->allocated ) + { + list->loc = xrealloc( list->loc, + (list->allocated + 16) * sizeof(ptr_item) ); + list->allocated += 16; + } + + list->loc[ list->next++ ].addr = addr; +} + + +void +ptr_list_push_ptr (ptr_list *list, void *ptr) +{ + if( list->next >= list->allocated ) + { + list->loc = realloc( list->loc, + (list->allocated + 16) * sizeof(ptr_item) ); + if( !list->loc ) + { + fprintf( stderr, "failed to realloc ptr_list\n" ); + abort(); + } + + list->allocated += 16; + } + + list->loc[ list->next++ ].ptr = ptr; +} + +int +ptr_list_add_ptr (ptr_list *list, void *ptr, ptrcmp equals) +{ + for( size_t n = 0; n < list->next; n++ ) + if( equals( list->loc[ n ].ptr, ptr ) ) + return 0; + + ptr_list_push_ptr( list, ptr ); + + return 1; +} + +int +ptr_list_contains (ptr_list *list, ElfW(Addr) addr) +{ + if( list->next == 0 ) + return 0; + + for( size_t n = 0; n < list->next; n++ ) + if( list->loc[ n ].addr == addr ) + return 1; + return 0; +} + +void * +ptr_list_nth_ptr (ptr_list *list, size_t nth) +{ + if( nth < list->next ) + return list->loc[ nth ].ptr; + + return NULL; +} + +void +oom( void ) +{ + fprintf( stderr, "libcapsule: out of memory\n" ); + abort(); +} + +char * +xstrdup( const char *s ) +{ + char *ret = strdup( s ); + + if (s != NULL && ret == NULL) + oom(); + + return ret; +} + +void * +xrealloc( void *ptr, size_t size ) +{ + void *ret = realloc( ptr, size ); + + if( ptr != NULL && size != 0 && ret == NULL ) + oom(); + + return ret; +} + +void * +xcalloc( size_t n, size_t size ) +{ + void *ret = calloc( n, size ); + + if( n != 0 && size != 0 && ret == NULL ) + oom(); + + return ret; +} + +int +xasprintf( char **dest, const char *format, ...) +{ + int ret; + va_list ap; + + va_start( ap, format ); + ret = vasprintf( dest, format, ap ); + va_end( ap ); + + if( ret < 0 ) + oom(); + + return ret; +} + +/** + * free_strv_full: + * @strings_array: (nullable) (array zero-terminated=1): array of strings + * to free + * + * Completely frees the given @strings_array. + * If @strings_array is %NULL, this function simply returns. + */ +void +free_strv_full( char **strings_array ) +{ + if( strings_array ) + { + for( size_t i = 0; strings_array[i] != NULL; i++ ) + free( strings_array[i] ); + free( strings_array ); + } +} + +/* + * _capsule_set_error_literal: + * @code_dest: (out) (optional): used to return an errno value + * @message_dest: (out) (optional) (transfer full): used to return an + * error message + * @code: an errno value + * @message: an error message + * + * Set an error code, like g_set_error_literal(). + */ +void +_capsule_set_error_literal( int *code_dest, char **message_dest, + int code, const char *message ) +{ + if( code_dest != NULL ) + *code_dest = code; + + if( message_dest != NULL ) + *message_dest = xstrdup( message ); +} + +/* + * _capsule_set_error: + * @code_dest: (out) (optional): used to return an errno value + * @message_dest: (out) (optional) (transfer full): used to return an + * error message + * @code: an errno value + * @message: an error message + * + * Set an error code, like g_set_error(). + */ +void +_capsule_set_error( int *code_dest, char **message_dest, + int code, const char *format, + ... ) +{ + va_list ap; + + if( code_dest != NULL ) + *code_dest = code; + + if( message_dest != NULL ) + { + va_start( ap, format ); + + if( vasprintf( message_dest, format, ap ) < 0 ) + oom(); + + va_end( ap ); + } +} + +/* + * _capsule_basename: + * @path: A path + * + * Return the part of @path after the last directory separator, or @path + * if there is no directory separator at all, or an empty string if @path + * ends with a directory separator. + * + * Equivalent to GNU basename(3) from string.h, but not POSIX + * basename(3) from libgen.h. + * + * Returns: (transfer none): A pointer into @path + */ +const char * +_capsule_basename (const char *path) +{ + const char *ret = strrchr( path, '/' ); + + if( ret == NULL ) + return path; + + assert( ret[0] == '/' ); + return ret + 1; +} diff --git a/subprojects/libcapsule/utils/utils.h b/subprojects/libcapsule/utils/utils.h new file mode 100644 index 0000000000000000000000000000000000000000..66316b0cd25b7b464429adade6fa47bacb694a85 --- /dev/null +++ b/subprojects/libcapsule/utils/utils.h @@ -0,0 +1,238 @@ +// Copyright © 2017-2020 Collabora Ltd + +// This file is part of libcapsule. + +// libcapsule is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as +// published by the Free Software Foundation; either version 2.1 of the +// License, or (at your option) any later version. + +// libcapsule 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 Lesser General Public License for more details. + +// You should have received a copy of the GNU Lesser General Public +// License along with libcapsule. If not, see <http://www.gnu.org/licenses/>. + +#pragma once + +#include <assert.h> +#include <limits.h> +#include <stdarg.h> +#include <stddef.h> +#include <stdlib.h> + +#include <link.h> // for __ELF_NATIVE_CLASS + +#include "debug.h" + +#ifdef HAVE_STDALIGN_H +#include <stdalign.h> +#endif + +#define UNLIKELY(x) __builtin_expect(x, 0) +#define LIKELY(x) __builtin_expect(x, 1) + +// these macros are secretly the same for elf32 & elf64: +#define ELFW_ST_TYPE(a) ELF32_ST_TYPE(a) +#define ELFW_ST_BIND(a) ELF32_ST_BIND(a) +#define ELFW_ST_VISIBILITY(a) ELF32_ST_VISIBILITY(a) + +#if __ELF_NATIVE_CLASS == 64 +#define FMT_OFF "lu" +#define FMT_SWORD "lu" +#define FMT_WORD "ld" +#define FMT_SIZE "lu" +#define FMT_ADDR "ld" +#define FMT_XADDR "lx" +#define FMT_XU64 "lx" +#else +#define FMT_OFF "u" +#define FMT_SWORD "u" +#define FMT_WORD "d" +#define FMT_SIZE "u" +#define FMT_ADDR "d" +#define FMT_XADDR "x" +#define FMT_XU64 "llx" +#endif + +typedef union ptr_item +{ + ElfW(Addr) addr; + void *ptr; +} ptr_item; + +typedef struct ptr_list +{ + size_t allocated; + size_t next; + ptr_item *loc; +} ptr_list; + +typedef int (*ptrcmp) (const void *a, const void *b); + +ptr_list *ptr_list_alloc (size_t size); +void *ptr_list_nth_ptr (ptr_list *list, size_t nth); +void ptr_list_free (ptr_list *list); +void **ptr_list_free_to_array (ptr_list *list, size_t *n); +void ptr_list_push_ptr (ptr_list *list, void *ptr); +void ptr_list_push_addr (ptr_list *list, ElfW(Addr) addr); +int ptr_list_contains (ptr_list *list, ElfW(Addr) addr); +int ptr_list_add_ptr (ptr_list *list, void *ptr, ptrcmp equals); + +#define strstarts(str, start) \ + (strncmp( str, start, strlen( start ) ) == 0) + +char *safe_strncpy (char *dest, const char *src, size_t n); +int resolve_link (const char *prefix, char path[PATH_MAX]); +int soname_matches_path (const char *soname, const char *path); +size_t build_filename_va (char *buf, size_t len, const char *first_path, va_list ap); +size_t build_filename (char *buf, size_t len, const char *first_path, ...) __attribute__((sentinel)); +char *build_filename_alloc (const char *first_path, ...) __attribute__((sentinel)); + +const void * fix_addr (const void *base, ElfW(Addr) offset_or_addr); +const ElfW(Dyn) * find_dyn (ElfW(Addr) base, void *start, int what); +size_t find_value (ElfW(Addr) base, void *start, int what); +ElfW(Addr) find_ptr (ElfW(Addr) base, void *start, int what); +const char * dynamic_section_find_strtab (const ElfW(Dyn) *entries, const void *base, size_t *siz); +const ElfW(Sym) * find_symbol (int idx, + const ElfW(Sym) *stab, + size_t symsz, + const char *str, + size_t strsz, + const char **name); + +void oom( void ) __attribute__((noreturn)); +char *xstrdup( const char *s ); +void *xrealloc( void *ptr, size_t size ) __attribute__((alloc_size(2))); +void *xcalloc( size_t n, size_t size ) __attribute__((alloc_size(1, 2), malloc)); +int xasprintf( char **s, const char *format, ...) __attribute__((format(printf, 2, 3))); +void free_strv_full( char **strings_array ); + +/* + * _capsule_steal_pointer: + * @pp: A non-%NULL pointer to any pointer type (void ** or Something **) + * + * Transfer the contents of @pp to the caller, setting @pp to %NULL. + * This is essentially the same thing as g_steal_pointer(). + */ +static inline void * +_capsule_steal_pointer( void *pp ) +{ + typedef void *__attribute__((may_alias)) voidp_alias; + voidp_alias *pointer_to_pointer = pp; + void *ret = *pointer_to_pointer; + *pointer_to_pointer = NULL; + return ret; +} + +/* + * _capsule_propagate_error: + * @code_dest: (out) (optional): where to put the error code (errno), or %NULL + * @message_dest: (out) (transfer full) (optional) (nullable): where to + * put the message, or %NULL + * @code_src: the error code (errno) + * @message_src: (nullable) (transfer full): the error message + * + * This is essentially the same thing as g_propagate_error(), and leaves + * message_src undefined. It should typically be used as: + * + * _capsule_propagate_error( &code, &message, local_code, + * _capsule_steal_pointer( &local_message ) ); + */ +static inline void +_capsule_propagate_error( int *code_dest, char **message_dest, int code_src, char *message_src ) +{ + if( code_dest != NULL ) + *code_dest = code_src; + + if( message_dest != NULL ) + *message_dest = message_src; + else + free( message_src ); +} + +void _capsule_set_error_literal( int *code_dest, char **message_dest, + int code, const char *message ); + +void _capsule_set_error( int *code_dest, char **message_dest, + int code, const char *format, ... ) + __attribute__((format(printf, 4, 5))); + +/* + * N_ELEMENTS: + * @array: A fixed-size array (not a pointer!) + * + * Same as `G_N_ELEMENTS`, `_DBUS_N_ELEMENTS`, systemd `ELEMENTSOF`, etc. + */ +#define N_ELEMENTS(array) ( sizeof(array) / sizeof(array[0]) ) + +/* + * _capsule_clear: + * @pp: A pointer to a pointer that can be freed by Standard C `free()`, + * i.e. type `void **`, `char **` or more rarely `something **` + * + * Free whatever object is pointed to by `*pp`, and set `*pp` to NULL. + */ +static inline void +_capsule_clear( void *pp ) +{ + free( _capsule_steal_pointer( pp ) ); +} + +/* + * _capsule_cleanup: + * @clear: + * + * An attribute marking a variable to be cleared by `clear(&variable)` + * on exit from its scope. + */ +#define _capsule_cleanup(clear) __attribute__((cleanup(clear))) + +/* + * _capsule_autofree: + * + * An attribute marking a variable to be freed by `free(variable)` + * on exit from its scope. + */ +#define _capsule_autofree _capsule_cleanup(_capsule_clear) + +/* + * alignof: + * @type: A type + * + * The same as in Standard C: return the minimal alignment of a type. + * + * Note that this is not the same as gcc __alignof__, which returns the + * type's *preferred* alignment, which is sometimes greater than the + * *minimal* alignment returned by this macro. + */ +#ifndef alignof +# define alignof(type) (__builtin_offsetof(struct { char a; type b; }, b)) +#endif + +/* + * offsetof: + * @t: A `struct` type + * @m: A member + * + * The same as in Standard C: return the offset of @m within @t. + */ +#ifndef offsetof +# define offsetof(t, m) (__builtin_offsetof(t, m)) +#endif + +/* + * static_assert: + * @expr: An expression to evaluate at compile-time + * @message: A diagnostic message used if @expr is not true + * + * The same as in Standard C: evaluate @expr as a compile-time constant + * expression, and fail to build if it is zero. + */ +#ifndef static_assert +# define static_assert(expr, message) _Static_assert(expr, message) +#endif + +const char *_capsule_basename (const char *path);