abicheck-1.2/0040755015017100001440000000000007723122041012323 5ustar krungeusersabicheck-1.2/CVS/0040755015017100001440000000000007723122041012756 5ustar krungeusersabicheck-1.2/CVS/Root0100644015017100001440000000004307723122041013616 0ustar krungeusers:ext:krunge@cvs1:/cvsroot/abicheck abicheck-1.2/CVS/Repository0100644015017100001440000000001107723122041015045 0ustar krungeusersabicheck abicheck-1.2/CVS/Entries0100644015017100001440000000045107723122041014307 0ustar krungeusers/COPYING/1.1.1.1/Thu Feb 7 14:40:20 2002// /ChangeLog/1.1/Wed Jun 19 01:59:21 2002// /INTRO/1.1.1.1/Thu Feb 7 14:40:20 2002// /Makefile/1.2/Tue Jun 18 18:20:10 2002// /README/1.3/Tue Jun 18 16:40:13 2002// /abicheck.1/1.3/Tue Aug 26 19:57:51 2003// /abicheck.pl/1.3/Tue Aug 26 19:57:51 2003// D abicheck-1.2/CVS/Entries.Log0100644015017100001440000000001507723122041015023 0ustar krungeusersA D/test//// abicheck-1.2/ChangeLog0100644015017100001440000000211507503762571014107 0ustar krungeusersVersion 1.1 RELEASE (6/18/2002) - Added information from GLIBC sourcebase (2.3) wrt the new GLIBC_PRIVATE version name. This is a bootstrapping measure to push back this important info to the tool running on earlier GLIBC releases. - Fixed a bug in parsing the LD_DEBUG output in newer GLIBC dynamic linkers: binding file abc to xyz: normal symbol `foo' [GLIBC_2.0] earlier: binding file abc to xyz: symbol `foo' [GLIBC_2.0] - Fixed some of the tests on more recent GLIBC environments, it turns out that __open() is a public symbol, so not a good test for a private binding. - Added an option -a to search through the binaries to be checked, collect all of the shared libraries, and then behave as though their basenames had been specified via the -l option. This is a simple way to check all "internal" bindings rather than just the direct ones. Useful if the internal shared libraries do not have their dependencies recorded. - Added an option -j to run jobs in parallel. Useful speedup on SMP machines. Version 1.0 RELEASE (2/7/2002) - Initial release. abicheck-1.2/COPYING0100644015017100001440000006347607430510724013377 0ustar krungeusers GNU LESSER GENERAL PUBLIC LICENSE Version 2.1, February 1999 Copyright (C) 1991, 1999 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 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. Copyright (C) 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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. , 1 April 1990 Ty Coon, President of Vice That's all there is to it! abicheck-1.2/Makefile0100644015017100001440000000066007503674732014001 0ustar krungeusers# Copyright (c) 2001 by Sun Microsystems, Inc. # All rights reserved. all: abicheck abicheck: abicheck.pl cat abicheck.pl > abicheck chmod +x abicheck test: abicheck FRC cd test; $(MAKE) clean: rm -f abicheck clobber: clean cd test; $(MAKE) clobber DIR = abicheck-0.0 tarball: clobber rm -f $(DIR); ln -s . $(DIR) tar -cvf abicheck.tar `find $(DIR)/* -type f | egrep -v 'SCCS|CKPT|abicheck.tar'` rm -f $(DIR) FRC: abicheck-1.2/INTRO0100644015017100001440000007353307430510724013155 0ustar krungeusers Library Interface Definition and Versioning ------- --------- ---------- --- ---------- Introduction: ------------- This project describes how techniques and tools used in Solaris for library interface definition and binary compatibility may also prove beneficial to Free and Open Source software development projects. Indeed, a number of the practices we describe here have already been adopted by the Linux GLIBC C library project [1,2]. The underlying theme of this project is working to ensure release-to-release binary stability: end-user's systems and applications keep on working even when other components of the system are upgraded. The process we describe involves library developers (for example GLIBC, X11, GNOME, KDE, ...) defining the public interfaces of their libraries and continuing to provide those interfaces in an upward compatible manner. Any exposed internal interfaces that are not intended for application developer consumption are clearly marked as private. These private interfaces are part of the internal implementation (used, say, for communication within the package) and so do not need to evolve compatibly: by keeping developers off of these private interfaces the library system is free to evolve and modify the implementation aspect without breaking end-users. Also included in the process we describe is the practice of scoping local to each library as many symbols as possible to further reduce exposure to application breakage. In general, the method we describe below is a useful scheme in terms of providing compatibility to a large established end-user population. This is because the costs of binary breakage (downtime, fixing, rebuilding and re-testing applications) is quite noticeable in that situation. We suspect that system distribution providers may find the techniques we describe to be most worthwhile, because they can be used to improve compatibility and stability for end users using their distribution. In the long term, however, basically everyone benefits from improved compatibility. In a certain sense this project is complimentary to standards projects, the most important being the Linux Standard Base [3]. One way of looking at the difference is the Linux standard is working on compatibility *across* different Linux distributions, whereas the mechanisms discussed here are more focussed on the compatibility of a given distribution going forward in *time*: to avoid end-user application breakage as he upgrades components of his system (in particular, the entire distribution). Both types of compatibility work are important. To a certain extent they overlap each other in what they accomplish, yet they also help each other out by focusing on different areas. It should be noted that the documentation and tools provided by this project do not (and cannot) by themselves provide a complete solution to the binary instability problem. Foremost, the project plan requires the participation and commitment of library providers to be successful. The more libraries (API/ABI's) that follow this plan, the more binary stability is enhanced and pays-off as time goes on. In addition, this project concentrates on defining and maintaining only a certain part of the interfaces an application depends upon (namely, the library binary interface between it and the "system" shared libraries). This scheme of course cannot stop all compatibility problems (e.g. changes in file formats or file locations), but historically since a good fraction of incompatibility occurs at this interface it is a good place to begin working. What is the ABI: ---------------- The Application Binary Interface (ABI) is the set of supported run-time interfaces available for an application to use on the OS. The ABI is very similar to the API (Application Programming Interface), but differs in that it is the result of the source compilation process. C source code written to the OS API is transformed by the C compiler into a processor architecture specific binary for one of the ABI's (e.g. 32-bit or 64-bit address spaces) supported by the system. The compilation process introduces several differences between the ABI and API which are important for binary compatibility: * Compiler directives (e.g., #define) can replace source-level constructs with different ones. The resulting binary may lack a symbol present in the source, or include one not present in the source. * The compiler may generate processor-specific symbols (e.g., arithmetic instructions) which invisibly augment or replace source constructs. * The compiler's layout of binaries may be specific to that compiler and the versions of the source language which it accepts. Thus identical code compiled with different compilers may produce incompatible binaries; this has been the case with C++. The ABI is essentially where binaries of differing origins meet and have to *work* together as a single process to accomplish the task at hand for the application. There are many opportunities for failure, e.g.: * Missing libraries or shared objects. * Missing interfaces. * Incompatible changes in library interfaces. * Libraries needed by an application that, in turn, depend on a different (often incompatible) version of a third library that is also needed by the application. * Incompatible changes in the output and behavior of system commands, utilities, and files. and so on. All of these need to be guarded against or otherwise applications will fail for the end-user after parts of their system or software are upgraded or replaced. There will be benefits if system integrators and ABI providers (e.g. library developers) can work successfully at further reducing exposure to application breakage. It may appear to be asking too much of the system integrators and library developers to do even more work with respect to compatibility (especially since much of this work is done on a volunteer and/or gratis basis), however the pay-off can be huge: the tens of millions of end-users vastly outnumber the number of developers. The ABI is important because it determines whether or not a binary built on one release of the OS is able to run on subsequent releases. This release-to-release binary compatibility is of increasing importance to users because it means that their investment in applications can be preserved across upgrades. Put another way, fear of application breakage due to binary incompatibility is the single biggest reason for user reluctance to adopt a new release of system software and technology; binary compatibility allays that fear. Defining the ABI: ----------------- Today a standard installation of a Unix operating system will have roughly 20,000 public symbol interfaces exported by the libraries it provides. The number of private interfaces in the libraries (used for intra-package communication e.g. library->library or utility-command->library) is of the same order of magnitude. Library interface management is a large problem and it will continue to get even larger: API's are growing rapidly, primarily fueled by the contributions from many Free and Open Source development projects. It is true that in the life-cycle of an API there is a rapid growth phase where there are a great deal of changes, and many of these changes introduce incompatibilities. However, as time goes on certain (and eventually nearly all) parts stabilize. Due to the utility of the particular API, (and the utility resulting from applications that use it), dependencies upon that API grow, and hence it becomes increasingly important for it to be provided in a stable and well defined manner. The task of maintaining an ABI stably is non-trivial. We describe here some techniques applied in Solaris over the past five years that aid in accomplishing this task. We describe a useful framework for defining and maintaining the ABI, but, of course, a good deal of effort is required on the part of library developers to adhere to this framework and focus on and maintain compatibility. For a given release of the system (that contains a number of "independent" library packages, e.g. GLIBC, X11, GNOME, ...) the simplest way to define the ABI is at the symbol interface level: e.g. shared library libxyz.so.1 provides the list of public interfaces: {sym1, sym2, ...} and exports (presumably out of necessity to communicate with its fellow libraries and utilities in the same package, but not for consumption by external applications) the list of private symbols: {private_sym1, private_sym2, ...}. Everything else is scoped local to the library and cannot be accessed, even by other libraries or utilities in the same library package. Ideally the public symbol information is not only described by manual pages and documentation, but also resides inside the shared library binary "libxyz.so.1" itself so as to avoid possible discrepancies. In Solaris and in the GLIBC package of Linux a further step is taken that adds a rather useful structure to the set of public symbols. It is described as follows. When a library first appears, its public symbols are put into a single named set, for example: PUBLIC_1: {sym, sym, ...} and the remaining private symbols are all placed in, for example, the named set: PRIVATE: {sym, ...}. (These names are made up for the sake of example and are not currently used.) Now, when the next release of the library appears, it will likely have some new functionality in the form of new interfaces [4]. Then we add a new public set that reflects this new functionality: PUBLIC_2: {sym', sym', ...} as well as the original PUBLIC_1 set and the PRIVATE set (the latter may have changed in an arbitrary way, but that doesn't matter because only co-shipped libraries and/or utilities in the same library package are supposed to use the private interfaces and for a given release they all work together). Similarly, as more releases of the library come out, additional public sets are added: PUBLIC_3, PUBLIC_4, ... etc. All of this information, the set names and set members, is recorded in the library itself in special ELF sections. We refer to this procedure as "Library Versioning" [5]. Library versioning is useful in that it can be used to avoid renaming the shared object with new minor release version numbers as it evolves. That is, instead of the sequence of new files: libxyz.so.1.1, libxyz.so.1.2, libxyz.so.1.3, ... as the library evolves, the shared object name can remain fixed at libxyz.so.1 (as long as it evolves upward compatibly). Furthermore, the traditional minor release incrementing "1.1" -> "1.2" really only indicates "something was added". With library versioning recorded in the shared library itself, the information is *exactly* what was added: it is the interfaces listed in the PUBLIC_2 set. When an application is built, the Solaris and GNU link editors (ld(1)) record in an ELF section of the resulting binary executable the highest level "watermark" required by the application for each versioned library (e.g. application binary "foo" needs "libxyz.so.1" at level PUBLIC_2 and "libabc.so.1" at level PUBLIC_1, etc). At runtime, the dynamic linker reads this information and while it loads the needed shared libraries it can also quickly check whether the required version levels are supplied by the loaded shared libraries. If not, it will exit with an error since it is known at this point at least one symbol needed by the application (or shared libraries) is missing [6]. The recording of the library version symbol information and needed version levels in shared libraries and executables and also the local scoping symbols practice (see the following section) are useful aspects of dynamic linking. However, a more subtle benefit is having the framework in place in the shared library's source tree for maintaining the monotonically increasing and upward compatible chain of public symbols (the PUBLIC_1, PUBLIC_2, sets in the above example) as well as scoping local any symbols that do not need to be visible. Mechanics of Versioning Libraries: ---------------------------------- We describe briefly here the basic technique used to add the versioning information to shared libraries. The complete details can be found in references [5] and [7]. Both the Solaris and GNU link editors ld(1) support the notion of library symbol versioning by use of an input file (usually called a "versioning mapfile" or "version script"). When this file is passed to the link editor (via command line arguments: ld ... -M and ld ... --version-script= on Solaris and Linux, respectively) and the shared library is assembled, the versioning mapfile will be parsed and its information recorded in special ELF sections of the library. Here is a made-up example of a versioning mapfile for a fictitious library libfoo.so.1: PUBLIC_2 { global: symbolD; symbolE; } PUBLIC_1; PUBLIC_1 { global: symbolA; symbolB; symbolC; }; PRIVATE { global: __fooimpl; local: *; }; When libfoo.so.1 was first released, it exported just the three public (i.e. intended for developer's use) symbols "symbolA", "symbolB", "symbolC" and it also exported the library package private symbol "__fooimpl". For intuition on the role of "__fooimpl", imagine that a companion library "libbar.so.1" is co-shipped with "libfoo.so.1" and libbar.so.1 occasionally calls __fooimpl() in libfoo.so.1 as, say, a private communication channel for use in the (current) library package implementation. In a later release of libfoo.so.1 additional functionality was added in the form of the two new functions "symbolD" and "symbolE". Note the "watermark" chaining that occurs in the syntax: PUBLIC_2 *includes* all of the symbols at level PUBLIC_1. This chaining continues with subsequent releases of the library e.g.: PUBLIC_4 { global: symbolH; symbolI; } PUBLIC_3; PUBLIC_3 { global: symbolF; symbolG; } PUBLIC_2; ... etc. This mechanism emphasizes the strictly monotonic increase with time of the public interface offering: removing a public symbol (e.g. symbolD) in some later release is very bad since it will break all applications that require symbolD. When an application binary is built the highest level of the public chain is recorded by the link editor. For example, if the application only used "symbolB" the version dependency "PUBLIC_1" for libfoo.so.1 would be recorded in the binary. If, however, the application used "symbolA", "symbolE", and "symbolF" then the level PUBLIC_3 would be recorded instead. If that application was then distributed to an older system with a library libfoo.so.1 that was only at level PUBLIC_2, then the runtime linker would immediately know something was wrong and would indicate the error and exit [8]. An additional benefit can be achieved with the library versioning technology. Once the symbol sets (e.g. PUBLIC_1, PUBLIC_2, ..., PRIVATE the above example) are defined it is also possible to provide a directive when building a shared library to scope local to the library all remaining symbols. This is done by the "local: *" directive in the mapfile shown above. Traditionally, scoping for libraries is done by using the "static" C keyword in defining internal-implementation functions. However, this only provides a per-file level of scoping, and will not work if the shared library is composed of a number of object files (.o files) and the internal implementation functions are called between the object files. The library versioning "local: *" directive allows the final scoping to occur when the whole shared object is assembled to remove any symbols that do not need to be exported. This local scoping technique is a convenient and powerful way to make sure no internal implementation symbols "slip out" accidentally. If application developers, either accidentally or intentionally, started using these library-internal implementation symbols, their applications would be at a high risk of breaking in the future. One's initial reaction might be "too bad; that developer should not have used those symbols". However, if the application, customer, or installed base is "important enough" (by some measure) there could be pressure placed on the library developer to actually support these otherwise internal interfaces. It is best to use the "local: *" scoping technique to simply avoid this problem in the first place. Tools to check for correct use of the ABI: ------------------------------------------ Once the library symbol versioning is in place in the libraries of a library package, including the "PRIVATE" labelling of the library-package internal symbols discussed above, it is a straight-forward matter to construct tools that can test for applications' conformance to public portion of the ABI. Ideally, one would desire the entire ABI (i.e. *all* library packages on the system) to be versioned and classified as described above. However, even when only some of the library packages are versioned there is still much utility in checking against an application's usage against the portion of the ABI provided by those packages. In Solaris, nearly all of the 30,000 symbols in the 160 shared libraries Solaris provides have been versioned in the manner outlined above. The version set names convention used in Solaris is "SUNW_n.m" for the public chain (where "n" and "m" are integers; "m" plays the role of the minor-release number in the traditional scheme), and "SUNWprivate" for the private symbol set. The "n" may be thought of as the major-release number, but it does not really matter because a major-release, n -> n+1 indicates incompatible change for which there would have to be a separate shared library. The tool we provide in this project is called "abicheck". It is a simple perl script that runs system utility commands [9] to extract the dynamic bindings of a built executable [10]. For each symbol binding it deduces the symbol version set name the symbol resides in. If that symbol's version set matches "private" abicheck prints out a warning. The user may specify a different matching pattern on the command line. abicheck is the core functionality of a tool used by Sun in Solaris application certification branding programs called "appcert" [11]. abicheck runs on both Linux and Solaris and is basically appcert with the certification "baggage" removed. We felt it was best to start with a simpler, straight-forward tool that is easier to understand and add enhancements to, rather than port all of appcert to Linux yielding a situation where a fair amount of functionality that doesn't really apply to the task at hand. One feature that has been carried over from appcert, as an example of possible extensions to abicheck, is a useful check for static linking of system archive files (e.g. libc.a or libsocket.a). The practice of statically linking system libraries into application binaries is not good with respect to binary stability since the "old code" (from the archive) that is bolted into the application may fail to work properly when moved to newer or upgraded systems. The use of static linking of non-co-shipped libraries is strongly discouraged from the binary stability standpoint. Here is example output from abicheck: # uname -a SunOS abi 5.8 Generic sun4u sparc SUNW,Ultra-1 # abicheck reader myclient gdate reader: PRIVATE: (libc.so.1:SUNWprivate_1.1) _select myclient: STATIC_LINK: libsocket.a myclient: STATIC_LINK: libnsl.a gdate: OK This output indicates the application "reader" has latched onto a direct call to the private interface, _select(). It should be calling the published interface select(3C). The application "myclient" has statically linked in the networking libraries: libsocket.a and libnsl.a. The application binary "gdate" had no problems detected by the tool and so gets an "OK". Here is some analogous example output from Redhat Linux 6.2: # abicheck reader myclient /bin/date reader: PRIVATE: (libc.so.6:GLIBC_2.1) __poll myclient: STATIC_LINK: libc.a /bin/date: OK One important issue with respect to Linux is that currently no shipped libraries (i.e. libraries in a distribution) have collected the private symbols into a version set with a private label (e.g. there is no GLIBC_PRIVATE for the GLIBC library package). GLIBC is the only library package on Linux that has non-trivial library versioning, and so currently abicheck has hard-wired in the criterion used in GLIBC libraries that a leading underscore "_" indicates a private symbol. There are a number of exceptions to this rule, and so abicheck currently carries along an exception list. We hope in the future a GLIBC_PRIVATE set will be established in the GLIBC library package. It should be emphasized that abicheck is an initial tool provided as an example of what can be done with public/private library versioning in place. In principle the checking that it does could even be moved to the dynamic linker itself thereby making the abicheck script obsolete. The important thing we feel is to spread meaningful library versioning to many library packages beyond GLIBC (e.g. X11 and GNOME) and to also adopt the private symbol set classifying of library-package internal symbols. Then tools that use the library symbol versioning information become more useful in checking for binary stability and also place a useful structure for library interface definition and maintenance and local scoping in the library package source tree. As API-providing libraries become larger and more complicated (and also more numerous) it will likely pay-off to have this infrastructure in place. Discussion: ----------- The process described here (library symbol versioning using "versioning mapfiles" including the creation of a SUNWprivate set for Solaris internal interfaces) has been in place in Solaris for a number of years. Currently, (Solaris8) nearly all of the libraries shipped in Solaris (including library sources imported into Solaris: e.g. X11, Motif, and CDE) have this interface definition practice. It is not absolutely clear how much Free and Open Source library development projects will benefit from this type of practice. However, we feel it is likely they will benefit a great deal and certainly believe practices of this sort are worth looking into. An interesting distinction comes about in that Solaris is basically shipped as a monolithic blob, whereas Open Source operating systems tend to be much more modular with respect to their ABI's. This is true in principle at least, since it is likely most end-users install a particular distribution (that is also a monolithic blob). In any event, the interesting possibility exists that there will be more fruitful ways to create the library interface definitions and versioning than has been done for Solaris. We feel a namespace separation based on library package name (e.g. GNOME_1.4, GNOME_PRIVATE; GLIBC_n.m.l, GLIBC_PRIVATE) is a useful generalization beyond what is done in Solaris. Additional practices may be discovered as useful. Also interesting is the possibility of "softening" the public/private distinction. As an API is going through a rapid initial growth phase it may be useful to have three categories of symbols: Public, Evolving, and Private. The public ones are set in stone and the library package is committed to maintaining their compatibility, and as before the Private ones are internal-only. The "Evolving" category would contain experimental interfaces. These may change incompatibly (e.g. by changing their arguments, their behavior, or disappear entirely). A developer concerned in producing stable applications should work to stay away from the Evolving interfaces until they are stabilized and have been moved to the Public set; on the other hand a developer needing to take advantage of the experimental interface may decide the potential binary incompatibility for his distributed application is worth the risk. Extensions: ----------- We feel the plan outlined above is the main message of this project and we hope that the practices will be adopted, suitably modified, by open source projects. There are, however, some interesting extensions one can apply to the library interface definition practice (in the spirit of IDL) that provide useful by-products. Rather than maintain regular mapfiles/version-scripts as described in detail above, the library package source tree can maintain simple ASCII repository files (one per library, say) that contain additional information about the library interfaces. The repository will have a number of different fields for each interface, for example: * Function signature (i.e. return value type, named arguments and with their associated types). * Data variables have the variable type and length recorded. * The list of needed C header files (in, say, format) associated with the interface and any required libraries (in, say, -l format). * The name of the library version (in the sense discussed in the previous sections) the symbol resides in (e.g. GLIBC_2.2.1 or GLIBC_PRIVATE) * Any architecture related information (e.g. a list of different architectures on which the symbol is present). * Conditions that indicate an exception has occurred when the interface is called (e.g. return value is NULL). * The list of the errno's associated with the interface. * Whether the symbol is associated with any interface aliases (i.e. weak symbols) * Descriptions or comments about the interface. and other information. By having such a repository for each library a library developer has a central location to go to when he adds a new interface to the library (or if there are changes to an interface). The central location provides *the* definition of the interface: one does not need to go rummaging around in header files and C source to find the interface definition and other information. Some applications using the above sort of fields come to mind: * Automatic generation of (some of the) documentation for the interface (e.g. manpages typically document function signatures, required libraries and header files, interface exceptions and errno's). * Generation of mapfiles/version-scripts used at the library's build time to record the versioning information. * Creation of comparison tools to detect incompatible changes to public interfaces in libraries (e.g. a tool to compare the signatures of interfaces in an earlier release or build with the current build). * Use of the function signatures and other information to create lint and debugging versions of the library. * Use of the function signatures and other information to create dynamic tracing and "pretty-printing" of calls to the library interfaces. This can be used by developers to debug their applications, and also by end-users to troubleshoot problems encountered in the field. Most of the libraries that are shipped with Solaris are maintained with an interface repository scheme like that described above. These are used in the Solaris build to create mapfiles that are passed to ld(1) to record library versions, monitor interface incompatibilities, and to generate code used to create tracing utilities for the apptrace(1) tool that is available on Solaris 8 and later. apptrace uses the link-auditing [5] feature of the Solaris dynamic linker that allows library bindings to be intercepted and replaced by bindings to one's own functions. This mechanism is in the spirit of LD_PRELOAD schemes but is more flexible. The apptrace interceptors for the Solaris libraries act as wrappers for each function call that pretty-print the function's arguments and return value along with calling the actual Solaris library function (so that the application proceeds as normal). It is a good deal faster than tracing tools such as truss(1) and strace(1) that require breakpoint services from the kernel. Interface call interception coupled with the information in the library interface repository can be generalized to a number of interesting applications, such as creating convenient library "unit tests" and also application fault injection tools (i.e. faked error conditions are passed up to the application). In principle, one can imagine recording much if not all of the repository information into the shared libraries themselves so that information will be available for, and encourage the development of, library interface (ABI) related tools. References: ----------- [1] http://www.gnu.org/software/libc/ [2] For convenience we will use the short term "Linux" to really mean a Linux kernel based operating system. I.e. a system composed of much free software (e.g. GNU, XFree86, ... etc.) placed around the Linux kernel to yield a complete operating system. Examples include Debian GNU/Linux, Redhat Linux, and SuSE Linux. [3] http://www.linuxbase.org/ [4] New functionality can, of course, be added to existing interfaces. For example, by passing new values of a parameter to the interface. The behavior of the existing interface cannot change in a way that breaks existing applications. [5] The Solaris Linker and Libraries Guide may be found in the documentation collection at: http://docs.sun.com/ab2/coll.45.13 [6] This is analogous to setting the LD_BIND_NOW environment variable (i.e. lazy binding is turned off) and looking for unresolved symbols, however the versioning scheme has no measurable impact on application performance. [7] See the URL in [1] and the "Commands" -> "Version Script" section of the GNU linker "ld" info page (e.g. /usr/info/ld.info*) [8] This behavior can be overridden via setting an environment variable LD_NOVERSION on Solaris. [9] On both Solaris and Linux abicheck runs the ldd(1) command with environment LD_DEBUG="files,bindings" to retrieve the dynamic linker's information about dynamic symbol bindings. Additionally, it will run pvs(1), dump(1), and elfdump(1) on Solaris and objdump(1) on Linux to extract additional information about the binaries. [10] Shared objects may also be checked, but this can often lead to difficulties if not enough information is recorded in the shared object. [11] http://www.sun.com/developers/tools/appcert/ [12] http://www.usenix.org/publications/library/proceedings/als2000/full_papers/browndavid/browndavid.pdf abicheck-1.2/abicheck.10100644015017100001440000002767507722735677014204 0ustar krungeusers.nr X .TH abicheck 1 "26 August 2003" .SH NAME abicheck \- check application binaries for calls to private or evolving symbols in libraries and for static linking of some system libraries. .\" .SH SYNOPSIS .B abicheck [-h] [-k] [-a] [-I] [-v] [-f .I listfile\] [-o .I outfile\] [-p .I pattern\] [-e .I pattern\] [-j .I njobs\] [-l .I library\] [-L .I ldpath\] [(-s|-S) .I dbfile\] [(-d|-D) .I dbfile\] [-O .I dbfile\] [-A .I listfile\] .I files .SH DESCRIPTION .LP .B abicheck is run on application binaries and issues warnings whenever any of the following three conditions are detected: .LP \(bu .I Private symbol usage. Private symbols are functions or data variables in a library package that are internal to that package. They are used by the libraries in the package for internal communication and are not part of the API/ABI that application developers should use. .LP \(bu .I Evolving symbol usage. Evolving symbols are functions or data variables in a library package that are intended for developer consumption, but have been marked as "evolving" or "unstable" in the sense that they may become incompatible or disappear on a later release of the library package. .LP \(bu .I Static linking. Static linking of system libraries (for example, libc.a) into an application is generally not a good idea because the system library code it "locks" into the application binary may become incompatible with later releases of the system. abicheck attempts to detect static linking of a few system libraries. .LP The default behavior is, for each binary object checked, to examine direct calls from that binary object only. The .B \-l option allows the libraries the binary object brings in to have their calls checked as well. .SH OPTIONS The following options are supported: .TP .B \-k Keep on checking binaries even if there are serious errors (dynamic linker reports unresolved symbols, .BR ldd (1) failures, no symbols detected). .TP .B \-h Print out long form of help. .TP .B \-v Verbose. Print out additional information. .TP .BI \-f \ listfile The .I listfile is a file containing a list of binary objects to check, one per line. This list is appended to any files provided as arguments on the command line. If .I listfile is "\-", then stdin is used. .TP .BI \-o \ outfile Write output to .I outfile instead of stdout. .TP .BI \-p \ pattern Modify the version name pattern match labelling private version sets. Default is .B /private/ using a case-insensitive match. .sp If a component of the regex .I pattern contains two colons in a row: .BR patt1::patt2 , then symbol-level matching will be activated by checking whether .I version::symbol or .I library::symbol matches .I pattern (where the symbol name, version (if any), and library basename are substituted for .IR symbol , .IR version , and .IR library ). For example, .sp .RS -p 'FOO_VERS.*::_foopriv' .br or .br -p 'libfoo\.so.*::_foopriv' .RE .TP .BI \-e \ pattern Same as .B \-p but for "evolving" interfaces. .TP .BI \-L \ ldpath Set the .B LD_LIBRARY_PATH environment variable to .I ldpath before invoking dynamic linker. Use \fB\-L \fR"" to unset .B LD_LIBRARY_PATH. If one of the components of .I ldpath is the string "find", then all shared libraries in .I files are found and their paths inserted into the "find" location. Note that the order will random. .TP .BI \-l \ library Add the basename or full pathname of the shared library .I library to the list of objects to be checked for making private calls. This option may occur more than once on the command line and is additive. By default, only direct calls from a binary to the system libraries are checked. The .B \-l switch allows checking of indirect calls e.g.: app -> supportlib -> systemlib. .TP .B \-a Loop through all of the binaries before checking and collect the list of all shared objects. Take the basename of each shared object found and act as though it was specified with the .B \-l option option and then run the abicheck checks. This way, calls from all "application internal" objects are checked rather than just the direct calls. (Useful when shared objects do not have their dependencies recorded.) .TP .BI \-I Ignore shared libraries in checking, only check executables. Compatible with .B \-a, libraries will be searched for first but then not checked. .TP .BI \-d \ dbfile, \ \-D \ dbfile Specify fallback flat-file symbol database for the dynamic (public vs. private) test. These classifications will be used if the library is not versioned (i.e. classification does not exist in the library itself). Use .B \-D to indicate that only information from .I dbfile should be used. Lines in .I dbfile can be of one of these forms: .sp library|symbol .br library|class|symbol .br library|FILE=path .sp .I library must be the full path to the library to be specified (it cannot be a basename). .sp The first form marks .I symbol as private. .sp The second form marks .I symbol with .I class where .I class may be public, private, or evolving. .sp The third form indicates the file .I path should be opened on demand when .I library is first encountered. File .I path contains lines of the first two forms except for the .I library field. The third form is a speedup to avoid processing many classification lines for libraries never encountered in the run. .TP .BI \-O \ dbfile Specify an override file to modify the symbol classification for the dynamic (public vs. private) test. The format for the override file is like: .sp library|symbol|class .sp The library can be the full path or basename. If library is "__SKIP__" the symbol will be ignored for any library it is found in. The class can be "public", "private", "evolving", or "deleted". The "deleted" class is special-cased, means the symbol was deleted from the library on some release. The symbol "__ALL__" for the "deleted" class means the entire library was deleted or is otherwise unstable to use. .sp Examples: .sp libfoo.so.1|__bar|private .br /lib/libxyz.so.1|baz|public .br __SKIP__|__fputwc_xpg5 .sp These settings override any classification inside the library (from library versioning, obtainable from .BR pvs (1), etc). .TP .BI \-A \ listfile Set the ABI libraries of interest to the libraries listed in .I listfile (full pathnames, one per line). Only calls into these libraries will be checked; all other library calls will be ignored. .TP .BI \-s \ dbfile, \ \-S \ dbfile Specify more extensive symbol databases for the static linking test. .I dbfile may be a comma separated list of files. If a file is a static archive (lib*.a) it is processed to extract the symbols. Otherwise it is a database file consisting of lines of the form symbol|library:module for example: .sp shmat|/usr/lib/libc.a:shmsys.o .br shmctl|/usr/lib/libc.a:shmsys.o .br shmdt|/usr/lib/libc.a:shmsys.o .br shmget|/usr/lib/libc.a:shmsys.o .br ... .sp When all symbols in a module.o are defined in the application, static linking of that module (and corresponding library archive) is assumed. Use .B \-S to indicate that only the static link test should be performed. .sp Use .B \-S \ int to do only the static link check and using the internal database. .sp Use .B \-s \ none or .B \-S \ none to skip the static linking check entirely. .TP .BI \-j \ njobs Run .I njobs in parallel as separate processes. Implies .BR \-k \. Primarily intended for multiple CPU machines where .I njobs should be close to the number of processors. Output is collected in tmp files and printed all at once near the end of the run as each job finishes. .sp If .I njobs is "-", "detect", or "n", then .I njobs will be set to a number depending on the number of processors on the current machine (if that can be determined). .SH OPERANDS .LP The following operands are supported: .TP .I files A list of application binary objects to check. .SH OUTPUT .LP There is one line per problem (there may be multiple problems per binary checked) which look like the following: .sp .RS If no problems were found: .br \fIfilename\fR: OK .sp If private symbol usage: .br \fIfilename:\fR PRIVATE (\fIlibrary\fR:\fIprivate_version\fR) \fIprivate_sym\fR .sp If evolving symbol usage: .br \fIfilename\fR: EVOLVING (\fIlibrary\fR:\fIevolving_vers\fR) \fIevolving_sym\fR .sp If file statically linked in a system archive library: .br \fIfilename\fR: STATIC_LINK (\fIarchive\fR) .sp If checking of the file was skipped: .br \fIfilename\fR: SKIP (\fIreason\fR) .RE .sp .LP Under use of the deleted class in the .B -O override file option, these problems may be found: .sp .RS If a symbol has been deleted from the library on some release: .br \fIfilename\fR: DELETED_SYM: \fIsymbol\fR/\fIlibrary\fR .sp (\fIlibrary\fR will be "unbound" if the symbol was unbound) .sp If an entire library has been deleted on some release or is otherwise unstable to use: .br \fIfilename\fR: UNSTABLE_LIB: \fIlibrary-soname\fR =\fR \fIlibrary-path\fR .sp (\fIlibrary-path\fR may be "file not found" if the library could not be found) .RE .sp .LP The following problems will cause a fatal error unless the .B \-k option is used: .sp .RS If the dynamic linker could not resolve .I N symbols when .B ldd \-r was run: .br \fIfilename\fR: UNBOUND_SYMBOLS: \fIN\fR .sp If the dynamic linker found no dynamic bindings: .br \fIfilename\fR: NO_BINDINGS .sp If .B ldd \-r with .B LD_DEBUG=files,bindings failed: .br \fIfilename\fR: LDD_ERROR .RE .LP In these latter three cases run .B ldd \-r on the binary file for more information on what went wrong (note that .B abicheck runs .B ldd \-r with .B LD_DEBUG=files,bindings set). On some systems the dynamic linker will not process SUID programs with .B LD_DEBUG set (this usually results in .B NO_BINDINGS in the .B abicheck output). .LP Note that if you are running abicheck on a shared library (for example, libfoo.so) that has .I not been built with .B \-l .I lib flags to record its library dependencies, then the "unbound symbols" problem is very likely. There is not much that can be done besides rebuilding the library or checking an application binary that uses the library and using the .B \-l option of .B abicheck. .SH EXIT STATUS .LP The following exit values are returned: .TP .B 0 No errors and no problems found. .TP .B 1 A fatal error occurred. .TP .B 2 No fatal errors occurred, but some binaries had problems detected. .SH NOTES .LP Only ELF objects are checked. .LP In the .BR \-s \, .BR \-S \, .BR \-d \, and .B \-O dbfiles the '#' character starts a comment line in the usual way. .LP Unless one is using the "::" custom matches supplied via the .B \-p or .B \-e flags, abicheck can only check against system libraries that have had symbol versioning applied to them (i.e. the private and/or evolving information recorded for each symbol in the library itself). For more info about symbol versioning, see the "Solaris Linker and Libraries Guide" answerbook at the URL http://docs.sun.com/ab2/coll.45.13 and the Commands/Version-Script section of the GNU linker "ld" info page. .LP The default symbol version name matching patterns are case insensitive matches to the strings "private" and "evolving" for the private and evolving cases, respectively. .LP Odd filenames containing the single-quote character or newline will be skipped; such characters interfere with calling commands via the shell. .LP To recurse directories use .BR find (1) and either collect the output to a file for use with the .B \-f option, or in a pipe in via: .sp find ... | abicheck -f - ... .SH BUGS The program is dependent on the form of the runtime linker's debug output. Since this output is intended to be human readable rather than machine readable, .B abicheck will break whenever the output format changes. On Solaris it is possible that the Link Auditing C interface could be used to avoid this problem. .LP On Linux when .BR ldd (1) is run on a SUID binary, it (ldd and the dynamic-linker) will sometimes actually run the binary. On Linux SUID/SGID binaries are currently skipped even if the user is root; test unprivileged copies instead. .SH SEE ALSO .BR ld (1), .BR ldd (1), abicheck-1.2/README0100644015017100001440000000746307503661155013224 0ustar krungeusers This is the README for the ABI checking tool "abicheck". For background information on library versioning, public/private classification, and other useful information please read the INTRO file that accompanies the source. abicheck is a tool that checks application binary executables and shared libraries for conformance to the (or an) ABI (Application Binary Interface). It requires the libraries making up the ABI to have the library versioning public/private symbol information recorded in the them. See the INTRO and references (especially the "Solaris Linker and Libraries Guide" at http://docs.sun.com/ab2/coll.45.13). With that public/private symbol information in place in the libraries, abicheck performs the following steps: 1) runs ldd(1) with the -r switch and environment variable LD_DEBUG=files,bindings to extract all of the symbol binding information for each binary to be checked. 2) The ABI symbols directly called by a binary are checked if they are in a library's private symbol set (that is, the set of library package internal symbols). If so, a warning in printed out. 3) Static linking of system library archives are checked for using objdump(1) or elfdump(1) to extract the list of .text symbols from the application binary and then applying heuristics to detect static linking of basic system libraries (libc, libsocket, libnsl) The details of 2) depend on the system (on Linux the ldd(1) output contains the information, while on Solaris pvs(1) is consulted to link symbols to version set names) abicheck currently works on Linux (GLIBC package 2.1 or higher), and Solaris (2.6 or higher). It has been tested on Solaris 2.6 through 9, Redhat Linux 6.2, 7.3 and Debian GNU/Linux 2.2, 3.0. The more libraries that have the public/private symbol information in place the more useful is abicheck in checking conformance. Many of the system libraries in Solaris 2.6 are versioned that way, and nearly all are in Solaris 8 and 9. On Linux only the GLIBC library package of ~20 are versioned. Prior to version 2.3, the GLIBC libraries do not contain the public/private symbol classification, and so the heuristic of leading underscore "_" is used to identify private symbols except for an "exclude list" derived from the 2.3 GLIBC source base that abicheck currently carries along with it (this is a bootstrap measure; in the long run abicheck will not contain long exclude lists). Here is an example run on Solaris 9: # abicheck ./reader ./date ./reader: PRIVATE: (libc.so.1:SUNWprivate_1.1) _select ./date: OK The application binary "reader" calls the private symbol "_select" in libc.so.1 and a warning is printed out. The application "date" makes no private calls and so is OK. See the INTRO file for more information on the what is being checked. Here is an example run on Redhat 7.3: # abicheck /usr/bin/gencat /bin/date /usr/bin/gencat: PRIVATE: (libc.so.6:GLIBC_PRIVATE) __open_catalog /bin/date: OK which at first looks like a problem for /usr/bin/gencat, but remember the gencat executable is part of the glibc package, so this binding is actually OK. A non-GLIBC application directly binding to the __open_catalog interface would be a potential problem (since __open_catalog may change incompatibly in a later GLIBC release). Running "abicheck -h" provides additional help and information about the format of the output and command line options. Regarding building and installation, there is not much to say since it is a simple stand-alone script. "make" will just copy the script to the file "abicheck" and set the executable bit. This file may be installed anywhere. The user's PATH must be set to find perl (usually /usr/bin/perl). abicheck is meant as a demonstration tool of an ABI conformance using versioning information in ABI shared libraries, and additional heuristic checks for binary stability. abicheck-1.2/test/0040755015017100001440000000000007723122041013302 5ustar krungeusersabicheck-1.2/test/CVS/0040755015017100001440000000000007723122041013735 5ustar krungeusersabicheck-1.2/test/CVS/Root0100644015017100001440000000004307723122041014575 0ustar krungeusers:ext:krunge@cvs1:/cvsroot/abicheck abicheck-1.2/test/CVS/Repository0100644015017100001440000000001607723122041016031 0ustar krungeusersabicheck/test abicheck-1.2/test/CVS/Entries0100644015017100001440000000033307723122041015265 0ustar krungeusers/Makefile/1.2/Tue Aug 26 19:57:51 2003// /libc_a.c/1.2/Tue Aug 26 19:57:51 2003// /private1.c/1.2/Tue Jun 18 18:16:07 2002// /public1.c/1.1.1.1/Thu Feb 7 14:40:20 2002// /run_tests/1.1.1.1/Thu Feb 7 14:40:20 2002// D abicheck-1.2/test/Makefile0100644015017100001440000000067607722735677015000 0ustar krungeusers# Copyright (c) 2001 by Sun Microsystems, Inc. # All rights reserved. PROGS = libc_a public1 private1 all: $(PROGS) tests # on Linux this gives # /usr/bin/ld: BFD 2.11.93.0.2 20020207 assertion fail elf-strtab.c:262 # but it still creates the binary properly. libc_a: libc_a.c cc -o libc_a libc_a.c -lm /usr/lib/libc.a private1: private1.c cc -D`uname` -o private1 private1.c tests: @sh run_tests clean: rm -f $(PROGS) clobber: clean abicheck-1.2/test/libc_a.c0100644015017100001440000000052107722735677014702 0ustar krungeusers/* * Copyright (c) 2001 by Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* This is used to catch libc.a linking. See Makefile & run_tests */ #include #include main() { /* libm interface is called just to get some dynamic binding */ printf("%f\n", cos(0.0)); exit(3); } abicheck-1.2/test/private1.c0100644015017100001440000000154007503674347015217 0ustar krungeusers/* * Copyright (c) 2001 by Sun Microsystems, Inc. * All rights reserved. */ /* This is for simple catching of some private symbol usage. See run_tests */ #include #if defined(SunOS) #define OS "Solaris" #include extern int __nanosleep(const struct timespec *r, struct timespec *m); #elif defined(Linux) #define OS "Linux" #define O_WRONLY 1 typedef int mode_t; extern int __open(__const char *file, int oflag, mode_t mode); extern int __open_catalog(void); /* FIXME get real signature */ #else #error "unsupported OS" #endif main() { char str[] = "abc"; fprintf(stderr, "running on: %s\n", OS); #if defined(SunOS) __nanosleep(0, 0); #elif defined(Linux) __open("/dev/null", O_WRONLY, 0644); if ( strlen(str) == 2 ) { __open_catalog(); /* just to get a binding ... */ } #endif fprintf(stderr, "Done.\n"); return(0); } abicheck-1.2/test/public1.c0100644015017100001440000000061707430510724015012 0ustar krungeusers/* * Copyright (c) 2001 by Sun Microsystems, Inc. * All rights reserved. */ /* This program should always pass. It only uses public syms, see run_tests */ #include #include #include #include main() { char str[] = "a_msg"; int fd = open("/dev/null", O_WRONLY); write(fd, str, 5); fprintf(stderr, "fd: %d str:%s\n", fd, str); return 0; } abicheck-1.2/test/run_tests0100644015017100001440000000165707430510724015265 0ustar krungeusers#!/bin/sh # Copyright (c) 2001 by Sun Microsystems, Inc. # All rights reserved. PATH="`pwd`/..:$PATH"; export PATH errs=0 pass() { echo "$0: passed: $name" 1>&2 } fail() { echo "$0: failed: $name" 1>&2 errs=`expr $errs + 1` } ############################################################################ # libc_a: name="libc.a static link test on libc_a" if abicheck ./libc_a 2>/dev/null | grep 'STATIC_LINK:.*libc' > /dev/null; then pass else fail fi ############################################################################ # public1: name="public1: only calls public syms" if [ "`abicheck ./public1 2>/dev/null`" = "./public1: OK" ]; then pass else fail fi ############################################################################ # private1: name="private1: calls private sym in libc" if abicheck ./private1 2>/dev/null | egrep 'PRIVATE:.*libc.*(__open|__nanosleep)' > /dev/null; then pass else fail fi exit $errs abicheck-1.2/abicheck.pl0100644015017100001440000025547107722735677014454 0ustar krungeusers #!/bin/sh -- # A comment mentioning perl. eval 'exec perl -S $0 ${1+"$@"}' if 0; # # Copyright (c) 2001-2003 by Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # # For more info run this script with -h or look at the help text near # the bottom of this file. # require 5.000; use strict; use Getopt::Std; use File::Basename; # os type and global database hashes and arrays: my ($OS, %Inodes, %Filter, %Private, %Evolving, %Override, %Deleted, %Skipsym); my (%Lib_Dirs, %External_DB, %ABI_Libs, @Rerun_Args, @Check_List, %Cmds); # library archive (.a) structures. my (%AR_sym, %AR_cnt, %AR_lib, @Archive_Skip); # regex patterns for matching and parsing output: my ($binding_match, $filter_match, $unbound_match, $dtneeded_match); my ($lddstub_match, $ld_debug_match); my ($file_match, %lib_match); my $lib_match = ''; # command option related variables: my $find_all_libs = 0; # -a option my $check_libraries = 1; # -I option my $ld_path = undef; # -L option my $ld_path_all_libs = 0; my $private_match = q/(?i)private/; # -p option my $evolving_match = q/(?i)evolving/; # -e option my $extended_private_match = 0; my $extended_evolving_match = 0; my $private_db_file = ''; # -d option my $private_db_only = 0; # -D option my $static_archive_file = ''; # -s option my $static_only = 0; # -S option my $static_check = 1; my $override_db_file = ''; # -O option my $abi_library_file = ''; # -A option my $output_file = ''; # -o option my $njobs = 0; # -j option my $keep_going = 0; # -k option my $verbose = 0; # -v option my $insane_verbosity = 0; # -V option (not yet in doc) # globals for speed or signal handling: my (@pids, @tmpfiles); # arrays used in parallel mode select(STDERR); $| = 1; select(STDOUT); $| = 1; # # determine OS and setup OS specific things: # os_setup(); # # process cmd line args: # get_options(); # # run separate processes if instructed to: # if ( $njobs > 1 ) { parallel(); exit 1; } # # initialize various info and pattern matches: # data_setup(); # # load external database from -s/-S, -d/-D, -O, -A options. # load_external_dbs(); # # loop over all binary objects and check each one: # my $problem_count = 0; my $file; foreach $file (@ARGV) { process($file); } if ( $find_all_libs || $ld_path_all_libs ) { # # This is for the -a and -L find options. In process() we only # gathered all the libraries in the set to be tested into # lib_match. Now we test each binary so calls from these # libraries will also be checked and/or substitue the "find" # string in LD_LIBRARY_PATH. # modify_ld_path() if $ld_path_all_libs; foreach $file (@Check_List) { check($file); } } if ( $problem_count > 0 ) { exit 2; } else { exit 0; } ########################################################################### # subroutines # # Top level routine for processing one binary. # sub process { my($file) = @_; # we handle some obvious "skip" cases here: if ( ! -e $file ) { file_header($file); print "$file: SKIP: $!\n"; return; } if ( ! -f $file ) { file_header($file); print "$file: SKIP: not a regular file\n"; return; } if ( $OS eq 'linux' && (-u $file || -g $file) ) { file_header($file); print "$file: SKIP: file is SUID or SGID\n"; return; } if ( $file =~ /['\n]/ ) { # # these characters will likely cause our calls to the shell # to fail, so we skip these (oddly named) files. # my $tmp = $file; $tmp =~ s/\n/\\n/g; file_header($file); print "$tmp: SKIP: bad character in filename\n"; return; } if ( ! is_elf($file) ) { if ( ! -r $file ) { # it could be ELF it we could not read it. open(TMP, "<$file"); file_header($file); print "$file: SKIP: cannot read: $!\n"; close(TMP); } else { file_header($file); print "$file: SKIP: not an ELF file\n"; } return; } if ( $find_all_libs || $ld_path_all_libs ) { # # we just collect the list of shared objects before any # checking. (done in another loop once the collection # is finished). # if ( is_lib($file) ) { my $b = basename($file); my $d = dirname($file); if ( $ld_path_all_libs ) { # record lib dirname $Lib_Dirs{$d} = 1; } if ( $find_all_libs == 1 ) { # record lib basename $lib_match{$b} = 1; } } push(@Check_List, $file); } else { check($file); } } # # Wrapper for the dynamic and static checks. # sub check { my ($file) = @_; my (%dtneeded, $cnt); $cnt = 0; file_header($file); # # This SKIP is here instead of process() to let all libraries be # found (find_all_libs/ld_path_all_libs) # if (! $check_libraries && is_lib($file) ) { print "$file: SKIP: is a shared library\n"; return; } $cnt += dynamic_check($file, \%dtneeded); $cnt += static_check($file); $problem_count += $cnt; # increment by how many problems found. if ( ! $cnt ) { print "$file: OK\n"; # no problems found, indicate OK. } } # # optionally put extra tags and info at top for automatic parsers (-V). # sub file_header { my($file) = @_; if ($insane_verbosity) { print "BEGIN FILE: $file\n"; print "BEGIN LD_LIBRARY_PATH: $ENV{LD_LIBRARY_PATH}\n"; print "BEGIN ld_path: $ld_path\n"; print "BEGIN LD_PRELOAD: $ENV{LD_PRELOAD}\n"; } } # # Extracts the command line options via getopts() # if -f switch has been specified, reads the filelist and append filenames # to @ARGV for processing. # sub get_options { # # We want the -l option to be additive. For simplicity we gather # all of them them up now, and just ignore $opt{l} later: # my $lib_str = ''; my $lib_rgx = ''; my $i; my @orig = @ARGV; for ($i=0; $i < @ARGV; $i++) { if ( $ARGV[$i] eq '-l' ) { my $str = $ARGV[$i+1]; if ( $str =~ m,^/(.*)/$, ) { # regex: /libfoo.*/ $lib_rgx .= $1 . '|'; } else { # fixed string $lib_str .= $str . '|'; } } } $lib_str =~ s/\|+$//; # trim any trailing | $lib_rgx =~ s/\|+$//; if ( $lib_rgx ne '' ) { $lib_match = $lib_rgx; } if ( $lib_str ne '' ) { my $lib; foreach $lib (split(/\|/, $lib_str)) { $lib = basename($lib); $lib_match{$lib} = 1; } } # now call ordinary getopts: my %opt; if ( ! getopts('kh?aIvVf:o:p:e:l:L:j:d:D:s:S:O:A:', \%opt) ) { usage(); exit 1; } # help: if ( exists($opt{h}) || exists($opt{'?'}) ) { help(); exit 1; } # boolean switches: $keep_going = 1 if $opt{k}; $verbose = 1 if $opt{v}; $insane_verbosity = 1 if $opt{V}; $find_all_libs = 1 if $opt{a} && ! $ENV{AC_PARALLEL}; $check_libraries = 0 if $opt{I}; # options and environment for parallel jobs if (exists $opt{j} && $opt{j} ne '') { $njobs = $opt{j}; } elsif (exists $ENV{AC_NJOBS} && $ENV{AC_NJOBS} ne '') { $njobs = $ENV{AC_NJOBS}; } # child is a single job and so shuts it off: $njobs = 0 if $ENV{AC_PARALLEL}; # there is also a trick to autodect number of cpus: if ( $njobs =~ /^(-|detect|n)$/i ) { my $ncpu = ncpu(); $njobs = $ncpu; if ( $njobs == 2 ) { $njobs++; } } # user specified private and evolving matches: $private_match = $opt{p} if exists $opt{p} && $opt{p} ne ''; $evolving_match = $opt{e} if exists $opt{e} && $opt{e} ne ''; # output to $output_file instead of to STDOUT if (exists $opt{o} && $opt{o} ne '') { $output_file = $opt{o}; if (! $ENV{AC_PARALLEL}) { # only applies to master # redirect right now: open(STDOUT, ">$output_file") || die "cannot open: $output_file: $!\n"; } } # user specified LD_LIBRARY_PATH if ( exists $opt{L} ) { $ld_path = $opt{L}; my $path; foreach $path (split(/:/, $ld_path)) { # need to find all libs for special token "find" $ld_path_all_libs = 1 if $path eq 'find'; } } # various external database files: # public/private dynamic db: if ( exists $opt{D} ) { # -D means only use the db file. $private_db_file = $opt{D}; $private_db_only = 1; } elsif ( exists $opt{d} ) { $private_db_file = $opt{d}; } # public/private dynamic db overrides: if ( exists $opt{O} ) { $override_db_file = $opt{O}; } # list of ABI libraries we are int: if ( exists $opt{A} ) { $abi_library_file = $opt{A}; } # static linking db: if ( exists $opt{S} ) { # -S is static checking only. $static_archive_file = $opt{S}; $static_only = 1; } elsif ( exists $opt{s} ) { $static_archive_file = $opt{s}; } # this indicates skip the static checking: if ($static_archive_file =~ /^(skip|none)$/ ) { $static_archive_file = ''; $static_check = 0; $static_only = 0; } # collect option args into @Rerun_Args for parallel mode: for ($i=0; $i < scalar(@orig) - scalar(@ARGV); $i++) { my $a = $orig[$i]; # Try to clean up options for parallel mode: if ($njobs > 0) { if ( $a =~ /^-[jfLlo]$/ ) { $i++; next; } elsif ( $a =~ /^-[jfLlo]./ ) { next; } elsif ( $a =~ /^-[ka]$/ ) { next; } } push(@Rerun_Args, $a); } # # read in the -f list of binaries to check and append # them to @ARGV # if ( defined $opt{f} ) { if ( $opt{f} ne '-' && ! -f $opt{f} ) { # '-' is STDIN, so the list can be piped in. die "invalid -f option: $opt{f}: $!\n"; } open(LIST, "<$opt{f}") || die "cannot open: $opt{f}: $!\n"; while () { chomp; push(@ARGV, $_); } close(LIST); } } # # Determine OS, exit if not supported, adjust PATH, etc. # sub os_setup { # This path will find uname(1) and other needed utils: $ENV{PATH} .= ":/bin:/usr/bin"; $ENV{LC_ALL} = "C"; my ($os, $rel); chomp($os = `uname -s`); chomp($rel = `uname -r`); if ( $os =~ /^SunOS/ && $rel !~ /^4\./ ) { # Note we cannot do SunOS 4.x (original SunOS & a.out) $OS = 'solaris'; $ENV{PATH} .= ":/usr/ccs/bin"; # for dump(1) system("type elfdump >/dev/null 2>&1"); if ($? == 0) { $Cmds{elfdump} = 1; } else { $Cmds{elfdump} = 0; } } elsif ( $os =~ /linux/i ) { # # We call this case "linux", though there are many # distros and version levels, some for which this tool # may not work. Our actual requirement is the GNU # linker version 2.1 or higher. # $OS = 'linux'; } else { die "unsupported OS: $os $rel\n"; } } # # Check a binary for any private or evolving symbol calls. # Runs ldd -r with LD_DEBUG=files,bindings and parses the output, # then checks the private/evolving database to see if the symbol is listed. # sub dynamic_check { my ($file, $dtneeded_ref) = @_; my $cnt = 0; # record inode for matching later on: my $file_inode = get_inode($file); # # quick test to see if it is completely statically linked: # this may give 2-5% performance hit due to fork. # my $file_output = `file '$file' 2>&1`; if ( $file_output =~ /statically linked/i ) { print "$file: STATIC_LINK: (completely statically linked)\n"; return 1; } if ( $static_only ) { my $line; my $ldd_output = `ldd '$file' 2>&1`; foreach $line (split(/\n/, $ldd_output)) { if ( $line =~ /$dtneeded_match/o ) { $dtneeded_ref->{$1} = $2; } } return 0; } # # LD_DEBUG output may prepend "./" to objects. We remove it # from everything, including the filename we profile. # my $file0 = $file; # store original name for output. $file =~ s,^\./,,; # open a pipe to a child process that will run ldd(1): my $pid = open(LDD, "-|"); die "$!\n" if ! defined $pid; # fork failed. if ( ! $pid ) { # child here, go run ldd -r with debug env: exec_ldd($file); exit 1; } my ($obj, $lib, $sym, $rest, $info, $tmp, %saw, %objhit, %libskip); my $bindings_cnt = 0; my $unbound_cnt = 0; my $direct_only = 1; $direct_only = 0 if (%lib_match || $lib_match ne ''); my $prefix = ''; # substrs for index() to speed line processing my $skip_prefix = ''; # process the LD_DEBUG output: while () { chomp; if ( ! /$ld_debug_match/o ) { # regular ldd -r output line (no leading PID). if ( /$unbound_match/o ) { # see set_matches() $sym = $1; $rest = $2; next if exists $saw{"UNBOUND|$sym"}; $saw{"UNBOUND|$sym"} = 1; if (exists $Deleted{__DELETED__}{$sym}) { print "$file0: DELETED_SYM: $sym/unbound\n"; $cnt++; } if ($insane_verbosity) { $rest =~ s/[()]//g; $rest = trim($rest); print "$file0: UNBOUND: ($rest) $sym\n"; } $unbound_cnt++; } elsif ( /$dtneeded_match/o ) { # see set_matches() my $from = $1; my $to = $2; $dtneeded_ref->{$from} = $to; if (exists $Deleted{$from}{__ALL__}) { print "$file0: UNSTABLE_LIB: $from => $to\n"; $cnt++; } } next; } if ( $prefix eq '' ) { # # this skips over the ldd bindings for other pids: # if($skip_prefix ne '' && index($_, $skip_prefix) == 0) { # n.b. substr() test gave no speedup. next; } # # This will detect if the "12345:" pid prefix applies # to the file we want checked. # if ( /$file_match/o ) { $tmp = $1; $obj = $2; my $ok = 0; if ( $obj eq $file ) { $ok = 1; } elsif ( defined $file_inode ) { my $obj_inode = get_inode($obj); if ( defined $obj_inode && $file_inode eq $obj_inode ) { $ok = 1; } } if ( $ok ) { $prefix = $tmp; } elsif ($lddstub_match ne '' && $obj =~ /$lddstub_match/o) { # processing library do not want to skip it. ; } else { $skip_prefix = $tmp; } } next; } elsif ( index($_, $prefix) != 0 ) { # n.b. substr() test gave no speedup. next; } # leading PID: specific matches: if ( /$binding_match/o ) { # see set_matches() $obj = $1; $lib = $2; $sym = $3; $rest = $4; $obj =~ s,^\./,,; # remove any leading ./ $lib =~ s,^\./,,; my $ob = ''; # a string for output # ld_debug output for $file: # binding file=$obj to file=$lib symbol `$sym' if ($obj eq $lib || $lib eq $file) { # ignore self and reverse bindings next; } elsif (%libskip && exists $libskip{$lib}) { # quick skip for when ABI_Libs is used next; } if ($direct_only) { next if $obj ne $file; # note: filtering for direct only case is done. } elsif ($obj eq $file) { # this the primary one we want ; } elsif (%objhit && exists $objhit{$obj}) { # avoid the lib_match lookup work below $ob = $objhit{$obj}; next if $ob eq ''; } else { # # $obj must be a shared lib here since we # already tested $obj eq $file above. # my $matched = 0; my $obj_b = basename($obj); if (%lib_match && exists $lib_match{$obj_b}) { $matched = 1; } if (! $matched && $lib_match ne '' && $obj_b =~ /$lib_match/o) { $matched = 1; } if ($matched) { # set to desired output string: $ob = $obj_b . '->'; $objhit{$obj} = $ob; } else { # set to null string to indicate skip $objhit{$obj} = ''; next; } } if ( defined $Filter{$lib} ) { # apply the alias for the filter: $lib = $Filter{$lib}; next if $obj eq $lib; next if $lib eq $file; next if %libskip && exists $libskip{$lib}; } # skip repeated bindings (non-PIC): next if exists $saw{"$lib|$sym"}; $saw{"$lib|$sym"} = 1; $bindings_cnt++; next if $Skipsym{$sym}; # # set the inode for later use (we use the inodes of # libraries instead of pathnames to avoid chasing # down symlinks, etc) # my $lib_inode = get_inode($lib); if ( ! defined $lib_inode ) { die "invalid library: $lib, $!\n"; } if (%ABI_Libs) { if (exists $ABI_Libs{$lib_inode} || exists $ABI_Libs{$lib} ) { ; # let it through. } else { $libskip{$lib} = 1; next; } if (! $direct_only) { my $obj_inode = get_inode($obj); if (! defined $obj_inode) { $obj_inode = '__NONE__'; } if (exists $ABI_Libs{$obj_inode} || exists $ABI_Libs{$obj} ) { # skip intra ABI calls next; } } } load_classification($lib_inode, $lib, $sym, $rest); my $mentioned = 0; $info = $Private{$lib_inode}{$sym}; if ( $info ) { $mentioned++; print "$file0: PRIVATE: ($ob$info) $sym\n"; } $info = $Evolving{$lib_inode}{$sym}; if ( $info ) { $mentioned++; print "$file0: EVOLVING: ($ob$info) $sym\n"; } if ( %Deleted && exists $Deleted{__DELETED__}{$sym} ) { my $base = basename($lib); if ( exists $Deleted{$base}{$sym} ) { print "$file0: DELETED_SYM: $sym/$base\n"; $mentioned++; } } $cnt += $mentioned; if ( $insane_verbosity && ! $mentioned ) { print "$file0: PUBLIC: ($ob$lib:public) $sym\n"; } } elsif ( /$filter_match/o ) { # see set_matches() my $filtee = $1; my $filter = $2; $filtee =~ s,^\./,,; $filter =~ s,^\./,,; if ( ! exists $Filter{$filtee} ) { load_filter($filtee, $filter); } } } #undef %saw; close(LDD); if ( $? != 0 ) { $cnt++; print "$file0: LDD_ERROR"; print " (run ldd -r on binary for more info)\n"; exit 1 unless $keep_going; } elsif ( $bindings_cnt == 0 ) { $cnt++; print "$file0: NO_BINDINGS"; my $priv = ''; $priv = 'SGID' if -g $file; $priv = 'SUID' if -u $file; if ( $priv ) { print " ($priv binary. "; print "no info from LD_DEBUG=bindings ldd -r)\n"; } else { print " (run ldd -r on binary for more info)\n"; } exit 1 unless $keep_going; } if ( $unbound_cnt != 0 ) { $cnt++; print "$file0: UNBOUND_SYMBOLS: $unbound_cnt"; print " (run ldd -r on binary for more info)\n"; exit 1 unless $keep_going; } return $cnt; } # # Takes binding line information and determines if the lib:sym is private # or evolving. Stores results in %Private and %Evolving databases. # sub load_classification { my ($inode, $lib, $sym, $rest) = @_; if ( $OS eq 'solaris' ) { # we are done if we have already processed this library: return if exists $Private{$inode}; # set something so we only get in here once per lib: $Private{$inode}{'__LOADED_SYMS__'} = 1; } elsif ( $OS eq 'linux' ) { # return if we already processed the library and symbol: return if exists $Private{$inode}{$sym}; } my $cnt = 0; if ($private_db_only) { ; # skip to External_DB lookup below. } elsif ( $OS eq 'solaris' ) { return if $lib =~ /['\n]/; # bad chars for shell if ( ! -e $lib ) { # the dynamic linker found it; so should we. die "invalid library: $lib: $!\n"; } open(PVS, "pvs -dos '$lib' 2>/dev/null|") || die "$!\n"; while () { chomp; my ($l, $d, $version, $sym) = split(' ', $_); $version =~ s/:$//; $sym =~ s/;$//; set_db($lib, $inode, $version, $sym); $cnt++; } close(PVS); } elsif ( $OS eq 'linux' ) { # load the classification one symbol at a time: my $version = ''; if ( $rest =~ /^\s*\[(.*)\]/ ) { # e.g. [GLIBC_2.1] $version = $1; } # # N.B.: the above will only work if the application being checked # has been was built against a *versioned* library. Otherwise # the dynamic linker does not provided the [VERS] info. # # To handle this properly, we will likely have to do # it the Solaris way with a method similar to pvs(1). # set_db($lib, $inode, $version, $sym); $cnt++ if $version ne ''; } # if we did not find anything, see if an external db file is supplied: if ($cnt == 0 && ( exists $External_DB{$lib} || exists $External_DB{$inode} ) ) { my $db = ''; if ( exists $External_DB{$lib} ) { $db = $External_DB{$lib}; } else { $db = $External_DB{$inode}; } if ( $db !~ /\n/ ) { if (! open(EXT, "<$db")) { die "cannot open: $db: $!\n"; } $db = ''; while () { next if /^\s*#/; next if /^\s*$/; $db .= $_; } close(EXT); } my(@a, $line); foreach $line (split(/\n/, $db)) { my($version, $arch, $sym2); next if $line =~ /^\s*$/; @a = split(/\|/, $line); if ( @a == 1 ) { $version = 'private'; $arch = 'none'; $sym2 = $a[0]; } elsif ( @a == 2 ) { $version = $a[0]; $arch = 'none'; $sym2 = $a[1]; } elsif ( @a == 3 ) { $version = $a[0]; $arch = $a[1]; $sym2 = $a[2]; } if ( $sym2 ne '' ) { set_db($lib, $inode, $version, $sym2); } } # # XXX there is an issue here if some other value of $lib # will be found corresponding to this inode... (for both # solaris and linux) # if ($OS eq 'linux' && ! exists $Private{$inode}{$sym} ) { # need to set a marker to not return. $Private{$inode}{$sym} = undef; } } } # # Set the entries in the %Private and %Evolving database hashes. # Both are referenced by the inode of the library, then the symbol name. # The value is a little piece of information to report to user when usage # is detected. # # $base is only used to look up Overrides and to go in the info piece. # sub set_db { my ($lib, $inode, $version, $sym) = @_; $Private{$inode}{$sym} = undef; $Evolving{$inode}{$sym} = undef; my $have_override = 0; my $base = basename($lib); if ( exists $Override{$base} ) { $have_override = 1; } my $lname = $base; $lname = $lib if $verbose; if( $have_override && ! $extended_private_match && ! $extended_evolving_match && exists $Override{$base}{$sym} ) { return if $Override{$base}{$sym} eq 'public'; } if( $extended_private_match ) { if ( $version ne '' && "${version}::${sym}" =~ /$private_match/o ) { $Private{$inode}{$sym} = "$lname:$version"; return; } elsif ( "${base}::${sym}" =~ /$private_match/o ) { $Private{$inode}{$sym} = "$lname"; return; } } elsif ( $have_override && exists $Override{$base}{$sym} && $Override{$base}{$sym} eq 'private' ) { $Private{$inode}{$sym} = "$lname:private"; return; } elsif ( $version ne '' && $version =~ /$private_match/o ) { $Private{$inode}{$sym} = "$lname:$version"; return; } # # Simple additional heuristic for GLIBC (leading underscore) # see special_case_syms(). # if( $OS eq 'linux' && ! $extended_private_match && ! defined $Override{$base}{$sym} && $version =~ /^GLIBC_/ ) { if ( $sym =~ /^_/ && 'private' =~ /$private_match/o ) { $Private{$inode}{$sym} = "$lname:$version"; return; } } if( $extended_evolving_match ) { if ( $version ne '' && "${version}::${sym}" =~ /$evolving_match/o ) { $Evolving{$inode}{$sym} = "$lname:$version"; return; } elsif ( "${base}::${sym}" =~ /$evolving_match/o ) { $Evolving{$inode}{$sym} = "$lname"; return; } } elsif ( $have_override && exists $Override{$base}{$sym} && $Override{$base}{$sym} eq 'evolving' ) { $Evolving{$inode}{$sym} = "$lname:evolving"; return; } elsif ( $version ne '' && $version =~ /$evolving_match/o ) { $Evolving{$inode}{$sym} = "$lname:$version"; return; } } # # Record filter library "aliasing" for auxiliary filter libraries. # The global hash %Filter will be used to apply the aliasing. # sub load_filter { my ($filtee, $filter) = @_; # set it to undef to indicate we have touched this one: $Filter{$filtee} = undef; if ( ! -e $filter ) { # the dynamic linker found it; so should we. die "invalid filter library: $filter: $!\n"; } # # These characters would never happen under normal circumstances, # but could cause our shell commands to fail: # return if $filter =~ /['\n]/; # currently only know how to handle filters on Solaris: if ( $OS eq 'solaris' ) { my $dump = `dump -Lv '$filter' 2>/dev/null`; my $base = basename($filtee); if ($dump =~ /\bAUXILIARY\b.*\b${base}\b/ || $filter =~ /\blibdl\.so\.\d+/) { $Filter{$filtee} = $filter; # set the alias. } } elsif ( $OS eq 'linux' ) { return; } } # # Load any external symbol databases. # sub load_external_dbs { if ($static_archive_file ne '' && $static_archive_file ne 'int') { my $db; foreach $db (split(/,/, $static_archive_file)) { if ( ! -e $db ) { # errant comma in path? load_static_archive_db($static_archive_file); last; } load_static_archive_db($db); } } if ($private_db_file) { load_private_db_file($private_db_file); } if ($abi_library_file) { load_abi_library_file($abi_library_file); } } sub load_static_archive_db { my($db) = @_; if ( ! -e $db ) { die "invalid static archive database: $db, $!\n"; } my $filetype = `file '$db'`; my $is_archive = 0; if ( $OS eq 'solaris' || $OS eq 'linux' ) { if ( $filetype =~ /ar\s+archive/ ) { $is_archive = 1; } } if ($is_archive) { load_archive($db); } else { open(DB, "<$db") || die "cannot open: $db: $!\n"; while () { chomp; next if /^\s*#/; next if /^\s*$/; # format: read_vtoc|/usr/lib/libadm.a:rdwr_vtoc.o my ($sym, $rest) = split(/\|/, $_, 2); my ($lib, $file) = split(/:/, $rest, 2); add_archive($sym, $lib, $file); } } # # In the case of there being no archives on the current machine, # the AR_* hashes will not be setup, leading to uninitilized use (-w) # during the checking. So we set some values here. # $AR_sym{__NO_SYMBOL__} = 1 if ! %AR_sym; $AR_cnt{__NO_SYMBOL__} = 1 if ! %AR_cnt; $AR_lib{__NO_SYMBOL__} = 1 if ! %AR_lib; } # add a sym, lib, module triple to the static archive database sub add_archive { my($sym, $lib, $file) = @_; $AR_sym{$sym}{"$file:$lib"} = 1; $AR_cnt{"$file:$lib"}++; $AR_lib{$lib}++; } # # Examine a library archive and record the static linking lookup # information. # sub load_archive { my($lib) = @_; return if exists $AR_lib{$lib}; # do not repeat an archive. # # These archive object files are problematic in that their # symbols are too common that they cause many false positives. # my (%skip, $skip); foreach $skip (@Archive_Skip) { $skip{$skip} = 1; } if ( $OS eq 'solaris' ) { # Run elfdump to extract the archive file members and symbols: if (! $Cmds{elfdump}) { die "no elfdump(1), cannot process archive $lib"; } open(ELFDUMP, "elfdump -s -N ARSYM '$lib'|") || die "elfdump -s -N ARSYM '$lib': $!\n"; $AR_lib{$lib} = 0; while() { # # e.g.: [1] 0x000022ac (_addchnstr.o):addchnstr # ( $1 ) ( $2 ) # if ( /^\s*\[.*\((.*)\):(\S+)/ ) { my $file = $1; my $sym = $2; next if $skip{$file}; add_archive($sym, $lib, $file); } } close(ELFDUMP); } elsif ( $OS eq 'linux' ) { open(OBJDUMP, "objdump -t '$lib' 2>/dev/null|") || die "objdump -t '$lib': $!\n"; my $current_module = ''; $AR_lib{$lib} = 0; while() { # # e.g.: nis_clone_obj.o: file format elf32-i386 # if ( /^(\S+):\s+file/ ) { $current_module = $1; next; } # # 00000450 g F .text 00000309 __nis_create_callback # ( $1 ) ( $2 ) # if ( /\s[gG]\s+\S*\s+\.text\b.*\s(\S+)$/ ) { my $file = $current_module; my $sym = $1; if ( $file eq '' ) { die "could not find module " . "file name for archive $lib\n"; } next if $skip{$file}; add_archive($sym, $lib, $file); } } close(OBJDUMP); } } # # Read in an externally supplied public/private database file. # sub load_private_db_file { my($db) = @_; if ( ! -e $db ) { die "invalid private archive database: $db, $!\n"; } open(DB, "<$db") || die "cannot open: $db: $!\n"; while () { chomp; next if /^\s*#/; next if /^\s*$/; my ($lib, $rest) = split(/\|/, $_, 2); if ($rest =~ /^FILE=(.*)/) { # keep a reference to a file to read in on demand: my $file = $1; $External_DB{$lib} = $file; } else { # otherwise store the text for this lib: $External_DB{$lib} .= $rest . "\n"; } } close(DB); # copy over filename paths to inode lookup: my ($lib, $inode); foreach $lib (keys(%External_DB)) { next if ! -e $lib; $inode = get_inode($lib); if ( defined $inode && ! exists $External_DB{$inode} ) { $External_DB{$inode} = $External_DB{$lib}; } } } # # read in a list of ABI libraries to check calls into: # (calls into all other libraries will be ignored) # sub load_abi_library_file { my ($lib_file) = @_; open(ABI_LIBS, "<$lib_file") || die "cannot open: $lib_file: $!\n"; while () { chomp; next if /^\s*#/; next if /^\s*$/; my $lib = $_; $ABI_Libs{$lib} = 1; my $inode = get_inode($lib); if ( defined $inode ) { $ABI_Libs{$inode} = 1; } } close(ABI_LIBS); } # # exec ldd -r (or equivalent) on binary. Never returns, parent # will read command output. # sub exec_ldd { my ($file) = @_; open(STDERR, ">&STDOUT"); # need to close stdin on linux for some suid programs e.g. chsh (!) close(STDIN); if ( defined $ld_path ) { $ENV{LD_LIBRARY_PATH} = $ld_path; } # currently, no difference between OSs $ENV{LD_DEBUG} = "files,bindings"; exec 'ldd', '-r', $file; exit 1; # exec failed } # # Adjust $ld_path under $ld_path_all_libs by substituting 'find' # sub modify_ld_path { my $new = ''; my $path; foreach $path (keys(%Lib_Dirs)) { next if $path =~ /^\s*$/; $new .= $path . ":"; } $new =~ s/:+$//; my $new_ld_path = ''; foreach $path (split(/:/, $ld_path)) { next if $path =~ /^\s*$/; if ( $path eq 'find' ) { if ( $new ne '' ) { $new_ld_path .= $new . ":"; } } else { $new_ld_path .= $path . ":"; } } $new_ld_path =~ s/:+$//; $ld_path = $new_ld_path; } # # Perform the static linking of system library archives check. # The heuristic is simply to see if "well-known" symbols exist in the # binary's .text area. # sub static_check { my ($file) = @_; return 0 if ! $static_check; my (%syms, %linkslib, $lib); # # N.B. technically we should use the %linkslib set below to verify # that the binary does not actually have the library on its DTNEEDED # list. Currently need to check if the above $dtneeded_ref does # not include extra libs from the full closure of the dynamic linking. # # XXX need to have $dtneeded_ref passed in if we are doing this, # that causes a slowdown under -S. see the calling code where # dynamic_check is currently skipped under -S. # # my $dtlib; # foreach $dtlib ( keys %{$dtneeded_ref} ) { # $linkslib{libc} = 1 if $dtlib =~ m,\blibc\.so,; # $linkslib{libsocket} = 1 if $dtlib =~ m,\blibsocket\.so,; # $linkslib{libnsl} = 1 if $dtlib =~ m,\blibnsl\.so,; # } get_syms($file, \%syms); my $cnt = 0; if ( $static_archive_file && %AR_sym ) { # use external list: $cnt += static_check_from_database($file, \%syms); } elsif ( $OS eq 'solaris' ) { # libc.a: if ($syms{'_exit'}) { $cnt++; print "$file: STATIC_LINK: libc.a\n"; } # libsocket.a: if ($syms{'socket'} && $syms{'_socket'} && $syms{'bind'} && $syms{'_bind'} && $syms{'connect'} && $syms{'_connect'}) { print "$file: STATIC_LINK: libsocket.a\n"; $cnt++; } # libnsl.a: if ($syms{'_xti_bind'} && $syms{'_xti_connect'} && $syms{'_tx_bind'} && $syms{'_tx_connect'}) { print "$file: STATIC_LINK: libnsl.a\n"; $cnt++; } } elsif ( $OS eq 'linux' ) { # libc.a: if ($syms{'_exit'}) { $cnt++; print "$file: STATIC_LINK: libc.a\n"; } } return $cnt; } # # Check (somewhat heuristically) for any static linking of static # archives in the database. # sub static_check_from_database { my($file, $sym_ref) = @_; my(%hits, %hitsyms); my $cnt = 0; my $sym; foreach $sym (keys( %{$sym_ref} ) ) { if ( exists $AR_sym{$sym} ) { my $id; foreach $id ( keys %{$AR_sym{$sym}} ) { # # collect hits. $id is an object file name # plus pathname tag "file.o:lib" # $hits{$id}++; push(@{$hitsyms{$id}}, $sym); } } } if ( ! %hits ) { # there can be no problems if there were no hits. return 0; } my (%static_links, %lib_hits, %long_msg, %short_msg); # # Now look for specific overlaps with the object file buckets, # using these fudge factors: # # minimum_syms_per_module: e.g. 4 means 4 symbols minimum per module.o # 0 indicates the constraint is not applied. # my $minimum_syms_per_module = 0; # # minimum_fraction: e.g. 0.9 means 90% or more of module.o must be hit. # my $minimum_fraction = 1.00; # # minimum_syms_per_library: e.g. 3 means we must have 3 or more # hits over the whole library before we give the warning. # my $minimum_syms_per_library = 3; # Loop over the id's (i.e the "file.o:lib" tags) my $id; foreach $id (keys %hits) { # Check if the module, i.e. file.o, is too small: my $size = $AR_cnt{$id}; next if $size < $minimum_syms_per_module; # Next, check if we have an acceptable fraction of them: my $f = $minimum_fraction * $size; my $n = int($f); $n++ if $n < $f; next if $hits{$id} < $n; # If so, set up the various warning variables: my($module, $lib) = split(/:/, $id, 2); $static_links{$lib} = 1; $lib_hits{$lib} += $hits{$id}; $long_msg{$lib} .= "\t$module\[$hits{$id}/$size\] " . join( ', ', @{$hitsyms{$id}} ) . "\n"; $short_msg{$lib} .= "$module\[$hits{$id}/$size\]," } if ( ! %static_links ) { # No modules overlapped enough, so no problems detected: return 0; } # Print out the warning messages: my ($lib, %didlib); foreach $lib (keys %static_links) { my $msg; next if $lib_hits{$lib} < $minimum_syms_per_library; my $blib = $lib; if ( $lib =~ /^[\d.]+--(.*)$/ ) { # # handle the full release N.M--/usr/lib/... # notation, and print only one # $blib = $1; next if $didlib{$blib}; $didlib{$blib} = 1; } my $msg_l = "\n" . $long_msg{$lib}; my $msg_s = $short_msg{$lib}; $msg_s =~ s/,+$//; $msg = "($lib_hits{$lib} symbols overlap)"; $msg .= $msg_l if $verbose; print "$file: STATIC_LINK: $blib $msg\n"; $cnt++; } return $cnt; } # # Extract the symbols defined in the binary's .text section. # Even if the the binary is stripped, these will be in .dynsym because # during dynamic linking shared objects may bind to these symbols. # sub get_syms { my ($file, $sym_ref) = @_; # # run the respective binary utilities and extract the .text symbols # TODO: extend to .bss, .data, etc. # if ( $OS eq 'solaris' ) { if ($Cmds{elfdump}) { open(ELFDUMP, "elfdump -s -N .dynsym '$file' 2>/dev/null |") || die "$!\n"; # line looks like: # [88] 0x00011110 0x00000974 FUNC GLOB 0 .text main while () { chomp; if (/\s\.text\s+(\S+)$/) { $sym_ref->{$1} = 1; } } close(ELFDUMP); } else { open(NM, "nm -Dp '$file' 2>/dev/null |") || die "$!\n"; # line looks like: # 0000311596 T Load_input_line while () { chomp; if (/^\w+\s+T\s+(\S+)$/) { $sym_ref->{$1} = 1; } } close(NM); } } elsif ( $OS eq 'linux' ) { # previously had -T ... open(OBJDUMP, "objdump -t '$file' 2>/dev/null |") || die "$!\n"; # line looks like: # 08051660 g DF .text 0000001a Base _exit while () { chomp; if ( /\s[gG]\s+\S*\s+\.text\b.*\s(\S+)$/ ) { $sym_ref->{$1} = 1; } } close(OBJDUMP); } } # # call the various initialization subroutines: # sub data_setup { set_matches(); set_extended_flags(); special_case_syms(); } sub set_extended_flags { # # Store whether we are doing extended matching for private or # evolving checks. These are set by -p & -e with a component of # the regex looking like: # # -p 'GLIBC.*::__str' (set syms __str* to private in GLIBC pkg) # -p 'libgkt.*::__gtk_' (set syms __gtk_* to private in libgtk) # # (those are made-up examples) # if ( $private_match =~ /::/ ) { $extended_private_match = 1; } if ( $evolving_match =~ /::/ ) { $extended_evolving_match = 1; } } # # sets and compiles the regex's for parsing the LD_DEBUG output. # sub set_matches { # # Set the various pattern matches for parsing the LD_DEBUG output. # They are similar for the OS's, but not the same. If that # output ever changes this script will no longer work... # # One can LD_DEBUG=files,bindings ldd -r to see what the # output looks like on a given OS. # if ( $OS eq 'solaris' ) { $ld_debug_match = q/^\d/; $lddstub_match = q/\blddstub\b/; $binding_match = q/binding file=(.*) to file=(.*): symbol `(\w+)'(.*)$/; $filter_match = q/file=(.*); filtered by (.*)$/; $unbound_match = q/^\s*symbol not found:\s+(\S+)(.*)/; $dtneeded_match = q/^\s*(\S+)\s+=>\s+(\(file not found\)|\S+)/; $file_match = q/^(\d+:)\s+file=(.*);\s+analyzing/; } elsif ( $OS eq 'linux' ) { # # be careful, is it "normal symbol" in newer GLIBC, # just "symbol" in earlier GLIBC. # $ld_debug_match = q/^\s*\d+:/; $lddstub_match = ''; $binding_match = q/binding file (.*) to (.*): .*symbol `(\w+)'(.*)$/; $filter_match = q/file=(.*); filtered by (.*)$/; # XXX not checked $unbound_match = q/^\s*undefined symbol:\s+(\S+)(.*)/; $dtneeded_match = q/^\s*(\S+)\s+=>\s+(not found|\S+)/; $file_match = q/^(\s*\d+:).*needed by (.*)$/; # # n.b. there is a PID mismatch in the GNU linker output if # we watch for the more sensible: # q/^(\d+:)\s+file=(.*);\s+generating link map/; # } } sub special_case_syms { my (@public, @private, @evolving, @skip, @archive_skip); my ($sym, $pair); get_special_case_syms(\@public, \@private, \@evolving, \@skip, \@archive_skip); if ($private_db_only) { # user supplied public/private database and wants only that. @public = (); @private = (); @evolving = (); } # Symbols to ignore: foreach $sym (@skip) { $Skipsym{$sym} = 1; } # Noisy archives to skip: @Archive_Skip = @archive_skip; # Symbols to reset to public: foreach $pair (@public) { my ($lib, $sym) = split(/:/, $pair); $Override{$lib}{$sym} = 'public'; $lib =~ s/\.so.*$/.so/; $Override{$lib}{$sym} = 'public'; } # Symbols to reset to evolving: foreach $pair (@evolving) { my ($lib, $sym) = split(/:/, $pair); $Override{$lib}{$sym} = 'evolving'; $lib =~ s/\.so.*$/.so/; $Override{$lib}{$sym} = 'evolving'; } # Symbols to reset to private: foreach $pair (@private) { my ($lib, $sym) = split(/:/, $pair); $Override{$lib}{$sym} = 'private'; $lib =~ s/\.so.*$/.so/; $Override{$lib}{$sym} = 'private'; } # read in data from use supplied override file: if ($override_db_file) { open(OVERRIDE, "<$override_db_file") || die "cannot open: $override_db_file: $!\n"; while () { chomp; next if /^\s*#/; next if /^\s*$/; my($lib, $sym, $class) = split(/\|/, $_, 3); # XXX what to do about full path for overrided? ###$Override{$lib}{$sym} = $class; if ($class eq 'deleted') { $Deleted{$lib}{$sym} = 1; if ($sym ne '__ALL__') { $Deleted{__DELETED__}{$sym} = 1; } next; } if ($lib eq '__SKIP__') { $Skipsym{$sym} = 1; next; } $lib = basename($lib); $Override{$lib}{$sym} = $class; $lib =~ s/\.so.*$/.so/; $Override{$lib}{$sym} = $class; } close(OVERRIDE); } } sub trim { my ($x) = @_; $x =~ s/^\s*//; $x =~ s/\s*$//; return $x; } # # Determine inode of file. Returns undef if there was a problem. # (including if it could not read the file: stat failed). # sub get_inode { my ($file) = @_; if ( exists $Inodes{$file} ) { return $Inodes{$file}; } my ($dev, $inode) = (stat($file))[0,1]; if (defined $inode) { $inode = "$inode:$dev"; } $Inodes{$file} = $inode; return $inode; } # # Determine if a file is ELF, returns 1 if so, 0 otherwise # (including if it could not read the file) # sub is_elf { my ($file) = @_; open(FILE, "<$file") || return 0; my ($n, $buf); $n = read(FILE, $buf, 4); close(FILE); if ( $n != 4 || $buf ne "\177ELF" ) { return 0; } return 1; } # # Use a simple criterion to see if a file is a library # shared object. # sub is_lib { my ($file) = @_; my $base = basename($file); if ( $base =~ /^lib.*\.so/ ) { # just a naming convention, but good enough return 1; } # less accurate, skip for now #} elsif ( $base =~ /\.so[\d.]*$/ ) { # otherwise try the output of file(1): if ( $OS eq 'solaris' ) { my $file_output = `file '$file'`; $file_output =~ s/^.*?:\s//; if ( $file_output =~ /dynamic lib/ ) { return 1; } } elsif ( $OS eq 'linux' ) { my $file_output = `file - < '$file'`; # for symlinks $file_output =~ s/^.*?:\s//; if ( $file_output =~ /shared object/ ) { return 1; } } return 0; } # # Run $njobs jobs in parallel (e.g. on SMP machine). # sub parallel { my (%tmps, $job, $p, $file); my @args = @Rerun_Args; if ($find_all_libs || $ld_path_all_libs) { my $serial = 0; if ($serial) { # # unfortunately, we do this part serially to # obtain @Check_List before proceeding: # foreach $file (@ARGV) { # # Does not actually check $file except # for SKIPs. Builds @Check_List and # lib info instead. # process($file); } } else { # experiment to do the lib search in parallel too: wait; # needed to clear out a previous child(??) @Check_List = @ARGV; # run case 1 in parallel: my ($err, $prob) = do_parallel('lib_find'); if ($err) { die "error in parallel task lib_find\n"; } } if ($ld_path_all_libs) { modify_ld_path(); if ( $ld_path ne '' ) { push(@args, '-L', $ld_path); } } if ( $lib_match ne '' ) { push(@args, '-l', "/$lib_match/"); } if ( %lib_match ) { my $str = join('|', keys(%lib_match)); push(@args, '-l', $str); } } else { @Check_List = @ARGV; } wait; # needed to clear out a previous child(??) $ENV{AC_PARALLEL} = 1; # run case 0 in parallel: my ($error_count, $problem_count) = do_parallel('check', @args); if ( $error_count ) { exit 1; } elsif ( $problem_count ) { exit 2; } else { exit 0; } } # # Manages the (current) two types of tasks we want to parallize: # 'check') The actual checking. # 'lib_find') Find libraries (i.e. for -a and/or -L find) # sub do_parallel { my ($case, @args) = @_; # @args is for case 'check' ... my (%tmps, $job, $p, $file); # spawn the child processes for the task: for ($job=0; $job < $njobs; $job++) { my $tmpfile0 = "/tmp/abicheck.in.$$.$job"; my $tmpfile1 = "/tmp/abicheck.out.$$.$job"; push(@tmpfiles, $tmpfile0, $tmpfile1); if ($case eq 'check') { # load the files to be processed by this job: open(TMP, ">$tmpfile0") || (killpids(), exit 1); my $cnt = 0; foreach $file (@Check_List) { if ($cnt++ % $njobs == $job) { print TMP $file, "\n"; } } close(TMP); } $p = fork(); if (! defined $p) { my $err = $!; killpids(); die "could not fork: $err\n"; } elsif ($p) { # parent: push(@pids, $p); $tmps{$p} = $tmpfile1; } else { # child: close(STDOUT); open(STDOUT, ">$tmpfile1"); if ($case eq 'check') { push(@args, '-k', '-f', $tmpfile0); print STDERR "job $job ($$) running: ", basename($0); if (! $ENV{AC_QUIET}) { print STDERR " ", join(' ', @args); } print STDERR "\n"; exec $0, @args; exit 1; # child must exit } elsif ($case eq 'lib_find') { my $cnt = 0; my $libs = 0; my $try = 0; foreach $file (@Check_List) { if ($cnt++ % $njobs == $job) { $try++; if (is_elf($file) && is_lib($file)) { $libs++; print "LIB: $file\n"; } } } if (! $ENV{AC_QUIET}) { print STDERR "job $job ($$) " . "LIB FOUND($libs/$try/$cnt)\n"; } exit 0; # child must exit } } } $SIG{INT} = \&interrupted; $SIG{TERM} = \&interrupted; my $error_count = 0; my $problem_count = 0; # wait for child processes to finish: for ($job=0; $job < $njobs; $job++) { $p = wait; my $rc = $?/256; if ($tmps{$p} && open(TMP, "<$tmps{$p}")) { while () { if ($case eq 'check') { print; } elsif ($case eq 'lib_find') { chomp; if ( /^LIB: (.*)$/ ) { my $file = $1; my $b = basename($file); my $d = dirname($file); if ( $ld_path_all_libs ) { $Lib_Dirs{$d} = 1; } if ( $find_all_libs == 1 ) { $lib_match{$b} = 1; } } } } close(TMP); unlink($tmps{$p}); delete($tmps{$p}); } else { $error_count++; } if ($rc == 0) { ; } elsif ($rc == 2) { $problem_count++; } else { $error_count++; } } $SIG{INT} = 'DEFAULT'; $SIG{TERM} = 'DEFAULT'; unlink(@tmpfiles); @pids = (); @tmpfiles = (); return ($error_count, $problem_count); } # # Emergency killing of child processes, tmpfiles removal, etc. # sub interrupted { killpids(); exit 1; } sub killpids { kill 'INT', @pids; kill 'TERM', @pids; kill 'KILL', @pids; unlink(@tmpfiles); } # # used with njobs "-j detect" or "-j N", switch, try to determine the # number of cpus. # sub ncpu { my $output = ''; if ($OS eq 'solaris') { my $psrinfo = "/usr/sbin/psrinfo"; $psrinfo = "psrinfo" if ! -x $psrinfo; $output = `$psrinfo 2>/dev/null`; } elsif ( $OS eq 'linux' ) { $output = `egrep '^processor[ ]*:' /proc/cpuinfo 2>/dev/null`; } my $ncpu = 0; my $line; foreach $line (split(/\n/, $output)) { next if $line =~ /^\s*$/; $ncpu++; } $ncpu = 1 if $ncpu == 0; return $ncpu; } # # print short usage statement # sub usage { print <<"END"; abicheck [-h|-?] [-k] [-a] [-I] [-v] [-f ] [-o ] [-p ] [-e ] [-j ] [-l -l ...] [-L ] [(-s|-S) ] [(-d|-D) ] [-O ] [-A ] ... END } # # print long description # sub help { if ( ! $ENV{ABICHECK_HELP} ) { # try to use a pager $ENV{ABICHECK_HELP} = 1; if ( $ENV{PAGER} ) { system("$0 -h | $ENV{PAGER}"); return; } else { system("type more 1>/dev/null 2>&1"); if ( $? == 0 ) { system("$0 -h | more"); return } } } usage(); print <<"END"; abicheck: check application binaries for calls to private or evolving symbols in libraries and check for static linking of some system libraries. abicheck is run on application binaries and issues warnings whenever any of the following three conditions are detected: Private symbol usage. Private symbols are functions or data variables in a library package that are internal to that package. They are used by the libraries in the package for internal communication and are not part of the API/ABI that application developers should use. Evolving symbol usage. Evolving symbols are functions or data variables in a library package that are intended for developer consumption, but have been marked as "evolving" or "unstable"" in the sense that they may become incompatible or disappear on a later release of the library package. Static linking. Static linking of system libraries (e.g. libc.a) into an application is generally not a good idea because the system library code it "locks" into the application binary may become incompatible with later releases of the system. abicheck attempts to detect static linking of a few system libraries. The -s and -S options can be used to specify more extensive databases. The default behavior is to, for each binary object checked, to examine direct calls from that binary object only. The -l option allows the libraries the binary object brings in to have their calls checked as well. Usage: abicheck is a list of application binary objects to check. are: -k Keep on checking binaries even if there are serious errors (dynamic linker reports unresolved symbols, ldd(1) failures, no symbols detected) -h, -? Print out long form of help. -v Verbose. Print out additional information. -f A file containing a list of binary objects to check, one per line. This list is appended to any list on the cmd line via . If is '-', then stdin is used. -o Write output to instead of stdout. -p Modify the version name pattern match labelling private version sets. Default is /private/ using a case insensitive match. If a component of the regex contains two colons in a row: 'patt1::patt2', then symbol level matching will be activated by checking whether "::" or "::" matches (where the symbol name, version (if any), and library basename are substituted for , , and ). E.g.: -p 'FOO_VERS.*::_foopriv' or -p 'libfoo\.so.*::_foopriv' -e Same as -p but for "evolving" interfaces. -L Set the LD_LIBRARY_PATH environment variable to before invoking dynamic linker. Use -L "" to unset LD_LIBRARY_PATH. If one of the components of is the string "find", then all shared libraries in are found and their paths inserted into the "find" location. Note that the order will random. -l Add the basename or full path to the shared object library to the list of objects to be checked for making private calls. This option may occur more than once on the command line and is additive. By default, only direct calls from a binary to the system libraries are checked. The -l switch allows checking of indirect calls e.g.: app -> supportlib -> systemlib. -a Loop through all of the binaries before checking and collect the list of all shared objects. Take the basename of each shared object found and act as though it was specified with the -l option option and then run the abicheck checks. This way, calls from all "application internal" objects are checked rather than just the direct calls. (Useful when shared objects do not have their dependencies recorded.) -I Ignore shared libraries in checking, only check executables. Compatible with -a: libraries will be searched for first but then not checked. -d Specify fallback flat-file symbol database for -D the dynamic (public vs. private) test. These classifications will be used if the library is not versioned (i.e. classification does not exist in the library itself). Use -D to indicate that only information from should be used. Lines in can be of one of these forms: | || |FILE= must be the full path to the library to be specified (it cannot be a basename). The first form marks as private. The second form marks with where may be public, private, or evolving. The third form indicates the file should be opened on demand when is first encountered. File contains lines of the first two forms except for the field. The third form is a speedup to avoid processing many classification lines for libraries never encountered in the run. -O Specify an override file to modify the symbol classification for the dynamic (public vs. private) test. The format for the override file is like: || The library can be the full path or basename. If library is "__SKIP__" the symbol will be ignored for any library it is found in. The class can be "public", "private", "evolving", or "deleted". The "deleted" class is special-cased, means the symbol was deleted from the library on some release. The symbol "__ALL__" for the "deleted" class means the entire library was deleted or is otherwise unstable to use. Examples: libfoo.so.1|__bar|private /lib/libxyz.so.1|baz|public __SKIP__|__fputwc_xpg5 These settings override any classification inside the library (from library versioning, obtainable from pvs(1), etc). -A Set the ABI libraries of interest to the libraries listed in (full pathnames, one per line). Only calls into these libraries will be checked; all other library calls will be ignored. -s Specify more extensive symbol databases for the -S static linking test. may be a comma separated list of files. If a file is a static archive (lib*.a) it is processed to extract the symbols. Otherwise it is a database file consisting of lines of the form |: for example: shmat|/usr/lib/libc.a:shmsys.o shmctl|/usr/lib/libc.a:shmsys.o shmdt|/usr/lib/libc.a:shmsys.o shmget|/usr/lib/libc.a:shmsys.o ... When all symbols in a module.o are defined in the application, static linking of that module (and corresponding library archive) is assumed. Use -S to indicate that only the static link test should be performed. Use "-S int" to do only the static link check and using the internal database. Use "-s none" or "-S none" to skip the static linking check entirely. -j Run in parallel as separate processes. Implies -k. Primarily intended for multiple CPU machines where should be close to the number of processors. Output is collected in tmp files and printed all at once near the end of the run as each job finishes. If is "-", "detect", or "n", then njobs will be set to a number depending on the number of processors on the current machine (if that can be determined). Output: There is one line per problem (there may be multiple problems per binary checked) which look like the following: If no problems were found: : OK If private symbol usage: : PRIVATE (:) If evolving symbol usage: : EVOLVING (:) If file statically linked in a system archive library: : STATIC_LINK ( symbols overlap) (use of the -v option will cause the statically bound symbols to be listed.) If checking of the file was skipped: : SKIP () Under use of the deleted class in the -O override file option, these problems may be found: If a symbol has been deleted from the library on some release: : DELETED_SYM: / ( will be "unbound" if the symbol was unbound) If an entire library has been deleted on some release or is otherwise unstable to use: : UNSTABLE_LIB: => ( may be "file not found" if the library could not be found) The following problems will cause a fatal error unless the -k switch is used: If the dynamic linker could not resolve N symbols when ldd -r was run: : UNBOUND_SYMBOLS: If the dynamic linker found no dynamic bindings: : NO_BINDINGS If ldd(1) -r with LD_DEBUG=files,bindings failed: : LDD_ERROR In these latter 3 cases run "ldd -r" on the binary file for more information on what went wrong. (abicheck runs ldd -r with LD_DEBUG=files,bindings set). On some systems the dynamic linker will not process SUID programs with LD_DEBUG set (this usually results in NO_BINDINGS in the abicheck output). Note that if you are running abicheck on a shared library (e.g. libfoo.so) that has NOT been built with -l flags to record its library dependencies, then the "unbound symbols" problem is very likely. There is not much that can be done besides rebuilding the library or checking an application binary that uses the library and use the abicheck -l switch. Exit status: 0 no errors and no problems found. 1 a fatal error occurred. 2 no fatal errors but some binaries with problems were detected. Notes: Only ELF objects are checked. In the -s, -S, -d, and -O dbfiles the '#' character starts a comment line in the usual way. Unless one is using the "::" custom matches supplied via the -p or -e flags, abicheck can only check against system libraries that have had symbol versioning applied to them (i.e. the private and/or evolving information recorded for each symbol in the library itself). For more info about symbol versioning, see the "Solaris Linker and Libraries Guide" answerbook at the URL http://docs.sun.com/ab2/coll.45.13 and the Commands/Version-Script section of the GNU linker "ld" info page. The default symbol version name matching patterns are case insensitive matches to the strings "private" and "evolving" for the private and evolving cases, respectively. Odd filenames containing the single-quote character or newline will be skipped; these characters interfere with calling commands via the shell. To recurse directories use find(1) and either collect the output to a file for use with the -f option, or in a pipe in via: find ... | abicheck -f - ... Bugs: The program is dependent on the form of the runtime linkers debug output. Since this output is intended to be human readable rather than machine readable, abicheck will break whenever the output format changes. On Solaris it is possible that the Link Auditing C interface could be used to avoid this problem. On Linux when ldd(1) is run on a SUID binary, it (ldd and the dynamic-linker) will sometimes actually run the binary. On Linux SUID/SGID binaries are currently skipped even if the user is root; test unprivileged copies instead. END } # # Setup some special case symbols to skip the processing of. # Yes, this is a lot of ugly data... # sub get_special_case_syms { my ($public, $evolving, $private, $skip, $archive_skip) = @_; # skip undef items introduced by compiler via crt, etc, # or known private/evolving versioning errors. # false positives in static linking if ( $OS eq 'solaris' ) { push(@{$archive_skip}, qw( malloc.o textmem.o )); } elsif ( $OS eq 'linux' ) { @{$archive_skip} = qw( getopt.o snprintf.o xmalloc.o ); } if ( $OS eq 'solaris' ) { # # Some low-level symbols we are not interested in. # @{$skip} = qw( _END_ _START_ __1cG__CrunMdo_exit_code6F_v_ __1cG__CrunVdo_exit_code_in_range6Fpv1_v_ __1cH__CimplKcplus_fini6F_v_ __1cH__CimplKcplus_init6F_v_ __fsr_init_value _ex_deregister _ex_register _fini _get_exit_frame_monitor _init _objcInit main _ctype __ctype __ctype_mask _end _environ environ errno __iob _lib_version __fpstart __xtol __xtoll ); # # These are frequently called symbols that are incorrectly # classified in Solaris. # @{$public} = qw( libDtSvc.so.1:DtActionInvoke libDtSvc.so.1:DtCreateHelpDialog libDtSvc.so.1:DtCreateHelpQuickDialog libDtSvc.so.1:DtDtsIsTrue libDtSvc.so.1:DtHelpQuickDialogGetChild libDtSvc.so.1:DtHelpReturnSelectedWidgetId libDtSvc.so.1:DtSaverGetWindows libDtSvc.so.1:DtWsmRemoveWorkspaceCallback libX11.so.4:XReadBitmapFileData libX11.so.4:_XAllocScratch libX11.so.4:_XAllocTemp libX11.so.4:_XAsyncErrorHandler libX11.so.4:_XDeq libX11.so.4:_XEatData libX11.so.4:_XEnq libX11.so.4:_XError libX11.so.4:_XErrorFunction libX11.so.4:_XEventsQueued libX11.so.4:_XFlush libX11.so.4:_XFreeTemp libX11.so.4:_XGetAsyncReply libX11.so.4:_XGetHostname libX11.so.4:_XIOError libX11.so.4:_XIOErrorFunction libX11.so.4:_XRead libX11.so.4:_XReadEvents libX11.so.4:_XReadPad libX11.so.4:_XRegisterInternalConnection libX11.so.4:_XReply libX11.so.4:_XScreenOfWindow libX11.so.4:_XSend libX11.so.4:_XSetLastRequestRead libX11.so.4:_XUnregisterInternalConnection libX11.so.4:_XVIDtoVisual libX11.so.4:_Xdebug libX11.so.4:_Xglobal_lock libXaw.so.5:FMT8BIT libXm.so.3:_XmInheritClass libXm.so.4:_XmStrings libXm.so.4:_XmStrings21 libXt.so.4:XtGetSelectionParameters libXt.so.4:XtOpenApplication libXt.so.4:XtR6ShellStrings libXt.so.4:XtR6Strings libXt.so.4:XtSendSelectionRequest libXt.so.4:XtSetSelectionParameters libXt.so.4:XtShellStrings libXt.so.4:XtStrings libXt.so.4:_XLockMutex_fn libXt.so.4:_XUnlockMutex_fn libXt.so.4:_XtCheckSubclassFlag libXt.so.4:_XtInherit libXt.so.4:_XtInheritTranslations libXt.so.4:_XtIsSubclassOf libc.so.1:__fpstart libc.so.1:htonl libc.so.1:htons libc.so.1:mkstemp libc.so.1:ntohl libc.so.1:ntohs libc.so.1:thr_main libdmi.so.1:dmi_error libdmi.so.1:freeDmiString libdmi.so.1:newDmiString libdmi.so.1:printDmiDataUnion libdmi.so.1:printDmiString libnsl.so.1:gethostname libresolv.so.2:res_mkupdrec ); @{$private} = qw( ); } elsif ( $OS eq 'linux' ) { # # Some low-level symbols we are not interested in. # @{$skip} = qw( atexit _environ etext exit _fini _fp_hw _GLOBAL_OFFSET_TABLE_ __gmon_start__ _init __libc_init_first __libc_start_main main _mcleanup __monstartup monstartup ); # # This big hardwired exception list is pretty gross, but # without it nearly all applications use at least one of # the following symbols. # # The good news is that it is based on the new GLIBC_PRIVATE # version set in GLIBC 2.2.5. This list was extracted from the # GLIBC 2.3 sources using the versioning scripts "Versions". # If the symbol name started with "_" but is not in the # GLIBC_PRIVATE set, it is assumed private and is listed here. # # As GLIBC 2.2.5 and above appears more widely in the field # we will # remove this list entirely, or keep it as an option. # @{$public} = qw( ld:___tls_get_addr ld:__close ld:__fxstat ld:__getcwd ld:__getpid ld:__hurd_sigthread_stack_base ld:__hurd_sigthread_stack_end ld:__hurd_sigthread_variables ld:__hurd_threadvar_stack_mask ld:__hurd_threadvar_stack_offset ld:__libc_memalign ld:__libc_read ld:__libc_write ld:__lseek ld:__mmap ld:__open ld:__tls_get_addr ld:__xstat ld:_dl_mcount ld:_exit ld:_hurd_intr_rpc_mach_msg ld:_r_debug libBrokenLocale:__ctype_get_mb_cur_max libc.so.6:_Exit libc.so.6:_IO_2_1_stderr_ libc.so.6:_IO_2_1_stdin_ libc.so.6:_IO_2_1_stdout_ libc.so.6:_IO_adjust_column libc.so.6:_IO_adjust_wcolumn libc.so.6:_IO_clearerr libc.so.6:_IO_default_doallocate libc.so.6:_IO_default_finish libc.so.6:_IO_default_pbackfail libc.so.6:_IO_default_uflow libc.so.6:_IO_default_xsgetn libc.so.6:_IO_default_xsputn libc.so.6:_IO_do_write libc.so.6:_IO_doallocbuf libc.so.6:_IO_fclose libc.so.6:_IO_fdopen libc.so.6:_IO_feof libc.so.6:_IO_ferror libc.so.6:_IO_fflush libc.so.6:_IO_fgetc libc.so.6:_IO_fgetpos libc.so.6:_IO_fgetpos64 libc.so.6:_IO_fgets libc.so.6:_IO_file_attach libc.so.6:_IO_file_close libc.so.6:_IO_file_close_it libc.so.6:_IO_file_doallocate libc.so.6:_IO_file_finish libc.so.6:_IO_file_fopen libc.so.6:_IO_file_init libc.so.6:_IO_file_jumps libc.so.6:_IO_file_open libc.so.6:_IO_file_overflow libc.so.6:_IO_file_read libc.so.6:_IO_file_seek libc.so.6:_IO_file_seekoff libc.so.6:_IO_file_setbuf libc.so.6:_IO_file_stat libc.so.6:_IO_file_sync libc.so.6:_IO_file_underflow libc.so.6:_IO_file_write libc.so.6:_IO_file_xsputn libc.so.6:_IO_fileno libc.so.6:_IO_flockfile libc.so.6:_IO_flush_all libc.so.6:_IO_flush_all_linebuffered libc.so.6:_IO_fopen libc.so.6:_IO_fprintf libc.so.6:_IO_fputs libc.so.6:_IO_fread libc.so.6:_IO_free_backup_area libc.so.6:_IO_free_wbackup_area libc.so.6:_IO_freopen libc.so.6:_IO_fscanf libc.so.6:_IO_fseek libc.so.6:_IO_fsetpos libc.so.6:_IO_fsetpos64 libc.so.6:_IO_ftell libc.so.6:_IO_ftrylockfile libc.so.6:_IO_funlockfile libc.so.6:_IO_fwrite libc.so.6:_IO_getc libc.so.6:_IO_getline libc.so.6:_IO_getline_info libc.so.6:_IO_gets libc.so.6:_IO_init libc.so.6:_IO_init_marker libc.so.6:_IO_init_wmarker libc.so.6:_IO_iter_begin libc.so.6:_IO_iter_end libc.so.6:_IO_iter_file libc.so.6:_IO_iter_next libc.so.6:_IO_least_wmarker libc.so.6:_IO_link_in libc.so.6:_IO_list_all libc.so.6:_IO_list_lock libc.so.6:_IO_list_resetlock libc.so.6:_IO_list_unlock libc.so.6:_IO_marker_delta libc.so.6:_IO_marker_difference libc.so.6:_IO_padn libc.so.6:_IO_pclose libc.so.6:_IO_peekc_locked libc.so.6:_IO_peekc_unlocked libc.so.6:_IO_perror libc.so.6:_IO_popen libc.so.6:_IO_printf libc.so.6:_IO_proc_close libc.so.6:_IO_proc_open libc.so.6:_IO_putc libc.so.6:_IO_puts libc.so.6:_IO_remove_marker libc.so.6:_IO_rewind libc.so.6:_IO_scanf libc.so.6:_IO_seekmark libc.so.6:_IO_seekoff libc.so.6:_IO_seekpos libc.so.6:_IO_seekwmark libc.so.6:_IO_setb libc.so.6:_IO_setbuf libc.so.6:_IO_setbuffer libc.so.6:_IO_setlinebuf libc.so.6:_IO_setvbuf libc.so.6:_IO_sgetn libc.so.6:_IO_sprintf libc.so.6:_IO_sputbackc libc.so.6:_IO_sputbackwc libc.so.6:_IO_sscanf libc.so.6:_IO_stderr_ libc.so.6:_IO_stdin_ libc.so.6:_IO_stdout_ libc.so.6:_IO_str_init_readonly libc.so.6:_IO_str_init_static libc.so.6:_IO_str_overflow libc.so.6:_IO_str_pbackfail libc.so.6:_IO_str_seekoff libc.so.6:_IO_str_underflow libc.so.6:_IO_sungetc libc.so.6:_IO_sungetwc libc.so.6:_IO_switch_to_get_mode libc.so.6:_IO_switch_to_main_wget_area libc.so.6:_IO_switch_to_wbackup_area libc.so.6:_IO_switch_to_wget_mode libc.so.6:_IO_un_link libc.so.6:_IO_ungetc libc.so.6:_IO_unsave_markers libc.so.6:_IO_unsave_wmarkers libc.so.6:_IO_vfprintf libc.so.6:_IO_vfscanf libc.so.6:_IO_vsprintf libc.so.6:_IO_wdefault_doallocate libc.so.6:_IO_wdefault_finish libc.so.6:_IO_wdefault_pbackfail libc.so.6:_IO_wdefault_setbuf libc.so.6:_IO_wdefault_uflow libc.so.6:_IO_wdefault_xsgetn libc.so.6:_IO_wdefault_xsputn libc.so.6:_IO_wdo_write libc.so.6:_IO_wdoallocbuf libc.so.6:_IO_wfile_jumps libc.so.6:_IO_wfile_overflow libc.so.6:_IO_wfile_seekoff libc.so.6:_IO_wfile_setbuf libc.so.6:_IO_wfile_sync libc.so.6:_IO_wfile_underflow libc.so.6:_IO_wfile_xsputn libc.so.6:_IO_wmarker_delta libc.so.6:_IO_wsetb libc.so.6:_Qp_add libc.so.6:_Qp_cmp libc.so.6:_Qp_cmpe libc.so.6:_Qp_div libc.so.6:_Qp_dtoq libc.so.6:_Qp_feq libc.so.6:_Qp_fge libc.so.6:_Qp_fgt libc.so.6:_Qp_fle libc.so.6:_Qp_flt libc.so.6:_Qp_fne libc.so.6:_Qp_itoq libc.so.6:_Qp_mul libc.so.6:_Qp_neg libc.so.6:_Qp_qtod libc.so.6:_Qp_qtoi libc.so.6:_Qp_qtos libc.so.6:_Qp_qtoui libc.so.6:_Qp_qtoux libc.so.6:_Qp_qtox libc.so.6:_Qp_sqrt libc.so.6:_Qp_stoq libc.so.6:_Qp_sub libc.so.6:_Qp_uitoq libc.so.6:_Qp_uxtoq libc.so.6:_Qp_xtoq libc.so.6:_S_catch_exception_raise libc.so.6:_S_catch_exception_raise_state libc.so.6:_S_catch_exception_raise_state_identity libc.so.6:_S_msg_add_auth libc.so.6:_S_msg_del_auth libc.so.6:_S_msg_describe_ports libc.so.6:_S_msg_get_dtable libc.so.6:_S_msg_get_env_variable libc.so.6:_S_msg_get_environment libc.so.6:_S_msg_get_fd libc.so.6:_S_msg_get_init_int libc.so.6:_S_msg_get_init_ints libc.so.6:_S_msg_get_init_port libc.so.6:_S_msg_get_init_ports libc.so.6:_S_msg_proc_newids libc.so.6:_S_msg_report_wait libc.so.6:_S_msg_set_dtable libc.so.6:_S_msg_set_env_variable libc.so.6:_S_msg_set_environment libc.so.6:_S_msg_set_fd libc.so.6:_S_msg_set_init_int libc.so.6:_S_msg_set_init_ints libc.so.6:_S_msg_set_init_port libc.so.6:_S_msg_set_init_ports libc.so.6:_S_msg_sig_post libc.so.6:_S_msg_sig_post_untraced libc.so.6:__Qp_handle_exceptions libc.so.6:___brk_addr libc.so.6:__adjtimex libc.so.6:__after_morecore_hook libc.so.6:__align_cpy_1 libc.so.6:__align_cpy_16 libc.so.6:__align_cpy_2 libc.so.6:__align_cpy_4 libc.so.6:__align_cpy_8 libc.so.6:__arch_prctl libc.so.6:__argz_count libc.so.6:__argz_next libc.so.6:__argz_stringify libc.so.6:__asprintf libc.so.6:__assert libc.so.6:__assert_fail libc.so.6:__assert_perror_fail libc.so.6:__backtrace libc.so.6:__backtrace_symbols libc.so.6:__backtrace_symbols_fd libc.so.6:__bsd_getpgrp libc.so.6:__bzero libc.so.6:__check_rhosts_file libc.so.6:__clone libc.so.6:__clone2 libc.so.6:__close libc.so.6:__cmsg_nxthdr libc.so.6:__connect libc.so.6:__ctype32_b libc.so.6:__ctype32_tolower libc.so.6:__ctype32_toupper libc.so.6:__ctype_b libc.so.6:__ctype_get_mb_cur_max libc.so.6:__ctype_tolower libc.so.6:__ctype_toupper libc.so.6:__curbrk libc.so.6:__cxa_atexit libc.so.6:__cxa_finalize libc.so.6:__cyg_profile_func_enter libc.so.6:__cyg_profile_func_exit libc.so.6:__daylight libc.so.6:__dcgettext libc.so.6:__dcngettext libc.so.6:__default_morecore libc.so.6:__deregister_frame libc.so.6:__deregister_frame_info libc.so.6:__dgettext libc.so.6:__divdi3 libc.so.6:__divl libc.so.6:__divls libc.so.6:__divlu libc.so.6:__divq libc.so.6:__divqs libc.so.6:__divqu libc.so.6:__dup2 libc.so.6:__duplocale libc.so.6:__endmntent libc.so.6:__environ libc.so.6:__errno_location libc.so.6:__fbufsize libc.so.6:__fcntl libc.so.6:__ffs libc.so.6:__fillbf libc.so.6:__finite libc.so.6:__finitef libc.so.6:__finitel libc.so.6:__flbf libc.so.6:__flshfp libc.so.6:__fork libc.so.6:__fpending libc.so.6:__fpu_control libc.so.6:__fpurge libc.so.6:__frame_state_for libc.so.6:__freadable libc.so.6:__freading libc.so.6:__free_hook libc.so.6:__freelocale libc.so.6:__fsetlocking libc.so.6:__fwritable libc.so.6:__fwriting libc.so.6:__fxstat libc.so.6:__fxstat64 libc.so.6:__getcwd libc.so.6:__getdelim libc.so.6:__getmntent_r libc.so.6:__getpagesize libc.so.6:__getpgid libc.so.6:__getpid libc.so.6:__gettimeofday libc.so.6:__getuids libc.so.6:__gmtime_r libc.so.6:__h_errno_location libc.so.6:__hurd_errno_location libc.so.6:__hurd_fail libc.so.6:__hurd_file_name_lookup libc.so.6:__hurd_sigthread_stack_base libc.so.6:__hurd_sigthread_stack_end libc.so.6:__hurd_sigthread_variables libc.so.6:__hurd_threadvar_max libc.so.6:__hurd_threadvar_stack_mask libc.so.6:__hurd_threadvar_stack_offset libc.so.6:__ieee_get_fp_control libc.so.6:__ieee_set_fp_control libc.so.6:__isalnum_l libc.so.6:__isalpha_l libc.so.6:__isascii_l libc.so.6:__isblank_l libc.so.6:__iscntrl_l libc.so.6:__isdigit_l libc.so.6:__isgraph_l libc.so.6:__isinf libc.so.6:__isinff libc.so.6:__isinfl libc.so.6:__islower_l libc.so.6:__isnan libc.so.6:__isnanf libc.so.6:__isnanl libc.so.6:__isprint_l libc.so.6:__ispunct_l libc.so.6:__isspace_l libc.so.6:__isupper_l libc.so.6:__iswalnum_l libc.so.6:__iswalpha_l libc.so.6:__iswblank_l libc.so.6:__iswcntrl_l libc.so.6:__iswctype libc.so.6:__iswctype_l libc.so.6:__iswdigit_l libc.so.6:__iswgraph_l libc.so.6:__iswlower_l libc.so.6:__iswprint_l libc.so.6:__iswpunct_l libc.so.6:__iswspace_l libc.so.6:__iswupper_l libc.so.6:__iswxdigit_l libc.so.6:__isxdigit_l libc.so.6:__ivaliduser libc.so.6:__key_decryptsession_pk_LOCAL libc.so.6:__key_encryptsession_pk_LOCAL libc.so.6:__key_gendes_LOCAL libc.so.6:__libc_allocate_rtsig libc.so.6:__libc_calloc libc.so.6:__libc_current_sigrtmax libc.so.6:__libc_current_sigrtmin libc.so.6:__libc_free libc.so.6:__libc_freeres libc.so.6:__libc_getspecific libc.so.6:__libc_init_first libc.so.6:__libc_mallinfo libc.so.6:__libc_malloc libc.so.6:__libc_mallopt libc.so.6:__libc_memalign libc.so.6:__libc_pvalloc libc.so.6:__libc_realloc libc.so.6:__libc_sa_len libc.so.6:__libc_start_main libc.so.6:__libc_valloc libc.so.6:__line_wrap_output libc.so.6:__line_wrap_update libc.so.6:__lseek libc.so.6:__lxstat libc.so.6:__lxstat64 libc.so.6:__mach_msg libc.so.6:__mach_msg_destroy libc.so.6:__mach_msg_overwrite libc.so.6:__mach_port_deallocate libc.so.6:__mach_reply_port libc.so.6:__mach_task_self_ libc.so.6:__mach_thread_self libc.so.6:__malloc_hook libc.so.6:__malloc_initialize_hook libc.so.6:__malloc_initialized libc.so.6:__mbrlen libc.so.6:__mbrtowc libc.so.6:__memalign_hook libc.so.6:__memcpy_by2 libc.so.6:__memcpy_by4 libc.so.6:__memcpy_c libc.so.6:__memcpy_g libc.so.6:__mempcpy libc.so.6:__mempcpy_by2 libc.so.6:__mempcpy_by4 libc.so.6:__mempcpy_byn libc.so.6:__mempcpy_small libc.so.6:__memset_cc libc.so.6:__memset_ccn_by2 libc.so.6:__memset_ccn_by4 libc.so.6:__memset_cg libc.so.6:__memset_gcn_by2 libc.so.6:__memset_gcn_by4 libc.so.6:__memset_gg libc.so.6:__mig_allocate libc.so.6:__mig_dealloc_reply_port libc.so.6:__mig_deallocate libc.so.6:__mig_get_reply_port libc.so.6:__mig_put_reply_port libc.so.6:__mig_strncpy libc.so.6:__mmap libc.so.6:__moddi3 libc.so.6:__monstartup libc.so.6:__morecore libc.so.6:__mutex_init libc.so.6:__mutex_lock libc.so.6:__mutex_lock_solid libc.so.6:__mutex_trylock libc.so.6:__mutex_unlock libc.so.6:__mutex_unlock_solid libc.so.6:__newlocale libc.so.6:__nl_langinfo_l libc.so.6:__nss_configure_lookup libc.so.6:__nss_database_lookup libc.so.6:__nss_group_lookup libc.so.6:__nss_hostname_digits_dots libc.so.6:__nss_hosts_lookup libc.so.6:__nss_next libc.so.6:__nss_passwd_lookup libc.so.6:__open libc.so.6:__open64 libc.so.6:__overflow libc.so.6:__pipe libc.so.6:__poll libc.so.6:__pread64 libc.so.6:__printf_fp libc.so.6:__profile_frequency libc.so.6:__progname libc.so.6:__progname_full libc.so.6:__pwrite64 libc.so.6:__rawmemchr libc.so.6:__rcmd_errstr libc.so.6:__read libc.so.6:__realloc_hook libc.so.6:__register_frame libc.so.6:__register_frame_info libc.so.6:__register_frame_info_table libc.so.6:__register_frame_table libc.so.6:__reml libc.so.6:__remls libc.so.6:__remlu libc.so.6:__remq libc.so.6:__remqs libc.so.6:__remqu libc.so.6:__res_init libc.so.6:__res_nclose libc.so.6:__res_ninit libc.so.6:__res_randomid libc.so.6:__res_state libc.so.6:__rpc_thread_createerr libc.so.6:__rpc_thread_svc_fdset libc.so.6:__rpc_thread_svc_max_pollfd libc.so.6:__rpc_thread_svc_pollfd libc.so.6:__sbrk libc.so.6:__sched_get_priority_max libc.so.6:__sched_get_priority_min libc.so.6:__sched_getparam libc.so.6:__sched_getscheduler libc.so.6:__sched_setscheduler libc.so.6:__sched_yield libc.so.6:__secure_getenv libc.so.6:__select libc.so.6:__send libc.so.6:__setmntent libc.so.6:__setpgid libc.so.6:__sigaction libc.so.6:__sigaddset libc.so.6:__sigdelset libc.so.6:__sigismember libc.so.6:__signbit libc.so.6:__signbitf libc.so.6:__signbitl libc.so.6:__sigpause libc.so.6:__sigsetjmp libc.so.6:__sigsuspend libc.so.6:__spin_lock libc.so.6:__spin_lock_init libc.so.6:__spin_lock_solid libc.so.6:__spin_try_lock libc.so.6:__spin_unlock libc.so.6:__statfs libc.so.6:__stpcpy libc.so.6:__stpcpy_g libc.so.6:__stpcpy_small libc.so.6:__stpncpy libc.so.6:__strcasecmp libc.so.6:__strcasecmp_l libc.so.6:__strcasestr libc.so.6:__strcat_c libc.so.6:__strcat_g libc.so.6:__strchr_c libc.so.6:__strchr_g libc.so.6:__strchrnul_c libc.so.6:__strchrnul_g libc.so.6:__strcmp_gg libc.so.6:__strcoll_l libc.so.6:__strcpy_g libc.so.6:__strcpy_small libc.so.6:__strcspn_c1 libc.so.6:__strcspn_c2 libc.so.6:__strcspn_c3 libc.so.6:__strcspn_cg libc.so.6:__strcspn_g libc.so.6:__strdup libc.so.6:__strerror_r libc.so.6:__strfmon_l libc.so.6:__strlen_g libc.so.6:__strncasecmp_l libc.so.6:__strncat_g libc.so.6:__strncmp_g libc.so.6:__strncpy_by2 libc.so.6:__strncpy_by4 libc.so.6:__strncpy_byn libc.so.6:__strncpy_gg libc.so.6:__strndup libc.so.6:__strpbrk_c2 libc.so.6:__strpbrk_c3 libc.so.6:__strpbrk_cg libc.so.6:__strpbrk_g libc.so.6:__strrchr_c libc.so.6:__strrchr_g libc.so.6:__strsep_1c libc.so.6:__strsep_2c libc.so.6:__strsep_3c libc.so.6:__strsep_g libc.so.6:__strspn_c1 libc.so.6:__strspn_c2 libc.so.6:__strspn_c3 libc.so.6:__strspn_cg libc.so.6:__strspn_g libc.so.6:__strstr_cg libc.so.6:__strstr_g libc.so.6:__strtod_internal libc.so.6:__strtod_l libc.so.6:__strtof_internal libc.so.6:__strtof_l libc.so.6:__strtok_r libc.so.6:__strtok_r_1c libc.so.6:__strtol_internal libc.so.6:__strtol_l libc.so.6:__strtold_internal libc.so.6:__strtold_l libc.so.6:__strtoll_internal libc.so.6:__strtoll_l libc.so.6:__strtoq_internal libc.so.6:__strtoul_internal libc.so.6:__strtoul_l libc.so.6:__strtoull_internal libc.so.6:__strtoull_l libc.so.6:__strtouq_internal libc.so.6:__strverscmp libc.so.6:__strxfrm_l libc.so.6:__sysconf libc.so.6:__sysctl libc.so.6:__sysv_signal libc.so.6:__timezone libc.so.6:__toascii_l libc.so.6:__tolower_l libc.so.6:__toupper_l libc.so.6:__towctrans libc.so.6:__towctrans_l libc.so.6:__towlower_l libc.so.6:__towupper_l libc.so.6:__tzname libc.so.6:__udivdi3 libc.so.6:__uflow libc.so.6:__umoddi3 libc.so.6:__underflow libc.so.6:__vfork libc.so.6:__vfscanf libc.so.6:__vm_allocate libc.so.6:__vm_page_size libc.so.6:__vsnprintf libc.so.6:__vsscanf libc.so.6:__wait libc.so.6:__waitpid libc.so.6:__wcscasecmp_l libc.so.6:__wcscoll_l libc.so.6:__wcsncasecmp_l libc.so.6:__wcstod_internal libc.so.6:__wcstod_l libc.so.6:__wcstof_internal libc.so.6:__wcstof_l libc.so.6:__wcstol_internal libc.so.6:__wcstol_l libc.so.6:__wcstold_internal libc.so.6:__wcstold_l libc.so.6:__wcstoll_internal libc.so.6:__wcstoll_l libc.so.6:__wcstoul_internal libc.so.6:__wcstoul_l libc.so.6:__wcstoull_internal libc.so.6:__wcstoull_l libc.so.6:__wcsxfrm_l libc.so.6:__wctrans_l libc.so.6:__wctype_l libc.so.6:__woverflow libc.so.6:__write libc.so.6:__wuflow libc.so.6:__wunderflow libc.so.6:__xmknod libc.so.6:__xpg_basename libc.so.6:__xpg_sigpause libc.so.6:__xstat libc.so.6:__xstat64 libc.so.6:_argp_unlock_xxx libc.so.6:_authenticate libc.so.6:_bus_base libc.so.6:_bus_base_sparse libc.so.6:_cthread_exit_routine libc.so.6:_cthread_init_routine libc.so.6:_dl_mcount_wrapper libc.so.6:_dl_mcount_wrapper_check libc.so.6:_end libc.so.6:_environ libc.so.6:_errno libc.so.6:_exit libc.so.6:_flush_cache libc.so.6:_flushlbf libc.so.6:_fp_hw libc.so.6:_h_errno libc.so.6:_hae_shift libc.so.6:_hurd_canonicalize_directory_name_internal libc.so.6:_hurd_critical_section_lock libc.so.6:_hurd_critical_section_unlock libc.so.6:_hurd_device_master libc.so.6:_hurd_dtable libc.so.6:_hurd_dtable_lock libc.so.6:_hurd_dtablesize libc.so.6:_hurd_exception2signal libc.so.6:_hurd_exec libc.so.6:_hurd_fd_get libc.so.6:_hurd_host_priv libc.so.6:_hurd_init libc.so.6:_hurd_intern_fd libc.so.6:_hurd_intr_rpc_mach_msg libc.so.6:_hurd_msgport libc.so.6:_hurd_port_cleanup libc.so.6:_hurd_port_free libc.so.6:_hurd_port_get libc.so.6:_hurd_port_init libc.so.6:_hurd_port_locked_get libc.so.6:_hurd_port_set libc.so.6:_hurd_ports libc.so.6:_hurd_ports_use libc.so.6:_hurd_proc_init libc.so.6:_hurd_raise_signal libc.so.6:_hurd_self_sigstate libc.so.6:_hurd_thread_sigstate libc.so.6:_hurd_umask libc.so.6:_hurdsig_fault_catch_exception_raise libc.so.6:_hurdsig_fault_catch_exception_raise_state libc.so.6:_hurdsig_fault_catch_exception_raise_state_identity libc.so.6:_hurdsig_fault_env libc.so.6:_hurdsig_fault_preemptor libc.so.6:_hurdsig_interrupt_timeout libc.so.6:_inb libc.so.6:_inl libc.so.6:_inw libc.so.6:_libc_intl_domainname libc.so.6:_longjmp libc.so.6:_mcleanup libc.so.6:_mcount libc.so.6:_nl_default_dirname libc.so.6:_nl_domain_bindings libc.so.6:_nl_msg_cat_cntr libc.so.6:_null_auth libc.so.6:_obstack libc.so.6:_obstack_allocated_p libc.so.6:_obstack_begin libc.so.6:_obstack_begin_1 libc.so.6:_obstack_free libc.so.6:_obstack_memory_used libc.so.6:_obstack_newchunk libc.so.6:_outb libc.so.6:_outl libc.so.6:_outw libc.so.6:_q_add libc.so.6:_q_cmp libc.so.6:_q_cmpe libc.so.6:_q_div libc.so.6:_q_dtoq libc.so.6:_q_feq libc.so.6:_q_fge libc.so.6:_q_fgt libc.so.6:_q_fle libc.so.6:_q_flt libc.so.6:_q_fne libc.so.6:_q_itoq libc.so.6:_q_lltoq libc.so.6:_q_mul libc.so.6:_q_neg libc.so.6:_q_qtod libc.so.6:_q_qtoi libc.so.6:_q_qtoll libc.so.6:_q_qtos libc.so.6:_q_qtou libc.so.6:_q_qtoull libc.so.6:_q_sqrt libc.so.6:_q_stoq libc.so.6:_q_sub libc.so.6:_q_ulltoq libc.so.6:_q_utoq libc.so.6:_res libc.so.6:_res_hconf libc.so.6:_rpc_dtablesize libc.so.6:_seterr_reply libc.so.6:_setjmp libc.so.6:_sys_errlist libc.so.6:_sys_nerr libc.so.6:_sys_siglist libc.so.6:_test_and_set libc.so.6:_tolower libc.so.6:_toupper libm.so.6:_LIB_VERSION libm.so.6:__atan2 libm.so.6:__clog10 libm.so.6:__clog10f libm.so.6:__clog10l libm.so.6:__expl libm.so.6:__expm1l libm.so.6:__fe_dfl_env libm.so.6:__fe_enabled_env libm.so.6:__fe_nomask_env libm.so.6:__fe_nonieee_env libm.so.6:__finite libm.so.6:__finitef libm.so.6:__finitel libm.so.6:__fpclassify libm.so.6:__fpclassifyf libm.so.6:__fpclassifyl libm.so.6:__signbit libm.so.6:__signbitf libm.so.6:__signbitl libm.so.6:_restgpr0_13 libm.so.6:_restgpr0_14 libm.so.6:_restgpr0_15 libm.so.6:_restgpr0_16 libm.so.6:_restgpr0_17 libm.so.6:_restgpr0_18 libm.so.6:_restgpr0_19 libm.so.6:_restgpr0_20 libm.so.6:_restgpr0_21 libm.so.6:_restgpr0_22 libm.so.6:_restgpr0_23 libm.so.6:_restgpr0_24 libm.so.6:_restgpr0_25 libm.so.6:_restgpr0_26 libm.so.6:_restgpr0_27 libm.so.6:_restgpr0_28 libm.so.6:_restgpr0_29 libm.so.6:_restgpr0_30 libm.so.6:_restgpr0_31 libm.so.6:_restgpr1_13 libm.so.6:_restgpr1_14 libm.so.6:_restgpr1_15 libm.so.6:_restgpr1_16 libm.so.6:_restgpr1_17 libm.so.6:_restgpr1_18 libm.so.6:_restgpr1_19 libm.so.6:_restgpr1_20 libm.so.6:_restgpr1_21 libm.so.6:_restgpr1_22 libm.so.6:_restgpr1_23 libm.so.6:_restgpr1_24 libm.so.6:_restgpr1_25 libm.so.6:_restgpr1_26 libm.so.6:_restgpr1_27 libm.so.6:_restgpr1_28 libm.so.6:_restgpr1_29 libm.so.6:_restgpr1_30 libm.so.6:_restgpr1_31 libm.so.6:_savegpr0_13 libm.so.6:_savegpr0_14 libm.so.6:_savegpr0_15 libm.so.6:_savegpr0_16 libm.so.6:_savegpr0_17 libm.so.6:_savegpr0_18 libm.so.6:_savegpr0_19 libm.so.6:_savegpr0_20 libm.so.6:_savegpr0_21 libm.so.6:_savegpr0_22 libm.so.6:_savegpr0_23 libm.so.6:_savegpr0_24 libm.so.6:_savegpr0_25 libm.so.6:_savegpr0_26 libm.so.6:_savegpr0_27 libm.so.6:_savegpr0_28 libm.so.6:_savegpr0_29 libm.so.6:_savegpr0_30 libm.so.6:_savegpr0_31 libm.so.6:_savegpr1_13 libm.so.6:_savegpr1_14 libm.so.6:_savegpr1_15 libm.so.6:_savegpr1_16 libm.so.6:_savegpr1_17 libm.so.6:_savegpr1_18 libm.so.6:_savegpr1_19 libm.so.6:_savegpr1_20 libm.so.6:_savegpr1_21 libm.so.6:_savegpr1_22 libm.so.6:_savegpr1_23 libm.so.6:_savegpr1_24 libm.so.6:_savegpr1_25 libm.so.6:_savegpr1_26 libm.so.6:_savegpr1_27 libm.so.6:_savegpr1_28 libm.so.6:_savegpr1_29 libm.so.6:_savegpr1_30 libm.so.6:_savegpr1_31 libnsl.so.1:__free_fdresult libnsl.so.1:__nis_default_access libnsl.so.1:__nis_default_group libnsl.so.1:__nis_default_owner libnsl.so.1:__nis_default_ttl libnsl.so.1:__nis_finddirectory libnsl.so.1:__nis_hash libnsl.so.1:__nisbind_connect libnsl.so.1:__nisbind_create libnsl.so.1:__nisbind_destroy libnsl.so.1:__nisbind_next libnsl.so.1:__yp_check libpthread.so.0:_IO_flockfile libpthread.so.0:_IO_ftrylockfile libpthread.so.0:_IO_funlockfile libpthread.so.0:__close libpthread.so.0:__connect libpthread.so.0:__errno_location libpthread.so.0:__fcntl libpthread.so.0:__fork libpthread.so.0:__h_errno_location libpthread.so.0:__libc_allocate_rtsig libpthread.so.0:__libc_current_sigrtmax libpthread.so.0:__libc_current_sigrtmin libpthread.so.0:__lseek libpthread.so.0:__open libpthread.so.0:__open64 libpthread.so.0:__pread64 libpthread.so.0:__pthread_atfork libpthread.so.0:__pthread_getspecific libpthread.so.0:__pthread_initialize libpthread.so.0:__pthread_key_create libpthread.so.0:__pthread_mutex_destroy libpthread.so.0:__pthread_mutex_init libpthread.so.0:__pthread_mutex_lock libpthread.so.0:__pthread_mutex_trylock libpthread.so.0:__pthread_mutex_unlock libpthread.so.0:__pthread_mutexattr_destroy libpthread.so.0:__pthread_mutexattr_init libpthread.so.0:__pthread_mutexattr_settype libpthread.so.0:__pthread_once libpthread.so.0:__pthread_rwlock_destroy libpthread.so.0:__pthread_rwlock_init libpthread.so.0:__pthread_rwlock_rdlock libpthread.so.0:__pthread_rwlock_tryrdlock libpthread.so.0:__pthread_rwlock_trywrlock libpthread.so.0:__pthread_rwlock_unlock libpthread.so.0:__pthread_rwlock_wrlock libpthread.so.0:__pthread_setspecific libpthread.so.0:__pwrite64 libpthread.so.0:__read libpthread.so.0:__res_state libpthread.so.0:__send libpthread.so.0:__sigaction libpthread.so.0:__vfork libpthread.so.0:__wait libpthread.so.0:__write libpthread.so.0:_pthread_cleanup_pop libpthread.so.0:_pthread_cleanup_pop_restore libpthread.so.0:_pthread_cleanup_push libpthread.so.0:_pthread_cleanup_push_defer libresolv.so.2:__b64_ntop libresolv.so.2:__b64_pton libresolv.so.2:__dn_comp libresolv.so.2:__dn_count_labels libresolv.so.2:__dn_expand libresolv.so.2:__dn_skipname libresolv.so.2:__fp_nquery libresolv.so.2:__fp_query libresolv.so.2:__fp_resstat libresolv.so.2:__hostalias libresolv.so.2:__loc_aton libresolv.so.2:__loc_ntoa libresolv.so.2:__p_cdname libresolv.so.2:__p_cdnname libresolv.so.2:__p_class libresolv.so.2:__p_class_syms libresolv.so.2:__p_fqname libresolv.so.2:__p_fqnname libresolv.so.2:__p_option libresolv.so.2:__p_query libresolv.so.2:__p_rr libresolv.so.2:__p_secstodate libresolv.so.2:__p_time libresolv.so.2:__p_type libresolv.so.2:__p_type_syms libresolv.so.2:__putlong libresolv.so.2:__putshort libresolv.so.2:__res_close libresolv.so.2:__res_dnok libresolv.so.2:__res_hnok libresolv.so.2:__res_hostalias libresolv.so.2:__res_isourserver libresolv.so.2:__res_mailok libresolv.so.2:__res_mkquery libresolv.so.2:__res_nameinquery libresolv.so.2:__res_nmkquery libresolv.so.2:__res_nquery libresolv.so.2:__res_nquerydomain libresolv.so.2:__res_nsearch libresolv.so.2:__res_nsend libresolv.so.2:__res_ownok libresolv.so.2:__res_queriesmatch libresolv.so.2:__res_query libresolv.so.2:__res_querydomain libresolv.so.2:__res_search libresolv.so.2:__res_send libresolv.so.2:__sym_ntop libresolv.so.2:__sym_ntos libresolv.so.2:__sym_ston libresolv.so.2:_gethtbyaddr libresolv.so.2:_gethtbyname libresolv.so.2:_gethtbyname2 libresolv.so.2:_gethtent libresolv.so.2:_getlong libresolv.so.2:_getshort libresolv.so.2:_res_opcodes libresolv.so.2:_res_resultcodes libresolv.so.2:_sethtent libc.so.6:__ctype_b_loc libc.so.6:__ctype_tolower_loc libc.so.6:__ctype_toupper_loc ); @{$private} = qw( ); # # These are are from older libraries. # push(@{$public}, qw( libdb.so.3:__bam_init_print libdb.so.3:__bam_pgin libdb.so.3:__bam_pgout libdb.so.3:__db_dispatch libdb.so.3:__db_dump libdb.so.3:__db_err libdb.so.3:__db_init_print libdb.so.3:__db_jump libdb.so.3:__db_omode libdb.so.3:__db_prdbt libdb.so.3:__ham_init_print libdb.so.3:__ham_pgin libdb.so.3:__ham_pgout libdb.so.3:__lock_dump_region libdb.so.3:__log_init_print libdb.so.3:__memp_dump_region libdb.so.3:__txn_init_print libresolv.so.2:__dn_skipname ) ); # # These are non-leading "_" that are actually private: # If one reads the comment in nis/Versions it strongly # suggests they are private, even though they are in # GLIBC_2.1 set. We keep them as tweaks for now. # @{$private} = qw( libnsl.so.1:readColdStartFile libnsl.so.1:writeColdStartFile ); } }