diff options
196 files changed, 19692 insertions, 0 deletions
@@ -0,0 +1,6 @@ +* Manual page is not accurate (config files...) +* Doesn't do inter-library calls (BP is in the executable's PLT) +* It lacks support for several Linux archs, and many operating systems +* 2008-12-29: this line in config file does not work (2nd argument not used): + string setlocale(enum(LC_ALL=6), string); +* 2009-04-07 doesn't work with threads (processes sharing memory) @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + <signature of Ty Coon>, 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 0000000..49b785f --- /dev/null +++ b/ChangeLog @@ -0,0 +1,539 @@ +2009-07-25 Juan Cespedes <cespedes@debian.org> + + * New release 0.5.3 + * Created "libltrace.a" and a simple main program that calls it + * Added support for callbacks to libltrace + * Got rid of GNU's Autoconf stuff + * Make it work again in sparc with new kernel headers + +2009-05-21 Juan Cespedes <cespedes@debian.org> + + * Release version 0.5.2 + * new fork() and clone() approach: + + used PTRACE_O_TRACE{FORK,VFORK,CLONE} to trace newly created + processes instead of figuring it out whether a given syscall + would create another process or not + That way, new processes are always traced from the very beginning + * Use PTRACE_O_TRACEEXEC to check if a process has called exec() + +2009-05-07 Juan Cespedes <cespedes@debian.org> + + * clean-up of structs Process, Breakpoint, Function and Event + +2009-04-07 Juan Cespedes <cespedes@debian.org> + + * Improved documentation + * Cleaning up of "struct options" + * wait_for_something -> sysdeps/linux/events.c:next_event() + * wait for new children to stop instead of assuming they have stopped + +2009-02-11 Juan Cespedes <cespedes@debian.org> + + * Fixed bug present since the first version (!) of ltrace, + which caused many programs to segfault when a signal is received: + + When a breakpoint is hit, and we need to continue with it, we: + 1) remove the breakpoint + 2) order a SINGLESTEP + 3) when control comes back, set the breakpoint again + 4) let the process continue + + The problem came when a signal is received in the middle + of all this (specifically, between 2) and 3)). + If this is so, we treat the signal "in the usual way", + it is, at the end we issue a "CONTINUE" instead of the + needed SINGLESTEP. + +2008-12-10 Juan Cespedes <cespedes@debian.org> + + * summary.c: Fix "ltrace -o -c" + * mkdist: rm -rf autom4te.cache + * debian/control.ini: re-added armel and armeb + +2008-12-10 Juan Cespedes <cespedes@debian.org> + + * Release version 0.5.1 + +2008-12-10 Juan Cespedes <cespedes@debian.org> + + * Patches from Anderson Lizardo and Riku Voipio: + + Add generic support for arm targets + + Save funtion arguments on arm + + Add thumb instruction support + + Add basic arm/eabi support + + fix exec() testcase cleanup + + fix memory corruption in clone() test + + fix tracing child with "-p" option + +2008-02-27 Luis Machado <luisgpm@br.ibm.com> + + * sysdeps/linux-gnu/ppc/trace.c (arch_umovelong): New function. + * sysdeps/linux-gnu/ppc/regs.c (get_instruction): New function. + (get_count_register): New function. + * sysdeps/linux-gnu/ppc/arch.h (ARCH_HAVE_UMOVELONG): New define. + * sysdeps/linux-gnu/trace.c (umovelong): Create arch-specific + variant. + * ltrace.h (umovelong): Change prototype. + * process_event.c (process_breakpoint): Handle specifics of ppc32 PLT. + * display_args.c: Call umovelong with info parameter. + +2007-09-04 Juan Cespedes <cespedes@debian.org> + + * ltrace.h: Take arg_num out of arg_type_info + * linux-gnu/*/trace.c: gimme_arg(): Add arg_num as argument + * ltrace.c: check for existence of $HOME before using it + * General: Small fixes (indentation) + +2007-08-31 Juan Cespedes <cespedes@debian.org> + + * General: Small fixes (indentation, typos, clean-up of code) + * ltrace.c: Close output file on exit + * ltrace.c: use getenv("HOME") instead of getpwuid(geteuid())->pw_dir + * read_config_file.c, display_args.c: remove "ignore" argtype; + that's what "void" is for + * packaging/debian/: misc fixes, sync with version 0.5-2 + * etc/ltrace.conf: added more system calls + * testsuite/ltrace.minor/trace-clone.c: sleep(1) to avoid earlier + termination of process + * sysdeps/linux-gnu/trace.c: trace_pid(): reverted Petr's patch + to wait for child to stop, as it stopped following clone() + * process_event.c: Disable breakpoints before doing fork() (again!), + to make children work as expected + +2007-05-10 Petr Machata <pmachata@redhat.com> + + * Based on work of Supriya Kannery <supriyak@in.ibm.com> + * wait_for_something.c, process_event.c: Tracing across exec. + * sysdeps/linux-gnu/trace.c, ltrace.h: New interface was_exec. + * testsuite/ltrace.minor/trace-exec.c, + testsuite/ltrace.minor/trace-exec.exp, + testsuite/ltrace.minor/trace-exec1.c: Testcase for same. + +2007-05-09 Petr Machata <pmachata@redhat.com> + + * wait_for_something.c (wait_for_something): Interpret SIGILL, + SIGEMT and SIGSEGV as valid breakpoint signals, if instruction + pointer referes to breakpoint. + * testsuite/ltrace.torture/ia64-sigill.s, + * testsuite/ltrace.torture/ia64-sigill.exp: Testcase for same. + IA64-centric, because the only reproducer is there. + +2007-01-19 Petr Machata <pmachata@redhat.com> + + * sysdeps/linux-gnu/trace.c (trace_pid): wait for child to stop, + as indicated by ptrace documentation. + * proc.c (open_pid): start the traced child again, it will have + been stopped after trace_pid. Fixes tracing with -p. + * breakpoints.c: initialize proc->breakpoints always, don't wait + untill it might be needed. This renders a check in insert_breakpoint + superfluous. Fixes a sigsegvs experienced with -L. + +2006-12-28 Eric Vaitl <evaitl@cisco.com> + + * sysdeps/linux-gnu/mipsel/* Added mipsel support + * debug.h Added printf format attribute to debug_ + * elf.h Added mips relocation data to struct ltelf + * elf.c (do_init_elf) Read mips relocation data + * elf.c (read_elf) On the mips loop through mips_gotsym + instead of relplt_count. + * process_event.c (process_breakpoint) For the mips, + conditionally add a new breakpoint if the address of the + function changes because of lazy relocation. + * breakpoints.c (enable_all_breakpoints) For the mips, + reinsert breakpoints after the child has been started. + + +2006-11-30 Petr Machata <pmachata@redhat.com> + + * elf.c (elf_gnu_hash): renamed to private_elf_gnu_hash to avoid + conflicts with non-static version from libelf. + +2006-11-30 Petr Machata <pmachata@redhat.com> + + * elf.c (in_load_libraries): removed unused variables + bitmask_idxbits and shift. + * elf.c (do_init_elf, opd2addr): use ARCH_SUPPORTS_OPD to + determine whether to load/use .opd section + * sysdeps/linux-gnu/*/arch.h: define ARCH_SUPPORTS_OPD accordingly + * breakpoints.c (insert_breakpoint): rewrite loop to canonical for + +2006-10-13 Olaf Hering <olh@suse.de> + + * options.c: fix up typo for config file + +2006-09-25 Olaf Hering <olh@suse.de> + + * elf.c, elf.h : remove confilict with glibc SHT_GNU_HASH, include + elf_gnu_hash() directly, remove special casing and fix up output + specifier. + +2006-09-18 Steve Fink <sphink@gmail.com> + + * display_args.c: store arg_num in arg_type_info + * display_args.c: support 'double' parameters + * display_args.c: fix implementation of float,double params for ia64 + * output.c, process_event.c: store arg_num in arg_type_info + * read_config_file.c: support 'double' parameters + * read_config_file.c: store arg_num in arg_type_info, and as a result, + stop using singleton objects for any of the arg_type_info's. + * read_config_file.c: improve support for struct field alignments + * read_config_file.c: count floating-point parameters to support ia64 + float parameter passing + * sysdeps/README, sysdeps/linux-gnu/*/trace.c: pass in the full + arg_type_info to gimme_arg rather than just the arg_num (necessary + for float params on some architectures) + * sysdeps/linux-gnu/ia64/trace.c: accommodate register renaming when + fetching the parameters of a function after it has returned + * sysdeps/linux-gnu/ia64/trace.c: support floating point parameters + +2006-09-15 Olaf Hering <olh@suse.de> + + * Makefile.in : allow installation as non-root user, print out + some debugging information before running test suite. + * summary.c : allow compilation without USE_DEMANGLE + * sysdeps/linux-gnu/ppc/plt.c : fix warning in sym2addr + * sysdeps/linux-gnu/ia64/regs.c : fix warning when finding + instruction slot + * elf.c : fix up error created in 2006-07-26 refactor + +2006-08-14 Steve Fink <sphink@gmail.com> + + * demangle.c: remove my_demagle_dict_clear(), remove atexit() call + for same. Avoid potential segfault as demangling uses the + dictionary. + +2006-08-07 Steve Fink <sphink@gmail.com> + + * display_args.c, etc/ltrace.conf, ltrace.h, read_config_file.c, + testsuite/ltrace.main/parameters-lib.c, + testsuite/ltrace.main/parameters.c, + testsuite/ltrace.main/parameters.conf, + testsuite/ltrace.main/parameters.exp: Allow parameters to be + pointers to structs, which themselves can contain + (nearly) any other type, including other structs or pointers to + structs. + + +2006-08-07 Steve Fink <sphink@gmail.com> + + * defs.h, display_args.c, etc/ltrace.conf, ltrace.1, ltrace.h, + options.c, options.h, read_config_file.c, + testsuite/ltrace.main/parameters-lib.c, + testsuite/ltrace.main/parameters.c, + testsuite/ltrace.main/parameters.conf, + testsuite/ltrace.main/parameters.exp: array arguments + +2006-08-07 Steve Fink <sphink@gmail.com> + + * etc/ltrace.conf, read_config_file.c, + testsuite/ltrace.main/parameters-lib.c, + testsuite/ltrace.main/parameters.c, + testsuite/ltrace.main/parameters.conf, + testsuite/ltrace.main/parameters.exp: add ability to typedef + +2006-08-07 Steve Fink <sphink@gmail.com> + + * display_args.c, etc/ltrace.conf, ltrace.h, read_config_file.c, + testsuite/ltrace.main/parameters-lib.c, + testsuite/ltrace.main/parameters.c, + testsuite/ltrace.main/parameters.conf, + testsuite/ltrace.main/parameters.exp: short, ushort and float types + +2006-08-07 Steve Fink <sphink@gmail.com> + + * display_args.c, etc/ltrace.conf, ltrace.h, read_config_file.c, + testsuite/ltrace.main/parameters-lib.c, + testsuite/ltrace.main/parameters.c, + testsuite/ltrace.main/parameters.conf, + testsuite/ltrace.main/parameters.exp: implement enumerated parameters + +2006-08-07 Steve Fink <sphink@gmail.com> + + * testsuite/ltrace.main/Makefile.in : update testsuite for + new parameters + * testsuite/ltrace.main/parameters-lib.c : added + * testsuite/ltrace.main/parameters.c : added + * testsuite/ltrace.main/parameters.conf : added + * testsuite/ltrace.main/parameters.exp : added + +2006-08-07 Steve Fink <sphink@gmail.com> + + * display_args.c, etc/ltrace.conf, ltrace.h, read_config_file.c, + sysdeps/README, sysdeps/linux-gnu-trace.c : switch to passing + around values rather than argument numbers that need to be fetched + (needed for pointer params) + +2006-08-07 Steve Fink <sphink@gmail.com> + + * display_args.c, etc/ltrace.conf, ltrace.h, read_config_file.c: + implement ignored arguments + +2006-08-07 Steve Fink <sphink@gmail.com> + + * display_args.c, etc/ltrace.conf, ltrace.h, read_config_file.c: + implement string[argN] and string[N] parameter descriptors + +2006-08-07 Steve Fink <sphink@gmail.com> + + * ltrace.h, output.c, read_config_file.c : use arg_type_info in + place of arg_type in order to eventually be able to record + properties along with types. + +2006-07-20 Steve Fink <sphink@gmail.com> + + * testsuite/lib/ltrace.exp: better quoting and error detection for + ltrace_verify_output's call to grep + +2006-08-07 Steve Fink <sphink@gmail.com> + + * ltrace.1: update bug email addr + +2006-07-26 Ian Wienand <ianw@debian.org> + + * elf.c: refactor opd2addr to not pass void* (fix warnings) + +2006-07-18 Petr Machata <pmachata@redhat.com> + + * elf.c: replace nonexistant elf_plt2addr with opd2addr, fix + typo, and fix WEAK symbol detection + * sysdeps/linux-gnu/ppc/arch.h: define breakpoint instruction, + its length, etc., also on ppc32 + +2006-07-18 Petr Machata <pmachata@redhat.com> + + * elf.c: support .gnu.hash ELF entry + * elf.h: likewise + +2006-07-18 Petr Machata <pmachata@redhat.com> + + * options.c: don't hardcode version number + +2006-07-18 Justin Pryzby <justinpryzby@users.sourceforge.net> + + * ltrace.1: make demagle clearer + +2006-07-16 Steve Fink <sphink@gmail.com> + + * options.c: implement -F flag for alternate config file(s) + * ltrace.c: load SYSCONFDIR/ltrace.conf and ~/.ltrace.conf by default + +2006-06-19 Ian Wienand <ianw@ieee.org> + + * sysdeps/linux-gnu/mksyscallent: update, fix for ia64 + * sysdeps/linux-gnu/i386/syscallent.h: regenerate to 2.6.17 + * sysdeps/linux-gnu/i386/signalent.h: likewise + * sysdeps/linux-gnu/arm/syscallent.h: likewise + * sysdeps/linux-gnu/arm/signalent.h: likewise + * sysdeps/linux-gnu/m68k/syscallent.h: likewise + * sysdeps/linux-gnu/m68k/signalent.h: likewise + * sysdeps/linux-gnu/ia64/syscallent.h: likewise + * sysdeps/linux-gnu/ia64/signalent.h: likewise + +2006-06-19 Heiko Carstens <heiko.carstens@de.ibm.com> + + * sysdeps/linux-gnu/s390/syscalls31.h: update to 2.6.17 + * sysdeps/linux-gnu/s390/syscalls64.h: ditto + +2006-06-16 Justin Pryzby <justinpryzby@users.sourceforge.net> + + * ltrace.1: spelling fix + * TODO: spelling fix + +2006-06-14 Ian Wienand <ianw@gelato.unsw.edu.au> + + * configure.ac: Bump version to 0.5 for Paull Gillam's PPC64 + non-exec PLT patch (as merged below). + * breakpoints.c: merge + * elf.c: merge + * elf.h: merge + * ltrace.h: merge + * output.c: merge + * process_event.c: merge + * sysdeps/linux-gnu/alpha/plt.c: merge + * sysdeps/linux-gnu/arm/plt.c: merge + * sysdeps/linux-gnu/breakpoint.c: merge + * sysdeps/linux-gnu/i386/plt.c: merge + * sysdeps/linux-gnu/ia64/plt.c: merge + * sysdeps/linux-gnu/m68k/plt.c: merge + * sysdeps/linux-gnu/ppc/arch.h: merge + * sysdeps/linux-gnu/ppc/arch.h.rej: merge + * sysdeps/linux-gnu/ppc/plt.c: merge + * sysdeps/linux-gnu/s390/plt.c: merge + * sysdeps/linux-gnu/sparc/plt.c: merge + * sysdeps/linux-gnu/x86_64/plt.c: merge + + +2006-05-11 Heiko Carstens <heiko.carstens@de.ibm.com> + + * sysdeps/linux-gnu/mksyscallent_s390: add + * sysdeps/linux-gnu/s390/syscalls31.h: update to 2.6.16 + * sysdeps/linux-gnu/s390/syscalls64.h: ditto + +2006-04-24 Paul Gilliam <pgilliam@us.ibm.com> + + * elf.c: Use PLT_REINITALISATION_BP for those architectures that need + to re-initialize breakpoints after the dynamic linker has run. Also, + use value of "e_entry" for address of PLT_REINITALISATION_BP if the + target program has been stripped. + * ltrace.1: Note that fact that "-X" is only available on architectures + that need it. + * options.c: Use PLT_REINITALISATION_BP for those architectures that + need to re-initialize breakpoints after the dynamic linker has run. + * process_event.c: ditto. + * sysdeps/linux-gnu/ppc/arch.h: This is the only such architecture. + * sysdeps/linux-gnu/arm/arch.h: Delete use of PLT_REINITALISATION_BP. + * sysdeps/linux-gnu/m68k/arch.h: ditto. + * sysdeps/linux-gnu/alpha/arch.h: ditto. + * sysdeps/linux-gnu/i386/arch.h: ditto. + * sysdeps/linux-gnu/x86_64/arch.h: ditto. + * sysdeps/linux-gnu/s390/arch.h: ditto. + * sysdeps/linux-gnu/ia64/arch.h: ditto. + * sysdeps/linux-gnu/sparc/arch.h: ditto. + +2006-04-24 Paul Gilliam <pgilliam@us.ibm.com> + + * elf.c: Adds some casts to keep a more picky version of GCC happy. + * sysdeps/linux-gnu/trace.c: ditto. + * sysdeps/linux-gnu/breakpoint.c: ditto. + * ltrace.h: ditto. + +2006-04-24 Paul Gilliam <pgilliam@us.ibm.com> + + * summery.c: Correct a typo prevented the inclusion of "demangle.h". + +2006-03-16 Ian Wienand <ianw@gelato.unsw.edu.au> + + * testsuite/ltrace.minor/trace-clone.c: use __clone2() for IA64 + clone test + +2006-03=13 Paul Gilliam <pgilliam@us.ibm.com> + + * Makefile.in: Add targets to support testsuite, including 'check'. + * confiure.ac: Add testsuite Makefile's to AC_OUTPUT. + * testsuite: Add dejagnu base testsuite. + * testsuite/config/: Add + * testsuite/config/unix.exp: Add + * testsuite/lib/: Add + * testsuite/lib/compiler.c: Add + * testsuite/lib/compiler.cc: Add + * testsuite/lib/ltrace.exp: Add + * testsuite/ltrace.main/: Add + * testsuite/ltrace.main/main.c: Add + * testsuite/ltrace.main/main.exp: Add + * testsuite/ltrace.main/main-internal-1.c: Add + * testsuite/ltrace.main/main-internal.c: Add + * testsuite/ltrace.main/main-internal.exp: Add + * testsuite/ltrace.main/main-lib.c: Add + * testsuite/ltrace.main/Makefile.in: Add + * testsuite/ltrace.main/signals.c: Add + * testsuite/ltrace.main/signals.exp: Add + * testsuite/ltrace.main/system_calls.c: Add + * testsuite/ltrace.main/system_calls.exp: Add + * testsuite/ltrace.minor/: Add + * testsuite/ltrace.minor/attach-process.c: Add + * testsuite/ltrace.minor/attach-process.exp: Add + * testsuite/ltrace.minor/count-record.c: Add + * testsuite/ltrace.minor/count-record.exp: Add + * testsuite/ltrace.minor/demangle.cpp: Add + * testsuite/ltrace.minor/demangle.exp: Add + * testsuite/ltrace.minor/demangle.h: Add + * testsuite/ltrace.minor/demangle-lib.cpp: Add + * testsuite/ltrace.minor/Makefile.in: Add + * testsuite/ltrace.minor/print-instruction-pointer.c: Add + * testsuite/ltrace.minor/print-instruction-pointer.exp: Add + * testsuite/ltrace.minor/time-record.c: Add + * testsuite/ltrace.minor/time-record-T.exp: Add + * testsuite/ltrace.minor/time-record-tt.exp: Add + * testsuite/ltrace.minor/time-record-ttt.exp: Add + * testsuite/ltrace.minor/trace-clone.c: Add + * testsuite/ltrace.minor/trace-clone.exp: Add + * testsuite/ltrace.minor/trace-fork.c: Add + * testsuite/ltrace.minor/trace-fork.exp: Add + * testsuite/ltrace.torture/: Add + * testsuite/ltrace.torture/Makefile.in: Add + * testsuite/ltrace.torture/signals.c: Add + * testsuite/ltrace.torture/signals.exp: Add + * testsuite/Makefile.in: Add + * testsuite/README: Add + * testsuite/run-my-tests.sh: Add + * testsuite/so_test1/: Add + * testsuite/so_test2/: Add + +2006-03-13 Paul Gilliam <pgilliam@us.ibm.com> + + * options.h: New structure for opt_x list elements, now with 'found'. + * options.c: Use new opt_x_t structure, initializing 'found' to 0. + * elf.c: Use new 'found' field for better error checking. + +2006-03-06 Ian Wienand <ianw@ieee.org> + + * Makefile.in: remove unneeded dirs from make dist; use rm + directly. + +2006-02-22 Ian Wienand <ianw@ieee.org> + + * COPYING: update from FSF to update address + * Makefile.in: check for SVN checkout with make dist. + +2006-02-21 Ian Wienand <ianw@ieee.org> + + * README: update to point to Alioth list + +2006-02-21 Ian Wienand <ianw@ieee.org> + + * lots!: Rebase from RedHat 0.3.36-4.2 package. Forward port most + of the below changes that weren't already there. Bump version to + 0.4 as there are two added architectures and internal API changes. + All changes from this point on should be reflected in this file. + +2006-02-17 Ian Wienand <ianw@ieee.org> + + * sysdeps/linux-gnu/ia64/arch.h: add ia64 support + * sysdeps/linux-gnu/ia64/breakpoint.c: add + * sysdeps/linux-gnu/ia64/Makefile: add + * sysdeps/linux-gnu/ia64/plt.c: add + * sysdeps/linux-gnu/ia64/ptrace.h: add + * sysdeps/linux-gnu/ia64/regs.c: add + * sysdeps/linux-gnu/ia64/signalent.h: add + * sysdeps/linux-gnu/ia64/syscallent.h: add + * sysdeps/linux-gnu/ia64/trace.c: add + * elf.h: add extra field for PLT size + * elf.c: put in PLT size + * sysdeps/linux-gnu/breakpoint.c: add arch breakpoint override + * sysdeps/linux-gnu/trace.c: don't single step after breakpoint for + ia64 + + * configure.ac: add version to AC_INIT, bump version to 0.3.38 + * options.c: use PACKAGE_VERSION + +2006-02-16 Ian Wienand <ianw@ieee.org> + + * Makefile.in: install documentation into share/doc, make dist + target from SVN export. + +2006-02-16 Rajeev V. Pillai <rajeevvp@yahoo.com> + + * Makefile.in: pass through CPP and LD FLAGS + +2006-02-16 Ian Wienand <ianw@ieee.org> + + * read_config_file.c: initialise pt stack argument to stop warning + * summary.c: make show_summary() obey -C for demangaling function names + +2006-02-16 Bernd Zeimetz <bernd@zeimetz.de> + + * ltrace.1: reference reportbug + +2006-02-16 Colin S. Miller <csmiller@iname.com> + + * ltrace.1: fix debug typo + +2006-02-16 Andrew Stribblehill <ads@debian.org> + + * etc/ltrace.conf: fix putenv typo + +2006-02-16 Ian Wienand <ianw@ieee.org> + + * README: update + * Makefile.in: remove obsolete -I- for -iquote, add TAGS target + * debug.c, debug.h: __PRETTY_FUNCTION__ is const; change specifier + to stop warnings. + * ltrace.1: add a note about not tracing dlopen()ed libraries @@ -0,0 +1,19 @@ +How to build ltrace from source +------------------------------- + +To get the latest version from GIT: + + git clone git://git.debian.org/git/collab-maint/ltrace.git + +To create a distribution (ltrace-<version>.tar.gz): + + ./mkdist + +To compile: + + ./configure + make + +To build debian/control (to create Debian package): + + debian/rules debian/control DEB_AUTO_UPDATE_DEBIAN_CONTROL:=yes diff --git a/Makefile.in b/Makefile.in new file mode 100644 index 0000000..1e8adcd --- /dev/null +++ b/Makefile.in @@ -0,0 +1,74 @@ +# +# ltrace's Makefile.in +# + +#OS := $(shell uname -s) +OS := @HOST_OS@ + +TOPDIR = $(shell pwd) + +prefix = @prefix@ +sysconfdir = @sysconfdir@ +bindir = $(prefix)/bin +mandir = @mandir@ +docdir = $(prefix)/share/doc/ltrace + +CC = @CC@ +CFLAGS = -Wall @CFLAGS@ +CPPFLAGS = -iquote $(TOPDIR) -iquote $(TOPDIR)/sysdeps/$(OS) -DSYSCONFDIR=\"$(sysconfdir)\" @CPPFLAGS@ +LDFLAGS = @LDFLAGS@ +LIBS = @LIBS@ + +INSTALL = @INSTALL@ +INSTALL_FILE = $(INSTALL) -p -m 644 +INSTALL_PROGRAM = $(INSTALL) -p -m 755 +INSTALL_SCRIPT = $(INSTALL) -p -m 755 +INSTALL_DIR = $(INSTALL) -p -d -m 755 + +OBJ = libltrace.o options.o elf.o output.o read_config_file.o \ + execute_program.o handle_event.o display_args.o \ + breakpoints.o proc.o demangle.o dict.o debug.o summary.o + +VERSION = @PACKAGE_VERSION@ + +all: ltrace + +ltrace: main.o libltrace.a + $(CC) $(LDFLAGS) $^ $(LIBS) -o $@ + +libltrace.a: sysdeps/sysdep.o $(OBJ) + $(AR) rcv $@ $^ + +sysdeps/sysdep.o: dummy + $(MAKE) -C sysdeps/$(OS) + +clean-deja: + $(RM) testrun.log testrun.sum + cd testsuite; make clean + +clean: clean-deja + $(MAKE) -C sysdeps/$(OS) clean + rm -f ltrace main.o libltrace.a $(OBJ) + rm -f *~ *.bak a.out core + +distclean: clean + rm -f config.h Makefile + +realclean: distclean + +install: ltrace + $(INSTALL_DIR) $(DESTDIR)$(bindir) $(DESTDIR)$(docdir) $(DESTDIR)$(mandir)/man1 + $(INSTALL_DIR) $(DESTDIR)$(sysconfdir) + $(INSTALL_PROGRAM) ltrace $(DESTDIR)$(bindir) + $(INSTALL_FILE) etc/ltrace.conf $(DESTDIR)$(sysconfdir) + $(INSTALL_FILE) COPYING README TODO BUGS ChangeLog $(DESTDIR)$(docdir) + $(INSTALL_FILE) ltrace.1 $(DESTDIR)$(mandir)/man1 + +check: + cd testsuite;cat /proc/version;uptime;free -m;$(MAKE) check + +dummy: + +.PHONY: all clean distclean dist install dummy + +.EXPORT_ALL_VARIABLES: @@ -0,0 +1,92 @@ + ltrace + + A Dynamic Library Tracer + + Copyright 1997-2009 Juan Cespedes <cespedes@debian.org> + + +Contents +-------- + 0. Authors + 1. Introduction + 2. Where can I find it + 3. How does it work + 4. Where does it work + 5. Bugs + 6. License + + +0. Authors +---------- + +ltrace has been developed mainly by Juan Cespedes <cespedes@debian.org>, +but he has received many contributions from other people. The following +people have contributed significantly to this project: + +* César Sánchez <cesar.sanchez@imdea.org> +* Santiago Romero <santiago.romero@imdea.org> +* Pat Beirne <pbeirne@home.com> (ARM port) +* Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de> (m68k port) +* Morten Eriksen <mortene@sim.no> (misc fixes) +* Silvio Cesare <silvio@big.net.au> (ELF hacking) +* Timothy Fesig <slate@us.ibm.com> (S390 port) +* Anton Blanchard <anton@samba.org> (Powerpc port) +* Jakub Jelinek <jakub@redhat.com> (SPARC port, support for libelf, many fixes) +* Jakub Bogusz <qboosh@pld-linux.org> (alpha port) +* SuSE (amd64 port) +* Ian Wienand <ianw@gelato.unsw.edu.au> (IA64 port) +* Eric Vaitl <evaitl@cisco.com> (mipsel port) +* Petr Machata <pmachata@redhat.com> (misc fixes) + +1. Introduction +--------------- + +ltrace is a debugging tool, similar to strace, but it traces library +calls instead of system calls. + +2. Where can I find it +---------------------- + +http://www.ltrace.org + +3. How does it work +------------------- + +Using software breakpoints, just like gdb. + +4. Where does it work +--------------------- + +It works with ELF based Linux systems running on i386, m68k, S/390, +ARM, PowerPC, PowerPC64, IA64, AMD64, SPARC and Alpha processors. + +It is part of at least Debian GNU/Linux, RedHat, SuSE, Mandrake... + +5. Bugs +------- + +Too many to list here :). If you like to submit a bug report, or a +feature request, either do that against the Debian `ltrace' package, +or mail ltrace-devel@lists.alioth.debian.org. + +This file is very incomplete and out-of-date. + +6. License +---------- + + Copyright (C) 1997-2009 Juan Cespedes <cespedes@debian.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + @@ -0,0 +1,36 @@ +* BFD: + + New executable formats + + Read list of libraries needed + + Read list of undefined symbols in executables + + Read list of exported symbols in libraries + + Read debugging info from executables/libraries +* Automatically update list of syscalls? +* Improve documentation +* Improve -e/-x options (regexp?) +* Improve -l option +* Improve C++ name demangling +* Display different argument types +* Update /etc/ltrace.conf +* More architectures, cleaner way to port +* More operating systems (solaris?) +* Option -I (inter-library calls) +* Modify ARGTYPE_STRING[0-5] types so that they don't stop displaying chars when '\0' is seen +* Get rid of EVENT_ARCH_SYSCALL and EVENT_ARCH_SYSRET +* Cleaner way to use breakpoints: + + BP is placed in the PLT + + When control hits there: + - write down return address + - change return address with another one (handled by ltrace) + - get arguments... + - change the process' PC to be in the correct place, + without removing breakpoint + + When control hits one of our return addresses: + - get return value... + - change PC to the right place +* To be able to work with processes sharing memory, we must: + + ptrace() every single thread + + place breakpoints only in places where the process control can continue + without having to remove it +* List source dependencies in Makefile +* Create different ltrace processes to trace different children +* After a clone(), syscalls may be seen as sysrets in s390 (see trace.c:syscall_p()) @@ -0,0 +1 @@ +0.5.3 diff --git a/aclocal.m4 b/aclocal.m4 new file mode 100644 index 0000000..abe07e0 --- /dev/null +++ b/aclocal.m4 @@ -0,0 +1,833 @@ +dnl aclocal.m4t generated automatically by aclocal 1.4-p6 + +dnl Copyright (C) 1994, 1995-8, 1999, 2001 Free Software Foundation, Inc. +dnl This file is free software; the Free Software Foundation +dnl gives unlimited permission to copy and/or distribute it, +dnl with or without modifications, as long as this notice is preserved. + +dnl This program is distributed in the hope that it will be useful, +dnl but WITHOUT ANY WARRANTY, to the extent permitted by law; without +dnl even the implied warranty of MERCHANTABILITY or FITNESS FOR A +dnl PARTICULAR PURPOSE. + +# lib-prefix.m4 serial 3 (gettext-0.13) +dnl Copyright (C) 2001-2003 Free Software Foundation, Inc. +dnl This file is free software, distributed under the terms of the GNU +dnl General Public License. As a special exception to the GNU General +dnl Public License, this file may be distributed as part of a program +dnl that contains a configuration script generated by Autoconf, under +dnl the same distribution terms as the rest of that program. + +dnl From Bruno Haible. + +dnl AC_LIB_ARG_WITH is synonymous to AC_ARG_WITH in autoconf-2.13, and +dnl similar to AC_ARG_WITH in autoconf 2.52...2.57 except that is doesn't +dnl require excessive bracketing. +ifdef([AC_HELP_STRING], +[AC_DEFUN([AC_LIB_ARG_WITH], [AC_ARG_WITH([$1],[[$2]],[$3],[$4])])], +[AC_DEFUN([AC_][LIB_ARG_WITH], [AC_ARG_WITH([$1],[$2],[$3],[$4])])]) + +dnl AC_LIB_PREFIX adds to the CPPFLAGS and LDFLAGS the flags that are needed +dnl to access previously installed libraries. The basic assumption is that +dnl a user will want packages to use other packages he previously installed +dnl with the same --prefix option. +dnl This macro is not needed if only AC_LIB_LINKFLAGS is used to locate +dnl libraries, but is otherwise very convenient. +AC_DEFUN([AC_LIB_PREFIX], +[ + AC_BEFORE([$0], [AC_LIB_LINKFLAGS]) + AC_REQUIRE([AC_PROG_CC]) + AC_REQUIRE([AC_CANONICAL_HOST]) + AC_REQUIRE([AC_LIB_PREPARE_PREFIX]) + dnl By default, look in $includedir and $libdir. + use_additional=yes + AC_LIB_WITH_FINAL_PREFIX([ + eval additional_includedir=\"$includedir\" + eval additional_libdir=\"$libdir\" + ]) + AC_LIB_ARG_WITH([lib-prefix], +[ --with-lib-prefix[=DIR] search for libraries in DIR/include and DIR/lib + --without-lib-prefix don't search for libraries in includedir and libdir], +[ + if test "X$withval" = "Xno"; then + use_additional=no + else + if test "X$withval" = "X"; then + AC_LIB_WITH_FINAL_PREFIX([ + eval additional_includedir=\"$includedir\" + eval additional_libdir=\"$libdir\" + ]) + else + additional_includedir="$withval/include" + additional_libdir="$withval/lib" + fi + fi +]) + if test $use_additional = yes; then + dnl Potentially add $additional_includedir to $CPPFLAGS. + dnl But don't add it + dnl 1. if it's the standard /usr/include, + dnl 2. if it's already present in $CPPFLAGS, + dnl 3. if it's /usr/local/include and we are using GCC on Linux, + dnl 4. if it doesn't exist as a directory. + if test "X$additional_includedir" != "X/usr/include"; then + haveit= + for x in $CPPFLAGS; do + AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"]) + if test "X$x" = "X-I$additional_includedir"; then + haveit=yes + break + fi + done + if test -z "$haveit"; then + if test "X$additional_includedir" = "X/usr/local/include"; then + if test -n "$GCC"; then + case $host_os in + linux*) haveit=yes;; + esac + fi + fi + if test -z "$haveit"; then + if test -d "$additional_includedir"; then + dnl Really add $additional_includedir to $CPPFLAGS. + CPPFLAGS="${CPPFLAGS}${CPPFLAGS:+ }-I$additional_includedir" + fi + fi + fi + fi + dnl Potentially add $additional_libdir to $LDFLAGS. + dnl But don't add it + dnl 1. if it's the standard /usr/lib, + dnl 2. if it's already present in $LDFLAGS, + dnl 3. if it's /usr/local/lib and we are using GCC on Linux, + dnl 4. if it doesn't exist as a directory. + if test "X$additional_libdir" != "X/usr/lib"; then + haveit= + for x in $LDFLAGS; do + AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"]) + if test "X$x" = "X-L$additional_libdir"; then + haveit=yes + break + fi + done + if test -z "$haveit"; then + if test "X$additional_libdir" = "X/usr/local/lib"; then + if test -n "$GCC"; then + case $host_os in + linux*) haveit=yes;; + esac + fi + fi + if test -z "$haveit"; then + if test -d "$additional_libdir"; then + dnl Really add $additional_libdir to $LDFLAGS. + LDFLAGS="${LDFLAGS}${LDFLAGS:+ }-L$additional_libdir" + fi + fi + fi + fi + fi +]) + +dnl AC_LIB_PREPARE_PREFIX creates variables acl_final_prefix, +dnl acl_final_exec_prefix, containing the values to which $prefix and +dnl $exec_prefix will expand at the end of the configure script. +AC_DEFUN([AC_LIB_PREPARE_PREFIX], +[ + dnl Unfortunately, prefix and exec_prefix get only finally determined + dnl at the end of configure. + if test "X$prefix" = "XNONE"; then + acl_final_prefix="$ac_default_prefix" + else + acl_final_prefix="$prefix" + fi + if test "X$exec_prefix" = "XNONE"; then + acl_final_exec_prefix='${prefix}' + else + acl_final_exec_prefix="$exec_prefix" + fi + acl_save_prefix="$prefix" + prefix="$acl_final_prefix" + eval acl_final_exec_prefix=\"$acl_final_exec_prefix\" + prefix="$acl_save_prefix" +]) + +dnl AC_LIB_WITH_FINAL_PREFIX([statement]) evaluates statement, with the +dnl variables prefix and exec_prefix bound to the values they will have +dnl at the end of the configure script. +AC_DEFUN([AC_LIB_WITH_FINAL_PREFIX], +[ + acl_save_prefix="$prefix" + prefix="$acl_final_prefix" + acl_save_exec_prefix="$exec_prefix" + exec_prefix="$acl_final_exec_prefix" + $1 + exec_prefix="$acl_save_exec_prefix" + prefix="$acl_save_prefix" +]) + +# lib-link.m4 serial 4 (gettext-0.12) +dnl Copyright (C) 2001-2003 Free Software Foundation, Inc. +dnl This file is free software, distributed under the terms of the GNU +dnl General Public License. As a special exception to the GNU General +dnl Public License, this file may be distributed as part of a program +dnl that contains a configuration script generated by Autoconf, under +dnl the same distribution terms as the rest of that program. + +dnl From Bruno Haible. + +dnl AC_LIB_LINKFLAGS(name [, dependencies]) searches for libname and +dnl the libraries corresponding to explicit and implicit dependencies. +dnl Sets and AC_SUBSTs the LIB${NAME} and LTLIB${NAME} variables and +dnl augments the CPPFLAGS variable. +AC_DEFUN([AC_LIB_LINKFLAGS], +[ + AC_REQUIRE([AC_LIB_PREPARE_PREFIX]) + AC_REQUIRE([AC_LIB_RPATH]) + define([Name],[translit([$1],[./-], [___])]) + define([NAME],[translit([$1],[abcdefghijklmnopqrstuvwxyz./-], + [ABCDEFGHIJKLMNOPQRSTUVWXYZ___])]) + AC_CACHE_CHECK([how to link with lib[]$1], [ac_cv_lib[]Name[]_libs], [ + AC_LIB_LINKFLAGS_BODY([$1], [$2]) + ac_cv_lib[]Name[]_libs="$LIB[]NAME" + ac_cv_lib[]Name[]_ltlibs="$LTLIB[]NAME" + ac_cv_lib[]Name[]_cppflags="$INC[]NAME" + ]) + LIB[]NAME="$ac_cv_lib[]Name[]_libs" + LTLIB[]NAME="$ac_cv_lib[]Name[]_ltlibs" + INC[]NAME="$ac_cv_lib[]Name[]_cppflags" + AC_LIB_APPENDTOVAR([CPPFLAGS], [$INC]NAME) + AC_SUBST([LIB]NAME) + AC_SUBST([LTLIB]NAME) + dnl Also set HAVE_LIB[]NAME so that AC_LIB_HAVE_LINKFLAGS can reuse the + dnl results of this search when this library appears as a dependency. + HAVE_LIB[]NAME=yes + undefine([Name]) + undefine([NAME]) +]) + +dnl AC_LIB_HAVE_LINKFLAGS(name, dependencies, includes, testcode) +dnl searches for libname and the libraries corresponding to explicit and +dnl implicit dependencies, together with the specified include files and +dnl the ability to compile and link the specified testcode. If found, it +dnl sets and AC_SUBSTs HAVE_LIB${NAME}=yes and the LIB${NAME} and +dnl LTLIB${NAME} variables and augments the CPPFLAGS variable, and +dnl #defines HAVE_LIB${NAME} to 1. Otherwise, it sets and AC_SUBSTs +dnl HAVE_LIB${NAME}=no and LIB${NAME} and LTLIB${NAME} to empty. +AC_DEFUN([AC_LIB_HAVE_LINKFLAGS], +[ + AC_REQUIRE([AC_LIB_PREPARE_PREFIX]) + AC_REQUIRE([AC_LIB_RPATH]) + define([Name],[translit([$1],[./-], [___])]) + define([NAME],[translit([$1],[abcdefghijklmnopqrstuvwxyz./-], + [ABCDEFGHIJKLMNOPQRSTUVWXYZ___])]) + + dnl Search for lib[]Name and define LIB[]NAME, LTLIB[]NAME and INC[]NAME + dnl accordingly. + AC_LIB_LINKFLAGS_BODY([$1], [$2]) + + dnl Add $INC[]NAME to CPPFLAGS before performing the following checks, + dnl because if the user has installed lib[]Name and not disabled its use + dnl via --without-lib[]Name-prefix, he wants to use it. + ac_save_CPPFLAGS="$CPPFLAGS" + AC_LIB_APPENDTOVAR([CPPFLAGS], [$INC]NAME) + + AC_CACHE_CHECK([for lib[]$1], [ac_cv_lib[]Name], [ + ac_save_LIBS="$LIBS" + LIBS="$LIBS $LIB[]NAME" + AC_TRY_LINK([$3], [$4], [ac_cv_lib[]Name=yes], [ac_cv_lib[]Name=no]) + LIBS="$ac_save_LIBS" + ]) + if test "$ac_cv_lib[]Name" = yes; then + HAVE_LIB[]NAME=yes + AC_DEFINE([HAVE_LIB]NAME, 1, [Define if you have the $1 library.]) + AC_MSG_CHECKING([how to link with lib[]$1]) + AC_MSG_RESULT([$LIB[]NAME]) + else + HAVE_LIB[]NAME=no + dnl If $LIB[]NAME didn't lead to a usable library, we don't need + dnl $INC[]NAME either. + CPPFLAGS="$ac_save_CPPFLAGS" + LIB[]NAME= + LTLIB[]NAME= + fi + AC_SUBST([HAVE_LIB]NAME) + AC_SUBST([LIB]NAME) + AC_SUBST([LTLIB]NAME) + undefine([Name]) + undefine([NAME]) +]) + +dnl Determine the platform dependent parameters needed to use rpath: +dnl libext, shlibext, hardcode_libdir_flag_spec, hardcode_libdir_separator, +dnl hardcode_direct, hardcode_minus_L. +AC_DEFUN([AC_LIB_RPATH], +[ + AC_REQUIRE([AC_PROG_CC]) dnl we use $CC, $GCC, $LDFLAGS + AC_REQUIRE([AC_LIB_PROG_LD]) dnl we use $LD, $with_gnu_ld + AC_REQUIRE([AC_CANONICAL_HOST]) dnl we use $host + AC_REQUIRE([AC_CONFIG_AUX_DIR_DEFAULT]) dnl we use $ac_aux_dir + AC_CACHE_CHECK([for shared library run path origin], acl_cv_rpath, [ + CC="$CC" GCC="$GCC" LDFLAGS="$LDFLAGS" LD="$LD" with_gnu_ld="$with_gnu_ld" \ + ${CONFIG_SHELL-/bin/sh} "$ac_aux_dir/config.rpath" "$host" > conftest.sh + . ./conftest.sh + rm -f ./conftest.sh + acl_cv_rpath=done + ]) + wl="$acl_cv_wl" + libext="$acl_cv_libext" + shlibext="$acl_cv_shlibext" + hardcode_libdir_flag_spec="$acl_cv_hardcode_libdir_flag_spec" + hardcode_libdir_separator="$acl_cv_hardcode_libdir_separator" + hardcode_direct="$acl_cv_hardcode_direct" + hardcode_minus_L="$acl_cv_hardcode_minus_L" + dnl Determine whether the user wants rpath handling at all. + AC_ARG_ENABLE(rpath, + [ --disable-rpath do not hardcode runtime library paths], + :, enable_rpath=yes) +]) + +dnl AC_LIB_LINKFLAGS_BODY(name [, dependencies]) searches for libname and +dnl the libraries corresponding to explicit and implicit dependencies. +dnl Sets the LIB${NAME}, LTLIB${NAME} and INC${NAME} variables. +AC_DEFUN([AC_LIB_LINKFLAGS_BODY], +[ + define([NAME],[translit([$1],[abcdefghijklmnopqrstuvwxyz./-], + [ABCDEFGHIJKLMNOPQRSTUVWXYZ___])]) + dnl By default, look in $includedir and $libdir. + use_additional=yes + AC_LIB_WITH_FINAL_PREFIX([ + eval additional_includedir=\"$includedir\" + eval additional_libdir=\"$libdir\" + ]) + AC_LIB_ARG_WITH([lib$1-prefix], +[ --with-lib$1-prefix[=DIR] search for lib$1 in DIR/include and DIR/lib + --without-lib$1-prefix don't search for lib$1 in includedir and libdir], +[ + if test "X$withval" = "Xno"; then + use_additional=no + else + if test "X$withval" = "X"; then + AC_LIB_WITH_FINAL_PREFIX([ + eval additional_includedir=\"$includedir\" + eval additional_libdir=\"$libdir\" + ]) + else + additional_includedir="$withval/include" + additional_libdir="$withval/lib" + fi + fi +]) + dnl Search the library and its dependencies in $additional_libdir and + dnl $LDFLAGS. Using breadth-first-seach. + LIB[]NAME= + LTLIB[]NAME= + INC[]NAME= + rpathdirs= + ltrpathdirs= + names_already_handled= + names_next_round='$1 $2' + while test -n "$names_next_round"; do + names_this_round="$names_next_round" + names_next_round= + for name in $names_this_round; do + already_handled= + for n in $names_already_handled; do + if test "$n" = "$name"; then + already_handled=yes + break + fi + done + if test -z "$already_handled"; then + names_already_handled="$names_already_handled $name" + dnl See if it was already located by an earlier AC_LIB_LINKFLAGS + dnl or AC_LIB_HAVE_LINKFLAGS call. + uppername=`echo "$name" | sed -e 'y|abcdefghijklmnopqrstuvwxyz./-|ABCDEFGHIJKLMNOPQRSTUVWXYZ___|'` + eval value=\"\$HAVE_LIB$uppername\" + if test -n "$value"; then + if test "$value" = yes; then + eval value=\"\$LIB$uppername\" + test -z "$value" || LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$value" + eval value=\"\$LTLIB$uppername\" + test -z "$value" || LTLIB[]NAME="${LTLIB[]NAME}${LTLIB[]NAME:+ }$value" + else + dnl An earlier call to AC_LIB_HAVE_LINKFLAGS has determined + dnl that this library doesn't exist. So just drop it. + : + fi + else + dnl Search the library lib$name in $additional_libdir and $LDFLAGS + dnl and the already constructed $LIBNAME/$LTLIBNAME. + found_dir= + found_la= + found_so= + found_a= + if test $use_additional = yes; then + if test -n "$shlibext" && test -f "$additional_libdir/lib$name.$shlibext"; then + found_dir="$additional_libdir" + found_so="$additional_libdir/lib$name.$shlibext" + if test -f "$additional_libdir/lib$name.la"; then + found_la="$additional_libdir/lib$name.la" + fi + else + if test -f "$additional_libdir/lib$name.$libext"; then + found_dir="$additional_libdir" + found_a="$additional_libdir/lib$name.$libext" + if test -f "$additional_libdir/lib$name.la"; then + found_la="$additional_libdir/lib$name.la" + fi + fi + fi + fi + if test "X$found_dir" = "X"; then + for x in $LDFLAGS $LTLIB[]NAME; do + AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"]) + case "$x" in + -L*) + dir=`echo "X$x" | sed -e 's/^X-L//'` + if test -n "$shlibext" && test -f "$dir/lib$name.$shlibext"; then + found_dir="$dir" + found_so="$dir/lib$name.$shlibext" + if test -f "$dir/lib$name.la"; then + found_la="$dir/lib$name.la" + fi + else + if test -f "$dir/lib$name.$libext"; then + found_dir="$dir" + found_a="$dir/lib$name.$libext" + if test -f "$dir/lib$name.la"; then + found_la="$dir/lib$name.la" + fi + fi + fi + ;; + esac + if test "X$found_dir" != "X"; then + break + fi + done + fi + if test "X$found_dir" != "X"; then + dnl Found the library. + LTLIB[]NAME="${LTLIB[]NAME}${LTLIB[]NAME:+ }-L$found_dir -l$name" + if test "X$found_so" != "X"; then + dnl Linking with a shared library. We attempt to hardcode its + dnl directory into the executable's runpath, unless it's the + dnl standard /usr/lib. + if test "$enable_rpath" = no || test "X$found_dir" = "X/usr/lib"; then + dnl No hardcoding is needed. + LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$found_so" + else + dnl Use an explicit option to hardcode DIR into the resulting + dnl binary. + dnl Potentially add DIR to ltrpathdirs. + dnl The ltrpathdirs will be appended to $LTLIBNAME at the end. + haveit= + for x in $ltrpathdirs; do + if test "X$x" = "X$found_dir"; then + haveit=yes + break + fi + done + if test -z "$haveit"; then + ltrpathdirs="$ltrpathdirs $found_dir" + fi + dnl The hardcoding into $LIBNAME is system dependent. + if test "$hardcode_direct" = yes; then + dnl Using DIR/libNAME.so during linking hardcodes DIR into the + dnl resulting binary. + LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$found_so" + else + if test -n "$hardcode_libdir_flag_spec" && test "$hardcode_minus_L" = no; then + dnl Use an explicit option to hardcode DIR into the resulting + dnl binary. + LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$found_so" + dnl Potentially add DIR to rpathdirs. + dnl The rpathdirs will be appended to $LIBNAME at the end. + haveit= + for x in $rpathdirs; do + if test "X$x" = "X$found_dir"; then + haveit=yes + break + fi + done + if test -z "$haveit"; then + rpathdirs="$rpathdirs $found_dir" + fi + else + dnl Rely on "-L$found_dir". + dnl But don't add it if it's already contained in the LDFLAGS + dnl or the already constructed $LIBNAME + haveit= + for x in $LDFLAGS $LIB[]NAME; do + AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"]) + if test "X$x" = "X-L$found_dir"; then + haveit=yes + break + fi + done + if test -z "$haveit"; then + LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }-L$found_dir" + fi + if test "$hardcode_minus_L" != no; then + dnl FIXME: Not sure whether we should use + dnl "-L$found_dir -l$name" or "-L$found_dir $found_so" + dnl here. + LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$found_so" + else + dnl We cannot use $hardcode_runpath_var and LD_RUN_PATH + dnl here, because this doesn't fit in flags passed to the + dnl compiler. So give up. No hardcoding. This affects only + dnl very old systems. + dnl FIXME: Not sure whether we should use + dnl "-L$found_dir -l$name" or "-L$found_dir $found_so" + dnl here. + LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }-l$name" + fi + fi + fi + fi + else + if test "X$found_a" != "X"; then + dnl Linking with a static library. + LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$found_a" + else + dnl We shouldn't come here, but anyway it's good to have a + dnl fallback. + LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }-L$found_dir -l$name" + fi + fi + dnl Assume the include files are nearby. + additional_includedir= + case "$found_dir" in + */lib | */lib/) + basedir=`echo "X$found_dir" | sed -e 's,^X,,' -e 's,/lib/*$,,'` + additional_includedir="$basedir/include" + ;; + esac + if test "X$additional_includedir" != "X"; then + dnl Potentially add $additional_includedir to $INCNAME. + dnl But don't add it + dnl 1. if it's the standard /usr/include, + dnl 2. if it's /usr/local/include and we are using GCC on Linux, + dnl 3. if it's already present in $CPPFLAGS or the already + dnl constructed $INCNAME, + dnl 4. if it doesn't exist as a directory. + if test "X$additional_includedir" != "X/usr/include"; then + haveit= + if test "X$additional_includedir" = "X/usr/local/include"; then + if test -n "$GCC"; then + case $host_os in + linux*) haveit=yes;; + esac + fi + fi + if test -z "$haveit"; then + for x in $CPPFLAGS $INC[]NAME; do + AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"]) + if test "X$x" = "X-I$additional_includedir"; then + haveit=yes + break + fi + done + if test -z "$haveit"; then + if test -d "$additional_includedir"; then + dnl Really add $additional_includedir to $INCNAME. + INC[]NAME="${INC[]NAME}${INC[]NAME:+ }-I$additional_includedir" + fi + fi + fi + fi + fi + dnl Look for dependencies. + if test -n "$found_la"; then + dnl Read the .la file. It defines the variables + dnl dlname, library_names, old_library, dependency_libs, current, + dnl age, revision, installed, dlopen, dlpreopen, libdir. + save_libdir="$libdir" + case "$found_la" in + */* | *\\*) . "$found_la" ;; + *) . "./$found_la" ;; + esac + libdir="$save_libdir" + dnl We use only dependency_libs. + for dep in $dependency_libs; do + case "$dep" in + -L*) + additional_libdir=`echo "X$dep" | sed -e 's/^X-L//'` + dnl Potentially add $additional_libdir to $LIBNAME and $LTLIBNAME. + dnl But don't add it + dnl 1. if it's the standard /usr/lib, + dnl 2. if it's /usr/local/lib and we are using GCC on Linux, + dnl 3. if it's already present in $LDFLAGS or the already + dnl constructed $LIBNAME, + dnl 4. if it doesn't exist as a directory. + if test "X$additional_libdir" != "X/usr/lib"; then + haveit= + if test "X$additional_libdir" = "X/usr/local/lib"; then + if test -n "$GCC"; then + case $host_os in + linux*) haveit=yes;; + esac + fi + fi + if test -z "$haveit"; then + haveit= + for x in $LDFLAGS $LIB[]NAME; do + AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"]) + if test "X$x" = "X-L$additional_libdir"; then + haveit=yes + break + fi + done + if test -z "$haveit"; then + if test -d "$additional_libdir"; then + dnl Really add $additional_libdir to $LIBNAME. + LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }-L$additional_libdir" + fi + fi + haveit= + for x in $LDFLAGS $LTLIB[]NAME; do + AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"]) + if test "X$x" = "X-L$additional_libdir"; then + haveit=yes + break + fi + done + if test -z "$haveit"; then + if test -d "$additional_libdir"; then + dnl Really add $additional_libdir to $LTLIBNAME. + LTLIB[]NAME="${LTLIB[]NAME}${LTLIB[]NAME:+ }-L$additional_libdir" + fi + fi + fi + fi + ;; + -R*) + dir=`echo "X$dep" | sed -e 's/^X-R//'` + if test "$enable_rpath" != no; then + dnl Potentially add DIR to rpathdirs. + dnl The rpathdirs will be appended to $LIBNAME at the end. + haveit= + for x in $rpathdirs; do + if test "X$x" = "X$dir"; then + haveit=yes + break + fi + done + if test -z "$haveit"; then + rpathdirs="$rpathdirs $dir" + fi + dnl Potentially add DIR to ltrpathdirs. + dnl The ltrpathdirs will be appended to $LTLIBNAME at the end. + haveit= + for x in $ltrpathdirs; do + if test "X$x" = "X$dir"; then + haveit=yes + break + fi + done + if test -z "$haveit"; then + ltrpathdirs="$ltrpathdirs $dir" + fi + fi + ;; + -l*) + dnl Handle this in the next round. + names_next_round="$names_next_round "`echo "X$dep" | sed -e 's/^X-l//'` + ;; + *.la) + dnl Handle this in the next round. Throw away the .la's + dnl directory; it is already contained in a preceding -L + dnl option. + names_next_round="$names_next_round "`echo "X$dep" | sed -e 's,^X.*/,,' -e 's,^lib,,' -e 's,\.la$,,'` + ;; + *) + dnl Most likely an immediate library name. + LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$dep" + LTLIB[]NAME="${LTLIB[]NAME}${LTLIB[]NAME:+ }$dep" + ;; + esac + done + fi + else + dnl Didn't find the library; assume it is in the system directories + dnl known to the linker and runtime loader. (All the system + dnl directories known to the linker should also be known to the + dnl runtime loader, otherwise the system is severely misconfigured.) + LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }-l$name" + LTLIB[]NAME="${LTLIB[]NAME}${LTLIB[]NAME:+ }-l$name" + fi + fi + fi + done + done + if test "X$rpathdirs" != "X"; then + if test -n "$hardcode_libdir_separator"; then + dnl Weird platform: only the last -rpath option counts, the user must + dnl pass all path elements in one option. We can arrange that for a + dnl single library, but not when more than one $LIBNAMEs are used. + alldirs= + for found_dir in $rpathdirs; do + alldirs="${alldirs}${alldirs:+$hardcode_libdir_separator}$found_dir" + done + dnl Note: hardcode_libdir_flag_spec uses $libdir and $wl. + acl_save_libdir="$libdir" + libdir="$alldirs" + eval flag=\"$hardcode_libdir_flag_spec\" + libdir="$acl_save_libdir" + LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$flag" + else + dnl The -rpath options are cumulative. + for found_dir in $rpathdirs; do + acl_save_libdir="$libdir" + libdir="$found_dir" + eval flag=\"$hardcode_libdir_flag_spec\" + libdir="$acl_save_libdir" + LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$flag" + done + fi + fi + if test "X$ltrpathdirs" != "X"; then + dnl When using libtool, the option that works for both libraries and + dnl executables is -R. The -R options are cumulative. + for found_dir in $ltrpathdirs; do + LTLIB[]NAME="${LTLIB[]NAME}${LTLIB[]NAME:+ }-R$found_dir" + done + fi +]) + +dnl AC_LIB_APPENDTOVAR(VAR, CONTENTS) appends the elements of CONTENTS to VAR, +dnl unless already present in VAR. +dnl Works only for CPPFLAGS, not for LIB* variables because that sometimes +dnl contains two or three consecutive elements that belong together. +AC_DEFUN([AC_LIB_APPENDTOVAR], +[ + for element in [$2]; do + haveit= + for x in $[$1]; do + AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"]) + if test "X$x" = "X$element"; then + haveit=yes + break + fi + done + if test -z "$haveit"; then + [$1]="${[$1]}${[$1]:+ }$element" + fi + done +]) + +# lib-ld.m4 serial 3 (gettext-0.13) +dnl Copyright (C) 1996-2003 Free Software Foundation, Inc. +dnl This file is free software, distributed under the terms of the GNU +dnl General Public License. As a special exception to the GNU General +dnl Public License, this file may be distributed as part of a program +dnl that contains a configuration script generated by Autoconf, under +dnl the same distribution terms as the rest of that program. + +dnl Subroutines of libtool.m4, +dnl with replacements s/AC_/AC_LIB/ and s/lt_cv/acl_cv/ to avoid collision +dnl with libtool.m4. + +dnl From libtool-1.4. Sets the variable with_gnu_ld to yes or no. +AC_DEFUN([AC_LIB_PROG_LD_GNU], +[AC_CACHE_CHECK([if the linker ($LD) is GNU ld], acl_cv_prog_gnu_ld, +[# I'd rather use --version here, but apparently some GNU ld's only accept -v. +case `$LD -v 2>&1 </dev/null` in +*GNU* | *'with BFD'*) + acl_cv_prog_gnu_ld=yes ;; +*) + acl_cv_prog_gnu_ld=no ;; +esac]) +with_gnu_ld=$acl_cv_prog_gnu_ld +]) + +dnl From libtool-1.4. Sets the variable LD. +AC_DEFUN([AC_LIB_PROG_LD], +[AC_ARG_WITH(gnu-ld, +[ --with-gnu-ld assume the C compiler uses GNU ld [default=no]], +test "$withval" = no || with_gnu_ld=yes, with_gnu_ld=no) +AC_REQUIRE([AC_PROG_CC])dnl +AC_REQUIRE([AC_CANONICAL_HOST])dnl +# Prepare PATH_SEPARATOR. +# The user is always right. +if test "${PATH_SEPARATOR+set}" != set; then + echo "#! /bin/sh" >conf$$.sh + echo "exit 0" >>conf$$.sh + chmod +x conf$$.sh + if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then + PATH_SEPARATOR=';' + else + PATH_SEPARATOR=: + fi + rm -f conf$$.sh +fi +ac_prog=ld +if test "$GCC" = yes; then + # Check if gcc -print-prog-name=ld gives a path. + AC_MSG_CHECKING([for ld used by GCC]) + case $host in + *-*-mingw*) + # gcc leaves a trailing carriage return which upsets mingw + ac_prog=`($CC -print-prog-name=ld) 2>&5 | tr -d '\015'` ;; + *) + ac_prog=`($CC -print-prog-name=ld) 2>&5` ;; + esac + case $ac_prog in + # Accept absolute paths. + [[\\/]* | [A-Za-z]:[\\/]*)] + [re_direlt='/[^/][^/]*/\.\./'] + # Canonicalize the path of ld + ac_prog=`echo $ac_prog| sed 's%\\\\%/%g'` + while echo $ac_prog | grep "$re_direlt" > /dev/null 2>&1; do + ac_prog=`echo $ac_prog| sed "s%$re_direlt%/%"` + done + test -z "$LD" && LD="$ac_prog" + ;; + "") + # If it fails, then pretend we aren't using GCC. + ac_prog=ld + ;; + *) + # If it is relative, then search for the first ld in PATH. + with_gnu_ld=unknown + ;; + esac +elif test "$with_gnu_ld" = yes; then + AC_MSG_CHECKING([for GNU ld]) +else + AC_MSG_CHECKING([for non-GNU ld]) +fi +AC_CACHE_VAL(acl_cv_path_LD, +[if test -z "$LD"; then + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS="${IFS}${PATH_SEPARATOR-:}" + for ac_dir in $PATH; do + test -z "$ac_dir" && ac_dir=. + if test -f "$ac_dir/$ac_prog" || test -f "$ac_dir/$ac_prog$ac_exeext"; then + acl_cv_path_LD="$ac_dir/$ac_prog" + # Check to see if the program is GNU ld. I'd rather use --version, + # but apparently some GNU ld's only accept -v. + # Break only if it was the GNU/non-GNU ld that we prefer. + case `"$acl_cv_path_LD" -v 2>&1 < /dev/null` in + *GNU* | *'with BFD'*) + test "$with_gnu_ld" != no && break ;; + *) + test "$with_gnu_ld" != yes && break ;; + esac + fi + done + IFS="$ac_save_ifs" +else + acl_cv_path_LD="$LD" # Let the user override the test with a path. +fi]) +LD="$acl_cv_path_LD" +if test -n "$LD"; then + AC_MSG_RESULT($LD) +else + AC_MSG_RESULT(no) +fi +test -z "$LD" && AC_MSG_ERROR([no acceptable ld found in \$PATH]) +AC_LIB_PROG_LD_GNU +]) + diff --git a/breakpoints.c b/breakpoints.c new file mode 100644 index 0000000..ba3b060 --- /dev/null +++ b/breakpoints.c @@ -0,0 +1,230 @@ +#include "config.h" + +#include <stdlib.h> +#include <string.h> +#include <assert.h> + +#ifdef __powerpc__ +#include <sys/ptrace.h> +#endif + +#include "common.h" + +/*****************************************************************************/ + +Breakpoint * +address2bpstruct(Process *proc, void *addr) { + debug(DEBUG_FUNCTION, "address2bpstruct(pid=%d, addr=%p)", proc->pid, addr); + return dict_find_entry(proc->breakpoints, addr); +} + +void +insert_breakpoint(Process *proc, void *addr, + struct library_symbol *libsym) { + Breakpoint *sbp; + + debug(DEBUG_FUNCTION, "insert_breakpoint(pid=%d, addr=%p, symbol=%s)", proc->pid, addr, libsym ? libsym->name : "NULL"); + debug(1, "symbol=%s, addr=%p", libsym?libsym->name:"(nil)", addr); + + if (!addr) + return; + + if (libsym) + libsym->needs_init = 0; + + sbp = dict_find_entry(proc->breakpoints, addr); + if (!sbp) { + sbp = calloc(1, sizeof(Breakpoint)); + if (!sbp) { + return; /* TODO FIXME XXX: error_mem */ + } + dict_enter(proc->breakpoints, addr, sbp); + sbp->addr = addr; + sbp->libsym = libsym; + } +#ifdef __arm__ + sbp->thumb_mode = proc->thumb_mode; + proc->thumb_mode = 0; +#endif + sbp->enabled++; + if (sbp->enabled == 1 && proc->pid) + enable_breakpoint(proc->pid, sbp); +} + +void +delete_breakpoint(Process *proc, void *addr) { + Breakpoint *sbp; + + debug(DEBUG_FUNCTION, "delete_breakpoint(pid=%d, addr=%p)", proc->pid, addr); + + sbp = dict_find_entry(proc->breakpoints, addr); + assert(sbp); /* FIXME: remove after debugging has been done. */ + /* This should only happen on out-of-memory conditions. */ + if (sbp == NULL) + return; + + sbp->enabled--; + if (sbp->enabled == 0) + disable_breakpoint(proc->pid, sbp); + assert(sbp->enabled >= 0); +} + +static void +enable_bp_cb(void *addr, void *sbp, void *proc) { + debug(DEBUG_FUNCTION, "enable_bp_cb(pid=%d)", ((Process *)proc)->pid); + if (((Breakpoint *)sbp)->enabled) { + enable_breakpoint(((Process *)proc)->pid, sbp); + } +} + +void +enable_all_breakpoints(Process *proc) { + debug(DEBUG_FUNCTION, "enable_all_breakpoints(pid=%d)", proc->pid); + if (proc->breakpoints_enabled <= 0) { +#ifdef __powerpc__ + unsigned long a; + + /* + * PPC HACK! (XXX FIXME TODO) + * If the dynamic linker hasn't populated the PLT then + * dont enable the breakpoints + */ + if (options.libcalls) { + a = ptrace(PTRACE_PEEKTEXT, proc->pid, + sym2addr(proc, proc->list_of_symbols), + 0); + if (a == 0x0) + return; + } +#endif + + debug(1, "Enabling breakpoints for pid %u...", proc->pid); + if (proc->breakpoints) { + dict_apply_to_all(proc->breakpoints, enable_bp_cb, + proc); + } +#ifdef __mips__ + { + /* + * I'm sure there is a nicer way to do this. We need to + * insert breakpoints _after_ the child has been started. + */ + struct library_symbol *sym; + struct library_symbol *new_sym; + sym=proc->list_of_symbols; + while(sym){ + void *addr= sym2addr(proc,sym); + if(!addr){ + sym=sym->next; + continue; + } + if(dict_find_entry(proc->breakpoints,addr)){ + sym=sym->next; + continue; + } + debug(2,"inserting bp %p %s",addr,sym->name); + new_sym=malloc(sizeof(*new_sym)); + memcpy(new_sym,sym,sizeof(*new_sym)); + new_sym->next=proc->list_of_symbols; + proc->list_of_symbols=new_sym; + insert_breakpoint(proc, addr, new_sym); + sym=sym->next; + } + } +#endif + } + proc->breakpoints_enabled = 1; +} + +static void +disable_bp_cb(void *addr, void *sbp, void *proc) { + debug(DEBUG_FUNCTION, "disable_bp_cb(pid=%d)", ((Process *)proc)->pid); + if (((Breakpoint *)sbp)->enabled) { + disable_breakpoint(((Process *)proc)->pid, sbp); + } +} + +void +disable_all_breakpoints(Process *proc) { + debug(DEBUG_FUNCTION, "disable_all_breakpoints(pid=%d)", proc->pid); + if (proc->breakpoints_enabled) { + debug(1, "Disabling breakpoints for pid %u...", proc->pid); + dict_apply_to_all(proc->breakpoints, disable_bp_cb, proc); + } + proc->breakpoints_enabled = 0; +} + +static void +free_bp_cb(void *addr, void *sbp, void *data) { + debug(DEBUG_FUNCTION, "free_bp_cb(sbp=%p)", sbp); + assert(sbp); + free(sbp); +} + +void +breakpoints_init(Process *proc) { + struct library_symbol *sym; + + debug(DEBUG_FUNCTION, "breakpoints_init(pid=%d)", proc->pid); + if (proc->breakpoints) { /* let's remove that struct */ + dict_apply_to_all(proc->breakpoints, free_bp_cb, NULL); + dict_clear(proc->breakpoints); + proc->breakpoints = NULL; + } + proc->breakpoints = dict_init(dict_key2hash_int, dict_key_cmp_int); + + if (options.libcalls && proc->filename) { + /* FIXME: memory leak when called by exec(): */ + proc->list_of_symbols = read_elf(proc); + if (opt_e) { + struct library_symbol **tmp1 = &(proc->list_of_symbols); + while (*tmp1) { + struct opt_e_t *tmp2 = opt_e; + int keep = !opt_e_enable; + + while (tmp2) { + if (!strcmp((*tmp1)->name, tmp2->name)) { + keep = opt_e_enable; + } + tmp2 = tmp2->next; + } + if (!keep) { + *tmp1 = (*tmp1)->next; + } else { + tmp1 = &((*tmp1)->next); + } + } + } + } else { + proc->list_of_symbols = NULL; + } + for (sym = proc->list_of_symbols; sym; sym = sym->next) { + /* proc->pid==0 delays enabling. */ + insert_breakpoint(proc, sym2addr(proc, sym), sym); + } + proc->callstack_depth = 0; + proc->breakpoints_enabled = -1; +} + +void +reinitialize_breakpoints(Process *proc) { + struct library_symbol *sym; + + debug(DEBUG_FUNCTION, "reinitialize_breakpoints(pid=%d)", proc->pid); + + sym = proc->list_of_symbols; + + while (sym) { + if (sym->needs_init) { + insert_breakpoint(proc, sym2addr(proc, sym), + sym); + if (sym->needs_init && !sym->is_weak) { + fprintf(stderr, + "could not re-initialize breakpoint for \"%s\" in file \"%s\"\n", + sym->name, proc->filename); + exit(1); + } + } + sym = sym->next; + } +} diff --git a/common.h b/common.h new file mode 100644 index 0000000..c672913 --- /dev/null +++ b/common.h @@ -0,0 +1,253 @@ +#include <sys/types.h> +#include <sys/time.h> +#include <stdio.h> + +#include "ltrace.h" +#include "defs.h" +#include "dict.h" +#include "sysdep.h" +#include "debug.h" +#include "elf.h" +#include "read_config_file.h" + +#if defined HAVE_LIBIBERTY || defined HAVE_LIBSUPC__ +# define USE_DEMANGLE +#endif + +extern char * command; + +extern int exiting; /* =1 if we have to exit ASAP */ + +typedef struct Breakpoint Breakpoint; +struct Breakpoint { + void * addr; + unsigned char orig_value[BREAKPOINT_LENGTH]; + int enabled; + struct library_symbol * libsym; +#ifdef __arm__ + int thumb_mode; +#endif +}; + +enum arg_type { + ARGTYPE_UNKNOWN = -1, + ARGTYPE_VOID, + ARGTYPE_INT, + ARGTYPE_UINT, + ARGTYPE_LONG, + ARGTYPE_ULONG, + ARGTYPE_OCTAL, + ARGTYPE_CHAR, + ARGTYPE_SHORT, + ARGTYPE_USHORT, + ARGTYPE_FLOAT, /* float value, may require index */ + ARGTYPE_DOUBLE, /* double value, may require index */ + ARGTYPE_ADDR, + ARGTYPE_FILE, + ARGTYPE_FORMAT, /* printf-like format */ + ARGTYPE_STRING, /* NUL-terminated string */ + ARGTYPE_STRING_N, /* String of known maxlen */ + ARGTYPE_ARRAY, /* Series of values in memory */ + ARGTYPE_ENUM, /* Enumeration */ + ARGTYPE_STRUCT, /* Structure of values */ + ARGTYPE_POINTER, /* Pointer to some other type */ + ARGTYPE_COUNT /* number of ARGTYPE_* values */ +}; + +typedef struct arg_type_info_t { + enum arg_type type; + union { + /* ARGTYPE_ENUM */ + struct { + size_t entries; + char ** keys; + int * values; + } enum_info; + + /* ARGTYPE_ARRAY */ + struct { + struct arg_type_info_t * elt_type; + size_t elt_size; + int len_spec; + } array_info; + + /* ARGTYPE_STRING_N */ + struct { + int size_spec; + } string_n_info; + + /* ARGTYPE_STRUCT */ + struct { + struct arg_type_info_t ** fields; /* NULL-terminated */ + size_t * offset; + size_t size; + } struct_info; + + /* ARGTYPE_POINTER */ + struct { + struct arg_type_info_t * info; + } ptr_info; + + /* ARGTYPE_FLOAT */ + struct { + size_t float_index; + } float_info; + + /* ARGTYPE_DOUBLE */ + struct { + size_t float_index; + } double_info; + } u; +} arg_type_info; + +enum tof { + LT_TOF_NONE = 0, + LT_TOF_FUNCTION, /* A real library function */ + LT_TOF_FUNCTIONR, /* Return from a real library function */ + LT_TOF_SYSCALL, /* A syscall */ + LT_TOF_SYSCALLR, /* Return from a syscall */ + LT_TOF_STRUCT /* Not a function; read args from struct */ +}; + +typedef struct Function Function; +struct Function { + const char * name; + arg_type_info * return_info; + int num_params; + arg_type_info * arg_info[MAX_ARGS]; + int params_right; + Function * next; +}; + +enum toplt { + LS_TOPLT_NONE = 0, /* PLT not used for this symbol. */ + LS_TOPLT_EXEC, /* PLT for this symbol is executable. */ + LS_TOPLT_POINT /* PLT for this symbol is a non-executable. */ +}; + +extern Function * list_of_functions; +extern char *PLTs_initialized_by_here; + +struct library_symbol { + char * name; + void * enter_addr; + char needs_init; + enum toplt plt_type; + char is_weak; + struct library_symbol * next; +}; + +struct callstack_element { + union { + int syscall; + struct library_symbol * libfunc; + } c_un; + int is_syscall; + void * return_addr; + struct timeval time_spent; +}; + +#define MAX_CALLDEPTH 64 + +typedef enum Process_State Process_State; +enum Process_State { + STATE_ATTACHED = 0, + STATE_BEING_CREATED, + STATE_IGNORED /* ignore this process (it's a fork and no -f was used) */ +}; + +struct Process { + Process_State state; + Process * parent; /* needed by STATE_BEING_CREATED */ + char * filename; + pid_t pid; + Dict * breakpoints; + int breakpoints_enabled; /* -1:not enabled yet, 0:disabled, 1:enabled */ + int mask_32bit; /* 1 if 64-bit ltrace is tracing 32-bit process */ + unsigned int personality; + int tracesysgood; /* signal indicating a PTRACE_SYSCALL trap */ + + int callstack_depth; + struct callstack_element callstack[MAX_CALLDEPTH]; + struct library_symbol * list_of_symbols; + + /* Arch-dependent: */ + void * instruction_pointer; + void * stack_pointer; /* To get return addr, args... */ + void * return_addr; + Breakpoint * breakpoint_being_enabled; + void * arch_ptr; + short e_machine; + short need_to_reinitialize_breakpoints; +#ifdef __arm__ + int thumb_mode; /* ARM execution mode: 0: ARM, 1: Thumb */ +#endif + + /* output: */ + enum tof type_being_displayed; + + Process * next; +}; + +struct opt_c_struct { + int count; + struct timeval tv; +}; + +#include "options.h" +#include "output.h" +#ifdef USE_DEMANGLE +#include "demangle.h" +#endif + +extern Dict * dict_opt_c; + +extern Process * list_of_processes; + +extern Event * next_event(void); +extern Process * pid2proc(pid_t pid); +extern void handle_event(Event * event); +extern void execute_program(Process *, char **); +extern int display_arg(enum tof type, Process * proc, int arg_num, arg_type_info * info); +extern Breakpoint * address2bpstruct(Process * proc, void * addr); +extern void breakpoints_init(Process * proc); +extern void insert_breakpoint(Process * proc, void * addr, struct library_symbol * libsym); +extern void delete_breakpoint(Process * proc, void * addr); +extern void enable_all_breakpoints(Process * proc); +extern void disable_all_breakpoints(Process * proc); +extern void reinitialize_breakpoints(Process *); + +extern Process * open_program(char * filename, pid_t pid); +extern void open_pid(pid_t pid); +extern void show_summary(void); +extern arg_type_info * lookup_prototype(enum arg_type at); + +/* Arch-dependent stuff: */ +extern char * pid2name(pid_t pid); +extern void trace_set_options(Process * proc, pid_t pid); +extern void trace_me(void); +extern int trace_pid(pid_t pid); +extern void untrace_pid(pid_t pid); +extern void get_arch_dep(Process * proc); +extern void * get_instruction_pointer(Process * proc); +extern void set_instruction_pointer(Process * proc, void * addr); +extern void * get_stack_pointer(Process * proc); +extern void * get_return_addr(Process * proc, void * stack_pointer); +extern void set_return_addr(Process * proc, void * addr); +extern void enable_breakpoint(pid_t pid, Breakpoint * sbp); +extern void disable_breakpoint(pid_t pid, const Breakpoint * sbp); +extern int syscall_p(Process * proc, int status, int * sysnum); +extern void continue_process(pid_t pid); +extern void continue_after_signal(pid_t pid, int signum); +extern void continue_after_breakpoint(Process * proc, Breakpoint * sbp); +extern void continue_enabling_breakpoint(pid_t pid, Breakpoint * sbp); +extern long gimme_arg(enum tof type, Process * proc, int arg_num, arg_type_info * info); +extern void save_register_args(enum tof type, Process * proc); +extern int umovestr(Process * proc, void * addr, int len, void * laddr); +extern int umovelong (Process * proc, void * addr, long * result, arg_type_info * info); +extern int ffcheck(void * maddr); +extern void * sym2addr(Process *, struct library_symbol *); + +#if 0 /* not yet */ +extern int umoven(Process * proc, void * addr, int len, void * laddr); +#endif diff --git a/configure b/configure new file mode 100755 index 0000000..d5392a0 --- /dev/null +++ b/configure @@ -0,0 +1,157 @@ +#!/bin/sh + +if [ ! -f libltrace.c ] +then + echo "configure: error: cannot find sources (libltrace.c)" 1>&2 + exit 1 +fi + +echo -n "checking package name... " +PACKAGE_NAME='ltrace' +echo $PACKAGE_NAME + +echo -n "checking $PACKAGE_NAME version... " +PACKAGE_VERSION=$( cat VERSION ) +echo $PACKAGE_VERSION + +echo -n "checking HOST_OS... " +HOST_OS=$( uname -s ) +if [ "$HOST_OS" = "Linux" ] +then + HOST_OS="linux-gnu" +fi +echo $HOST_OS + +# HAVE_LIBIBERTY +echo -n "checking for cplus_demangle in -liberty... " +cat > conftest.c << EOF +char cplus_demangle(); +int main () { + return cplus_demangle(); +} +EOF +if gcc conftest.c -liberty 2>/dev/null +then + HAVE_LIBIBERTY=1 + echo "yes" +else + unset HAVE_LIBIBERTY + echo "no" +fi +rm -f conftest.c a.out + +# HAVE_LIBSUPC__ +echo -n "checking for __cxa_demangle in -lsupc++... " +cat > conftest.c << EOF +char __cxa_demangle(); +int main () { + return __cxa_demangle(); +} +EOF +if gcc conftest.c -lsupc++ 2>/dev/null +then + HAVE_LIBSUPC__=1 + echo "yes" +else + unset HAVE_LIBSUPC__ + echo "no" +fi +rm -f conftest.c a.out + +# HAVE_ELF_C_READ_MMAP +echo -n "checking whether elf_begin accepts ELF_C_READ_MMAP... " +cat > conftest.c << EOF +#include <gelf.h> +int main () { + Elf *elf = elf_begin (4, ELF_C_READ_MMAP, 0); + return 0; +} +EOF +if gcc conftest.c 2>/dev/null +then + HAVE_ELF_C_READ_MMAP=1 + echo "yes" +else + unset HAVE_ELF_C_READ_MMAP + echo "no" +fi +rm -f conftest.c a.out + +CC=gcc +CPPFLAGS=' -I /usr/include/libelf' +CFLAGS='-g -O2' +LIBS='-lelf -lsupc++ -liberty ' +INSTALL='/usr/bin/install -c' +iquote='-iquote ' +iquoteend='' + +prefix=/usr/local +sysconfdir='${prefix}/etc' +bindir='${prefix}/bin' +mandir='${prefix}/share/man' +docdir='${prefix}/share/doc/ltrace' +for x_option +do + if test -n "$x_prev"; then + eval $x_prev=\$x_option + x_prev= + continue + fi + case $x_option in + --*=* | *=*) + x_var=`echo $x_option | sed 's/^--//' | sed 's/=.*//'` + x_val=`echo $x_option | sed 's/^.*=//'` + eval $x_var=$x_val + ;; + --*) + x_prev=`echo $x_option | sed 's/^--//'` + ;; + esac +done + +echo "configure: creating Makefile" +# +# Makefile.in -> Makefile +# +x_subst_vars='PACKAGE_VERSION HOST_OS INSTALL CC CPPFLAGS CFLAGS LDFLAGS LIBS iquote iquoteend prefix sysconfdir mandir docdir' + +for i in $x_subst_vars +do + x_val=`eval echo \\"\\$$i\\"` + x_sed="$x_sed +s&@$i@&$x_val&g" +done + +sed "$x_sed" Makefile.in > Makefile + +echo "configure: creating config.h" +# +# config.h +# +exec > config.h + +echo '#define PACKAGE_NAME "ltrace"' +echo '#define PACKAGE_VERSION "'$PACKAGE_VERSION'"' + +if [ "$HAVE_LIBIBERTY" ] +then + echo '#define HAVE_LIBIBERTY 1' +else + echo '#undef HAVE_LIBIBERTY' +fi + +if [ "$HAVE_LIBSUPC__" ] +then + echo '#define HAVE_LIBSUPC__ 1' +else + echo '#undef HAVE_LIBSUPC__' +fi + +if [ "$HAVE_ELF_C_READ_MMAP" ] +then + echo '#define HAVE_ELF_C_READ_MMAP 1' +else + echo '#undef HAVE_ELF_C_READ_MMAP' +fi + +exit 0 diff --git a/debian/changelog b/debian/changelog new file mode 100644 index 0000000..f97cb8f --- /dev/null +++ b/debian/changelog @@ -0,0 +1,652 @@ +ltrace (0.5.3-1) unstable; urgency=low + + * New upstream release + * Make it work again in sparc with new kernel headers (closes: Bug#532195) + + -- Juan Cespedes <cespedes@debian.org> Sat, 25 Jul 2009 16:24:38 +0200 + +ltrace (0.5.2-2) unstable; urgency=low + + * Make clone() work when child starts after parent finishes + * See syscalls as syscalls and not sysrets when we are a clone + + -- Juan Cespedes <cespedes@debian.org> Thu, 28 May 2009 16:30:08 +0200 + +ltrace (0.5.2-1) unstable; urgency=low + + * New upstream release (closes: Bug#463023) + * New approach for following forks; it should now attach + every newly created process (closes: Bug#483827) + * Fixed SEGFAULT when killing ltrace with SIGINT or SIGTERM (closes: #458923) + + -- Juan Cespedes <cespedes@debian.org> Thu, 21 May 2009 19:16:22 +0200 + +ltrace (0.5.1-2) unstable; urgency=low + + * Red-added armel and armeb to debian/control (closes: Bug#463023) + + -- Juan Cespedes <cespedes@debian.org> Tue, 16 Dec 2008 13:00:50 +0100 + +ltrace (0.5.1-1) unstable; urgency=low + + * New upstream release, fixing many bugs + (thanks to Petr Machata, Luis Machado...) + * Acknowledged NMU (Closes: Bug#463023) + * Update Standards-Version (3.8.0), no changes + + -- Juan Cespedes <cespedes@debian.org> Wed, 10 Dec 2008 18:41:20 +0100 + +ltrace (0.5-3.1) unstable; urgency=low + + * Non-maintainer upload. + * Big thanks for Anderson Lizardo for providing patches! + * Add generic support for arm targets, Closes: #176413 + * Save funtion arguments on arm, Closes: #462530 + * Add thumb instruction support, Closes: #462531 + * Add basic arm/eabi support, Closes: #450931 + * fix exec() testcase cleanup, Closes: #462532 + * fix memory corruption in clone() test, Closes: #462533 + * fix tracing child with "-p" option, Closes: #462535 + * Update standard, no changes + + -- Riku Voipio <riku.voipio@iki.fi> Tue, 29 Jan 2008 00:26:50 +0200 + +ltrace (0.5-3) unstable; urgency=low + + * Really fix compilation problems in ppc (!) + + -- Juan Cespedes <cespedes@debian.org> Fri, 31 Aug 2007 19:04:03 +0200 + +ltrace (0.5-2) unstable; urgency=low + + * Fixed compilation issue in ppc + + -- Juan Cespedes <cespedes@debian.org> Fri, 31 Aug 2007 13:53:27 +0200 + +ltrace (0.5-1) unstable; urgency=low + + * New upstream version + * Remove some unneeded files in /usr/share/doc (ChangeLog, COPYING...) + * Fix several typos (closes: Bug#372928) + * Added more system calls to ltrace.conf + + -- Juan Cespedes <cespedes@debian.org> Thu, 30 Aug 2007 14:54:44 +0200 + +ltrace (0.4-2) unstable; urgency=low + + * Use fclose() to close the output file when using option '-o' + (thanks to GuiJianfeng <jianfenggui@gmail.com>) + + -- Juan Cespedes <cespedes@debian.org> Tue, 07 Aug 2007 11:49:27 +0200 + +ltrace (0.4-1) unstable; urgency=low + + * Rebase code from Redhat patches, now everything lives in SVN + * Closes: #297483,#315889,#318009 -- add PowerPC64 support + * Add s390x, ppc64 support + * This removes the patch from #257903 as it seems unnecessary now. + + -- Ian Wienand <ianw@ieee.org> Tue, 21 Feb 2006 09:23:09 +1100 + +ltrace (0.3.38-1) unstable; urgency=low + + * Closes: 306862 -- Add IA64 support + + -- Ian Wienand <ianw@ieee.org> Fri, 17 Feb 2006 11:17:46 +1100 + +ltrace (0.3.37-2) unstable; urgency=low + + * Convert to use CDBS + * Use CDBS to build control; you need to run + DEB_AUTO_UPDATE_DEBIAN_CONROL=yes ./debian/rules clean + to make the control file if you checkout from CVS + * Remove autoconf from build-deps, fix up upstream dist target so we don't + need it. + + -- Ian Wienand <ianw@ieee.org> Thu, 16 Feb 2006 22:56:14 +1100 + +ltrace (0.3.37-1) unstable; urgency=low + + [ Ian Wienand ] + * Non-maintainer upload + * Start a "friendly takeover" from Juan + * Closes: #127503,#280608 -- update man page typos + * Closes: #339348 -- fix putenv typo in ltrace.conf + * Closes: #257903 -- incorporate variable length args patch + * Closes: #282051 -- demange names when -c used with -C + * Closes: #352389 -- pass build flags through to Makefile + * Closes: #155571 -- add note in man page about dlopened libraries + * See "upstream" ChangeLog for other changes (mostly warning fixes) + * Update README to point to Alioth home: http://ltrace.alioth.debian.org + + [ Christoph Berg ] + * Create non-native package. + * Add autoconf to build-deps + + -- Ian Wienand <ianw@ieee.org> Thu, 16 Feb 2006 11:51:32 +1100 + +ltrace (0.3.36-2) unstable; urgency=low + + * Corrected path for Debian changelog + + -- Juan Cespedes <cespedes@debian.org> Wed, 10 Nov 2004 00:33:21 +0100 + +ltrace (0.3.36-1) unstable; urgency=low + + * Changed distribution to pristine source + * New Standards-Version (3.6.1) + * Fixed "--indent" option (closes: Bug#265185) + + -- Juan Cespedes <cespedes@debian.org> Wed, 10 Nov 2004 00:14:19 +0100 + +ltrace (0.3.35.1) unstable; urgency=low + + * Non-maintainer upload + * Applied patch from Jakub Jelinek <jakub@redhat.com> to fix problems with + binaries built with recent binutils (closes: #274955) + * Applied patch from Jakub Jelinek to add long/ulong types to ltrace.conf + for amd64 + * Applied patch from Jakub Jelinek to fix -C + * Applied patch from Jakub Jelinek to update syscallent.h + * debian/control: build-depend on dpatch and libelfg0-dev + * debian/rules: add dpatch support + * debian/changelog: convert to utf-8 + + -- Andrew Pollock <apollock@debian.org> Fri, 22 Oct 2004 21:43:16 +1000 + +ltrace (0.3.35) unstable; urgency=low + + * Fixed include line in m68k, caused build problems + + -- Juan Cespedes <cespedes@debian.org> Fri, 16 Jul 2004 18:00:10 +0200 + +ltrace (0.3.34) unstable; urgency=low + + * Fixed prototype declaration problem in arm, m68k, powerpc, s390 + * Added "amd64" to list of architectures (closes: Bug#252756) + * Sparc port is hopefully working (closes: Bug#35524) + + -- Juan Cespedes <cespedes@debian.org> Wed, 07 Jul 2004 10:40:56 +0200 + +ltrace (0.3.33) unstable; urgency=low + + * Fixed two bugs, thanks to Mauro Meneghin <G1ld0@lycos.it>: + + Cope correctly with breakpoint values greater than + sizeof(long) bytes + + Fixed small bug in option -r (closes: Bug#212792) + * Show help if no (or few) arguments are given, just like + strace and fenris (thanks, Tomasz Wegrzanowski <taw@users.sf.net>) + * Some fixes from Jakub Bogusz <qboosh@pld-linux.org>: + + Small 64-bit cleanup of code + + support for more than 6 function arguments on amd64 + + Adapted SPARC port from Jakub Jelinek <jakub@redhat.com> + + Added alpha support + + -- Juan Cespedes <cespedes@debian.org> Mon, 14 Jun 2004 18:01:12 +0200 + +ltrace (0.3.32) unstable; urgency=low + + * Fixed wrong version number + * Removed unused file "opt_c.c" + * Remove error when tracing no calls and doing fork() + * Clean-up of sysdeps/linux-gnu/s390/trace.c + * Clean-up of sysdeps/linux-gnu/ppc/trace.c + * Make `--library' option really work (closes: Bug#232321) + * Merged several patches from SuSE: + + Added some functions to ltrace.conf + + Handle 64-big ELF files nicely + + AMD64 support + + Updated list of syscalls for S/390 + + Improved some debugging statements + Many thanks to Bernhard Kaindl <bk@suse.de> for his great work + + -- Juan Cespedes <cespedes@debian.org> Sun, 04 Apr 2004 01:31:37 +0200 + +ltrace (0.3.31) unstable; urgency=low + + * Added a lot of functions to ltrace.conf, + thanks to Jakub Jelinek <jakub@redhat.com> (closes: Bug#144518) + * Fixed off-by-one problem in checking syscall number + * Removed some warnings + + -- Juan Cespedes <cespedes@debian.org> Tue, 04 Feb 2003 23:22:46 +0100 + +ltrace (0.3.30) unstable; urgency=low + + * Implemented -T option (show time spent inside each call) + * Alphabetically sort options in help and manual page + * Added -c option (summary of calls on program exit) + + -- Juan Cespedes <cespedes@debian.org> Mon, 03 Feb 2003 00:22:28 +0100 + +ltrace (0.3.29) unstable; urgency=low + + * Align return values depending on screen width + * Updated list of syscalls and signals to Linux 2.4.20 + * Fixed bug introduced in 0.3.27 which caused -L option to segfault + + -- Juan Cespedes <cespedes@debian.org> Sat, 01 Feb 2003 19:01:39 +0100 + +ltrace (0.3.28) unstable; urgency=medium + + * Fixed memory corruption when using execve() in a traced program + (closes: Bug#160341, Bug#165626) + + -- Juan Cespedes <cespedes@debian.org> Fri, 31 Jan 2003 19:51:28 +0100 + +ltrace (0.3.27) unstable; urgency=low + + * Removed dependency on libdl (it is no longer needed) + * Wrote generic dictionary, used in demangle.c and breakpoints.c + * Added debug.c for better debugging output + + -- Juan Cespedes <cespedes@debian.org> Fri, 31 Jan 2003 18:58:55 +0100 + +ltrace (0.3.26) unstable; urgency=low + + * Fixed `ltrace -L' in powerpc + + -- Juan Cespedes <cespedes@debian.org> Sun, 31 Mar 2002 20:53:49 +0200 + +ltrace (0.3.25) unstable; urgency=low + + * Finally added powerpc support (Anton Blanchard <anton@samba.org>) + + -- Juan Cespedes <cespedes@debian.org> Sun, 31 Mar 2002 19:58:25 +0200 + +ltrace (0.3.24) unstable; urgency=low + + * Fixed 2 minor buffer overflows (closes: Bug#130746) + * Obey --prefix, --sysconfdir, --mandir options in configure + * Adding powerpc support (doesn't work yet) + (Anton Blanchard <anton@samba.org>) + + -- Juan Cespedes <cespedes@debian.org> Wed, 27 Mar 2002 00:20:57 +0100 + +ltrace (0.3.23) unstable; urgency=low + + * Fixed missing include <unistd.h> in trace.c + * One arch-dependent function less (continue_after_breakpoint) + * Fixed S/390 port (it didn't compile yet...) + + -- Juan Cespedes <cespedes@debian.org> Sun, 3 Mar 2002 18:58:36 +0100 + +ltrace (0.3.22) unstable; urgency=low + + * S/390: Removed extra target in sysdeps/linux-gnu/s390 which avoided + compiling... + + -- Juan Cespedes <cespedes@debian.org> Sun, 3 Mar 2002 14:04:38 +0100 + +ltrace (0.3.21) unstable; urgency=low + + * Get rid of arch/breakpoint.c; we can do it arch-independent + + -- Juan Cespedes <cespedes@debian.org> Sun, 3 Mar 2002 02:37:46 +0100 + +ltrace (0.3.20) unstable; urgency=low + + * Added s390 port (Timothy R. Fesig <slate@us.ibm.com>) + * Modified configure process to use ltrace.spec.in to generate + ltrace.spec (Timothy R. Fesig <slate@us.ibm.com>) + * Fixed some problems using ltrace.spec on Intel platform. + (Timothy R. Fesig <slate@us.ibm.com>) + + -- Juan Cespedes <cespedes@debian.org> Sat, 2 Mar 2002 23:33:00 +0100 + +ltrace (0.3.19) unstable; urgency=low + + * Fixed small bug: "<unifinished...>" lines were sometimes incorrectly + displayed + * Added new functions to /etc/ltrace.conf (thanks to James R. Van Zandt + <jrv@vanzandt.mv.com>) (closes: Bug#91349) + + -- Juan Cespedes <cespedes@debian.org> Fri, 1 Mar 2002 21:05:37 +0100 + +ltrace (0.3.18) unstable; urgency=low + + * Simplified arch-dependent stuff + * Updated list of syscalls and signals to Linux 2.4.18 + * Unified coding-style of all function declarations + * Do not indent lines indicating signals, exit codes, etc + * Updated description + * fix off-by-one problem in checking syscall number (Tim Waugh + <twaugh@redhat.com> fixed this problem in RedHat two years ago; + thank you for NOT noticing me...) + + -- Juan Cespedes <cespedes@debian.org> Fri, 1 Mar 2002 19:52:43 +0100 + +ltrace (0.3.17) unstable; urgency=low + + * Added a bit more debugging + * Fixed display of return address in nested functions + * Added posibility to exit from a function different from the last called + one (this fixes "ltrace gnome-terminal", for example) + + -- Juan Cespedes <cespedes@debian.org> Mon, 25 Feb 2002 00:19:19 +0100 + +ltrace (0.3.16) unstable; urgency=low + + * ltrace works again after an execve is received (closes: Bug#108835) + * Added prototypes for fnmatch() and bsearch() (closes: Bug#106862) + * Re-wrote short description so it does not exceed 60 chars + (closes: Bug#114682) + + -- Juan Cespedes <cespedes@debian.org> Mon, 10 Dec 2001 04:11:26 +0100 + +ltrace (0.3.15) unstable; urgency=low + + * Fixed `-n' option so that it displays correct output even when + tracing several processes + + -- Juan Cespedes <cespedes@debian.org> Mon, 9 Jul 2001 01:02:46 +0200 + +ltrace (0.3.14) unstable; urgency=low + + * Assume a syscall is always immediatly followed by a sysret + in i386 (fixes bug which prevented ltrace to work properly + in any program using signals); I will have to rethink all + this and fix it correctly or port it to non-i386 archs + * Fixed -n option: now it is done in output.c (this still has + problems when tracing more than one process at a time) + + -- Juan Cespedes <cespedes@debian.org> Sat, 7 Jul 2001 20:56:42 +0200 + +ltrace (0.3.13) unstable; urgency=low + + * Fixed "ltrace -i", broken since version 0.3.11 + + -- Juan Cespedes <cespedes@debian.org> Tue, 3 Jul 2001 18:36:15 +0200 + +ltrace (0.3.12) unstable; urgency=low + + * Re-wrote of "elf.c" (Silvio Cesare <silvio@big.net.au>) + * Added "--library" option (Silvio) + * Updated list of syscalls and signals to Linux 2.4.5 + * Compile cleanly with gcc-3.0 (thanks to Frédéric L. W. Meunier) + + -- Juan Cespedes <cespedes@debian.org> Tue, 3 Jul 2001 00:43:25 +0200 + +ltrace (0.3.11) unstable; urgency=low + + * Clean up lintian bugs + * Fixed small bug reading start of arguments in config file + * Keep a stack of nested calls (Morten Eriksen, 1999-07-04) + * Add "--indent" option (Morten Eriksen, 1999-07-04) + * cleans up connection between a breakpoint address and + a call instance (Morten Eriksen, 1999-07-04) + * New Standards-Version (3.5.5) + + -- Juan Cespedes <cespedes@debian.org> Mon, 2 Jul 2001 00:24:11 +0200 + +ltrace (0.3.10) unstable; urgency=low + + * Added C++ demangle (again) + * Added correct Build-Depends + + -- Juan Cespedes <cespedes@debian.org> Thu, 23 Dec 1999 00:22:33 +0100 + +ltrace (0.3.9) unstable; urgency=low + + * New Standards-Version (3.1.1) + * Fixed Lintian bugs + + -- Juan Cespedes <cespedes@debian.org> Sun, 19 Dec 1999 17:49:40 +0100 + +ltrace (0.3.8) unstable; urgency=low + + * glibc-2.1 does no longer need `_GNU_SOURCE' defined to use <getopt.h> + * Changed description of package; adopted Red Hat's one + (thanks to whoever wrote it) + * Close all the file descriptors used before executing program (close-on-exec) + * Updated copyright file for new location /usr/share/common-licenses/GPL. + * Used GNU autoconf instead of "uname" to guess host system type + * Install man page in /usr/share/man instead of /usr/man + * Added a few functions to /etc/ltrace.conf + * Updated list of syscalls and signals to linux-2.2.12 + * Fixed bugs in C++ demangle (Morten Eriksen <mortene@sim.no>) + * New Standards-Version: 3.0.1 (but keeping docs in /usr/doc) + + -- Juan Cespedes <cespedes@debian.org> Mon, 30 Aug 1999 19:34:47 +0200 + +ltrace (0.3.7) unstable; urgency=low + + * Minor fixes + * Added minor patch from Alex Buell <alex.buell@tahallah.demon.co.uk> + to be able to compile under glibc 2.1 + * Additions to config file from David Dyck <dcd@tc.fluke.com> + * Clean-up Makefile a bit + * Changed `LT_PT_*' with `ARGTYPE_*' + * Display '\\' instead of '\' + * Updated list of syscalls and signals to linux-2.2.5 + * Compiled against glibc-2.1 + + -- Juan Cespedes <cespedes@debian.org> Sat, 3 Apr 1999 03:21:50 +0200 + +ltrace (0.3.6) unstable; urgency=low + + * Added m68k port (Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de>) (Bug#27075) + * Changed "int pid" with "pid_t pid" everywhere + * Fixed return type of some functions from "int" to "void *" (thanks, Roman) + + -- Juan Cespedes <cespedes@debian.org> Fri, 25 Sep 1998 14:48:37 +0200 + +ltrace (0.3.5) unstable; urgency=low + + * Added ARMLinux port (Pat Beirne <pbeirne@home.com>) (Bug#27040) + * Fixed minor things in options.c + + -- Juan Cespedes <cespedes@debian.org> Thu, 24 Sep 1998 13:18:01 +0200 + +ltrace (0.3.4) unstable; urgency=low + + * Added "ltrace.spec" to build a .rpm binary file. + * Added "-r" option + + -- Juan Cespedes <cespedes@debian.org> Sun, 20 Sep 1998 21:22:05 +0200 + +ltrace (0.3.3) unstable; urgency=low + + * Fixed a little bug in display_string + * A few more functions added to /etc/ltrace.conf + + -- Juan Cespedes <cespedes@debian.org> Sun, 6 Sep 1998 14:03:10 +0200 + +ltrace (0.3.2) unstable; urgency=low + + * Make the output line-buffered (Bug#22874) + * New Standards-Version (2.4.1) + * Make it compile cleanly with glibc 2.0.7 + + -- Juan Cespedes <cespedes@debian.org> Tue, 14 Jul 1998 13:45:24 +0200 + +ltrace (0.3.1) frozen unstable; urgency=low + + * setgid programs had their uid and gid swapped! Fixed. + + -- Juan Cespedes <cespedes@debian.org> Wed, 29 Apr 1998 19:25:11 +0200 + +ltrace (0.3.0) unstable; urgency=low + + * Preliminary autoconf support + * Switched to getopt() + * New option: -C (demangle C++ names) + * New options: --help, --version + * Display "format" (printf-like) argument types + * Updated manual page + * New option: -e + + -- Juan Cespedes <cespedes@debian.org> Sat, 25 Apr 1998 14:21:59 +0200 + +ltrace (0.2.9) frozen unstable; urgency=low + + * Bug#20616 wasn't completely fixed; it didn't work with some programs (Fixed) + * Stopping ltrace with ^C DIDN'T WORK if -p option is not used!! (Fixed) + * Option -f caused program to segfault; fixed + * Fixed nasty bug about executing set[ug]id binaries: + When executing a program fails, don't left the program STOPPED. + * Make ltrace work with all setuid and setgid binaries when invoked as root + + -- Juan Cespedes <cespedes@debian.org> Sat, 11 Apr 1998 22:50:38 +0200 + +ltrace (0.2.8) frozen unstable; urgency=low + + * Fix important bug regarding -p: disable all breakpoints on exit (Bug#20616) + * Compile cleanly on libc5 + * Added `-t' option (Bug#20615) + + -- Juan Cespedes <cespedes@debian.org> Sat, 4 Apr 1998 08:34:03 +0200 + +ltrace (0.2.7) unstable; urgency=low + + * Some minor fixes + + -- Juan Cespedes <cespedes@debian.org> Sun, 15 Mar 1998 14:01:40 +0100 + +ltrace (0.2.6) unstable; urgency=low + + * Option `-f' now works (but it fails to attach some processes...) + * Output is now more similar to strace's + + -- Juan Cespedes <cespedes@debian.org> Sat, 14 Mar 1998 20:50:16 +0100 + +ltrace (0.2.5) unstable; urgency=low + + * After a successful execve(), library calls are now logged + * Enhanced displaying of non-printable chars + * Added some functions to /etc/ltrace.conf + + -- Juan Cespedes <cespedes@debian.org> Fri, 13 Mar 1998 19:16:47 +0100 + +ltrace (0.2.4) unstable; urgency=low + + * Option `-p' now works (but programs fail when ltrace is interrupted) + + -- Juan Cespedes <cespedes@debian.org> Fri, 13 Mar 1998 00:29:10 +0100 + +ltrace (0.2.3) unstable; urgency=low + + * Don't display `...' in strings when limit of bytes is reached + * Added some functions to /etc/ltrace.conf + + -- Juan Cespedes <cespedes@debian.org> Wed, 11 Mar 1998 23:33:14 +0100 + +ltrace (0.2.2) unstable; urgency=low + + * After a successful execve(), syscalls are now logged correctly + + -- Juan Cespedes <cespedes@debian.org> Wed, 11 Mar 1998 00:02:35 +0100 + +ltrace (0.2.1) unstable; urgency=low + + * Added -u option (run command as other username) + * Updated manual page a bit + + -- Juan Cespedes <cespedes@debian.org> Tue, 10 Mar 1998 00:08:38 +0100 + +ltrace (0.2.0) unstable; urgency=low + + * First `unstable' release + * Complete re-structured all the code to be able to add support for + different architectures (but only i386 arch is supported in this + version) + * Log also return values + * Log arguments (and return values) for syscalls + * Added preliminary support for various simultaneous processes + * getopt-like options + * New option: -a (alignment column) + * New option: -L (don't display library calls) + * New option: -s (maximum # of chars in strings) + * Now it reads config files with function names and parameter types + * Programs using clone() should work ok now + * debian/rules: gzipped only big files in /usr/doc/ltrace + * New Standards-Version: 2.4.0.0 + * beginning to work on sparc port (not yet done) + + -- Juan Cespedes <cespedes@debian.org> Sun, 8 Mar 1998 22:27:30 +0100 + +ltrace (0.1.7) experimental; urgency=low + + * Internal release. + * New Standards-Version (2.3.0.1) + * Clean up structures a bit + * Trying to log return values... + + -- Juan Cespedes <cespedes@debian.org> Sun, 26 Oct 1997 19:53:20 +0100 + +ltrace (0.1.6) experimental; urgency=low + + * New maintainer address + * New Standards-Version + + -- Juan Cespedes <cespedes@debian.org> Thu, 11 Sep 1997 23:22:32 +0200 + +ltrace (0.1.5) experimental; urgency=low + + * `command' is now searched in the PATH + + -- Juan Cespedes <cespedes@etsit.upm.es> Wed, 27 Aug 1997 22:27:33 +0200 + +ltrace (0.1.4) experimental; urgency=low + + * Updated execute_process() + * No longer uses signals to wait for children. Should be a little faster. + * Now every function uses output.c:send_*() instead of `FILE * output' + + -- Juan Cespedes <cespedes@etsit.upm.es> Mon, 25 Aug 1997 16:08:36 +0200 + +ltrace (0.1.3) experimental; urgency=low + + * Added options `-i', `-S' + * Added syscall names + * Added signal names + * Added `output.c', `signal.c' + + -- Juan Cespedes <cespedes@etsit.upm.es> Sun, 24 Aug 1997 01:45:49 +0200 + +ltrace (0.1.2) experimental; urgency=low + + * Updated ``TODO'' + * Added process.c:execute_process + * Added i386.c:type_of_stop + * Hopefully, system dependent stuff is now only in i386.[ch] and process.[ch] + * `-d' can now be used many times: many levels of debug + * removed breakpoint for children detecting fork()s. + Now, *every* program should work ok + * struct process now also has a field for the process filename + * Added "syscall.c" with a list of system call names in Linux/i386 + + -- Juan Cespedes <cespedes@etsit.upm.es> Sat, 23 Aug 1997 15:00:23 +0200 + +ltrace (0.1.1) experimental; urgency=low + + * Added ``TODO'' + * Added symbols.c:disable_all_breakpoints + * Added ``process.[ch]'': init_sighandler, pid2proc + * Removed ``trace.c'' + * Added rudimentary support for multiple processes + * Now tracing syscalls (fork() needs a special treatment (TODO)) + * Added process.c:detach_process + * Added i386.c:trace_me,untrace_pid + + -- Juan Cespedes <cespedes@etsit.upm.es> Sat, 23 Aug 1997 02:09:14 +0200 + +ltrace (0.1.0) experimental; urgency=low + + * Some clean-ups + * Added simple manual page + + -- Juan Cespedes <cespedes@etsit.upm.es> Thu, 21 Aug 1997 17:01:36 +0200 + +ltrace (0.0.1997.08.14) experimental; urgency=low + + * Still re-structuring code... new file: symbols.c + + -- Juan Cespedes <cespedes@etsit.upm.es> Thu, 14 Aug 1997 22:22:43 +0200 + +ltrace (0.0.1997.08.09) experimental; urgency=low + + * Added Debian files + * Re-structured most of the code; new files: elf.c, i386.c, trace.c + + -- Juan Cespedes <cespedes@etsit.upm.es> Sat, 9 Aug 1997 20:55:24 +0200 diff --git a/debian/compat b/debian/compat new file mode 100644 index 0000000..7ed6ff8 --- /dev/null +++ b/debian/compat @@ -0,0 +1 @@ +5 diff --git a/debian/control.in b/debian/control.in new file mode 100644 index 0000000..aff5316 --- /dev/null +++ b/debian/control.in @@ -0,0 +1,23 @@ +Source: ltrace +Section: utils +Priority: optional +Maintainer: Juan Cespedes <cespedes@debian.org> +Standards-Version: 3.8.2 +Build-Depends: @cdbs@, binutils-dev, libelfg0-dev + +Package: ltrace +Architecture: i386 arm armeb armel m68k s390 powerpc sparc alpha amd64 ia64 ppc64 +Depends: ${shlibs:Depends}, ${misc:Depends} +Description: Tracks runtime library calls in dynamically linked programs + ltrace is a debugging program which runs a specified command until it + exits. While the command is executing, ltrace intercepts and records + the dynamic library calls which are called by + the executed process and the signals received by that process. + It can also intercept and print the system calls executed by the program. + . + The program to be traced need not be recompiled for this, so you can + use it on binaries for which you don't have the source handy. + . + You should install ltrace if you need a sysadmin tool for tracking the + execution of processes. + diff --git a/debian/copyright b/debian/copyright new file mode 100644 index 0000000..facd682 --- /dev/null +++ b/debian/copyright @@ -0,0 +1,39 @@ +This is the Debian GNU/Linux's prepackaged version of the +Dynamic Library Tracer ``ltrace''. + +It was downloaded from http://www.ltrace.org/ + + +Copyrights +---------- +Copyright (C) 1997-2009 Juan Cespedes <cespedes@debian.org> + +ARMLinux port: Copyright (C) 1998 Pat Beirne <pbeirne@home.com> +m68k port: Copyright (C) 1998 Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de> +Misc fixes: Copyright (C) 1999 Morten Eriksen <mortene@sim.no> +s390 port: Copyright (C) 2001 IBM Poughkeepsie, IBM Cororation <slate@us.ibm.com> +ELF hacking: Copyright (C) 1999 Silvio Cesare <silvio@big.net.au> +PowerPC port: Copyright (C) 2001-2002 Anton Blanchard <anton@samba.org> +SPARC port: Copyright (C) 1999 Jakub Jelinek <jakub@redhat.com> + + +C++ demangle: Copyright 1989-1997 Free Software Foundation, Inc. + + +License +------- +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +This program is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +A copy of the GNU General Public License is available as +`/usr/share/common-licenses/GPL' in the Debian GNU/Linux distribution +or on the World Wide Web at `http://www.gnu.org/copyleft/gpl.html'. +You can also obtain it by writing to the Free Software Foundation, +Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA diff --git a/debian/rules b/debian/rules new file mode 100755 index 0000000..4105eb7 --- /dev/null +++ b/debian/rules @@ -0,0 +1,7 @@ +#!/usr/bin/make -f + +include /usr/share/cdbs/1/rules/debhelper.mk +include /usr/share/cdbs/1/class/autotools.mk + +install/ltrace:: + rm -f debian/ltrace/usr/share/doc/ltrace/* @@ -0,0 +1,112 @@ +#include <stdio.h> +#include <stdarg.h> + +#include "common.h" + +void +debug_(int level, const char *file, int line, const char *fmt, ...) { + char buf[1024]; + va_list args; + + if (!(options.debug & level)) { + return; + } + va_start(args, fmt); + vsnprintf(buf, 1024, fmt, args); + va_end(args); + + output_line(NULL, "DEBUG: %s:%d: %s", file, line, buf); +} + +/* + * The following section provides a way to print things, like hex dumps, + * with out using buffered output. This was written by Steve Munroe of IBM. + */ + +#include <stdio.h> +#include <errno.h> +#include <unistd.h> +#include <stdlib.h> +#include <sys/ptrace.h> + +static int +xwritehexl(long i) { + int rc = 0; + char text[17]; + int j; + unsigned long temp = (unsigned long)i; + + for (j = 15; j >= 0; j--) { + char c; + c = (char)((temp & 0x0f) + '0'); + if (c > '9') { + c = (char)(c + ('a' - '9' - 1)); + } + text[j] = c; + temp = temp >> 4; + } + + rc = write(1, text, 16); + return rc; +} + +static int +xwritec(char c) { + char temp = c; + char *text = &temp; + int rc = 0; + rc = write(1, text, 1); + return rc; +} + +static int +xwritecr(void) { + return xwritec('\n'); +} + +static int +xwritedump(void *ptr, long addr, int len) { + int rc = 0; + long *tprt = (long *)ptr; + int i; + + for (i = 0; i < len; i += 8) { + xwritehexl(addr); + xwritec('-'); + xwritec('>'); + xwritehexl(*tprt++); + xwritecr(); + addr += sizeof(long); + } + + return rc; +} + +int +xinfdump(long pid, void *ptr, int len) { + int rc; + int i; + long wrdcnt; + long *infwords; + long addr; + + wrdcnt = len / sizeof(long) + 1; + infwords = malloc(wrdcnt * sizeof(long)); + if (!infwords) { + perror("ltrace: malloc"); + exit(1); + } + addr = (long)ptr; + + addr = ((addr + sizeof(long) - 1) / sizeof(long)) * sizeof(long); + + for (i = 0; i < wrdcnt; ++i) { + infwords[i] = ptrace(PTRACE_PEEKTEXT, pid, addr); + addr += sizeof(long); + } + + rc = xwritedump(infwords, (long)ptr, len); + + free(infwords); + return rc; +} @@ -0,0 +1,17 @@ +#include <features.h> + +/* debug levels: + */ +enum { + DEBUG_EVENT = 010, + DEBUG_PROCESS = 020, + DEBUG_FUNCTION = 040 +}; + +void debug_(int level, const char *file, int line, + const char *fmt, ...) __attribute__((format(printf,4,5))); + +int xinfdump(long, void *, int); + +# define debug(level, expr...) debug_(level, __FILE__, __LINE__, expr) + @@ -0,0 +1,18 @@ + +#ifndef DEFAULT_ALIGN +#define DEFAULT_ALIGN 50 /* default alignment column for results */ +#endif /* (-a switch) */ + +#ifndef MAX_ARGS +#define MAX_ARGS 32 /* maximum number of args for a function */ +#endif + +#ifndef DEFAULT_STRLEN +#define DEFAULT_STRLEN 32 /* default maximum # of bytes printed in */ +#endif /* strings (-s switch) */ + +#ifndef DEFAULT_ARRAYLEN +#define DEFAULT_ARRAYLEN 4 /* default maximum # array elements */ +#endif /* (-A switch) */ + +#define MAX_LIBRARIES 30 diff --git a/demangle.c b/demangle.c new file mode 100644 index 0000000..5825e28 --- /dev/null +++ b/demangle.c @@ -0,0 +1,44 @@ +#include "config.h" + +#include <string.h> +#include <stdlib.h> +#include <stdio.h> + +#include "common.h" + +#ifdef USE_DEMANGLE + +/*****************************************************************************/ + +static Dict *d = NULL; + +const char * +my_demangle(const char *function_name) { + const char *tmp, *fn_copy; +#if !defined HAVE_LIBIBERTY && defined HAVE_LIBSUPC__ + extern char *__cxa_demangle(const char *, char *, size_t *, int *); + int status = 0; +#endif + + debug(DEBUG_FUNCTION, "my_demangle(name=%s)", function_name); + + if (!d) + d = dict_init(dict_key2hash_string, dict_key_cmp_string); + + tmp = dict_find_entry(d, (void *)function_name); + if (!tmp) { + fn_copy = strdup(function_name); +#ifdef HAVE_LIBIBERTY + tmp = cplus_demangle(function_name, DMGL_ANSI | DMGL_PARAMS); +#elif defined HAVE_LIBSUPC__ + tmp = __cxa_demangle(function_name, NULL, NULL, &status); +#endif + if (!tmp) + tmp = fn_copy; + if (tmp) + dict_enter(d, (void *)fn_copy, (void *)tmp); + } + return tmp; +} + +#endif diff --git a/demangle.h b/demangle.h new file mode 100644 index 0000000..beac791 --- /dev/null +++ b/demangle.h @@ -0,0 +1,12 @@ +#include "config.h" + +extern char *cplus_demangle(const char *mangled, int options); + +const char *my_demangle(const char *function_name); + +/* Options passed to cplus_demangle (in 2nd parameter). */ + +#define DMGL_NO_OPTS 0 /* For readability... */ +#define DMGL_PARAMS (1 << 0) /* Include function args */ +#define DMGL_ANSI (1 << 1) /* Include const, volatile, etc */ +#define DMGL_JAVA (1 << 2) /* Demangle as Java rather than C++. */ @@ -0,0 +1,215 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <assert.h> + +#include "common.h" + +/* + Dictionary based on code by Morten Eriksen <mortene@sim.no>. +*/ + +struct dict_entry { + unsigned int hash; + void *key; + void *value; + struct dict_entry *next; +}; + +/* #define DICTTABLESIZE 97 */ +#define DICTTABLESIZE 997 /* Semi-randomly selected prime number. */ +/* #define DICTTABLESIZE 9973 */ +/* #define DICTTABLESIZE 99991 */ +/* #define DICTTABLESIZE 999983 */ + +struct dict { + struct dict_entry *buckets[DICTTABLESIZE]; + unsigned int (*key2hash) (void *); + int (*key_cmp) (void *, void *); +}; + +Dict * +dict_init(unsigned int (*key2hash) (void *), + int (*key_cmp) (void *, void *)) { + Dict *d; + int i; + + debug(DEBUG_FUNCTION, "dict_init()"); + + d = malloc(sizeof(Dict)); + if (!d) { + perror("malloc()"); + exit(1); + } + for (i = 0; i < DICTTABLESIZE; i++) { /* better use memset()? */ + d->buckets[i] = NULL; + } + d->key2hash = key2hash; + d->key_cmp = key_cmp; + return d; +} + +void +dict_clear(Dict *d) { + int i; + struct dict_entry *entry, *nextentry; + + debug(DEBUG_FUNCTION, "dict_clear()"); + assert(d); + for (i = 0; i < DICTTABLESIZE; i++) { + for (entry = d->buckets[i]; entry != NULL; entry = nextentry) { + nextentry = entry->next; + free(entry); + } + d->buckets[i] = NULL; + } + free(d); +} + +int +dict_enter(Dict *d, void *key, void *value) { + struct dict_entry *entry, *newentry; + unsigned int hash; + unsigned int bucketpos; + + debug(DEBUG_FUNCTION, "dict_enter()"); + + hash = d->key2hash(key); + bucketpos = hash % DICTTABLESIZE; + + assert(d); + newentry = malloc(sizeof(struct dict_entry)); + if (!newentry) { + perror("malloc"); + exit(1); + } + + newentry->hash = hash; + newentry->key = key; + newentry->value = value; + newentry->next = NULL; + + entry = d->buckets[bucketpos]; + while (entry && entry->next) + entry = entry->next; + + if (entry) + entry->next = newentry; + else + d->buckets[bucketpos] = newentry; + + debug(3, "new dict entry at %p[%d]: (%p,%p)", d, bucketpos, key, value); + return 0; +} + +void * +dict_find_entry(Dict *d, void *key) { + unsigned int hash; + unsigned int bucketpos; + struct dict_entry *entry; + + debug(DEBUG_FUNCTION, "dict_find_entry()"); + + hash = d->key2hash(key); + bucketpos = hash % DICTTABLESIZE; + + assert(d); + for (entry = d->buckets[bucketpos]; entry; entry = entry->next) { + if (hash != entry->hash) { + continue; + } + if (!d->key_cmp(key, entry->key)) { + break; + } + } + return entry ? entry->value : NULL; +} + +void +dict_apply_to_all(Dict *d, + void (*func) (void *key, void *value, void *data), void *data) { + int i; + + debug(DEBUG_FUNCTION, "dict_apply_to_all()"); + + if (!d) { + return; + } + for (i = 0; i < DICTTABLESIZE; i++) { + struct dict_entry *entry = d->buckets[i]; + while (entry) { + func(entry->key, entry->value, data); + entry = entry->next; + } + } +} + +/*****************************************************************************/ + +unsigned int +dict_key2hash_string(void *key) { + const char *s = (const char *)key; + unsigned int total = 0, shift = 0; + + assert(key); + while (*s) { + total = total ^ ((*s) << shift); + shift += 5; + if (shift > 24) + shift -= 24; + s++; + } + return total; +} + +int +dict_key_cmp_string(void *key1, void *key2) { + assert(key1); + assert(key2); + return strcmp((const char *)key1, (const char *)key2); +} + +unsigned int +dict_key2hash_int(void *key) { + return (unsigned long)key; +} + +int +dict_key_cmp_int(void *key1, void *key2) { + return key1 - key2; +} + +Dict * +dict_clone(Dict *old, void * (*key_clone)(void*), void * (*value_clone)(void*)) { + Dict *d; + int i; + + debug(DEBUG_FUNCTION, "dict_clone()"); + + d = malloc(sizeof(Dict)); + if (!d) { + perror("malloc()"); + exit(1); + } + memcpy(d, old, sizeof(Dict)); + for (i = 0; i < DICTTABLESIZE; i++) { /* better use memset()? */ + struct dict_entry *de_old; + struct dict_entry **de_new; + + de_old = old->buckets[i]; + de_new = &d->buckets[i]; + while (de_old) { + *de_new = malloc(sizeof(struct dict_entry)); + if (!*de_new) { + perror("malloc()"); + exit(1); + } + memcpy(*de_new, de_old, sizeof(struct dict_entry)); + (*de_new)->key = key_clone(de_old->key); + (*de_new)->value = value_clone(de_old->value); + de_new = &(*de_new)->next; + de_old = de_old->next; + } + } + return d; +} @@ -0,0 +1,20 @@ +/* + Dictionary based on code by Morten Eriksen <mortene@sim.no>. +*/ + +typedef struct dict Dict; + +extern Dict *dict_init(unsigned int (*key2hash) (void *), + int (*key_cmp) (void *, void *)); +extern void dict_clear(Dict *d); +extern int dict_enter(Dict *d, void *key, void *value); +extern void *dict_find_entry(Dict *d, void *key); +extern void dict_apply_to_all(Dict *d, + void (*func) (void *key, void *value, void *data), + void *data); + +extern unsigned int dict_key2hash_string(void *key); +extern int dict_key_cmp_string(void *key1, void *key2); +extern unsigned int dict_key2hash_int(void *key); +extern int dict_key_cmp_int(void *key1, void *key2); +extern Dict * dict_clone(Dict *old, void * (*key_clone)(void*), void * (*value_clone)(void*)); diff --git a/display_args.c b/display_args.c new file mode 100644 index 0000000..993a808 --- /dev/null +++ b/display_args.c @@ -0,0 +1,460 @@ +#include <ctype.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <limits.h> + +#include "common.h" + +static int display_char(int what); +static int display_string(enum tof type, Process *proc, + void* addr, size_t maxlen); +static int display_value(enum tof type, Process *proc, + long value, arg_type_info *info, + void *st, arg_type_info* st_info); +static int display_unknown(enum tof type, Process *proc, long value); +static int display_format(enum tof type, Process *proc, int arg_num); + +static int string_maxlength = INT_MAX; +static int array_maxlength = INT_MAX; + +static long +get_length(enum tof type, Process *proc, int len_spec, + void *st, arg_type_info* st_info) { + long len; + arg_type_info info; + + if (len_spec > 0) + return len_spec; + if (type == LT_TOF_STRUCT) { + umovelong (proc, st + st_info->u.struct_info.offset[-len_spec-1], + &len, st_info->u.struct_info.fields[-len_spec-1]); + return len; + } + + info.type = ARGTYPE_INT; + return gimme_arg(type, proc, -len_spec-1, &info); +} + +static int +display_ptrto(enum tof type, Process *proc, long item, + arg_type_info * info, + void *st, arg_type_info* st_info) { + arg_type_info temp; + temp.type = ARGTYPE_POINTER; + temp.u.ptr_info.info = info; + return display_value(type, proc, item, &temp, st, st_info); +} + +/* + * addr - A pointer to the first element of the array + * + * The function name is used to indicate that we're not actually + * looking at an 'array', which is a contiguous region of memory + * containing a sequence of elements of some type; instead, we have a + * pointer to that region of memory. + */ +static int +display_arrayptr(enum tof type, Process *proc, + void *addr, arg_type_info * info, + void *st, arg_type_info* st_info) { + int len = 0; + int i; + int array_len; + + if (addr == NULL) + return fprintf(options.output, "NULL"); + + array_len = get_length(type, proc, info->u.array_info.len_spec, + st, st_info); + len += fprintf(options.output, "[ "); + for (i = 0; i < options.arraylen && i < array_maxlength && i < array_len; i++) { + arg_type_info *elt_type = info->u.array_info.elt_type; + size_t elt_size = info->u.array_info.elt_size; + if (i != 0) + len += fprintf(options.output, ", "); + if (options.debug) + len += fprintf(options.output, "%p=", addr); + len += + display_ptrto(type, proc, (long) addr, elt_type, st, st_info); + addr += elt_size; + } + if (i < array_len) + len += fprintf(options.output, "..."); + len += fprintf(options.output, " ]"); + return len; +} + +/* addr - A pointer to the beginning of the memory region occupied by + * the struct (aka a pointer to the struct) + */ +static int +display_structptr(enum tof type, Process *proc, + void *addr, arg_type_info * info) { + int i; + arg_type_info *field; + int len = 0; + + if (addr == NULL) + return fprintf(options.output, "NULL"); + + len += fprintf(options.output, "{ "); + for (i = 0; (field = info->u.struct_info.fields[i]) != NULL; i++) { + if (i != 0) + len += fprintf(options.output, ", "); + if (options.debug) + len += + fprintf(options.output, "%p=", + addr + info->u.struct_info.offset[i]); + len += + display_ptrto(LT_TOF_STRUCT, proc, + (long) addr + info->u.struct_info.offset[i], + field, addr, info); + } + len += fprintf(options.output, " }"); + + return len; +} + +static int +display_pointer(enum tof type, Process *proc, long value, + arg_type_info * info, + void *st, arg_type_info* st_info) { + long pointed_to; + arg_type_info *inner = info->u.ptr_info.info; + + if (inner->type == ARGTYPE_ARRAY) { + return display_arrayptr(type, proc, (void*) value, inner, + st, st_info); + } else if (inner->type == ARGTYPE_STRUCT) { + return display_structptr(type, proc, (void *) value, inner); + } else { + if (value == 0) + return fprintf(options.output, "NULL"); + else if (umovelong (proc, (void *) value, &pointed_to, + info->u.ptr_info.info) < 0) + return fprintf(options.output, "?"); + else + return display_value(type, proc, pointed_to, inner, + st, st_info); + } +} + +static int +display_enum(enum tof type, Process *proc, + arg_type_info* info, long value) { + int ii; + for (ii = 0; ii < info->u.enum_info.entries; ++ii) { + if (info->u.enum_info.values[ii] == value) + return fprintf(options.output, "%s", info->u.enum_info.keys[ii]); + } + + return display_unknown(type, proc, value); +} + +/* Args: + type - syscall or shared library function or memory + proc - information about the traced process + value - the value to display + info - the description of the type to display + st - if the current value is a struct member, the address of the struct + st_info - type of the above struct + + Those last two parameters are used for structs containing arrays or + strings whose length is given by another structure element. +*/ +int +display_value(enum tof type, Process *proc, + long value, arg_type_info *info, + void *st, arg_type_info* st_info) { + int tmp; + + switch (info->type) { + case ARGTYPE_VOID: + return 0; + case ARGTYPE_INT: + return fprintf(options.output, "%d", (int) value); + case ARGTYPE_UINT: + return fprintf(options.output, "%u", (unsigned) value); + case ARGTYPE_LONG: + if (proc->mask_32bit) + return fprintf(options.output, "%d", (int) value); + else + return fprintf(options.output, "%ld", value); + case ARGTYPE_ULONG: + if (proc->mask_32bit) + return fprintf(options.output, "%u", (unsigned) value); + else + return fprintf(options.output, "%lu", (unsigned long) value); + case ARGTYPE_OCTAL: + return fprintf(options.output, "0%o", (unsigned) value); + case ARGTYPE_CHAR: + tmp = fprintf(options.output, "'"); + tmp += display_char(value == -1 ? value : (char) value); + tmp += fprintf(options.output, "'"); + return tmp; + case ARGTYPE_SHORT: + return fprintf(options.output, "%hd", (short) value); + case ARGTYPE_USHORT: + return fprintf(options.output, "%hu", (unsigned short) value); + case ARGTYPE_FLOAT: { + union { long l; float f; double d; } cvt; + cvt.l = value; + return fprintf(options.output, "%f", cvt.f); + } + case ARGTYPE_DOUBLE: { + union { long l; float f; double d; } cvt; + cvt.l = value; + return fprintf(options.output, "%lf", cvt.d); + } + case ARGTYPE_ADDR: + if (!value) + return fprintf(options.output, "NULL"); + else + return fprintf(options.output, "0x%08lx", value); + case ARGTYPE_FORMAT: + fprintf(stderr, "Should never encounter a format anywhere but at the top level (for now?)\n"); + exit(1); + case ARGTYPE_STRING: + return display_string(type, proc, (void*) value, + string_maxlength); + case ARGTYPE_STRING_N: + return display_string(type, proc, (void*) value, + get_length(type, proc, + info->u.string_n_info.size_spec, st, st_info)); + case ARGTYPE_ARRAY: + return fprintf(options.output, "<array without address>"); + case ARGTYPE_ENUM: + return display_enum(type, proc, info, value); + case ARGTYPE_STRUCT: + return fprintf(options.output, "<struct without address>"); + case ARGTYPE_POINTER: + return display_pointer(type, proc, value, info, + st, st_info); + case ARGTYPE_UNKNOWN: + default: + return display_unknown(type, proc, value); + } +} + +int +display_arg(enum tof type, Process *proc, int arg_num, arg_type_info * info) { + long arg; + + if (info->type == ARGTYPE_VOID) { + return 0; + } else if (info->type == ARGTYPE_FORMAT) { + return display_format(type, proc, arg_num); + } else { + arg = gimme_arg(type, proc, arg_num, info); + return display_value(type, proc, arg, info, NULL, NULL); + } +} + +static int +display_char(int what) { + switch (what) { + case -1: + return fprintf(options.output, "EOF"); + case '\r': + return fprintf(options.output, "\\r"); + case '\n': + return fprintf(options.output, "\\n"); + case '\t': + return fprintf(options.output, "\\t"); + case '\b': + return fprintf(options.output, "\\b"); + case '\\': + return fprintf(options.output, "\\\\"); + default: + if (isprint(what)) { + return fprintf(options.output, "%c", what); + } else { + return fprintf(options.output, "\\%03o", (unsigned char)what); + } + } +} + +#define MIN(a,b) (((a)<(b)) ? (a) : (b)) + +static int +display_string(enum tof type, Process *proc, void *addr, + size_t maxlength) { + unsigned char *str1; + int i; + int len = 0; + + if (!addr) { + return fprintf(options.output, "NULL"); + } + + str1 = malloc(MIN(options.strlen, maxlength) + 3); + if (!str1) { + return fprintf(options.output, "???"); + } + umovestr(proc, addr, MIN(options.strlen, maxlength) + 1, str1); + len = fprintf(options.output, "\""); + for (i = 0; i < MIN(options.strlen, maxlength); i++) { + if (str1[i]) { + len += display_char(str1[i]); + } else { + break; + } + } + len += fprintf(options.output, "\""); + if (str1[i] && (options.strlen <= maxlength)) { + len += fprintf(options.output, "..."); + } + free(str1); + return len; +} + +static int +display_unknown(enum tof type, Process *proc, long value) { + if (proc->mask_32bit) { + if ((int)value < 1000000 && (int)value > -1000000) + return fprintf(options.output, "%d", (int)value); + else + return fprintf(options.output, "%p", (void *)value); + } else if (value < 1000000 && value > -1000000) { + return fprintf(options.output, "%ld", value); + } else { + return fprintf(options.output, "%p", (void *)value); + } +} + +static int +display_format(enum tof type, Process *proc, int arg_num) { + void *addr; + unsigned char *str1; + int i; + int len = 0; + arg_type_info info; + + info.type = ARGTYPE_POINTER; + addr = (void *)gimme_arg(type, proc, arg_num, &info); + if (!addr) { + return fprintf(options.output, "NULL"); + } + + str1 = malloc(MIN(options.strlen, string_maxlength) + 3); + if (!str1) { + return fprintf(options.output, "???"); + } + umovestr(proc, addr, MIN(options.strlen, string_maxlength) + 1, str1); + len = fprintf(options.output, "\""); + for (i = 0; len < MIN(options.strlen, string_maxlength) + 1; i++) { + if (str1[i]) { + len += display_char(str1[i]); + } else { + break; + } + } + len += fprintf(options.output, "\""); + if (str1[i] && (options.strlen <= string_maxlength)) { + len += fprintf(options.output, "..."); + } + for (i = 0; str1[i]; i++) { + if (str1[i] == '%') { + int is_long = 0; + while (1) { + unsigned char c = str1[++i]; + if (c == '%') { + break; + } else if (!c) { + break; + } else if (strchr("lzZtj", c)) { + is_long++; + if (c == 'j') + is_long++; + if (is_long > 1 + && (sizeof(long) < sizeof(long long) + || proc->mask_32bit)) { + len += fprintf(options.output, ", ..."); + str1[i + 1] = '\0'; + break; + } + } else if (c == 'd' || c == 'i') { + info.type = ARGTYPE_LONG; + if (!is_long || proc->mask_32bit) + len += + fprintf(options.output, ", %d", + (int)gimme_arg(type, proc, ++arg_num, &info)); + else + len += + fprintf(options.output, ", %ld", + gimme_arg(type, proc, ++arg_num, &info)); + break; + } else if (c == 'u') { + info.type = ARGTYPE_LONG; + if (!is_long || proc->mask_32bit) + len += + fprintf(options.output, ", %u", + (int)gimme_arg(type, proc, ++arg_num, &info)); + else + len += + fprintf(options.output, ", %lu", + gimme_arg(type, proc, ++arg_num, &info)); + break; + } else if (c == 'o') { + info.type = ARGTYPE_LONG; + if (!is_long || proc->mask_32bit) + len += + fprintf(options.output, ", 0%o", + (int)gimme_arg(type, proc, ++arg_num, &info)); + else + len += + fprintf(options.output, ", 0%lo", + gimme_arg(type, proc, ++arg_num, &info)); + break; + } else if (c == 'x' || c == 'X') { + info.type = ARGTYPE_LONG; + if (!is_long || proc->mask_32bit) + len += + fprintf(options.output, ", %#x", + (int)gimme_arg(type, proc, ++arg_num, &info)); + else + len += + fprintf(options.output, ", %#lx", + gimme_arg(type, proc, ++arg_num, &info)); + break; + } else if (strchr("eEfFgGaACS", c) + || (is_long + && (c == 'c' || c == 's'))) { + len += fprintf(options.output, ", ..."); + str1[i + 1] = '\0'; + break; + } else if (c == 'c') { + info.type = ARGTYPE_LONG; + len += fprintf(options.output, ", '"); + len += + display_char((int) + gimme_arg(type, proc, ++arg_num, &info)); + len += fprintf(options.output, "'"); + break; + } else if (c == 's') { + info.type = ARGTYPE_POINTER; + len += fprintf(options.output, ", "); + len += + display_string(type, proc, + (void *)gimme_arg(type, proc, ++arg_num, &info), + string_maxlength); + break; + } else if (c == 'p' || c == 'n') { + info.type = ARGTYPE_POINTER; + len += + fprintf(options.output, ", %p", + (void *)gimme_arg(type, proc, ++arg_num, &info)); + break; + } else if (c == '*') { + info.type = ARGTYPE_LONG; + len += + fprintf(options.output, ", %d", + (int)gimme_arg(type, proc, ++arg_num, &info)); + } + } + } + } + free(str1); + return len; +} @@ -0,0 +1,619 @@ +# include "config.h" + +#include <endian.h> +#include <errno.h> +#include <error.h> +#include <fcntl.h> +#include <gelf.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "common.h" + +static void do_init_elf(struct ltelf *lte, const char *filename); +static void do_close_elf(struct ltelf *lte); +static void add_library_symbol(GElf_Addr addr, const char *name, + struct library_symbol **library_symbolspp, + enum toplt type_of_plt, int is_weak); +static int in_load_libraries(const char *name, struct ltelf *lte); +static GElf_Addr opd2addr(struct ltelf *ltc, GElf_Addr addr); + +#ifdef PLT_REINITALISATION_BP +extern char *PLTs_initialized_by_here; +#endif + +static void +do_init_elf(struct ltelf *lte, const char *filename) { + int i; + GElf_Addr relplt_addr = 0; + size_t relplt_size = 0; + + debug(DEBUG_FUNCTION, "do_init_elf(filename=%s)", filename); + debug(1, "Reading ELF from %s...", filename); + + memset(lte, 0, sizeof(*lte)); + lte->fd = open(filename, O_RDONLY); + if (lte->fd == -1) + error(EXIT_FAILURE, errno, "Can't open \"%s\"", filename); + +#ifdef HAVE_ELF_C_READ_MMAP + lte->elf = elf_begin(lte->fd, ELF_C_READ_MMAP, NULL); +#else + lte->elf = elf_begin(lte->fd, ELF_C_READ, NULL); +#endif + + if (lte->elf == NULL || elf_kind(lte->elf) != ELF_K_ELF) + error(EXIT_FAILURE, 0, "Can't open ELF file \"%s\"", filename); + + if (gelf_getehdr(lte->elf, <e->ehdr) == NULL) + error(EXIT_FAILURE, 0, "Can't read ELF header of \"%s\"", + filename); + + if (lte->ehdr.e_type != ET_EXEC && lte->ehdr.e_type != ET_DYN) + error(EXIT_FAILURE, 0, + "\"%s\" is not an ELF executable nor shared library", + filename); + + if ((lte->ehdr.e_ident[EI_CLASS] != LT_ELFCLASS + || lte->ehdr.e_machine != LT_ELF_MACHINE) +#ifdef LT_ELF_MACHINE2 + && (lte->ehdr.e_ident[EI_CLASS] != LT_ELFCLASS2 + || lte->ehdr.e_machine != LT_ELF_MACHINE2) +#endif +#ifdef LT_ELF_MACHINE3 + && (lte->ehdr.e_ident[EI_CLASS] != LT_ELFCLASS3 + || lte->ehdr.e_machine != LT_ELF_MACHINE3) +#endif + ) + error(EXIT_FAILURE, 0, + "\"%s\" is ELF from incompatible architecture", filename); + + for (i = 1; i < lte->ehdr.e_shnum; ++i) { + Elf_Scn *scn; + GElf_Shdr shdr; + const char *name; + + scn = elf_getscn(lte->elf, i); + if (scn == NULL || gelf_getshdr(scn, &shdr) == NULL) + error(EXIT_FAILURE, 0, + "Couldn't get section header from \"%s\"", + filename); + + name = elf_strptr(lte->elf, lte->ehdr.e_shstrndx, shdr.sh_name); + if (name == NULL) + error(EXIT_FAILURE, 0, + "Couldn't get section header from \"%s\"", + filename); + + if (shdr.sh_type == SHT_SYMTAB) { + Elf_Data *data; + + lte->symtab = elf_getdata(scn, NULL); + lte->symtab_count = shdr.sh_size / shdr.sh_entsize; + if ((lte->symtab == NULL + || elf_getdata(scn, lte->symtab) != NULL) + && opt_x != NULL) + error(EXIT_FAILURE, 0, + "Couldn't get .symtab data from \"%s\"", + filename); + + scn = elf_getscn(lte->elf, shdr.sh_link); + if (scn == NULL || gelf_getshdr(scn, &shdr) == NULL) + error(EXIT_FAILURE, 0, + "Couldn't get section header from \"%s\"", + filename); + + data = elf_getdata(scn, NULL); + if (data == NULL || elf_getdata(scn, data) != NULL + || shdr.sh_size != data->d_size || data->d_off) + error(EXIT_FAILURE, 0, + "Couldn't get .strtab data from \"%s\"", + filename); + + lte->strtab = data->d_buf; + } else if (shdr.sh_type == SHT_DYNSYM) { + Elf_Data *data; + + lte->dynsym = elf_getdata(scn, NULL); + lte->dynsym_count = shdr.sh_size / shdr.sh_entsize; + if (lte->dynsym == NULL + || elf_getdata(scn, lte->dynsym) != NULL) + error(EXIT_FAILURE, 0, + "Couldn't get .dynsym data from \"%s\"", + filename); + + scn = elf_getscn(lte->elf, shdr.sh_link); + if (scn == NULL || gelf_getshdr(scn, &shdr) == NULL) + error(EXIT_FAILURE, 0, + "Couldn't get section header from \"%s\"", + filename); + + data = elf_getdata(scn, NULL); + if (data == NULL || elf_getdata(scn, data) != NULL + || shdr.sh_size != data->d_size || data->d_off) + error(EXIT_FAILURE, 0, + "Couldn't get .dynstr data from \"%s\"", + filename); + + lte->dynstr = data->d_buf; + } else if (shdr.sh_type == SHT_DYNAMIC) { + Elf_Data *data; + size_t j; + + data = elf_getdata(scn, NULL); + if (data == NULL || elf_getdata(scn, data) != NULL) + error(EXIT_FAILURE, 0, + "Couldn't get .dynamic data from \"%s\"", + filename); + + for (j = 0; j < shdr.sh_size / shdr.sh_entsize; ++j) { + GElf_Dyn dyn; + + if (gelf_getdyn(data, j, &dyn) == NULL) + error(EXIT_FAILURE, 0, + "Couldn't get .dynamic data from \"%s\"", + filename); +#ifdef __mips__ +/** + MIPS ABI Supplement: + + DT_PLTGOT This member holds the address of the .got section. + + DT_MIPS_SYMTABNO This member holds the number of entries in the + .dynsym section. + + DT_MIPS_LOCAL_GOTNO This member holds the number of local global + offset table entries. + + DT_MIPS_GOTSYM This member holds the index of the first dyamic + symbol table entry that corresponds to an entry in the gobal offset + table. + + */ + if(dyn.d_tag==DT_PLTGOT){ + lte->pltgot_addr=dyn.d_un.d_ptr; + } + if(dyn.d_tag==DT_MIPS_LOCAL_GOTNO){ + lte->mips_local_gotno=dyn.d_un.d_val; + } + if(dyn.d_tag==DT_MIPS_GOTSYM){ + lte->mips_gotsym=dyn.d_un.d_val; + } +#endif // __mips__ + if (dyn.d_tag == DT_JMPREL) + relplt_addr = dyn.d_un.d_ptr; + else if (dyn.d_tag == DT_PLTRELSZ) + relplt_size = dyn.d_un.d_val; + } + } else if (shdr.sh_type == SHT_HASH) { + Elf_Data *data; + size_t j; + + lte->hash_type = SHT_HASH; + + data = elf_getdata(scn, NULL); + if (data == NULL || elf_getdata(scn, data) != NULL + || data->d_off || data->d_size != shdr.sh_size) + error(EXIT_FAILURE, 0, + "Couldn't get .hash data from \"%s\"", + filename); + + if (shdr.sh_entsize == 4) { + /* Standard conforming ELF. */ + if (data->d_type != ELF_T_WORD) + error(EXIT_FAILURE, 0, + "Couldn't get .hash data from \"%s\"", + filename); + lte->hash = (Elf32_Word *) data->d_buf; + } else if (shdr.sh_entsize == 8) { + /* Alpha or s390x. */ + Elf32_Word *dst, *src; + size_t hash_count = data->d_size / 8; + + lte->hash = (Elf32_Word *) + malloc(hash_count * sizeof(Elf32_Word)); + if (lte->hash == NULL) + error(EXIT_FAILURE, 0, + "Couldn't convert .hash section from \"%s\"", + filename); + lte->lte_flags |= LTE_HASH_MALLOCED; + dst = lte->hash; + src = (Elf32_Word *) data->d_buf; + if ((data->d_type == ELF_T_WORD + && __BYTE_ORDER == __BIG_ENDIAN) + || (data->d_type == ELF_T_XWORD + && lte->ehdr.e_ident[EI_DATA] == + ELFDATA2MSB)) + ++src; + for (j = 0; j < hash_count; ++j, src += 2) + *dst++ = *src; + } else + error(EXIT_FAILURE, 0, + "Unknown .hash sh_entsize in \"%s\"", + filename); + } else if (shdr.sh_type == SHT_GNU_HASH + && lte->hash == NULL) { + Elf_Data *data; + + lte->hash_type = SHT_GNU_HASH; + + if (shdr.sh_entsize != 0 + && shdr.sh_entsize != 4) { + error(EXIT_FAILURE, 0, + ".gnu.hash sh_entsize in \"%s\" should be 4, but is %llu", + filename, shdr.sh_entsize); + } + + data = elf_getdata(scn, NULL); + if (data == NULL || elf_getdata(scn, data) != NULL + || data->d_off || data->d_size != shdr.sh_size) + error(EXIT_FAILURE, 0, + "Couldn't get .gnu.hash data from \"%s\"", + filename); + + lte->hash = (Elf32_Word *) data->d_buf; + } else if (shdr.sh_type == SHT_PROGBITS + || shdr.sh_type == SHT_NOBITS) { + if (strcmp(name, ".plt") == 0) { + lte->plt_addr = shdr.sh_addr; + lte->plt_size = shdr.sh_size; + if (shdr.sh_flags & SHF_EXECINSTR) { + lte->lte_flags |= LTE_PLT_EXECUTABLE; + } + } +#ifdef ARCH_SUPPORTS_OPD + else if (strcmp(name, ".opd") == 0) { + lte->opd_addr = (GElf_Addr *) (long) shdr.sh_addr; + lte->opd_size = shdr.sh_size; + lte->opd = elf_rawdata(scn, NULL); + } +#endif + } + } + + if (lte->dynsym == NULL || lte->dynstr == NULL) + error(EXIT_FAILURE, 0, + "Couldn't find .dynsym or .dynstr in \"%s\"", filename); + + if (!relplt_addr || !lte->plt_addr) { + debug(1, "%s has no PLT relocations", filename); + lte->relplt = NULL; + lte->relplt_count = 0; + } else { + for (i = 1; i < lte->ehdr.e_shnum; ++i) { + Elf_Scn *scn; + GElf_Shdr shdr; + + scn = elf_getscn(lte->elf, i); + if (scn == NULL || gelf_getshdr(scn, &shdr) == NULL) + error(EXIT_FAILURE, 0, + "Couldn't get section header from \"%s\"", + filename); + if (shdr.sh_addr == relplt_addr + && shdr.sh_size == relplt_size) { + lte->relplt = elf_getdata(scn, NULL); + lte->relplt_count = + shdr.sh_size / shdr.sh_entsize; + if (lte->relplt == NULL + || elf_getdata(scn, lte->relplt) != NULL) + error(EXIT_FAILURE, 0, + "Couldn't get .rel*.plt data from \"%s\"", + filename); + break; + } + } + + if (i == lte->ehdr.e_shnum) + error(EXIT_FAILURE, 0, + "Couldn't find .rel*.plt section in \"%s\"", + filename); + + debug(1, "%s %zd PLT relocations", filename, lte->relplt_count); + } +} + +static void +do_close_elf(struct ltelf *lte) { + debug(DEBUG_FUNCTION, "do_close_elf()"); + if (lte->lte_flags & LTE_HASH_MALLOCED) + free((char *)lte->hash); + elf_end(lte->elf); + close(lte->fd); +} + +static void +add_library_symbol(GElf_Addr addr, const char *name, + struct library_symbol **library_symbolspp, + enum toplt type_of_plt, int is_weak) { + struct library_symbol *s; + + debug(DEBUG_FUNCTION, "add_library_symbol()"); + + s = malloc(sizeof(struct library_symbol) + strlen(name) + 1); + if (s == NULL) + error(EXIT_FAILURE, errno, "add_library_symbol failed"); + + s->needs_init = 1; + s->is_weak = is_weak; + s->plt_type = type_of_plt; + s->next = *library_symbolspp; + s->enter_addr = (void *)(uintptr_t) addr; + s->name = (char *)(s + 1); + strcpy(s->name, name); + *library_symbolspp = s; + + debug(2, "addr: %p, symbol: \"%s\"", (void *)(uintptr_t) addr, name); +} + +/* stolen from elfutils-0.123 */ +static unsigned long +private_elf_gnu_hash(const char *name) { + unsigned long h = 5381; + const unsigned char *string = (const unsigned char *)name; + unsigned char c; + for (c = *string; c; c = *++string) + h = h * 33 + c; + return h & 0xffffffff; +} + +static int +in_load_libraries(const char *name, struct ltelf *lte) { + size_t i; + unsigned long hash; + unsigned long gnu_hash; + + if (!library_num) + return 1; + + hash = elf_hash((const unsigned char *)name); + gnu_hash = private_elf_gnu_hash(name); + for (i = 1; i <= library_num; ++i) { + if (lte[i].hash == NULL) + continue; + + if (lte[i].hash_type == SHT_GNU_HASH) { + Elf32_Word * hashbase = lte[i].hash; + Elf32_Word nbuckets = *hashbase++; + Elf32_Word symbias = *hashbase++; + Elf32_Word bitmask_nwords = *hashbase++; + Elf32_Word * buckets; + Elf32_Word * chain_zero; + Elf32_Word bucket; + + // +1 for skipped `shift' + hashbase += lte[i].ehdr.e_ident[EI_CLASS] * bitmask_nwords + 1; + buckets = hashbase; + hashbase += nbuckets; + chain_zero = hashbase - symbias; + bucket = buckets[gnu_hash % nbuckets]; + + if (bucket != 0) { + const Elf32_Word *hasharr = &chain_zero[bucket]; + do + if ((*hasharr & ~1u) == (gnu_hash & ~1u)) { + int symidx = hasharr - chain_zero; + GElf_Sym sym; + + if (gelf_getsym(lte[i].dynsym, symidx, &sym) == NULL) + error(EXIT_FAILURE, 0, + "Couldn't get symbol from .dynsym"); + + if (sym.st_value != 0 + && sym.st_shndx != SHN_UNDEF + && strcmp(name, lte[i].dynstr + sym.st_name) == 0) + return 1; + } + while ((*hasharr++ & 1u) == 0); + } + } else { + Elf32_Word nbuckets, symndx; + Elf32_Word *buckets, *chain; + nbuckets = lte[i].hash[0]; + buckets = <e[i].hash[2]; + chain = <e[i].hash[2 + nbuckets]; + + for (symndx = buckets[hash % nbuckets]; + symndx != STN_UNDEF; symndx = chain[symndx]) { + GElf_Sym sym; + + if (gelf_getsym(lte[i].dynsym, symndx, &sym) == NULL) + error(EXIT_FAILURE, 0, + "Couldn't get symbol from .dynsym"); + + if (sym.st_value != 0 + && sym.st_shndx != SHN_UNDEF + && strcmp(name, lte[i].dynstr + sym.st_name) == 0) + return 1; + } + } + } + return 0; +} + +static GElf_Addr +opd2addr(struct ltelf *lte, GElf_Addr addr) { +#ifdef ARCH_SUPPORTS_OPD + unsigned long base, offset; + + if (!lte->opd) + return addr; + + base = (unsigned long)lte->opd->d_buf; + offset = (unsigned long)addr - (unsigned long)lte->opd_addr; + if (offset > lte->opd_size) + error(EXIT_FAILURE, 0, "static plt not in .opd"); + + return *(GElf_Addr*)(base + offset); +#else //!ARCH_SUPPORTS_OPD + return addr; +#endif +} + +struct library_symbol * +read_elf(Process *proc) { + struct library_symbol *library_symbols = NULL; + struct ltelf lte[MAX_LIBRARIES + 1]; + size_t i; + struct opt_x_t *xptr; + struct library_symbol **lib_tail = NULL; + int exit_out = 0; + + debug(DEBUG_FUNCTION, "read_elf(file=%s)", proc->filename); + + elf_version(EV_CURRENT); + + do_init_elf(lte, proc->filename); + proc->e_machine = lte->ehdr.e_machine; + for (i = 0; i < library_num; ++i) + do_init_elf(<e[i + 1], library[i]); +#ifdef __mips__ + // MIPS doesn't use the PLT and the GOT entries get changed + // on startup. + proc->need_to_reinitialize_breakpoints = 1; + for(i=lte->mips_gotsym; i<lte->dynsym_count;i++){ + GElf_Sym sym; + const char *name; + GElf_Addr addr = arch_plt_sym_val(lte, i, 0); + if (gelf_getsym(lte->dynsym, i, &sym) == NULL){ + error(EXIT_FAILURE, 0, + "Couldn't get relocation from \"%s\"", + proc->filename); + } + name=lte->dynstr+sym.st_name; + if(ELF64_ST_TYPE(sym.st_info) != STT_FUNC){ + debug(2,"sym %s not a function",name); + continue; + } + add_library_symbol(addr, name, &library_symbols, 0, + ELF64_ST_BIND(sym.st_info) != 0); + if (!lib_tail) + lib_tail = &(library_symbols->next); + } +#else + for (i = 0; i < lte->relplt_count; ++i) { + GElf_Rel rel; + GElf_Rela rela; + GElf_Sym sym; + GElf_Addr addr; + void *ret; + const char *name; + + if (lte->relplt->d_type == ELF_T_REL) { + ret = gelf_getrel(lte->relplt, i, &rel); + rela.r_offset = rel.r_offset; + rela.r_info = rel.r_info; + rela.r_addend = 0; + } else + ret = gelf_getrela(lte->relplt, i, &rela); + + if (ret == NULL + || ELF64_R_SYM(rela.r_info) >= lte->dynsym_count + || gelf_getsym(lte->dynsym, ELF64_R_SYM(rela.r_info), + &sym) == NULL) + error(EXIT_FAILURE, 0, + "Couldn't get relocation from \"%s\"", + proc->filename); + +#ifdef PLT_REINITALISATION_BP + if (!sym.st_value && PLTs_initialized_by_here) + proc->need_to_reinitialize_breakpoints = 1; +#endif + + name = lte->dynstr + sym.st_name; + if (in_load_libraries(name, lte)) { + addr = arch_plt_sym_val(lte, i, &rela); + add_library_symbol(addr, name, &library_symbols, + (PLTS_ARE_EXECUTABLE(lte) + ? LS_TOPLT_EXEC : LS_TOPLT_POINT), + ELF64_ST_BIND(sym.st_info) == STB_WEAK); + if (!lib_tail) + lib_tail = &(library_symbols->next); + } + } +#endif // !__mips__ +#ifdef PLT_REINITALISATION_BP + struct opt_x_t *main_cheat; + + if (proc->need_to_reinitialize_breakpoints) { + /* Add "PLTs_initialized_by_here" to opt_x list, if not + already there. */ + main_cheat = (struct opt_x_t *)malloc(sizeof(struct opt_x_t)); + if (main_cheat == NULL) + error(EXIT_FAILURE, 0, "Couldn't allocate memory"); + main_cheat->next = opt_x; + main_cheat->found = 0; + main_cheat->name = PLTs_initialized_by_here; + + for (xptr = opt_x; xptr; xptr = xptr->next) + if (strcmp(xptr->name, PLTs_initialized_by_here) == 0 + && main_cheat) { + free(main_cheat); + main_cheat = NULL; + break; + } + if (main_cheat) + opt_x = main_cheat; + } +#endif + + for (i = 0; i < lte->symtab_count; ++i) { + GElf_Sym sym; + GElf_Addr addr; + const char *name; + + if (gelf_getsym(lte->symtab, i, &sym) == NULL) + error(EXIT_FAILURE, 0, + "Couldn't get symbol from \"%s\"", + proc->filename); + + name = lte->strtab + sym.st_name; + addr = sym.st_value; + if (!addr) + continue; + + for (xptr = opt_x; xptr; xptr = xptr->next) + if (xptr->name && strcmp(xptr->name, name) == 0) { + /* FIXME: Should be able to use &library_symbols as above. But + when you do, none of the real library symbols cause breaks. */ + add_library_symbol(opd2addr(lte, addr), + name, lib_tail, LS_TOPLT_NONE, 0); + xptr->found = 1; + break; + } + } + for (xptr = opt_x; xptr; xptr = xptr->next) + if ( ! xptr->found) { + char *badthing = "WARNING"; +#ifdef PLT_REINITALISATION_BP + if (strcmp(xptr->name, PLTs_initialized_by_here) == 0) { + if (lte->ehdr.e_entry) { + add_library_symbol ( + opd2addr (lte, lte->ehdr.e_entry), + PLTs_initialized_by_here, + lib_tail, 1, 0); + fprintf (stderr, "WARNING: Using e_ent" + "ry from elf header (%p) for " + "address of \"%s\"\n", (void*) + (long) lte->ehdr.e_entry, + PLTs_initialized_by_here); + continue; + } + badthing = "ERROR"; + exit_out = 1; + } +#endif + fprintf (stderr, + "%s: Couldn't find symbol \"%s\" in file \"%s" + "\"\n", badthing, xptr->name, proc->filename); + } + if (exit_out) { + exit (1); + } + + for (i = 0; i < library_num + 1; ++i) + do_close_elf(<e[i]); + + return library_symbols; +} @@ -0,0 +1,49 @@ +#ifndef LTRACE_ELF_H +#define LTRACE_ELF_H + +#include <gelf.h> +#include <stdlib.h> + +struct ltelf { + int fd; + Elf *elf; + GElf_Ehdr ehdr; + Elf_Data *dynsym; + size_t dynsym_count; + const char *dynstr; + GElf_Addr plt_addr; + size_t plt_size; + Elf_Data *relplt; + size_t relplt_count; + Elf_Data *symtab; + const char *strtab; + size_t symtab_count; + Elf_Data *opd; + GElf_Addr *opd_addr; + size_t opd_size; + Elf32_Word *hash; + int hash_type; + int lte_flags; +#ifdef __mips__ + size_t pltgot_addr; + size_t mips_local_gotno; + size_t mips_gotsym; +#endif // __mips__ +}; + +#define LTE_HASH_MALLOCED 1 +#define LTE_PLT_EXECUTABLE 2 + +#define PLTS_ARE_EXECUTABLE(lte) ((lte->lte_flags & LTE_PLT_EXECUTABLE) != 0) + +extern int library_num; +extern char *library[MAX_LIBRARIES]; + +extern struct library_symbol *read_elf(Process *); + +extern GElf_Addr arch_plt_sym_val(struct ltelf *, size_t, GElf_Rela *); + +#ifndef SHT_GNU_HASH +#define SHT_GNU_HASH 0x6ffffff6 /* GNU-style hash table. */ +#endif +#endif diff --git a/etc/ltrace.conf b/etc/ltrace.conf new file mode 100644 index 0000000..ed4fc5a --- /dev/null +++ b/etc/ltrace.conf @@ -0,0 +1,604 @@ +; ltrace.conf +; +; ~/.ltrace.conf will also be read, if it exists. The -F option may be +; used to suppress the automatic inclusion of both this file and +; ~/.ltrace.conf, and load a different config file or config files +; instead. + +; Argument types: +; + == May vary (ie, is a returned value) (prefix) +; void +; int +; uint == (unsigned int) +; long +; ulong == (unsigned long) +; octal == (unsigned) [written in octal] +; char +; short == (short) +; ushort == (unsigned short) +; addr == (void *) [unsigned, written in hexa] +; file == (FILE *) [TODO] +; format == ((const char *), ...) [printf() like] [TODO] +; string == (char *) +; string[argN] == (char *) [N>0] [show only up to (arg N) bytes] +; string[eltN] == (char *) [N>0] [show only up to (elt N) bytes] +; string[retval] == (char *) [show only up to (return val) bytes] +; string[arg0] == (char *) [same as string[retval]] +; string[N] == (char *) [N>0] [show only up to N bytes] +; type* == (type *) [pointer to any other type] +; enum (key=value,key=value,...) [enumeration, see below] +; array(type,argN) +; == (type[SIZE]) [array of (arg N) elements] +; array(type,eltN) +; == (type[SIZE]) [array of (struct element N) elements] +; array(type,N) == (type[N]) [array of N elements] +; struct(type,type,...) +; == (struct {...}) [struct of several types] +; +; Backwards-compatibility: +; string0 == (char *) [same as string[retval]] +; stringN == (char *) [N>0] [same as string[argN]] + + + +; Typedefs +; +; To make it easier to specify argument lists, you can use 'typedef' +; directives to avoid repeating complex parameter descriptors: +; +; typedef color = enum(RED=1,BLUE=2,GREEN=3) +; void draw_line(color,int,int,int,int) +; void draw_square(color,int,int,int,int) +; +; Enumerations +; +; The syntax is a parenthesized list of key=value assignments, like so: +; enum (F_DUPFD=0,F_GETFD=1,F_SETFD=2) +; an example usage might look like +; int fcntl(int,enum (F_DUPFD=0,F_GETFD=1,F_SETFD=2)) +; +; Arrays +; +; NOTE: Uses of array(...) alone are very rare. You almost always +; want array(...)*. The exceptions are when you have a fixed-size +; array. +; +; Structs +; +; NOTE: Uses of struct(...) alone are very rare. You almost always +; want struct(...)* (a pointer to a struct) anyway. Most compilers +; pass structs as pointers anyway, and those that don't are not yet +; supported. The one time when you want to use a non-pointer +; struct(...) type are when you have an array of structs, or a struct +; containing another struct. +; +; For example, if you have +; struct s1 { +; int y_size; +; int * y; +; int z[3]; +; struct { char c; } a; +; struct { char c; } * b; +; } +; and a function +; void f(struct s1*) +; then the corresponding ltrace spec is +; void f(struct(int,array(int,elt0),array(int,3),struct(char),struct(char)*)*) +; which, formatted similarly to the C declaration, looks like +; void f(struct( +; int, +; array(int,elt0), +; array(int,3), +; struct(char), +; struct(char)* +; )* +; ) + + +; arpa/inet.h +int inet_aton(string,addr); +string inet_ntoa(addr); ; It isn't an ADDR but an hexa number... +addr inet_addr(string); + +; bfd.h +void bfd_init(void); +int bfd_set_default_target(string); +addr bfd_scan_vma(string, addr, int); +addr bfd_openr(string,string); +int bfd_check_format(addr,int); + +; ctype.h +char tolower(char); +char toupper(char); +addr __ctype_b_loc(void); +addr __ctype_tolower_loc(void); +addr __ctype_toupper_loc(void); + +; curses.h +int waddch(addr, char); +int mvprintw(int, int, format); +int wmove(addr, int, int); +int waddnstr(addr, string, int); +string tgoto(string, int, int); + +; dirent.h +int closedir(addr); +addr opendir(string); +addr readdir(addr); +addr readdir64(addr); + +; dlfcn.h +addr dlopen(string, int); +string dlerror(void); +addr dlsym(addr, string); +int dlclose(addr); + +; errno.h +addr __errno_location(void); + +; fcntl.h +int open(string,int,octal); ; WARNING: 3rd argument may not be there +int open64(string,int,octal); ; WARNING: 3rd argument may not be there + +; fnmatch.h +int fnmatch(string, string, int); + +; getopt.h +int getopt_long(int,addr,string,addr,int*); +int getopt_long_only(int,addr,string,addr,addr); + +; grp.h +void endgrent(void); +addr getgrnam(string); +void setgrent(void); +addr getgrent(void); + +; libintl.h +string __dcgettext(string,string,int); +string bindtextdomain(string, string); +string textdomain(string); + +; libio.h +char _IO_getc(file); +int _IO_putc(char,file); + +; locale.h +string setlocale(int, string); + +; mcheck.h +void mtrace(void); +void muntrace(void); + +; mntent.h +int endmntent(file); +file setmntent(string,string); +addr getmntent(addr); + +; mqueue.h +int mq_open(string, int, octal, addr); ; WARNING: 3rd and 4th arguments may not be there +int mq_close(int); +int mq_unlink(string); +int mq_getattr(int, addr); +int mq_setattr(int, addr, addr); +int mq_notify(int, addr); +int mq_send(int, string3, ulong, uint); +int mq_timedsend(int, string3, ulong, uint, addr); +long mq_receive(int, +string0, ulong, addr); +long mq_timedreceive(int, +string0, ulong, addr, addr); + +; netdb.h +void endhostent(void); +void endnetent(void); +void endnetgrent(void); +void endprotoent(void); +void endservent(void); +void freeaddrinfo(addr); +string gai_strerror(int); +int getaddrinfo(string, string, addr, addr); +addr gethostbyaddr(string, uint, int); +addr gethostbyname(string); +addr gethostent(void); +int getnameinfo(addr, uint, string, uint, string, uint, uint); +addr getnetbyaddr(uint, int); +addr getnetbyname(string); +addr getnetent(void); +int getnetgrent(addr, addr, addr); +addr getprotobyname(string); +addr getprotobynumber(int); +addr getprotoent(void); +addr getservbyname(string, string); +addr getservbyport(int, string); +addr getservent(void); +void herror(string); +string hstrerror(int); +int rcmd(addr, ushort, string, string, string, addr); +int rcmd_af(addr, ushort, string, string, string, addr, int); +int rexec(addr, int, string, string, string, addr); +int rexec_af(addr, int, string, string, string, addr, int); +int rresvport (addr); +int rresvport_af (addr, int); +int ruserok(string, int, string, string); +int ruserok_af(string, int, string, string, int); +void sethostent(int); +void setnetent(int); +int setnetgrent(string); +void setprotoent(int); +void setservent(int); + +; netinet/in.h +uint ntohs(uint); + +; pcap.h +string pcap_lookupdev(addr); +addr pcap_open_live(string, int, int, int, addr); +int pcap_snapshot(addr); +int pcap_lookupnet(string, addr, addr, addr); +int pcap_compile(addr, addr, string, int, addr); + +; pwd.h +string getpass(string); +void endpwent(void); +addr getpwnam(string); +void setpwent(void); + +; readline/readline.h +string readline(string); + +; signal.h +int kill(int,int); +addr signal(int,addr); +int sigemptyset(addr); +int sigfillset(addr); +int sigaddset(addr, int); +int sigdelset(addr, int); +int sigismember(addr, int); +int sigaction(int, addr, addr); +int sigprocmask(int, addr, addr); +int sigpending(addr); +int sigsuspend(addr); + +; stdio.h +int fclose(file); +int feof(file); +int ferror(file); +int fflush(file); +char fgetc(file); +addr fgets(+string, int, file); +int fileno(file); +file fopen(string,string); +file fopen64(string,string); +int fprintf(file,format); +int fputc(char,file); +int fputs(string,file); +ulong fread(addr,ulong,ulong,file); +ulong fread_unlocked(addr,ulong,ulong,file); +ulong fwrite(string,ulong,ulong,file); +ulong fwrite_unlocked(string,ulong,ulong,file); +int pclose(addr); +void perror(string); +addr popen(string, string); +int printf(format); +int puts(string); +int remove(string); +int snprintf(+string2,ulong,format); +int sprintf(+string,format); +string tempnam(string,string); +int vfprintf(file,string,addr); +int vsnprintf(+string2,ulong,string,addr); +int setvbuf(file,addr,int,ulong); +void setbuf(file,addr); +void setbuffer(file,addr,ulong); +void setlinebuf(file); +int rename(string,string); + +; stdlib.h +long __strtol_internal(string,addr,int); +ulong __strtoul_internal(string,addr,int); +int atexit(addr); +addr bsearch(string, addr, ulong, ulong, addr); +addr calloc(ulong, ulong); +void exit(int); +void free(addr); +string getenv(string); +int putenv(string); +int setenv(string,string,int); +void unsetenv(string); +addr malloc(ulong); +void qsort(addr,ulong,ulong,addr); +int random(void); +addr realloc(addr,ulong); +void srandom(uint); +int system(string); + +; string.h +void bcopy(addr,addr,ulong); +void bzero(addr,ulong); +string basename(string); +string index(string,char); +addr memchr(string,char,ulong); +addr memcpy(addr,string3,ulong); +addr memset(addr,char,long); +string rindex(string,char); +addr stpcpy(addr,string); +int strcasecmp(string, string); +string strcat(string, string); +string strchr(string,char); +int strcoll(string,string); +ulong strlen(string); +int strcmp(string,string); +addr strcpy(addr,string); +addr strdup(string); +string strerror(int); +int strncmp(string,string,ulong); +addr strncpy(addr,string3,ulong); +string strrchr(string,char); +string strsep(addr,string); +ulong strspn(string,string); +ulong strcspn(string,string); +string strstr(string,string); +string strtok(string, string); + +; sys/ioctl.h +int ioctl(int, int, addr); + +; sys/socket.h +int socket(int,int,int); + +; sys/stat.h +int __fxstat(int,int,addr); +int __xstat(int,string,addr); +int __lxstat(int,string,addr); +int __fxstat64(int,int,addr); +int __xstat64(int,string,addr); +int __lxstat64(int,string,addr); +int chmod(string,octal); +int fchmod(int,octal); +int mkfifo(string,octal); +octal umask(octal); + +; sys/utsname.h +int uname(addr); + +; sys/vfs.h +int statfs(string,addr); + +; syslog.h +void closelog(void); +void openlog(string,int,int); +void syslog(int,format); + +; term.h +int tputs(string, int, addr); + +; termios.h +int tcgetattr(int,addr); +int tcsetattr(int,int,addr); + +; time.h +string ctime(addr); +int gettimeofday(addr, addr); +addr gmtime(addr); +addr localtime(addr); +ulong strftime(+string2,ulong,string,addr); +long time(addr); + +; unistd.h +void _exit(int); +int access(string, int); +uint alarm(uint); +int chdir(string); +int chown(string,int,int); +int close(int); +string crypt(string,string); +int dup2(int,int); +int execlp(string,string,addr,addr,addr); +int execv(string,addr); +int fchdir(int); +int fork(void); +int ftruncate(int,ulong); +string2 getcwd(addr,ulong); +int getdomainname(+string2,ulong); +int geteuid(void); +int getegid(void); +int getgid(void); +int gethostname(+string2,ulong); +string getlogin(void); +int getopt(int,addr,string); +int getpid(void); +int getppid(void); +int getuid(void); +int getpgrp(void); +int setpgrp(void); +int getpgid(int); +int isatty(int); +int link(string,string); +int mkdir(string,octal); +long read(int, +string[retval], ulong); +int rmdir(string); +int seteuid(uint); +int setgid(int); +int sethostname(+string2,ulong); +int setpgid(int,int); +int setreuid(uint, uint); +int setuid(int); +uint sleep(uint); +int symlink(string,string); +int sync(void); +int truncate(string,ulong); +string ttyname(int); +int unlink(string); +void usleep(uint); +long write(int, string3, ulong); +addr sbrk(long); +int getpagesize(void); +long lseek(int,long,int); +int pipe(addr); + +; utmp.h +void endutent(void); +addr getutent(void); +void setutent(void); + +; wchar.h +int fwide(addr, int); + +; sys/wait.h +int wait(addr); +int waitpid(int,addr,int); + +; X11/Xlib.h +void XCloseDisplay(addr); +int XMapWindow(addr,addr); +addr XOpenDisplay(string); + +; sys/acl.h +int acl_add_perm(addr,uint); +int acl_calc_mask(addr); +int acl_clear_perms(addr); +int acl_copy_entry(addr,addr); +int acl_copy_ext(addr,addr,int); +addr acl_copy_int(addr); +int acl_create_entry(addr,addr); +int acl_delete_def_file(string); +int acl_delete_entry(addr,addr); +int acl_delete_perm(addr,uint); +addr acl_dup(addr); +int acl_free(addr); +addr acl_from_text(string); +int acl_get_entry(addr,int,addr); +addr acl_get_fd(int); +addr acl_get_file(string,int); +int acl_get_permset(addr,addr); +addr acl_get_qualifier(addr); +int acl_get_tag_type(addr,addr); +addr acl_init(int); +int acl_set_fd(int,addr); +int acl_set_file(string,int,addr); +int acl_set_permset(addr,addr); +int acl_set_qualifier(addr,addr); +int acl_set_tag_type(addr,int); +int acl_size(addr); +string acl_to_text(addr,addr); +itn acl_valid(addr); + +; acl/libacl.h +int acl_check(addr,addr); +int acl_cmp(addr,addr); +int acl_entries(addr); +int acl_equiv_mode(addr,addr); +string acl_error(int); +int acl_extended_fd(int); +int acl_extended_file(string); +addr acl_from_mode(octal); +int acl_get_perm(addr,uint); +string acl_to_any_text(addr,string,char,int); + +; SYSCALLS +addr SYS_brk(addr); +int SYS_close(int); +int SYS_execve(string,addr,addr); +void SYS_exit(int); +void SYS_exit_group(int); +int SYS_fork(void); +int SYS_getcwd(+string2,ulong); +int SYS_getpid(void); +;addr SYS_mmap(addr,ulong,int,int,int,long); +int SYS_munmap(addr,ulong); +int SYS_open(string,int,octal); +int SYS_personality(uint); +long SYS_read(int,+string0,ulong); +int SYS_stat(string,addr); +octal SYS_umask(octal); +int SYS_uname(addr); +long SYS_write(int,string3,ulong); +int SYS_sync(void); +int SYS_setxattr(string,string,addr,uint,int); +int SYS_lsetxattr(string,string,addr,uint,int); +int SYS_fsetxattr(int,string,addr,uint,int); +int SYS_getxattr(string,string,addr,uint); +int SYS_lgetxattr(string,string,addr,uint); +int SYS_fgetxattr(int,string,addr,uint); +int SYS_listxattr(string,addr,uint); +int SYS_llistxattr(string,addr,uint); +int SYS_flistxattr(int,addr,uint); +int SYS_removexattr(string,string); +int SYS_lremovexattr(string,string); +int SYS_fremovexattr(int,string); +int SYS_chdir(string); +int SYS_fchdir(int); +int SYS_chmod(string,octal); +int SYS_fchmod(int,octal); +int SYS_chown(string,int,int); +int SYS_fchown(int,int,int); +int SYS_lchown(string,int,int); +int SYS_chroot(string); +int SYS_dup(int); +int SYS_dup2(int,int); +int SYS_fdatasync(int); +int SYS_fsync(int); +int SYS_getpriority(int,int); +int SYS_setpriority(int,int,int); +int SYS_getrlimit(int,addr); +int SYS_setrlimit(int,addr); +int SYS_gettimeofday(addr,addr); +int SYS_settimeofday(addr,addr); +int SYS_setfsgid(int); +int SYS_setfsuid(int); +int SYS_getuid(void); +int SYS_setuid(int); +int SYS_getgid(void); +int SYS_setgid(int); +int SYS_getsid(int); +int SYS_setsid(int); +int SYS_setreuid(int,int); +int SYS_setregid(int,int); +int SYS_geteuid(void); +int SYS_getegid(void); +int SYS_setpgid(int,int); +int SYS_getresuid(addr,addr,addr); +int SYS_setresuid(int,int,int); +int SYS_getresgid(addr,addr,addr); +int SYS_setresgid(int,int,int); +int SYS_kill(int,int); +int SYS_link(string,string); +int SYS_madvise(addr,ulong,int); +int SYS_mkdir(string,octal); +int SYS_mknod(string,octal,int); +int SYS_msync(addr,ulong,int); +int SYS_nice(int); +int SYS_poll(addr,uint,int); +int SYS_readdir(uint,addr,uint); +int SYS_readlink(string,string,ulong); +int SYS_reboot(int,int,int,addr); +int SYS_rename(string,string); +int SYS_rmdir(string); +int SYS_sigaltstack(addr,addr); +int SYS_statfs(string,addr); +int SYS_fstatfs(int,addr); +int SYS_fstat(int,addr); +int SYS_lstat(string,addr); +int SYS_stime(addr); +int SYS_symlink(string, string); +int SYS_sysinfo(addr); +int SYS_syslog(int,string,int); +int SYS_truncate(string,long); +int SYS_ftruncate(int,long); +int SYS_mount(string,string,string,ulong,addr); +int SYS_umount(string); +int SYS_umount2(string,int); +int SYS_unlink(string); +int SYS_utime(string,addr); +long SYS_lseek(int,long,int); +addr SYS_signal(int,addr); +int SYS_sigaction(int,addr,addr); +int SYS_pause(void); +int SYS_sigpending(addr); +int SYS_sigprocmask(int,addr,addr); +int SYS_sigqueue(int,int,addr); +int SYS_sigsuspend(addr); +int SYS_wait(addr); +int SYS_waitpid(int,addr,int); +ulong SYS_readv(int,addr,int); +ulong SYS_writev(int,addr,int); +int SYS_mprotect(addr,int,int); +int SYS_access(string,octal); diff --git a/execute_program.c b/execute_program.c new file mode 100644 index 0000000..5fd6379 --- /dev/null +++ b/execute_program.c @@ -0,0 +1,91 @@ +#include "config.h" + +#include <stdio.h> +#include <stdlib.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <errno.h> +#include <string.h> +#include <pwd.h> +#include <grp.h> + +#include "common.h" + +static void +change_uid(Process *proc) { + uid_t run_uid, run_euid; + gid_t run_gid, run_egid; + + if (options.user) { + struct passwd *pent; + + if (getuid() != 0 || geteuid() != 0) { + fprintf(stderr, + "you must be root to use the -u option\n"); + exit(1); + } + if ((pent = getpwnam(options.user)) == NULL) { + fprintf(stderr, "cannot find user `%s'\n", options.user); + exit(1); + } + run_uid = pent->pw_uid; + run_gid = pent->pw_gid; + + if (initgroups(options.user, run_gid) < 0) { + perror("ltrace: initgroups"); + exit(1); + } + } else { + run_uid = getuid(); + run_gid = getgid(); + } + if (options.user || !geteuid()) { + struct stat statbuf; + run_euid = run_uid; + run_egid = run_gid; + + if (!stat(proc->filename, &statbuf)) { + if (statbuf.st_mode & S_ISUID) { + run_euid = statbuf.st_uid; + } + if (statbuf.st_mode & S_ISGID) { + run_egid = statbuf.st_gid; + } + } + if (setregid(run_gid, run_egid) < 0) { + perror("ltrace: setregid"); + exit(1); + } + if (setreuid(run_uid, run_euid) < 0) { + perror("ltrace: setreuid"); + exit(1); + } + } +} + +void +execute_program(Process *sp, char **argv) { + pid_t pid; + + debug(1, "Executing `%s'...", sp->filename); + + pid = fork(); + if (pid < 0) { + perror("ltrace: fork"); + exit(1); + } else if (!pid) { /* child */ + change_uid(sp); + trace_me(); + execvp(sp->filename, argv); + fprintf(stderr, "Can't execute `%s': %s\n", sp->filename, + strerror(errno)); + _exit(1); + } + + debug(1, "PID=%d", pid); + + sp->pid = pid; + + return; +} diff --git a/handle_event.c b/handle_event.c new file mode 100644 index 0000000..1dfb82b --- /dev/null +++ b/handle_event.c @@ -0,0 +1,696 @@ +#include "config.h" + +#define _GNU_SOURCE +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <signal.h> +#include <assert.h> +#include <sys/time.h> + +#include "common.h" + +#ifdef __powerpc__ +#include <sys/ptrace.h> +#endif + +static void handle_signal(Event *event); +static void handle_exit(Event *event); +static void handle_exit_signal(Event *event); +static void handle_syscall(Event *event); +static void handle_arch_syscall(Event *event); +static void handle_sysret(Event *event); +static void handle_arch_sysret(Event *event); +static void handle_clone(Event *event); +static void handle_exec(Event *event); +static void handle_breakpoint(Event *event); +static void handle_new(Event *event); +static void remove_proc(Process *proc); + +static void callstack_push_syscall(Process *proc, int sysnum); +static void callstack_push_symfunc(Process *proc, + struct library_symbol *sym); +static void callstack_pop(Process *proc); + +static char * shortsignal(Process *proc, int signum); +static char * sysname(Process *proc, int sysnum); +static char * arch_sysname(Process *proc, int sysnum); + +void +handle_event(Event *event) { + debug(DEBUG_FUNCTION, "handle_event(pid=%d, type=%d)", event->proc ? event->proc->pid : -1, event->type); + switch (event->type) { + case EVENT_NONE: + debug(1, "event: none"); + return; + case EVENT_SIGNAL: + debug(1, "event: signal (%s [%d])", + shortsignal(event->proc, event->e_un.signum), + event->e_un.signum); + handle_signal(event); + return; + case EVENT_EXIT: + debug(1, "event: exit (%d)", event->e_un.ret_val); + handle_exit(event); + return; + case EVENT_EXIT_SIGNAL: + debug(1, "event: exit signal (%s [%d])", + shortsignal(event->proc, event->e_un.signum), + event->e_un.signum); + handle_exit_signal(event); + return; + case EVENT_SYSCALL: + debug(1, "event: syscall (%s [%d])", + sysname(event->proc, event->e_un.sysnum), + event->e_un.sysnum); + handle_syscall(event); + return; + case EVENT_SYSRET: + debug(1, "event: sysret (%s [%d])", + sysname(event->proc, event->e_un.sysnum), + event->e_un.sysnum); + handle_sysret(event); + return; + case EVENT_ARCH_SYSCALL: + debug(1, "event: arch_syscall (%s [%d])", + arch_sysname(event->proc, event->e_un.sysnum), + event->e_un.sysnum); + handle_arch_syscall(event); + return; + case EVENT_ARCH_SYSRET: + debug(1, "event: arch_sysret (%s [%d])", + arch_sysname(event->proc, event->e_un.sysnum), + event->e_un.sysnum); + handle_arch_sysret(event); + return; + case EVENT_CLONE: + debug(1, "event: clone (%u)", event->e_un.newpid); + handle_clone(event); + return; + case EVENT_EXEC: + debug(1, "event: exec()"); + handle_exec(event); + return; + case EVENT_BREAKPOINT: + debug(1, "event: breakpoint"); + handle_breakpoint(event); + return; + case EVENT_NEW: + debug(1, "event: new process"); + handle_new(event); + return; + default: + fprintf(stderr, "Error! unknown event?\n"); + exit(1); + } +} + +/* TODO */ +static void * +address_clone(void * addr) { + debug(DEBUG_FUNCTION, "address_clone(%p)", addr); + return addr; +} + +static void * +breakpoint_clone(void * bp) { + Breakpoint * b; + debug(DEBUG_FUNCTION, "breakpoint_clone(%p)", bp); + b = malloc(sizeof(Breakpoint)); + if (!b) { + perror("malloc()"); + exit(1); + } + memcpy(b, bp, sizeof(Breakpoint)); + return b; +} + +typedef struct Pending_New Pending_New; +struct Pending_New { + pid_t pid; + Pending_New * next; +}; +static Pending_New * pending_news = NULL; + +static int +pending_new(pid_t pid) { + Pending_New * p; + + debug(DEBUG_FUNCTION, "pending_new(%d)", pid); + + p = pending_news; + while (p) { + if (p->pid == pid) { + return 1; + } + p = p->next; + } + return 0; +} + +static void +pending_new_insert(pid_t pid) { + Pending_New * p; + + debug(DEBUG_FUNCTION, "pending_new_insert(%d)", pid); + + p = malloc(sizeof(Pending_New)); + if (!p) { + perror("malloc()"); + exit(1); + } + p->pid = pid; + p->next = pending_news; + pending_news = p; +} + +static void +pending_new_remove(pid_t pid) { + Pending_New *p, *pred; + + debug(DEBUG_FUNCTION, "pending_new_remove(%d)", pid); + + p = pending_news; + if (p->pid == pid) { + pending_news = p->next; + free(p); + } else { + while (p) { + if (p->pid == pid) { + pred->next = p->next; + free(p); + } + pred = p; + p = p->next; + } + } +} + +static void +handle_clone(Event * event) { + Process *p; + + debug(DEBUG_FUNCTION, "handle_clone(pid=%d)", event->proc->pid); + + p = malloc(sizeof(Process)); + if (!p) { + perror("malloc()"); + exit(1); + } + memcpy(p, event->proc, sizeof(Process)); + p->breakpoints = dict_clone(event->proc->breakpoints, address_clone, breakpoint_clone); + p->pid = event->e_un.newpid; + p->parent = event->proc; + + if (pending_new(p->pid)) { + pending_new_remove(p->pid); + if (p->breakpoint_being_enabled) { + enable_breakpoint(p->pid, p->breakpoint_being_enabled); + p->breakpoint_being_enabled = NULL; + } + if (event->proc->state == STATE_ATTACHED && options.follow) { + p->state = STATE_ATTACHED; + } else { + p->state = STATE_IGNORED; + } + continue_process(p->pid); + p->next = list_of_processes; + list_of_processes = p; + } else { + p->state = STATE_BEING_CREATED; + p->next = list_of_processes; + list_of_processes = p; + } + continue_process(event->proc->pid); +} + +static void +handle_new(Event * event) { + Process * proc; + + debug(DEBUG_FUNCTION, "handle_new(pid=%d)", event->e_un.newpid); + + proc = pid2proc(event->e_un.newpid); + if (!proc) { + pending_new_insert(event->e_un.newpid); + } else { + assert(proc->state == STATE_BEING_CREATED); + if (proc->breakpoint_being_enabled) { + enable_breakpoint(proc->pid, proc->breakpoint_being_enabled); + proc->breakpoint_being_enabled = NULL; + } + if (options.follow) { + proc->state = STATE_ATTACHED; + } else { + proc->state = STATE_IGNORED; + } + continue_process(proc->pid); + } +} + +static char * +shortsignal(Process *proc, int signum) { + static char *signalent0[] = { +#include "signalent.h" + }; + static char *signalent1[] = { +#include "signalent1.h" + }; + static char **signalents[] = { signalent0, signalent1 }; + int nsignals[] = { sizeof signalent0 / sizeof signalent0[0], + sizeof signalent1 / sizeof signalent1[0] + }; + + debug(DEBUG_FUNCTION, "shortsignal(pid=%d, signum=%d)", proc->pid, signum); + + if (proc->personality > sizeof signalents / sizeof signalents[0]) + abort(); + if (signum < 0 || signum >= nsignals[proc->personality]) { + return "UNKNOWN_SIGNAL"; + } else { + return signalents[proc->personality][signum]; + } +} + +static char * +sysname(Process *proc, int sysnum) { + static char result[128]; + static char *syscalent0[] = { +#include "syscallent.h" + }; + static char *syscalent1[] = { +#include "syscallent1.h" + }; + static char **syscalents[] = { syscalent0, syscalent1 }; + int nsyscals[] = { sizeof syscalent0 / sizeof syscalent0[0], + sizeof syscalent1 / sizeof syscalent1[0] + }; + + debug(DEBUG_FUNCTION, "sysname(pid=%d, sysnum=%d)", proc->pid, sysnum); + + if (proc->personality > sizeof syscalents / sizeof syscalents[0]) + abort(); + if (sysnum < 0 || sysnum >= nsyscals[proc->personality]) { + sprintf(result, "SYS_%d", sysnum); + return result; + } else { + sprintf(result, "SYS_%s", + syscalents[proc->personality][sysnum]); + return result; + } +} + +static char * +arch_sysname(Process *proc, int sysnum) { + static char result[128]; + static char *arch_syscalent[] = { +#include "arch_syscallent.h" + }; + int nsyscals = sizeof arch_syscalent / sizeof arch_syscalent[0]; + + debug(DEBUG_FUNCTION, "arch_sysname(pid=%d, sysnum=%d)", proc->pid, sysnum); + + if (sysnum < 0 || sysnum >= nsyscals) { + sprintf(result, "ARCH_%d", sysnum); + return result; + } else { + sprintf(result, "ARCH_%s", + arch_syscalent[sysnum]); + return result; + } +} + +static void +handle_signal(Event *event) { + debug(DEBUG_FUNCTION, "handle_signal(pid=%d, signum=%d)", event->proc->pid, event->e_un.signum); + if (exiting && event->e_un.signum == SIGSTOP) { + pid_t pid = event->proc->pid; + disable_all_breakpoints(event->proc); + untrace_pid(pid); + remove_proc(event->proc); + return; + } + if (event->proc->state != STATE_IGNORED) { + output_line(event->proc, "--- %s (%s) ---", + shortsignal(event->proc, event->e_un.signum), + strsignal(event->e_un.signum)); + } + continue_after_signal(event->proc->pid, event->e_un.signum); +} + +static void +handle_exit(Event *event) { + debug(DEBUG_FUNCTION, "handle_exit(pid=%d, status=%d)", event->proc->pid, event->e_un.ret_val); + if (event->proc->state != STATE_IGNORED) { + output_line(event->proc, "+++ exited (status %d) +++", + event->e_un.ret_val); + } + remove_proc(event->proc); +} + +static void +handle_exit_signal(Event *event) { + debug(DEBUG_FUNCTION, "handle_exit_signal(pid=%d, signum=%d)", event->proc->pid, event->e_un.signum); + if (event->proc->state != STATE_IGNORED) { + output_line(event->proc, "+++ killed by %s +++", + shortsignal(event->proc, event->e_un.signum)); + } + remove_proc(event->proc); +} + +static void +remove_proc(Process *proc) { + Process *tmp, *tmp2; + + debug(DEBUG_FUNCTION, "remove_proc(pid=%d)", proc->pid); + + if (list_of_processes == proc) { + tmp = list_of_processes; + list_of_processes = list_of_processes->next; + free(tmp); + return; + } + tmp = list_of_processes; + while (tmp->next) { + if (tmp->next == proc) { + tmp2 = tmp->next; + tmp->next = tmp->next->next; + free(tmp2); + continue; + } + tmp = tmp->next; + } +} + +static void +handle_syscall(Event *event) { + debug(DEBUG_FUNCTION, "handle_syscall(pid=%d, sysnum=%d)", event->proc->pid, event->e_un.sysnum); + if (event->proc->state != STATE_IGNORED) { + if (options.syscalls) { + output_left(LT_TOF_SYSCALL, event->proc, + sysname(event->proc, event->e_un.sysnum)); + } + if (event->proc->breakpoints_enabled == 0) { + enable_all_breakpoints(event->proc); + } + callstack_push_syscall(event->proc, event->e_un.sysnum); + } + continue_process(event->proc->pid); +} + +static void +handle_exec(Event * event) { + Process * proc = event->proc; + pid_t saved_pid; + + debug(DEBUG_FUNCTION, "handle_exec(pid=%d)", proc->pid); + if (proc->state == STATE_IGNORED) { + untrace_pid(proc->pid); + remove_proc(proc); + return; + } + output_line(proc, "--- Called exec() ---"); + proc->mask_32bit = 0; + proc->personality = 0; + proc->arch_ptr = NULL; + free(proc->filename); + proc->filename = pid2name(proc->pid); + saved_pid = proc->pid; + proc->pid = 0; + breakpoints_init(proc); + proc->pid = saved_pid; + proc->callstack_depth = 0; + continue_process(proc->pid); +} + +static void +handle_arch_syscall(Event *event) { + debug(DEBUG_FUNCTION, "handle_arch_syscall(pid=%d, sysnum=%d)", event->proc->pid, event->e_un.sysnum); + if (event->proc->state != STATE_IGNORED) { + if (options.syscalls) { + output_left(LT_TOF_SYSCALL, event->proc, + arch_sysname(event->proc, event->e_un.sysnum)); + } + if (event->proc->breakpoints_enabled == 0) { + enable_all_breakpoints(event->proc); + } + callstack_push_syscall(event->proc, 0xf0000 + event->e_un.sysnum); + } + continue_process(event->proc->pid); +} + +struct timeval current_time_spent; + +static void +calc_time_spent(Process *proc) { + struct timeval tv; + struct timezone tz; + struct timeval diff; + struct callstack_element *elem; + + debug(DEBUG_FUNCTION, "calc_time_spent(pid=%d)", proc->pid); + elem = &proc->callstack[proc->callstack_depth - 1]; + + gettimeofday(&tv, &tz); + + diff.tv_sec = tv.tv_sec - elem->time_spent.tv_sec; + if (tv.tv_usec >= elem->time_spent.tv_usec) { + diff.tv_usec = tv.tv_usec - elem->time_spent.tv_usec; + } else { + diff.tv_sec++; + diff.tv_usec = 1000000 + tv.tv_usec - elem->time_spent.tv_usec; + } + current_time_spent = diff; +} + +static void +handle_sysret(Event *event) { + debug(DEBUG_FUNCTION, "handle_sysret(pid=%d, sysnum=%d)", event->proc->pid, event->e_un.sysnum); + if (event->proc->state != STATE_IGNORED) { + if (opt_T || options.summary) { + calc_time_spent(event->proc); + } + callstack_pop(event->proc); + if (options.syscalls) { + output_right(LT_TOF_SYSCALLR, event->proc, + sysname(event->proc, event->e_un.sysnum)); + } + } + continue_process(event->proc->pid); +} + +static void +handle_arch_sysret(Event *event) { + debug(DEBUG_FUNCTION, "handle_arch_sysret(pid=%d, sysnum=%d)", event->proc->pid, event->e_un.sysnum); + if (event->proc->state != STATE_IGNORED) { + if (opt_T || options.summary) { + calc_time_spent(event->proc); + } + callstack_pop(event->proc); + if (options.syscalls) { + output_right(LT_TOF_SYSCALLR, event->proc, + arch_sysname(event->proc, event->e_un.sysnum)); + } + } + continue_process(event->proc->pid); +} + +static void +handle_breakpoint(Event *event) { + int i, j; + Breakpoint *sbp; + + debug(DEBUG_FUNCTION, "handle_breakpoint(pid=%d, addr=%p)", event->proc->pid, event->e_un.brk_addr); + debug(2, "event: breakpoint (%p)", event->e_un.brk_addr); + +#ifdef __powerpc__ + /* Need to skip following NOP's to prevent a fake function from being stacked. */ + long stub_addr = (long) get_count_register(event->proc); + Breakpoint *stub_bp = NULL; + char nop_instruction[] = PPC_NOP; + + stub_bp = address2bpstruct (event->proc, event->e_un.brk_addr); + + if (stub_bp) { + unsigned char *bp_instruction = stub_bp->orig_value; + + if (memcmp(bp_instruction, nop_instruction, + PPC_NOP_LENGTH) == 0) { + if (stub_addr != (long) event->e_un.brk_addr) { + set_instruction_pointer (event->proc, event->e_un.brk_addr + 4); + continue_process(event->proc->pid); + return; + } + } + } +#endif + if ((sbp = event->proc->breakpoint_being_enabled) != 0) { + /* Reinsert breakpoint */ + continue_enabling_breakpoint(event->proc->pid, + event->proc-> + breakpoint_being_enabled); + event->proc->breakpoint_being_enabled = NULL; + return; + } + + for (i = event->proc->callstack_depth - 1; i >= 0; i--) { + if (event->e_un.brk_addr == + event->proc->callstack[i].return_addr) { +#ifdef __powerpc__ + /* + * PPC HACK! (XXX FIXME TODO) + * The PLT gets modified during the first call, + * so be sure to re-enable the breakpoint. + */ + unsigned long a; + struct library_symbol *libsym = + event->proc->callstack[i].c_un.libfunc; + void *addr = sym2addr(event->proc, libsym); + + if (libsym->plt_type != LS_TOPLT_POINT) { + unsigned char break_insn[] = BREAKPOINT_VALUE; + + sbp = address2bpstruct(event->proc, addr); + assert(sbp); + a = ptrace(PTRACE_PEEKTEXT, event->proc->pid, + addr); + + if (memcmp(&a, break_insn, BREAKPOINT_LENGTH)) { + sbp->enabled--; + insert_breakpoint(event->proc, addr, + libsym); + } + } else { + sbp = dict_find_entry(event->proc->breakpoints, sym2addr(event->proc, libsym)); + assert(sbp); + if (addr != sbp->addr) { + insert_breakpoint(event->proc, addr, + libsym); + } + } +#elif defined(__mips__) + void *addr; + void *old_addr; + struct library_symbol *sym= event->proc->callstack[i].c_un.libfunc; + assert(sym); + old_addr = dict_find_entry(event->proc->breakpoints, sym2addr(event->proc, sym))->addr; + addr=sym2addr(event->proc,sym); + assert(old_addr !=0 && addr !=0); + if(addr != old_addr){ + struct library_symbol *new_sym; + new_sym=malloc(sizeof(*new_sym)); + memcpy(new_sym,sym,sizeof(*new_sym)); + new_sym->next=event->proc->list_of_symbols; + event->proc->list_of_symbols=new_sym; + insert_breakpoint(event->proc, addr, new_sym); + } +#endif + for (j = event->proc->callstack_depth - 1; j > i; j--) { + callstack_pop(event->proc); + } + if (event->proc->state != STATE_IGNORED) { + if (opt_T || options.summary) { + calc_time_spent(event->proc); + } + } + callstack_pop(event->proc); + event->proc->return_addr = event->e_un.brk_addr; + if (event->proc->state != STATE_IGNORED) { + output_right(LT_TOF_FUNCTIONR, event->proc, + event->proc->callstack[i].c_un.libfunc->name); + } + continue_after_breakpoint(event->proc, + address2bpstruct(event->proc, + event->e_un.brk_addr)); + return; + } + } + + if ((sbp = address2bpstruct(event->proc, event->e_un.brk_addr))) { + if (event->proc->state != STATE_IGNORED) { + event->proc->stack_pointer = get_stack_pointer(event->proc); + event->proc->return_addr = + get_return_addr(event->proc, event->proc->stack_pointer); + output_left(LT_TOF_FUNCTION, event->proc, sbp->libsym->name); + callstack_push_symfunc(event->proc, sbp->libsym); + } +#ifdef PLT_REINITALISATION_BP + if (event->proc->need_to_reinitialize_breakpoints + && (strcmp(sbp->libsym->name, PLTs_initialized_by_here) == + 0)) + reinitialize_breakpoints(event->proc); +#endif + + continue_after_breakpoint(event->proc, sbp); + return; + } + + if (event->proc->state != STATE_IGNORED) { + output_line(event->proc, "unexpected breakpoint at %p", + (void *)event->e_un.brk_addr); + } + continue_process(event->proc->pid); +} + +static void +callstack_push_syscall(Process *proc, int sysnum) { + struct callstack_element *elem; + + debug(DEBUG_FUNCTION, "callstack_push_syscall(pid=%d, sysnum=%d)", proc->pid, sysnum); + /* FIXME: not good -- should use dynamic allocation. 19990703 mortene. */ + if (proc->callstack_depth == MAX_CALLDEPTH - 1) { + fprintf(stderr, "Error: call nesting too deep!\n"); + return; + } + + elem = &proc->callstack[proc->callstack_depth]; + elem->is_syscall = 1; + elem->c_un.syscall = sysnum; + elem->return_addr = NULL; + + proc->callstack_depth++; + if (opt_T || options.summary) { + struct timezone tz; + gettimeofday(&elem->time_spent, &tz); + } +} + +static void +callstack_push_symfunc(Process *proc, struct library_symbol *sym) { + struct callstack_element *elem; + + debug(DEBUG_FUNCTION, "callstack_push_symfunc(pid=%d, symbol=%s)", proc->pid, sym->name); + /* FIXME: not good -- should use dynamic allocation. 19990703 mortene. */ + if (proc->callstack_depth == MAX_CALLDEPTH - 1) { + fprintf(stderr, "Error: call nesting too deep!\n"); + return; + } + + elem = &proc->callstack[proc->callstack_depth]; + elem->is_syscall = 0; + elem->c_un.libfunc = sym; + + elem->return_addr = proc->return_addr; + if (elem->return_addr) { + insert_breakpoint(proc, elem->return_addr, 0); + } + + proc->callstack_depth++; + if (opt_T || options.summary) { + struct timezone tz; + gettimeofday(&elem->time_spent, &tz); + } +} + +static void +callstack_pop(Process *proc) { + struct callstack_element *elem; + assert(proc->callstack_depth > 0); + + debug(DEBUG_FUNCTION, "callstack_pop(pid=%d)", proc->pid); + elem = &proc->callstack[proc->callstack_depth - 1]; + if (!elem->is_syscall && elem->return_addr) { + delete_breakpoint(proc, elem->return_addr); + } + proc->callstack_depth--; +} diff --git a/libltrace.c b/libltrace.c new file mode 100644 index 0000000..0f48d11 --- /dev/null +++ b/libltrace.c @@ -0,0 +1,154 @@ +#include "config.h" + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <errno.h> +#include <sys/param.h> +#include <signal.h> +#include <sys/wait.h> + +#include "common.h" + +char *command = NULL; +Process *list_of_processes = NULL; + +int exiting = 0; /* =1 if a SIGINT or SIGTERM has been received */ + +static void +signal_alarm(int sig) { + Process *tmp = list_of_processes; + + signal(SIGALRM, SIG_DFL); + while (tmp) { + struct opt_p_t *tmp2 = opt_p; + while (tmp2) { + if (tmp->pid == tmp2->pid) { + tmp = tmp->next; + if (!tmp) { + return; + } + tmp2 = opt_p; + continue; + } + tmp2 = tmp2->next; + } + debug(2, "Sending SIGSTOP to process %u\n", tmp->pid); + kill(tmp->pid, SIGSTOP); + tmp = tmp->next; + } +} + +static void +signal_exit(int sig) { + exiting = 1; + debug(1, "Received interrupt signal; exiting..."); + signal(SIGINT, SIG_IGN); + signal(SIGTERM, SIG_IGN); + signal(SIGALRM, signal_alarm); + if (opt_p) { + struct opt_p_t *tmp = opt_p; + while (tmp) { + debug(2, "Sending SIGSTOP to process %u\n", tmp->pid); + kill(tmp->pid, SIGSTOP); + tmp = tmp->next; + } + } + alarm(1); +} + +static void +normal_exit(void) { + output_line(0, 0); + if (options.summary) { + show_summary(); + } + if (options.output) { + fclose(options.output); + options.output = NULL; + } +} + +void +ltrace_init(int argc, char **argv) { + struct opt_p_t *opt_p_tmp; + + atexit(normal_exit); + signal(SIGINT, signal_exit); /* Detach processes when interrupted */ + signal(SIGTERM, signal_exit); /* ... or killed */ + + argv = process_options(argc, argv); + while (opt_F) { + /* If filename begins with ~, expand it to the user's home */ + /* directory. This does not correctly handle ~yoda, but that */ + /* isn't as bad as it seems because the shell will normally */ + /* be doing the expansion for us; only the hardcoded */ + /* ~/.ltrace.conf should ever use this code. */ + if (opt_F->filename[0] == '~') { + char path[PATH_MAX]; + char *home_dir = getenv("HOME"); + if (home_dir) { + strncpy(path, home_dir, PATH_MAX - 1); + path[PATH_MAX - 1] = '\0'; + strncat(path, opt_F->filename + 1, + PATH_MAX - strlen(path) - 1); + read_config_file(path); + } + } else { + read_config_file(opt_F->filename); + } + opt_F = opt_F->next; + } + if (opt_e) { + struct opt_e_t *tmp = opt_e; + while (tmp) { + debug(1, "Option -e: %s\n", tmp->name); + tmp = tmp->next; + } + } + if (command) { + execute_program(open_program(command, 0), argv); + } + opt_p_tmp = opt_p; + while (opt_p_tmp) { + open_pid(opt_p_tmp->pid); + opt_p_tmp = opt_p_tmp->next; + } +} + +static int num_ltrace_callbacks[EVENT_MAX]; +static callback_func * ltrace_callbacks[EVENT_MAX]; + +void +ltrace_add_callback(callback_func func, Event_type type) { + ltrace_callbacks[type] = realloc(ltrace_callbacks[type], (num_ltrace_callbacks[type]+1)*sizeof(callback_func)); + ltrace_callbacks[type][num_ltrace_callbacks[type]++] = func; +} + +static void +dispatch_callbacks(Event * ev) { + int i; + /* Ignoring case 1: signal into a dying tracer */ + if (ev->type==EVENT_SIGNAL && + exiting && ev->e_un.signum == SIGSTOP) { + return; + } + /* Ignoring case 2: process being born before a clone event */ + if (ev->proc && ev->proc->state == STATE_IGNORED) { + return; + } + for (i=0; i<num_ltrace_callbacks[ev->type]; i++) { + ltrace_callbacks[ev->type][i](ev); + } +} + +void +ltrace_main(void) { + Event * ev; + while (1) { + ev = next_event(); + dispatch_callbacks(ev); + handle_event(ev); + } +} diff --git a/ltrace.1 b/ltrace.1 new file mode 100644 index 0000000..358d6aa --- /dev/null +++ b/ltrace.1 @@ -0,0 +1,206 @@ +.\" Copyright (c) 1997-2005 Juan Cespedes <cespedes@debian.org> +.\" This file is covered by the GNU GPL +.TH ltrace 1 +.SH NAME +ltrace \- A library call tracer + +.SH SYNOPSIS +.B ltrace +.I "[-CfhiLrStttV] [-a column] [-A maxelts] [-D level] [-e expr] [-l filename] [-n nr] [-o filename] [-p pid] ... [-s strsize] [-u username] [-X extern] [-x extern] ... [--align=column] [--debug=level] [--demangle] [--help] [--indent=nr] [--library=filename] [--output=filename] [--version] [command [arg ...]]" + +.SH DESCRIPTION +.B ltrace +is a program that simply runs the specified +.I command +until it exits. It intercepts and records the dynamic library calls +which are called by the executed process and the signals which are +received by that process. +It can also intercept and print the system calls executed by the program. +.PP +Its use is very similar to +.BR strace(1) . + +.SH OPTIONS +.TP +.I \-a, \-\-align column +Align return values in a specific column (default column is 5/8 of screen width). +.TP +.I \-A maxelts +Maximum number of array elements to print before suppressing the rest with an ellipsis ("...") +.TP +.I \-c +Count time and calls for each library call and report a summary on program exit. +.TP +.I \-C, \-\-demangle +Decode (demangle) low-level symbol names into user-level names. +Besides removing any initial underscore prefix used by the system, +this makes C++ function names readable. +.TP +.I \-D, \-\-debug level +Show debugging output of +.B ltrace +itself. +.I level +must be a sum of some of the following numbers: +.RS +.TP +.B 01 +DEBUG_GENERAL. Shows helpful progress information +.TP +.B 010 +DEBUG_EVENT. Shows every event received by a traced program +.TP +.B 020 +DEBUG_PROCESS. Shows every action +.B ltrace +carries upon a traced process +.TP +.B 040 +DEBUG_FUNCTION. Shows every entry to internal functions +.RE +.TP +.I \-e expr +A qualifying expression which modifies which events to trace. +The format of the expression is: +.br +[!]value1[,value2]... +.br +where the values are the functions to trace. Using an exclamation +mark negates the set of values. For example +.I \-e printf +means to trace only the printf library call. By contrast, +.I \-e !printf +means to trace every library call except printf. +.IP +Note that some shells use the exclamation point for history +expansion; even inside quoted arguments. If so, you must escape +the exclamation point with a backslash. +.TP +.I \-f +Trace child processes as they are created by +currently traced processes as a result of the fork(2) +or clone(2) system calls. +The new process is attached immediately. +.TP +.I \-F +Load an alternate config file. Normally, /etc/ltrace.conf and +~/.ltrace.conf will be read (the latter only if it exists). +Use this option to load the given file or files instead of +those two default files. +.TP +.I \-h, \-\-help +Show a summary of the options to ltrace and exit. +.TP +.I \-i +Print the instruction pointer at the time of the library call. +.TP +.I \-l, \-\-library filename +Display only the symbols included in the library +.I filename. +Up to 30 library names can be specified with several instances +of this option. +.TP +.I \-L +DON'T display library calls (use it with the +.I \-S +option). +.TP +.I \-n, \-\-indent nr +Indent trace output by +.I nr +number of spaces for each new nested call. Using this option makes +the program flow visualization easy to follow. +.TP +.I \-o, \-\-output filename +Write the trace output to the file +.I filename +rather than to stderr. +.TP +.I \-p pid +Attach to the process with the process ID +.I pid +and begin tracing. +.TP +.I \-r +Print a relative timestamp with each line of the trace. +This records the time difference between the beginning of +successive lines. +.TP +.I \-s strsize +Specify the maximum string size to print (the default is 32). +.TP +.I \-S +Display system calls as well as library calls +.TP +.I \-t +Prefix each line of the trace with the time of day. +.TP +.I \-tt +If given twice, the time printed will include the microseconds. +.TP +.I \-ttt +If given thrice, the time printed will include the microseconds and +the leading portion will be printed as the number of seconds since the +epoch. +.TP +.I \-T +Show the time spent inside each call. This records the time difference +between the beginning and the end of each call. +.TP +.I \-u username +Run command with the userid, groupid and supplementary groups of +.IR username . +This option is only useful when running as root and enables the +correct execution of setuid and/or setgid binaries. +.TP +.I \-X extern +Some architectures need to know where to set a breakpoint that will be hit +after the dynamic linker has run. If this flag is used, then the breakpoint +is set at +.IR extern , +which must be an external function. By default, '_start' is used. +NOTE: this flag is only available on the architectures that need it. +.TP +.I \-x extern +Trace the external function +.IR extern . +This option may be repeated. +.TP +.I \-V, \-\-version +Show the version number of ltrace and exit. + +.SH BUGS +It has most of the bugs stated in +.BR strace(1) . +.LP +Manual page and documentation are not very up-to-date. +.LP +Option -f sometimes fails to trace some children. +.LP +It only works on Linux and in a small subset of architectures. +.LP +Only ELF32 binaries are supported. +.LP +Calls to dlopen()ed libraries will not be traced. +.PP +If you would like to report a bug, send a message to the mailing list +(ltrace-devel@lists.alioth.debian.org), or use the +.BR reportbug(1) +program if you are under the Debian GNU/Linux distribution. + +.SH FILES +.TP +.I /etc/ltrace.conf +System configuration file +.TP +.I ~/.ltrace.conf +Personal config file, overrides +.I /etc/ltrace.conf + +.SH AUTHOR +Juan Cespedes <cespedes@debian.org> + +.SH "SEE ALSO" +.BR strace(1) , +.BR ptrace(2) + diff --git a/ltrace.h b/ltrace.h new file mode 100644 index 0000000..5e43ba5 --- /dev/null +++ b/ltrace.h @@ -0,0 +1,38 @@ +typedef enum Event_type Event_type; +enum Event_type { + EVENT_NONE=0, + EVENT_SIGNAL, + EVENT_EXIT, + EVENT_EXIT_SIGNAL, + EVENT_SYSCALL, + EVENT_SYSRET, + EVENT_ARCH_SYSCALL, + EVENT_ARCH_SYSRET, + EVENT_CLONE, + EVENT_EXEC, + EVENT_BREAKPOINT, + EVENT_LIBCALL, + EVENT_LIBRET, + EVENT_NEW, /* in this case, proc is NULL */ + EVENT_MAX +}; + +typedef struct Process Process; +typedef struct Event Event; +struct Event { + Process * proc; + Event_type type; + union { + int ret_val; /* EVENT_EXIT */ + int signum; /* EVENT_SIGNAL, EVENT_EXIT_SIGNAL */ + int sysnum; /* EVENT_SYSCALL, EVENT_SYSRET */ + void * brk_addr; /* EVENT_BREAKPOINT */ + int newpid; /* EVENT_CLONE, EVENT_NEW */ + } e_un; +}; + +typedef void (*callback_func) (Event *); + +extern void ltrace_init(int argc, char **argv); +extern void ltrace_add_callback(callback_func f, Event_type type); +extern void ltrace_main(void); diff --git a/ltrace.spec b/ltrace.spec new file mode 100644 index 0000000..3740190 --- /dev/null +++ b/ltrace.spec @@ -0,0 +1,164 @@ +Summary: Tracks runtime library calls from dynamically linked executables. +Name: ltrace +Version: 0.3.36 +Release: 4.2 +Source: ftp://ftp.debian.org/debian/pool/main/l/ltrace/ltrace_%{version}.orig.tar.gz +Patch1: ftp://ftp.debian.org/debian/pool/main/l/ltrace/ltrace_0.3.36-2.diff.gz +Patch2: ltrace-ppc64.patch +Patch3: ltrace-ppc64-2.patch +Patch4: ltrace-s390x.patch +Patch5: ltrace-syscallent-update.patch +Patch6: ltrace-fixes.patch +Patch7: ltrace-ia64.patch +License: GPL +Group: Development/Debuggers +ExclusiveArch: i386 x86_64 ia64 ppc ppc64 s390 s390x alpha sparc +Prefix: %{_prefix} +BuildRoot: /var/tmp/%{name}-root +BuildRequires: elfutils-libelf-devel + +%description +Ltrace is a debugging program which runs a specified command until the +command exits. While the command is executing, ltrace intercepts and +records both the dynamic library calls called by the executed process +and the signals received by the executed process. Ltrace can also +intercept and print system calls executed by the process. + +You should install ltrace if you need a sysadmin tool for tracking the +execution of processes. + +%prep +%setup -q +%patch1 -p1 +%patch2 -p1 +%patch3 -p1 +%patch4 -p1 +%patch5 -p1 +%patch6 -p1 +%patch7 -p1 +sed -i -e 's/-o root -g root//' Makefile.in + +%build +export CC="gcc`echo $RPM_OPT_FLAGS | sed -n 's/^.*\(-m[36][124]\).*$/ \1/p'`" +%configure CC="$CC" +make + +%install +make DESTDIR=$RPM_BUILD_ROOT mandir=%{_mandir} install +rm -f ChangeLog; mv -f debian/changelog ChangeLog +rm -rf $RPM_BUILD_ROOT/%{_prefix}/doc + +%clean +rm -rf $RPM_BUILD_ROOT + +%files +%defattr(-,root,root) +%doc COPYING README TODO BUGS ChangeLog +%{_prefix}/bin/ltrace +%{_mandir}/man1/ltrace.1* +%config /etc/ltrace.conf + +%changelog +* Fri Feb 10 2006 Jesse Keating <jkeating@redhat.com> - 0.3.36-4.2 +- bump again for double-long bug on ppc(64) + +* Tue Feb 07 2006 Jesse Keating <jkeating@redhat.com> - 0.3.36-4.1 +- rebuilt for new gcc4.1 snapshot and glibc changes + +* Mon Jan 9 2006 Jakub Jelinek <jakub@redhat.com> 0.3.36-4 +- added ppc64 and s390x support (IBM) +- added ia64 support (Ian Wienand) + +* Sat Mar 5 2005 Jakub Jelinek <jakub@redhat.com> 0.3.36-3 +- rebuilt with GCC 4 + +* Tue Dec 14 2004 Jakub Jelinek <jakub@redhat.com> 0.3.36-2 +- make x86_64 ltrace trace both 32-bit and 64-bit binaries (#141955, + IT#55600) +- fix tracing across execve +- fix printf-style format handling on 64-bit arches + +* Thu Nov 18 2004 Jakub Jelinek <jakub@redhat.com> 0.3.36-1 +- update to 0.3.36 + +* Mon Oct 11 2004 Jakub Jelinek <jakub@redhat.com> 0.3.35-1 +- update to 0.3.35 +- update syscall tables from latest kernel source + +* Tue Jun 15 2004 Elliot Lee <sopwith@redhat.com> +- rebuilt + +* Tue Jun 8 2004 Jakub Jelinek <jakub@redhat.com> 0.3.32-3 +- buildreq elfutils-libelf-devel (#124921) + +* Thu Apr 22 2004 Jakub Jelinek <jakub@redhat.com> 0.3.32-2 +- fix demangling + +* Thu Apr 22 2004 Jakub Jelinek <jakub@redhat.com> 0.3.32-1 +- update to 0.3.32 + - fix dict.c assertion (#114359) + - x86_64 support +- rewrite elf.[ch] using libelf +- don't rely on st_value of SHN_UNDEF symbols in binaries, + instead walk .rel{,a}.plt and compute the addresses (#115299) +- fix x86-64 support +- some ltrace.conf additions +- some format string printing fixes + +* Fri Feb 13 2004 Elliot Lee <sopwith@redhat.com> +- rebuilt + +* Mon Feb 3 2003 Jakub Jelinek <jakub@redhat.com> 0.3.29-1 +- update to 0.3.29 + +* Wed Jan 22 2003 Tim Powers <timp@redhat.com> +- rebuilt + +* Sun Sep 1 2002 Jakub Jelinek <jakub@redhat.com> 0.3.10-12 +- add a bunch of missing functions to ltrace.conf + (like strlen, ugh) + +* Fri Jun 21 2002 Tim Powers <timp@redhat.com> +- automated rebuild + +* Tue May 28 2002 Phil Knirsch <pknirsch@redhat.com> +- Added the 'official' s390 patch. + +* Thu May 23 2002 Tim Powers <timp@redhat.com> +- automated rebuild + +* Wed Jan 09 2002 Tim Powers <timp@redhat.com> +- automated rebuild + +* Fri Jul 20 2001 Jakub Jelinek <jakub@redhat.com> +- fix stale symlink in documentation directory (#47749) + +* Sun Jun 24 2001 Elliot Lee <sopwith@redhat.com> +- Bump release + rebuild. + +* Thu Aug 2 2000 Tim Waugh <twaugh@redhat.com> +- fix off-by-one problem in checking syscall number + +* Wed Jul 12 2000 Prospector <bugzilla@redhat.com> +- automatic rebuild + +* Mon Jun 19 2000 Matt Wilson <msw@redhat.com> +- rebuilt for next release +- patched Makefile.in to take a hint on mandir (patch2) +- use %%{_mandir} and %%makeinstall + +* Wed Feb 02 2000 Cristian Gafton <gafton@redhat.com> +- fix description + +* Fri Jan 7 2000 Jeff Johnson <jbj@redhat.com> +- update to 0.3.10. +- include (but don't apply) sparc patch from Jakub Jellinek. + +* Sun Mar 21 1999 Cristian Gafton <gafton@redhat.com> +- auto rebuild in the new build environment (release 2) + +* Fri Mar 12 1999 Jeff Johnson <jbj@redhat.com> +- update to 0.3.6. + +* Mon Sep 21 1998 Preston Brown <pbrown@redhat.com> +- upgraded to 0.3.4 @@ -0,0 +1,37 @@ +#include <stdio.h> +#include <unistd.h> + +#include "ltrace.h" + +/* +static int count_call =0; +static int count_ret =0; + +static void +callback_call(Event * ev) { + count_call ++; +} +static void +callback_ret(Event * ev) { + count_ret ++; +} + +static void +endcallback(Event *ev) { + printf("%d calls\n%d rets\n",count_call, count_ret); +} +*/ + +int +main(int argc, char *argv[]) { + ltrace_init(argc, argv); + +/* + ltrace_add_callback(callback_call, EVENT_SYSCALL); + ltrace_add_callback(callback_ret, EVENT_SYSRET); + ltrace_add_callback(endcallback, EVENT_EXIT); +*/ + + ltrace_main(); + return 0; +} @@ -0,0 +1,19 @@ +#!/bin/sh -e + +# Create ltrace-${version}.tar.gz from a GIT repository + +if [ ! -d .git -o ! -f libltrace.c ] +then + echo "This must be called inside a ltrace GIT repository" 1>&2 + exit 1 +fi + +VERSION=$( cat VERSION ) + +echo Building ltrace-$VERSION.tar.gz ... +rm -rf ltrace-$VERSION +git clone ./ ltrace-$VERSION >/dev/null +GZIP=-9 tar --exclude .git -zcf ltrace-$VERSION.tar.gz ltrace-$VERSION +rm -rf ltrace-$VERSION +echo Done. + diff --git a/options.c b/options.c new file mode 100644 index 0000000..aef73b1 --- /dev/null +++ b/options.c @@ -0,0 +1,431 @@ +#include "config.h" + +#include <string.h> +#include <stdlib.h> +#include <unistd.h> +#include <fcntl.h> +#include <errno.h> +#include <limits.h> +#include <sys/ioctl.h> + +#include <getopt.h> + +#include "common.h" + +#ifndef SYSCONFDIR +#define SYSCONFDIR "/etc" +#endif + +#define SYSTEM_CONFIG_FILE SYSCONFDIR "/ltrace.conf" +#define USER_CONFIG_FILE "~/.ltrace.conf" + +struct options_t options = { + .align = DEFAULT_ALIGN, /* alignment column for results */ + .user = NULL, /* username to run command as */ + .syscalls = 0, /* display syscalls */ + .libcalls = 1, /* display library calls */ +#ifdef USE_DEMANGLE + .demangle = 0, /* Demangle low-level symbol names */ +#endif + .indent = 0, /* indent output according to program flow */ + .output = NULL, /* output to a specific file */ + .summary = 0, /* Report a summary on program exit */ + .debug = 0, /* debug */ + .arraylen = DEFAULT_ARRAYLEN, /* maximum # array elements to print */ + .strlen = DEFAULT_STRLEN, /* maximum # of bytes printed in strings */ + .follow = 0, /* trace child processes */ +}; + +char *library[MAX_LIBRARIES]; +int library_num = 0; +static char *progname; /* Program name (`ltrace') */ +int opt_i = 0; /* instruction pointer */ +int opt_r = 0; /* print relative timestamp */ +int opt_t = 0; /* print absolute timestamp */ +int opt_T = 0; /* show the time spent inside each call */ + +/* List of pids given to option -p: */ +struct opt_p_t *opt_p = NULL; /* attach to process with a given pid */ + +/* List of function names given to option -e: */ +struct opt_e_t *opt_e = NULL; +int opt_e_enable = 1; + +/* List of global function names given to -x: */ +struct opt_x_t *opt_x = NULL; + +/* List of filenames give to option -F: */ +struct opt_F_t *opt_F = NULL; /* alternate configuration file(s) */ + +#ifdef PLT_REINITALISATION_BP +/* Set a break on the routine named here in order to re-initialize breakpoints + after all the PLTs have been initialzed */ +char *PLTs_initialized_by_here = PLT_REINITALISATION_BP; +#endif + +static void +err_usage(void) { + fprintf(stderr, "Try `%s --help' for more information\n", progname); + exit(1); +} + +static void +usage(void) { + fprintf(stdout, "Usage: %s [option ...] [command [arg ...]]\n" + "Trace library calls of a given program.\n\n" + " -a, --align=COLUMN align return values in a secific column.\n" + " -A ARRAYLEN maximum number of array elements to print.\n" + " -c count time and calls, and report a summary on exit.\n" +# ifdef USE_DEMANGLE + " -C, --demangle decode low-level symbol names into user-level names.\n" +# endif + " -D, --debug=LEVEL enable debugging (see -Dh or --debug=help).\n" + " -Dh, --debug=help show help on debugging.\n" + " -e expr modify which events to trace.\n" + " -f trace children (fork() and clone()).\n" + " -F, --config=FILE load alternate configuration file (may be repeated).\n" + " -h, --help display this help and exit.\n" + " -i print instruction pointer at time of library call.\n" + " -l, --library=FILE print library calls from this library only.\n" + " -L do NOT display library calls.\n" + " -n, --indent=NR indent output by NR spaces for each call level nesting.\n" + " -o, --output=FILE write the trace output to that file.\n" + " -p PID attach to the process with the process ID pid.\n" + " -r print relative timestamps.\n" + " -s STRLEN specify the maximum string size to print.\n" + " -S display system calls.\n" + " -t, -tt, -ttt print absolute timestamps.\n" + " -T show the time spent inside each call.\n" + " -u USERNAME run command with the userid, groupid of username.\n" + " -V, --version output version information and exit.\n" + " -x NAME treat the global NAME like a library subroutine.\n" +#ifdef PLT_REINITALISATION_BP + " -X NAME same as -x; and PLT's will be initialized by here.\n" +#endif + "\nReport bugs to ltrace-devel@lists.alioth.debian.org\n", + progname); +} + +static void +usage_debug(void) { + fprintf(stdout, "%s debugging option, --debug=<octal> or -D<octal>:\n", progname); + fprintf(stdout, + "\n" + " number ref. in source description\n" + " 1 general Generally helpful progress information\n" + " 10 event Shows every event received by a traced process\n" + " 20 process Shows actions carried upon a traced processes\n" + " 40 function Shows every entry to internal functions\n" + "\n" + "Debugging options are mixed using bitwise-or.\n" + "Note that the meanings and values are subject to change.\n" + ); +} + +static char * +search_for_command(char *filename) { + static char pathname[PATH_MAX]; + char *path; + int m, n; + + if (strchr(filename, '/')) { + return filename; + } + for (path = getenv("PATH"); path && *path; path += m) { + if (strchr(path, ':')) { + n = strchr(path, ':') - path; + m = n + 1; + } else { + m = n = strlen(path); + } + if (n + strlen(filename) + 1 >= PATH_MAX) { + fprintf(stderr, "Error: filename too long\n"); + exit(1); + } + strncpy(pathname, path, n); + if (n && pathname[n - 1] != '/') { + pathname[n++] = '/'; + } + strcpy(pathname + n, filename); + if (!access(pathname, X_OK)) { + return pathname; + } + } + return filename; +} + +static void +guess_cols(void) { + struct winsize ws; + char *c; + + options.align = DEFAULT_ALIGN; + c = getenv("COLUMNS"); + if (c && *c) { + char *endptr; + int cols; + cols = strtol(c, &endptr, 0); + if (cols > 0 && !*endptr) { + options.align = cols * 5 / 8; + } + } else if (ioctl(1, TIOCGWINSZ, &ws) != -1 && ws.ws_col > 0) { + options.align = ws.ws_col * 5 / 8; + } else if (ioctl(2, TIOCGWINSZ, &ws) != -1 && ws.ws_col > 0) { + options.align = ws.ws_col * 5 / 8; + } +} + +char ** +process_options(int argc, char **argv) { + progname = argv[0]; + options.output = stderr; + + guess_cols(); + + while (1) { + int c; + char *p; + int option_index = 0; + static struct option long_options[] = { + {"align", 1, 0, 'a'}, + {"config", 1, 0, 'F'}, + {"debug", 1, 0, 'D'}, +# ifdef USE_DEMANGLE + {"demangle", 0, 0, 'C'}, +#endif + {"indent", 1, 0, 'n'}, + {"help", 0, 0, 'h'}, + {"library", 1, 0, 'l'}, + {"output", 1, 0, 'o'}, + {"version", 0, 0, 'V'}, + {0, 0, 0, 0} + }; + c = getopt_long(argc, argv, "+cfhiLrStTV" +# ifdef USE_DEMANGLE + "C" +# endif + "a:A:D:e:F:l:n:o:p:s:u:x:X:", long_options, + &option_index); + if (c == -1) { + break; + } + switch (c) { + case 'a': + options.align = atoi(optarg); + break; + case 'A': + options.arraylen = atoi(optarg); + break; + case 'c': + options.summary++; + break; +#ifdef USE_DEMANGLE + case 'C': + options.demangle++; + break; +#endif + case 'D': + if (optarg[0]=='h') { + usage_debug(); + exit(0); + } + options.debug = strtoul(optarg,&p,8); + if (*p) { + fprintf(stderr, "%s: --debug requires an octal argument\n", progname); + err_usage(); + } + break; + case 'e': + { + char *str_e = strdup(optarg); + if (!str_e) { + perror("ltrace: strdup"); + exit(1); + } + if (str_e[0] == '!') { + opt_e_enable = 0; + str_e++; + } + while (*str_e) { + struct opt_e_t *tmp; + char *str2 = strchr(str_e, ','); + if (str2) { + *str2 = '\0'; + } + tmp = malloc(sizeof(struct opt_e_t)); + if (!tmp) { + perror("ltrace: malloc"); + exit(1); + } + tmp->name = str_e; + tmp->next = opt_e; + opt_e = tmp; + if (str2) { + str_e = str2 + 1; + } else { + break; + } + } + break; + } + case 'f': + options.follow = 1; + break; + case 'F': + { + struct opt_F_t *tmp = malloc(sizeof(struct opt_F_t)); + if (!tmp) { + perror("ltrace: malloc"); + exit(1); + } + tmp->filename = strdup(optarg); + tmp->next = opt_F; + opt_F = tmp; + break; + } + case 'h': + usage(); + exit(0); + case 'i': + opt_i++; + break; + case 'l': + if (library_num == MAX_LIBRARIES) { + fprintf(stderr, + "Too many libraries. Maximum is %i.\n", + MAX_LIBRARIES); + exit(1); + } + library[library_num++] = optarg; + break; + case 'L': + options.libcalls = 0; + break; + case 'n': + options.indent = atoi(optarg); + break; + case 'o': + options.output = fopen(optarg, "w"); + if (!options.output) { + fprintf(stderr, + "Can't open %s for output: %s\n", + optarg, strerror(errno)); + exit(1); + } + setvbuf(options.output, (char *)NULL, _IOLBF, 0); + fcntl(fileno(options.output), F_SETFD, FD_CLOEXEC); + break; + case 'p': + { + struct opt_p_t *tmp = malloc(sizeof(struct opt_p_t)); + if (!tmp) { + perror("ltrace: malloc"); + exit(1); + } + tmp->pid = atoi(optarg); + tmp->next = opt_p; + opt_p = tmp; + break; + } + case 'r': + opt_r++; + break; + case 's': + options.strlen = atoi(optarg); + break; + case 'S': + options.syscalls = 1; + break; + case 't': + opt_t++; + break; + case 'T': + opt_T++; + break; + case 'u': + options.user = optarg; + break; + case 'V': + printf("ltrace version " PACKAGE_VERSION ".\n" + "Copyright (C) 1997-2009 Juan Cespedes <cespedes@debian.org>.\n" + "This is free software; see the GNU General Public Licence\n" + "version 2 or later for copying conditions. There is NO warranty.\n"); + exit(0); + case 'X': +#ifdef PLT_REINITALISATION_BP + PLTs_initialized_by_here = optarg; +#else + fprintf(stderr, "WARNING: \"-X\" not used for this " + "architecture: assuming you meant \"-x\"\n"); +#endif + /* Fall Thru */ + + case 'x': + { + struct opt_x_t *p = opt_x; + + /* First, check for duplicate. */ + while (p && strcmp(p->name, optarg)) { + p = p->next; + } + if (p) { + break; + } + + /* If not duplicate, add to list. */ + p = malloc(sizeof(struct opt_x_t)); + if (!p) { + perror("ltrace: malloc"); + exit(1); + } + p->name = optarg; + p->found = 0; + p->next = opt_x; + opt_x = p; + break; + } + + default: + err_usage(); + } + } + argc -= optind; + argv += optind; + + if (!opt_F) { + opt_F = malloc(sizeof(struct opt_F_t)); + opt_F->next = malloc(sizeof(struct opt_F_t)); + opt_F->next->next = NULL; + opt_F->filename = USER_CONFIG_FILE; + opt_F->next->filename = SYSTEM_CONFIG_FILE; + } + /* Reverse the config file list since it was built by + * prepending, and it would make more sense to process the + * files in the order they were given. Probably it would make + * more sense to keep a tail pointer instead? */ + { + struct opt_F_t *egg = NULL; + struct opt_F_t *chicken; + while (opt_F) { + chicken = opt_F->next; + opt_F->next = egg; + egg = opt_F; + opt_F = chicken; + } + opt_F = egg; + } + + if (!opt_p && argc < 1) { + fprintf(stderr, "%s: too few arguments\n", progname); + err_usage(); + } + if (opt_r && opt_t) { + fprintf(stderr, "%s: Incompatible options -r and -t\n", + progname); + err_usage(); + } + if (argc > 0) { + command = search_for_command(argv[0]); + } + return &argv[0]; +} diff --git a/options.h b/options.h new file mode 100644 index 0000000..db253c5 --- /dev/null +++ b/options.h @@ -0,0 +1,55 @@ +#include <stdio.h> +#include <sys/types.h> + +struct options_t { + int align; /* -a: default alignment column for results */ + char * user; /* -u: username to run command as */ + int syscalls; /* -S: display system calls */ + int libcalls; /* -L: display library calls */ + int demangle; /* -C: demangle low-level names into user-level names */ + int indent; /* -n: indent trace output according to program flow */ + FILE *output; /* output to a specific file */ + int summary; /* count time, calls, and report a summary on program exit */ + int debug; /* debug */ + int arraylen; /* default maximum # of array elements printed */ + int strlen; /* default maximum # of bytes printed in strings */ + int follow; /* trace child processes */ +}; +extern struct options_t options; + +extern int opt_i; /* instruction pointer */ +extern int opt_r; /* print relative timestamp */ +extern int opt_t; /* print absolute timestamp */ +extern int opt_T; /* show the time spent inside each call */ + +struct opt_p_t { + pid_t pid; + struct opt_p_t *next; +}; + +struct opt_e_t { + char *name; + struct opt_e_t *next; +}; + +struct opt_F_t { + char *filename; + struct opt_F_t *next; +}; + +struct opt_x_t { + char *name; + int found; + struct opt_x_t *next; +}; + +extern struct opt_p_t *opt_p; /* attach to process with a given pid */ + +extern struct opt_e_t *opt_e; /* list of function names to display */ +extern int opt_e_enable; /* 0 if '!' is used, 1 otherwise */ + +extern struct opt_F_t *opt_F; /* alternate configuration file(s) */ + +extern struct opt_x_t *opt_x; /* list of functions to break at */ + +extern char **process_options(int argc, char **argv); diff --git a/output.c b/output.c new file mode 100644 index 0000000..bb07ab1 --- /dev/null +++ b/output.c @@ -0,0 +1,303 @@ +#include "config.h" + +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include <time.h> +#include <sys/time.h> +#include <unistd.h> + +#include "common.h" + +/* TODO FIXME XXX: include in common.h: */ +extern struct timeval current_time_spent; + +Dict *dict_opt_c = NULL; + +static Process *current_proc = 0; +static int current_depth = 0; +static int current_column = 0; + +static void +output_indent(Process *proc) { + current_column += + fprintf(options.output, "%*s", options.indent * proc->callstack_depth, ""); +} + +static void +begin_of_line(enum tof type, Process *proc) { + current_column = 0; + if (!proc) { + return; + } + if ((options.output != stderr) && (opt_p || options.follow)) { + current_column += fprintf(options.output, "%u ", proc->pid); + } else if (options.follow) { + current_column += fprintf(options.output, "[pid %u] ", proc->pid); + } + if (opt_r) { + struct timeval tv; + struct timezone tz; + static struct timeval old_tv = { 0, 0 }; + struct timeval diff; + + gettimeofday(&tv, &tz); + + if (old_tv.tv_sec == 0 && old_tv.tv_usec == 0) { + old_tv.tv_sec = tv.tv_sec; + old_tv.tv_usec = tv.tv_usec; + } + diff.tv_sec = tv.tv_sec - old_tv.tv_sec; + if (tv.tv_usec >= old_tv.tv_usec) { + diff.tv_usec = tv.tv_usec - old_tv.tv_usec; + } else { + diff.tv_sec--; + diff.tv_usec = 1000000 + tv.tv_usec - old_tv.tv_usec; + } + old_tv.tv_sec = tv.tv_sec; + old_tv.tv_usec = tv.tv_usec; + current_column += fprintf(options.output, "%3lu.%06d ", + diff.tv_sec, (int)diff.tv_usec); + } + if (opt_t) { + struct timeval tv; + struct timezone tz; + + gettimeofday(&tv, &tz); + if (opt_t > 2) { + current_column += fprintf(options.output, "%lu.%06d ", + tv.tv_sec, (int)tv.tv_usec); + } else if (opt_t > 1) { + struct tm *tmp = localtime(&tv.tv_sec); + current_column += + fprintf(options.output, "%02d:%02d:%02d.%06d ", + tmp->tm_hour, tmp->tm_min, tmp->tm_sec, + (int)tv.tv_usec); + } else { + struct tm *tmp = localtime(&tv.tv_sec); + current_column += fprintf(options.output, "%02d:%02d:%02d ", + tmp->tm_hour, tmp->tm_min, + tmp->tm_sec); + } + } + if (opt_i) { + if (type == LT_TOF_FUNCTION || type == LT_TOF_FUNCTIONR) { + current_column += fprintf(options.output, "[%p] ", + proc->return_addr); + } else { + current_column += fprintf(options.output, "[%p] ", + proc->instruction_pointer); + } + } + if (options.indent > 0 && type != LT_TOF_NONE) { + output_indent(proc); + } +} + +static Function * +name2func(char *name) { + Function *tmp; + const char *str1, *str2; + + tmp = list_of_functions; + while (tmp) { +#ifdef USE_DEMANGLE + str1 = options.demangle ? my_demangle(tmp->name) : tmp->name; + str2 = options.demangle ? my_demangle(name) : name; +#else + str1 = tmp->name; + str2 = name; +#endif + if (!strcmp(str1, str2)) { + + return tmp; + } + tmp = tmp->next; + } + return NULL; +} + +void +output_line(Process *proc, char *fmt, ...) { + va_list args; + + if (options.summary) { + return; + } + if (current_proc) { + if (current_proc->callstack[current_depth].return_addr) { + fprintf(options.output, " <unfinished ...>\n"); + } else { + fprintf(options.output, " <no return ...>\n"); + } + } + current_proc = 0; + if (!fmt) { + return; + } + begin_of_line(LT_TOF_NONE, proc); + + va_start(args, fmt); + vfprintf(options.output, fmt, args); + fprintf(options.output, "\n"); + va_end(args); + current_column = 0; +} + +static void +tabto(int col) { + if (current_column < col) { + fprintf(options.output, "%*s", col - current_column, ""); + } +} + +void +output_left(enum tof type, Process *proc, char *function_name) { + Function *func; + static arg_type_info *arg_unknown = NULL; + if (arg_unknown == NULL) + arg_unknown = lookup_prototype(ARGTYPE_UNKNOWN); + + if (options.summary) { + return; + } + if (current_proc) { + fprintf(options.output, " <unfinished ...>\n"); + current_proc = 0; + current_column = 0; + } + current_proc = proc; + current_depth = proc->callstack_depth; + proc->type_being_displayed = type; + begin_of_line(type, proc); +#ifdef USE_DEMANGLE + current_column += + fprintf(options.output, "%s(", + options.demangle ? my_demangle(function_name) : function_name); +#else + current_column += fprintf(options.output, "%s(", function_name); +#endif + + func = name2func(function_name); + if (!func) { + int i; + for (i = 0; i < 4; i++) { + current_column += + display_arg(type, proc, i, arg_unknown); + current_column += fprintf(options.output, ", "); + } + current_column += display_arg(type, proc, 4, arg_unknown); + return; + } else { + int i; + for (i = 0; i < func->num_params - func->params_right - 1; i++) { + current_column += + display_arg(type, proc, i, func->arg_info[i]); + current_column += fprintf(options.output, ", "); + } + if (func->num_params > func->params_right) { + current_column += + display_arg(type, proc, i, func->arg_info[i]); + if (func->params_right) { + current_column += fprintf(options.output, ", "); + } + } + if (func->params_right) { + save_register_args(type, proc); + } + } +} + +void +output_right(enum tof type, Process *proc, char *function_name) { + Function *func = name2func(function_name); + static arg_type_info *arg_unknown = NULL; + if (arg_unknown == NULL) + arg_unknown = lookup_prototype(ARGTYPE_UNKNOWN); + + if (options.summary) { + struct opt_c_struct *st; + if (!dict_opt_c) { + dict_opt_c = + dict_init(dict_key2hash_string, + dict_key_cmp_string); + } + st = dict_find_entry(dict_opt_c, function_name); + if (!st) { + char *na; + st = malloc(sizeof(struct opt_c_struct)); + na = strdup(function_name); + if (!st || !na) { + perror("malloc()"); + exit(1); + } + st->count = 0; + st->tv.tv_sec = st->tv.tv_usec = 0; + dict_enter(dict_opt_c, na, st); + } + if (st->tv.tv_usec + current_time_spent.tv_usec > 1000000) { + st->tv.tv_usec += current_time_spent.tv_usec - 1000000; + st->tv.tv_sec++; + } else { + st->tv.tv_usec += current_time_spent.tv_usec; + } + st->count++; + st->tv.tv_sec += current_time_spent.tv_sec; + +// fprintf(options.output, "%s <%lu.%06d>\n", function_name, +// current_time_spent.tv_sec, (int)current_time_spent.tv_usec); + return; + } + if (current_proc && (current_proc != proc || + current_depth != proc->callstack_depth)) { + fprintf(options.output, " <unfinished ...>\n"); + current_proc = 0; + } + if (current_proc != proc) { + begin_of_line(type, proc); +#ifdef USE_DEMANGLE + current_column += + fprintf(options.output, "<... %s resumed> ", + options.demangle ? my_demangle(function_name) : function_name); +#else + current_column += + fprintf(options.output, "<... %s resumed> ", function_name); +#endif + } + + if (!func) { + current_column += fprintf(options.output, ") "); + tabto(options.align - 1); + fprintf(options.output, "= "); + display_arg(type, proc, -1, arg_unknown); + } else { + int i; + for (i = func->num_params - func->params_right; + i < func->num_params - 1; i++) { + current_column += + display_arg(type, proc, i, func->arg_info[i]); + current_column += fprintf(options.output, ", "); + } + if (func->params_right) { + current_column += + display_arg(type, proc, i, func->arg_info[i]); + } + current_column += fprintf(options.output, ") "); + tabto(options.align - 1); + fprintf(options.output, "= "); + if (func->return_info->type == ARGTYPE_VOID) { + fprintf(options.output, "<void>"); + } else { + display_arg(type, proc, -1, func->return_info); + } + } + if (opt_T) { + fprintf(options.output, " <%lu.%06d>", + current_time_spent.tv_sec, + (int)current_time_spent.tv_usec); + } + fprintf(options.output, "\n"); + current_proc = 0; + current_column = 0; +} diff --git a/output.h b/output.h new file mode 100644 index 0000000..c58577a --- /dev/null +++ b/output.h @@ -0,0 +1,3 @@ +void output_line(Process *proc, char *fmt, ...); +void output_left(enum tof type, Process *proc, char *function_name); +void output_right(enum tof type, Process *proc, char *function_name); @@ -0,0 +1,65 @@ +#include <sys/types.h> +#include <string.h> +#include <stdio.h> +#include <errno.h> +#include <stdlib.h> + +#include "common.h" + +Process * +open_program(char *filename, pid_t pid) { + Process *proc; + proc = calloc(sizeof(Process), 1); + if (!proc) { + perror("malloc"); + exit(1); + } + proc->filename = strdup(filename); + proc->breakpoints_enabled = -1; + if (pid) { + proc->pid = pid; + } + breakpoints_init(proc); + + proc->next = list_of_processes; + list_of_processes = proc; + return proc; +} + +void +open_pid(pid_t pid) { + Process *proc; + char *filename; + + if (trace_pid(pid) < 0) { + fprintf(stderr, "Cannot attach to pid %u: %s\n", pid, + strerror(errno)); + return; + } + + filename = pid2name(pid); + + if (!filename) { + fprintf(stderr, "Cannot trace pid %u: %s\n", pid, + strerror(errno)); + return; + } + + proc = open_program(filename, pid); + continue_process(pid); + proc->breakpoints_enabled = 1; +} + +Process * +pid2proc(pid_t pid) { + Process *tmp; + + tmp = list_of_processes; + while (tmp) { + if (pid == tmp->pid) { + return tmp; + } + tmp = tmp->next; + } + return NULL; +} diff --git a/read_config_file.c b/read_config_file.c new file mode 100644 index 0000000..b4b1b56 --- /dev/null +++ b/read_config_file.c @@ -0,0 +1,680 @@ +#include "config.h" + +#include <string.h> +#include <stdlib.h> +#include <ctype.h> + +#include "common.h" + +static int line_no; +static char *filename; +static int error_count = 0; + +static arg_type_info *parse_type(char **str); + +Function *list_of_functions = NULL; + +/* Map of strings to type names. These do not need to be in any + * particular order */ +static struct list_of_pt_t { + char *name; + enum arg_type pt; +} list_of_pt[] = { + { + "void", ARGTYPE_VOID}, { + "int", ARGTYPE_INT}, { + "uint", ARGTYPE_UINT}, { + "long", ARGTYPE_LONG}, { + "ulong", ARGTYPE_ULONG}, { + "octal", ARGTYPE_OCTAL}, { + "char", ARGTYPE_CHAR}, { + "short", ARGTYPE_SHORT}, { + "ushort", ARGTYPE_USHORT}, { + "float", ARGTYPE_FLOAT}, { + "double", ARGTYPE_DOUBLE}, { + "addr", ARGTYPE_ADDR}, { + "file", ARGTYPE_FILE}, { + "format", ARGTYPE_FORMAT}, { + "string", ARGTYPE_STRING}, { + "array", ARGTYPE_ARRAY}, { + "struct", ARGTYPE_STRUCT}, { + "enum", ARGTYPE_ENUM}, { + NULL, ARGTYPE_UNKNOWN} /* Must finish with NULL */ +}; + +/* Array of prototype objects for each of the types. The order in this + * array must exactly match the list of enumerated values in + * common.h */ +static arg_type_info arg_type_prototypes[] = { + { ARGTYPE_VOID }, + { ARGTYPE_INT }, + { ARGTYPE_UINT }, + { ARGTYPE_LONG }, + { ARGTYPE_ULONG }, + { ARGTYPE_OCTAL }, + { ARGTYPE_CHAR }, + { ARGTYPE_SHORT }, + { ARGTYPE_USHORT }, + { ARGTYPE_FLOAT }, + { ARGTYPE_DOUBLE }, + { ARGTYPE_ADDR }, + { ARGTYPE_FILE }, + { ARGTYPE_FORMAT }, + { ARGTYPE_STRING }, + { ARGTYPE_STRING_N }, + { ARGTYPE_ARRAY }, + { ARGTYPE_ENUM }, + { ARGTYPE_STRUCT }, + { ARGTYPE_POINTER }, + { ARGTYPE_UNKNOWN } +}; + +arg_type_info * +lookup_prototype(enum arg_type at) { + if (at >= 0 && at <= ARGTYPE_COUNT) + return &arg_type_prototypes[at]; + else + return &arg_type_prototypes[ARGTYPE_COUNT]; /* UNKNOWN */ +} + +static arg_type_info * +str2type(char **str) { + struct list_of_pt_t *tmp = &list_of_pt[0]; + + while (tmp->name) { + if (!strncmp(*str, tmp->name, strlen(tmp->name)) + && index(" ,()#*;012345[", *(*str + strlen(tmp->name)))) { + *str += strlen(tmp->name); + return lookup_prototype(tmp->pt); + } + tmp++; + } + return lookup_prototype(ARGTYPE_UNKNOWN); +} + +static void +eat_spaces(char **str) { + while (**str == ' ') { + (*str)++; + } +} + +static char * +xstrndup(char *str, size_t len) { + char *ret = (char *) malloc(len + 1); + strncpy(ret, str, len); + ret[len] = 0; + return ret; +} + +static char * +parse_ident(char **str) { + char *ident = *str; + + if (!isalnum(**str) && **str != '_') { + output_line(0, "Syntax error in `%s', line %d: Bad identifier", + filename, line_no); + error_count++; + return NULL; + } + + while (**str && (isalnum(**str) || **str == '_')) { + ++(*str); + } + + return xstrndup(ident, *str - ident); +} + +/* + Returns position in string at the left parenthesis which starts the + function's argument signature. Returns NULL on error. +*/ +static char * +start_of_arg_sig(char *str) { + char *pos; + int stacked = 0; + + if (!strlen(str)) + return NULL; + + pos = &str[strlen(str)]; + do { + pos--; + if (pos < str) + return NULL; + while ((pos > str) && (*pos != ')') && (*pos != '(')) + pos--; + + if (*pos == ')') + stacked++; + else if (*pos == '(') + stacked--; + else + return NULL; + + } while (stacked > 0); + + return (stacked == 0) ? pos : NULL; +} + +static int +parse_int(char **str) { + char *end; + long n = strtol(*str, &end, 0); + if (end == *str) { + output_line(0, "Syntax error in `%s', line %d: Bad number (%s)", + filename, line_no, *str); + error_count++; + return 0; + } + + *str = end; + return n; +} + +/* + * Input: + * argN : The value of argument #N, counting from 1 (arg0 = retval) + * eltN : The value of element #N of the containing structure + * retval : The return value + * 0 : Error + * N : The numeric value N, if N > 0 + * + * Output: + * > 0 actual numeric value + * = 0 return value + * < 0 (arg -n), counting from one + */ +static int +parse_argnum(char **str) { + int multiplier = 1; + int n = 0; + + if (strncmp(*str, "arg", 3) == 0) { + (*str) += 3; + multiplier = -1; + } else if (strncmp(*str, "elt", 3) == 0) { + (*str) += 3; + multiplier = -1; + } else if (strncmp(*str, "retval", 6) == 0) { + (*str) += 6; + return 0; + } + + n = parse_int(str); + + return n * multiplier; +} + +struct typedef_node_t { + char *name; + arg_type_info *info; + struct typedef_node_t *next; +} *typedefs = NULL; + +static arg_type_info * +lookup_typedef(char **str) { + struct typedef_node_t *node; + char *end = *str; + while (*end && (isalnum(*end) || *end == '_')) + ++end; + if (end == *str) + return NULL; + + for (node = typedefs; node != NULL; node = node->next) { + if (strncmp(*str, node->name, end - *str) == 0) { + (*str) += strlen(node->name); + return node->info; + } + } + + return NULL; +} + +static void +parse_typedef(char **str) { + char *name; + arg_type_info *info; + struct typedef_node_t *binding; + + (*str) += strlen("typedef"); + eat_spaces(str); + + // Grab out the name of the type + name = parse_ident(str); + + // Skip = sign + eat_spaces(str); + if (**str != '=') { + output_line(0, + "Syntax error in `%s', line %d: expected '=', got '%c'", + filename, line_no, **str); + error_count++; + return; + } + (*str)++; + eat_spaces(str); + + // Parse the type + info = parse_type(str); + + // Insert onto beginning of linked list + binding = malloc(sizeof(*binding)); + binding->name = name; + binding->info = info; + binding->next = typedefs; + typedefs = binding; +} + +static size_t +arg_sizeof(arg_type_info * arg) { + if (arg->type == ARGTYPE_CHAR) { + return sizeof(char); + } else if (arg->type == ARGTYPE_SHORT || arg->type == ARGTYPE_USHORT) { + return sizeof(short); + } else if (arg->type == ARGTYPE_FLOAT) { + return sizeof(float); + } else if (arg->type == ARGTYPE_DOUBLE) { + return sizeof(double); + } else if (arg->type == ARGTYPE_ENUM) { + return sizeof(int); + } else if (arg->type == ARGTYPE_STRUCT) { + return arg->u.struct_info.size; + } else if (arg->type == ARGTYPE_POINTER) { + return sizeof(void*); + } else if (arg->type == ARGTYPE_ARRAY) { + if (arg->u.array_info.len_spec > 0) + return arg->u.array_info.len_spec * arg->u.array_info.elt_size; + else + return sizeof(void *); + } else { + return sizeof(int); + } +} + +#undef alignof +#define alignof(field,st) ((size_t) ((char*) &st.field - (char*) &st)) +static size_t +arg_align(arg_type_info * arg) { + struct { char c; char C; } cC; + struct { char c; short s; } cs; + struct { char c; int i; } ci; + struct { char c; long l; } cl; + struct { char c; void* p; } cp; + struct { char c; float f; } cf; + struct { char c; double d; } cd; + + static size_t char_alignment = alignof(C, cC); + static size_t short_alignment = alignof(s, cs); + static size_t int_alignment = alignof(i, ci); + static size_t long_alignment = alignof(l, cl); + static size_t ptr_alignment = alignof(p, cp); + static size_t float_alignment = alignof(f, cf); + static size_t double_alignment = alignof(d, cd); + + switch (arg->type) { + case ARGTYPE_LONG: + case ARGTYPE_ULONG: + return long_alignment; + case ARGTYPE_CHAR: + return char_alignment; + case ARGTYPE_SHORT: + case ARGTYPE_USHORT: + return short_alignment; + case ARGTYPE_FLOAT: + return float_alignment; + case ARGTYPE_DOUBLE: + return double_alignment; + case ARGTYPE_ADDR: + case ARGTYPE_FILE: + case ARGTYPE_FORMAT: + case ARGTYPE_STRING: + case ARGTYPE_STRING_N: + case ARGTYPE_POINTER: + return ptr_alignment; + + case ARGTYPE_ARRAY: + return arg_align(&arg->u.array_info.elt_type[0]); + + case ARGTYPE_STRUCT: + return arg_align(arg->u.struct_info.fields[0]); + + default: + return int_alignment; + } +} + +static size_t +align_skip(size_t alignment, size_t offset) { + if (offset % alignment) + return alignment - (offset % alignment); + else + return 0; +} + +/* I'm sure this isn't completely correct, but just try to get most of + * them right for now. */ +static void +align_struct(arg_type_info* info) { + size_t offset; + int i; + + if (info->u.struct_info.size != 0) + return; // Already done + + // Compute internal padding due to alignment constraints for + // various types. + offset = 0; + for (i = 0; info->u.struct_info.fields[i] != NULL; i++) { + arg_type_info *field = info->u.struct_info.fields[i]; + offset += align_skip(arg_align(field), offset); + info->u.struct_info.offset[i] = offset; + offset += arg_sizeof(field); + } + + info->u.struct_info.size = offset; +} + +static arg_type_info * +parse_nonpointer_type(char **str) { + arg_type_info *simple; + arg_type_info *info; + + if (strncmp(*str, "typedef", 7) == 0) { + parse_typedef(str); + return lookup_prototype(ARGTYPE_UNKNOWN); + } + + simple = str2type(str); + if (simple->type == ARGTYPE_UNKNOWN) { + info = lookup_typedef(str); + if (info) + return info; + else + return simple; // UNKNOWN + } + + info = malloc(sizeof(*info)); + info->type = simple->type; + + /* Code to parse parameterized types will go into the following + switch statement. */ + + switch (info->type) { + + /* Syntax: array ( type, N|argN ) */ + case ARGTYPE_ARRAY: + (*str)++; // Get past open paren + eat_spaces(str); + if ((info->u.array_info.elt_type = parse_type(str)) == NULL) + return NULL; + info->u.array_info.elt_size = + arg_sizeof(info->u.array_info.elt_type); + (*str)++; // Get past comma + eat_spaces(str); + info->u.array_info.len_spec = parse_argnum(str); + (*str)++; // Get past close paren + return info; + + /* Syntax: enum ( keyname=value,keyname=value,... ) */ + case ARGTYPE_ENUM:{ + struct enum_opt { + char *key; + int value; + struct enum_opt *next; + }; + struct enum_opt *list = NULL; + struct enum_opt *p; + int entries = 0; + int ii; + + eat_spaces(str); + (*str)++; // Get past open paren + eat_spaces(str); + + while (**str && **str != ')') { + p = (struct enum_opt *) malloc(sizeof(*p)); + eat_spaces(str); + p->key = parse_ident(str); + if (error_count) { + free(p); + return NULL; + } + eat_spaces(str); + if (**str != '=') { + free(p->key); + free(p); + output_line(0, + "Syntax error in `%s', line %d: expected '=', got '%c'", + filename, line_no, **str); + error_count++; + return NULL; + } + ++(*str); + eat_spaces(str); + p->value = parse_int(str); + p->next = list; + list = p; + ++entries; + + // Skip comma + eat_spaces(str); + if (**str == ',') { + (*str)++; + eat_spaces(str); + } + } + + info->u.enum_info.entries = entries; + info->u.enum_info.keys = + (char **) malloc(entries * sizeof(char *)); + info->u.enum_info.values = + (int *) malloc(entries * sizeof(int)); + for (ii = 0, p = NULL; list; ++ii, list = list->next) { + if (p) + free(p); + info->u.enum_info.keys[ii] = list->key; + info->u.enum_info.values[ii] = list->value; + p = list; + } + if (p) + free(p); + + return info; + } + + case ARGTYPE_STRING: + if (!isdigit(**str) && **str != '[') { + /* Oops, was just a simple string after all */ + free(info); + return simple; + } + + info->type = ARGTYPE_STRING_N; + + /* Backwards compatibility for string0, string1, ... */ + if (isdigit(**str)) { + info->u.string_n_info.size_spec = -parse_int(str); + return info; + } + + (*str)++; // Skip past opening [ + eat_spaces(str); + info->u.string_n_info.size_spec = parse_argnum(str); + eat_spaces(str); + (*str)++; // Skip past closing ] + return info; + + // Syntax: struct ( type,type,type,... ) + case ARGTYPE_STRUCT:{ + int field_num = 0; + (*str)++; // Get past open paren + info->u.struct_info.fields = + malloc((MAX_ARGS + 1) * sizeof(void *)); + info->u.struct_info.offset = + malloc((MAX_ARGS + 1) * sizeof(size_t)); + info->u.struct_info.size = 0; + eat_spaces(str); // Empty arg list with whitespace inside + while (**str && **str != ')') { + if (field_num == MAX_ARGS) { + output_line(0, + "Error in `%s', line %d: Too many structure elements", + filename, line_no); + error_count++; + return NULL; + } + eat_spaces(str); + if (field_num != 0) { + (*str)++; // Get past comma + eat_spaces(str); + } + if ((info->u.struct_info.fields[field_num++] = + parse_type(str)) == NULL) + return NULL; + + // Must trim trailing spaces so the check for + // the closing paren is simple + eat_spaces(str); + } + (*str)++; // Get past closing paren + info->u.struct_info.fields[field_num] = NULL; + align_struct(info); + return info; + } + + default: + if (info->type == ARGTYPE_UNKNOWN) { + output_line(0, "Syntax error in `%s', line %d: " + "Unknown type encountered", + filename, line_no); + free(info); + error_count++; + return NULL; + } else { + return info; + } + } +} + +static arg_type_info * +parse_type(char **str) { + arg_type_info *info = parse_nonpointer_type(str); + while (**str == '*') { + arg_type_info *outer = malloc(sizeof(*info)); + outer->type = ARGTYPE_POINTER; + outer->u.ptr_info.info = info; + (*str)++; + info = outer; + } + return info; +} + +static Function * +process_line(char *buf) { + Function fun; + Function *fun_p; + char *str = buf; + char *tmp; + int i; + int float_num = 0; + + line_no++; + debug(3, "Reading line %d of `%s'", line_no, filename); + eat_spaces(&str); + fun.return_info = parse_type(&str); + if (fun.return_info == NULL) + return NULL; + if (fun.return_info->type == ARGTYPE_UNKNOWN) { + debug(3, " Skipping line %d", line_no); + return NULL; + } + debug(4, " return_type = %d", fun.return_info->type); + eat_spaces(&str); + tmp = start_of_arg_sig(str); + if (!tmp) { + output_line(0, "Syntax error in `%s', line %d", filename, + line_no); + error_count++; + return NULL; + } + *tmp = '\0'; + fun.name = strdup(str); + str = tmp + 1; + debug(3, " name = %s", fun.name); + fun.params_right = 0; + for (i = 0; i < MAX_ARGS; i++) { + eat_spaces(&str); + if (*str == ')') { + break; + } + if (str[0] == '+') { + fun.params_right++; + str++; + } else if (fun.params_right) { + fun.params_right++; + } + fun.arg_info[i] = parse_type(&str); + if (fun.arg_info[i] == NULL) { + output_line(0, "Syntax error in `%s', line %d" + ": unknown argument type", + filename, line_no); + error_count++; + return NULL; + } + if (fun.arg_info[i]->type == ARGTYPE_FLOAT) + fun.arg_info[i]->u.float_info.float_index = float_num++; + else if (fun.arg_info[i]->type == ARGTYPE_DOUBLE) + fun.arg_info[i]->u.double_info.float_index = float_num++; + eat_spaces(&str); + if (*str == ',') { + str++; + continue; + } else if (*str == ')') { + continue; + } else { + if (str[strlen(str) - 1] == '\n') + str[strlen(str) - 1] = '\0'; + output_line(0, "Syntax error in `%s', line %d at ...\"%s\"", + filename, line_no, str); + error_count++; + return NULL; + } + } + fun.num_params = i; + fun_p = malloc(sizeof(Function)); + if (!fun_p) { + perror("ltrace: malloc"); + exit(1); + } + memcpy(fun_p, &fun, sizeof(Function)); + return fun_p; +} + +void +read_config_file(char *file) { + FILE *stream; + char buf[1024]; + + filename = file; + stream = fopen(filename, "r"); + if (!stream) { + return; + } + + debug(1, "Reading config file `%s'...", filename); + + line_no = 0; + while (fgets(buf, 1024, stream)) { + Function *tmp; + + error_count = 0; + tmp = process_line(buf); + + if (tmp) { + debug(2, "New function: `%s'", tmp->name); + tmp->next = list_of_functions; + list_of_functions = tmp; + } + } + fclose(stream); +} diff --git a/read_config_file.h b/read_config_file.h new file mode 100644 index 0000000..8000b1c --- /dev/null +++ b/read_config_file.h @@ -0,0 +1 @@ +extern void read_config_file(char *); diff --git a/summary.c b/summary.c new file mode 100644 index 0000000..dab845c --- /dev/null +++ b/summary.c @@ -0,0 +1,86 @@ +#include "config.h" + +#include <stdio.h> +#include <stdlib.h> +#include <sys/time.h> + +#include "common.h" + +static int num_entries = 0; +static struct entry_st { + char *name; + int count; + struct timeval tv; +} *entries = NULL; + +static int tot_count = 0; +static unsigned long int tot_usecs = 0; + +static void fill_struct(void *key, void *value, void *data) +{ + struct opt_c_struct *st = (struct opt_c_struct *)value; + + entries = realloc(entries, (num_entries + 1) * sizeof(struct entry_st)); + if (!entries) { + perror("realloc()"); + exit(1); + } + entries[num_entries].name = (char *)key; + entries[num_entries].count = st->count; + entries[num_entries].tv = st->tv; + + tot_count += st->count; + tot_usecs += 1000000 * st->tv.tv_sec; + tot_usecs += st->tv.tv_usec; + + num_entries++; +} + +static int compar(const void *a, const void *b) +{ + struct entry_st *en1, *en2; + + en1 = (struct entry_st *)a; + en2 = (struct entry_st *)b; + + if (en2->tv.tv_sec - en1->tv.tv_sec) { + return (en2->tv.tv_sec - en1->tv.tv_sec); + } else { + return (en2->tv.tv_usec - en1->tv.tv_usec); + } +} + +void show_summary(void) +{ + int i; + + num_entries = 0; + entries = NULL; + + dict_apply_to_all(dict_opt_c, fill_struct, NULL); + + qsort(entries, num_entries, sizeof(*entries), compar); + + fprintf(options.output, "%% time seconds usecs/call calls function\n"); + fprintf(options.output, "------ ----------- ----------- --------- --------------------\n"); + for (i = 0; i < num_entries; i++) { + unsigned long long int c; + unsigned long long int p; + c = 1000000 * (int)entries[i].tv.tv_sec + + (int)entries[i].tv.tv_usec; + p = 100000 * c / tot_usecs + 5; + fprintf(options.output, "%3lu.%02lu %4d.%06d %11lu %9d %s\n", + (unsigned long int)(p / 1000), + (unsigned long int)((p / 10) % 100), + (int)entries[i].tv.tv_sec, (int)entries[i].tv.tv_usec, + (unsigned long int)(c / entries[i].count), + entries[i].count, +#ifdef USE_DEMANGLE + options.demangle ? my_demangle(entries[i].name) : +#endif + entries[i].name); + } + fprintf(options.output, "------ ----------- ----------- --------- --------------------\n"); + fprintf(options.output, "100.00 %4lu.%06lu %9d total\n", tot_usecs / 1000000, + tot_usecs % 1000000, tot_count); +} diff --git a/sysdeps/README b/sysdeps/README new file mode 100644 index 0000000..ce033ef --- /dev/null +++ b/sysdeps/README @@ -0,0 +1,32 @@ +Each operating system must have a subdir here, with a Makefile +The first target of that Makefile must build a file "sysdep.o" in this +directory. +The "clean" target of that Makefile must undo all the efects of the +first target, and must remove "sysdep.o" in this dir. + +Files "sysdep.h", "signalent.h" and "syscallent.h" must be present +inside the directory after invoking the first target of the Makefile. + +----------- +"sysdep.o" must export the following functions: + +Event * next_event(void); +void continue_after_breakpoint(Process * proc, Breakpoint * sbp, int delete_it); +void continue_after_signal(pid_t pid, int signum); +void continue_enabling_breakpoint(pid_t pid, Breakpoint * sbp); +void continue_process(pid_t pid); +void enable_breakpoint(pid_t pid, Breakpoint * sbp); +void disable_breakpoint(pid_t pid, Breakpoint * sbp); +int fork_p(int sysnum); +int exec_p(int sysnum); +int syscall_p(Process * proc, int status, int * sysnum); +void * get_instruction_pointer(pid_t pid); +void * get_stack_pointer(pid_t pid); +void * get_return_addr(pid_t pid, void * stack_pointer); +long gimme_arg(enum tof type, Process * proc, arg_type_info*); +int umovestr(Process * proc, void * addr, int len, void * laddr); +int umovelong(Process * proc, void * addr, long * result); +char * pid2name(pid_t pid); +void trace_me(void); +int trace_pid(pid_t pid); +void untrace_pid(pid_t pid); diff --git a/sysdeps/linux-gnu/Makefile b/sysdeps/linux-gnu/Makefile new file mode 100644 index 0000000..eb1ec60 --- /dev/null +++ b/sysdeps/linux-gnu/Makefile @@ -0,0 +1,60 @@ +ARCH := $(shell uname -m | sed \ + -e s/i.86/i386/ \ + -e s/sun4u/sparc64/ \ + -e s/sparc64/sparc/ \ + -e s/arm.*/arm/ \ + -e s/sa110/arm/ \ + -e s/ppc64/ppc/ \ + -e s/s390x/s390/ \ + ) + +CPPFLAGS += -I$(TOPDIR)/sysdeps/linux-gnu/$(ARCH) + +OBJ = events.o trace.o proc.o breakpoint.o + +all: sysdep.h signalent.h syscallent.h arch_syscallent.h signalent1.h syscallent1.h ../sysdep.o + +sysdep.h: $(ARCH)/arch.h + cat $(ARCH)/arch.h > sysdep.h + +signalent.h: + cp $(ARCH)/signalent.h signalent.h +signalent1.h: + if [ -f $(ARCH)/signalent1.h ]; then \ + cp $(ARCH)/signalent1.h signalent1.h; \ + else \ + > signalent1.h; \ + fi + +syscallent.h: + cp $(ARCH)/syscallent.h syscallent.h + +syscallent1.h: + if [ -f $(ARCH)/syscallent1.h ]; then \ + cp $(ARCH)/syscallent1.h syscallent1.h; \ + else \ + > syscallent1.h; \ + fi + +arch_syscallent.h: + if [ -f $(ARCH)/arch_syscallent.h ]; then \ + cp $(ARCH)/arch_syscallent.h arch_syscallent.h; \ + else \ + > arch_syscallent.h; \ + fi + +../sysdep.o: os.o $(ARCH)/arch.o + $(CC) -nostdlib -r -o ../sysdep.o os.o $(ARCH)/arch.o + +os.o: $(OBJ) + $(CC) -nostdlib -r -o os.o $(OBJ) + +$(ARCH)/arch.o: dummy + $(MAKE) -C $(ARCH) + +clean: + $(MAKE) -C $(ARCH) clean + rm -f $(OBJ) sysdep.h signalent.h signalent1.h syscallent.h arch_syscallent.h + rm -f syscallent1.h os.o sysdep.o ../sysdep.o + +dummy: diff --git a/sysdeps/linux-gnu/README b/sysdeps/linux-gnu/README new file mode 100644 index 0000000..a8bc8ee --- /dev/null +++ b/sysdeps/linux-gnu/README @@ -0,0 +1,13 @@ +* "arch/syscallent.h" is made from <asm/unistd.h>. + It can be done automatically with "mksyscallent" for all the + architectures except "mips" (as of linux-2.2.12) + +* "arch/signalent.h" is made from <asm/signal.h>. + It can be done automatically with "mksignalent" for all the + architectures. (linux-2.2.12) + +* s390 uses mksyscallent_s390, rather than mksyscallent + +* NOTE: This does not currently work for cross-compilers; Maybe + I should guess the architecture using `gcc -print-libgcc-file-name' + instead of `uname -m' (or even better, use the value returned by autoconf) diff --git a/sysdeps/linux-gnu/alpha/Makefile b/sysdeps/linux-gnu/alpha/Makefile new file mode 100644 index 0000000..60d7531 --- /dev/null +++ b/sysdeps/linux-gnu/alpha/Makefile @@ -0,0 +1,10 @@ +OBJ = trace.o regs.o plt.o + +all: arch.o + +arch.o: $(OBJ) + $(CC) -nostdlib -r -o arch.o $(OBJ) + +clean: + $(RM) $(OBJ) arch.o + diff --git a/sysdeps/linux-gnu/alpha/arch.h b/sysdeps/linux-gnu/alpha/arch.h new file mode 100644 index 0000000..1107b5f --- /dev/null +++ b/sysdeps/linux-gnu/alpha/arch.h @@ -0,0 +1,8 @@ +#define BREAKPOINT_VALUE { 0x80, 0x00, 0x00, 0x00 } +#define BREAKPOINT_LENGTH 4 +#define DECR_PC_AFTER_BREAK 4 + +#define LT_ELFCLASS ELFCLASS64 +#define LT_ELF_MACHINE EM_ALPHA +#define LT_ELFCLASS2 ELFCLASS64 +#define LT_ELF_MACHINE2 EM_FAKE_ALPHA diff --git a/sysdeps/linux-gnu/alpha/plt.c b/sysdeps/linux-gnu/alpha/plt.c new file mode 100644 index 0000000..83337b2 --- /dev/null +++ b/sysdeps/linux-gnu/alpha/plt.c @@ -0,0 +1,12 @@ +#include <gelf.h> +#include "common.h" + +GElf_Addr +arch_plt_sym_val(struct ltelf *lte, size_t ndx, GElf_Rela * rela) { + return lte->plt_addr + ndx * 12 + 32; +} + +void * +sym2addr(Process *proc, struct library_symbol *sym) { + return sym->enter_addr; +} diff --git a/sysdeps/linux-gnu/alpha/ptrace.h b/sysdeps/linux-gnu/alpha/ptrace.h new file mode 100644 index 0000000..c3cbcb6 --- /dev/null +++ b/sysdeps/linux-gnu/alpha/ptrace.h @@ -0,0 +1 @@ +#include <sys/ptrace.h> diff --git a/sysdeps/linux-gnu/alpha/regs.c b/sysdeps/linux-gnu/alpha/regs.c new file mode 100644 index 0000000..9554e48 --- /dev/null +++ b/sysdeps/linux-gnu/alpha/regs.c @@ -0,0 +1,40 @@ +#include "config.h" + +#include <sys/types.h> +#include <sys/ptrace.h> +#include <asm/ptrace.h> + +#include "common.h" + +#if (!defined(PTRACE_PEEKUSER) && defined(PTRACE_PEEKUSR)) +# define PTRACE_PEEKUSER PTRACE_PEEKUSR +#endif + +#if (!defined(PTRACE_POKEUSER) && defined(PTRACE_POKEUSR)) +# define PTRACE_POKEUSER PTRACE_POKEUSR +#endif + +void * +get_instruction_pointer(Process *proc) { + return (void *)ptrace(PTRACE_PEEKUSER, proc->pid, 64 /* REG_PC */ , 0); +} + +void +set_instruction_pointer(Process *proc, void *addr) { + ptrace(PTRACE_POKEUSER, proc->pid, 64 /* REG_PC */ , addr); +} + +void * +get_stack_pointer(Process *proc) { + return (void *)ptrace(PTRACE_PEEKUSER, proc->pid, 30 /* REG_FP */ , 0); +} + +void * +get_return_addr(Process *proc, void *stack_pointer) { + return (void *)ptrace(PTRACE_PEEKUSER, proc->pid, 26 /* RA */ , 0); +} + +void +set_return_addr(Process *proc, void *addr) { + ptrace(PTRACE_POKEUSER, proc->pid, 26 /* RA */ , addr); +} diff --git a/sysdeps/linux-gnu/alpha/signalent.h b/sysdeps/linux-gnu/alpha/signalent.h new file mode 100644 index 0000000..c2a6170 --- /dev/null +++ b/sysdeps/linux-gnu/alpha/signalent.h @@ -0,0 +1,32 @@ +"SIG_0", /* 0 */ + "SIGHUP", /* 1 */ + "SIGINT", /* 2 */ + "SIGQUIT", /* 3 */ + "SIGILL", /* 4 */ + "SIGTRAP", /* 5 */ + "SIGABRT", /* 6 */ + "SIGEMT", /* 7 */ + "SIGFPE", /* 8 */ + "SIGKILL", /* 9 */ + "SIGBUS", /* 10 */ + "SIGSEGV", /* 11 */ + "SIGSYS", /* 12 */ + "SIGPIPE", /* 13 */ + "SIGALRM", /* 14 */ + "SIGTERM", /* 15 */ + "SIGURG", /* 16 */ + "SIGSTOP", /* 17 */ + "SIGTSTP", /* 18 */ + "SIGCONT", /* 19 */ + "SIGCHLD", /* 20 */ + "SIGTTIN", /* 21 */ + "SIGTTOU", /* 22 */ + "SIGIO", /* 23 */ + "SIGXCPU", /* 24 */ + "SIGXFSZ", /* 25 */ + "SIGVTALRM", /* 26 */ + "SIGPROF", /* 27 */ + "SIGWINCH", /* 28 */ + "SIGINFO", /* 29 */ + "SIGUSR1", /* 30 */ + "SIGUSR2", /* 31 */ diff --git a/sysdeps/linux-gnu/alpha/syscallent.h b/sysdeps/linux-gnu/alpha/syscallent.h new file mode 100644 index 0000000..7cacc8c --- /dev/null +++ b/sysdeps/linux-gnu/alpha/syscallent.h @@ -0,0 +1,439 @@ +"osf_syscall", /* 0, not implemented */ + "exit", /* 1 */ + "fork", /* 2 */ + "read", /* 3 */ + "write", /* 4 */ + "osf_old_open", /* 5, not implemented */ + "close", /* 6 */ + "osf_wait4", /* 7 */ + "osf_old_creat", /* 8, not implemented */ + "link", /* 9 */ + "unlink", /* 10 */ + "osf_execve", /* 11, not implemented */ + "chdir", /* 12 */ + "fchdir", /* 13 */ + "mknod", /* 14 */ + "chmod", /* 15 */ + "chown", /* 16 */ + "brk", /* 17 */ + "osf_getfsstat", /* 18, not implemented */ + "lseek", /* 19 */ + "getxpid", /* 20 */ + "osf_mount", /* 21 */ + "umount", /* 22 */ + "setuid", /* 23 */ + "getxuid", /* 24 */ + "exec_with_loader", /* 25, not implemented */ + "ptrace", /* 26 */ + "osf_nrecmsg", /* 27, not implemented */ + "osf_nsendmsg", /* 28, not implemented */ + "osf_nrecvfrom", /* 29, not implemented */ + "osf_naccept", /* 30, not implemented */ + "osf_ngetpeername", /* 31, not implemented */ + "osf_ngetsocketname", /* 32, not implemented */ + "access", /* 33 */ + "osf_chflags", /* 34, not implemented */ + "osf_fchflags", /* 35, not implemented */ + "sync", /* 36 */ + "kill", /* 37 */ + "osf_old_stat", /* 38, not implemented */ + "setpgid", /* 39 */ + "osf_old_lstat", /* 40, not implemented */ + "dup", /* 41 */ + "pipe", /* 42 */ + "osf_set_program_attributes", /* 43 */ + "osf_profil", /* 44, not implemented */ + "open", /* 45 */ + "osf_old_sigaction", /* 46, not implemented */ + "getxgid", /* 47 */ + "osf_sigprocmask", /* 48 */ + "osf_getlogin", /* 49, not implemented */ + "osf_setlogin", /* 50, not implemented */ + "acct", /* 51 */ + "sigpending", /* 52 */ + "SYS_53", /* 53 */ + "ioctl", /* 54 */ + "osf_reboot", /* 55, not implemented */ + "osf_revoke", /* 56, not implemented */ + "symlink", /* 57 */ + "readlink", /* 58 */ + "execve", /* 59 */ + "umask", /* 60 */ + "chroot", /* 61 */ + "osf_old_fstat", /* 62, not implemented */ + "getpgrp", /* 63 */ + "getpagesize", /* 64 */ + "osf_mremap", /* 65, not implemented */ + "vfork", /* 66 */ + "stat", /* 67 */ + "lstat", /* 68 */ + "osf_sbrk", /* 69, not implemented */ + "osf_sstk", /* 70, not implemented */ + "mmap", /* 71 */ + "osf_old_vadvise", /* 72, not implemented */ + "munmap", /* 73 */ + "mprotect", /* 74 */ + "madvise", /* 75 */ + "vhangup", /* 76 */ + "osf_kmodcall", /* 77, not implemented */ + "osf_mincore", /* 78, not implemented */ + "getgroups", /* 79 */ + "setgroups", /* 80 */ + "osf_old_getpgrp", /* 81, not implemented */ + "setpgrp", /* 82 */ + "osf_setitimer", /* 83 */ + "osf_old_wait", /* 84, not implemented */ + "osf_table", /* 85, not implemented */ + "osf_getitimer", /* 86 */ + "gethostname", /* 87 */ + "sethostname", /* 88 */ + "getdtablesize", /* 89 */ + "dup2", /* 90 */ + "fstat", /* 91 */ + "fcntl", /* 92 */ + "osf_select", /* 93 */ + "poll", /* 94 */ + "fsync", /* 95 */ + "setpriority", /* 96 */ + "socket", /* 97 */ + "connect", /* 98 */ + "accept", /* 99 */ + "osf_getpriority", /* 100 */ + "send", /* 101 */ + "recv", /* 102 */ + "sigreturn", /* 103 */ + "bind", /* 104 */ + "setsockopt", /* 105 */ + "listen", /* 106 */ + "osf_plock", /* 107, not implemented */ + "osf_old_sigvec", /* 108, not implemented */ + "osf_old_sigblock", /* 109, not implemented */ + "osf_old_sigsetmask", /* 110, not implemented */ + "sigsuspend", /* 111 */ + "sigstack", /* 112 */ + "recvmsg", /* 113 */ + "sendmsg", /* 114 */ + "osf_old_vtrace", /* 115, not implemented */ + "osf_gettimeofday", /* 116 */ + "osf_getrusage", /* 117 */ + "getsockopt", /* 118 */ + "SYS_119", /* 119 */ + "readv", /* 120 */ + "writev", /* 121 */ + "osf_settimeofday", /* 122 */ + "fchown", /* 123 */ + "fchmod", /* 124 */ + "recvfrom", /* 125 */ + "setreuid", /* 126 */ + "setregid", /* 127 */ + "rename", /* 128 */ + "truncate", /* 129 */ + "ftruncate", /* 130 */ + "flock", /* 131 */ + "setgid", /* 132 */ + "sendto", /* 133 */ + "shutdown", /* 134 */ + "socketpair", /* 135 */ + "mkdir", /* 136 */ + "rmdir", /* 137 */ + "osf_utimes", /* 138 */ + "osf_old_sigreturn", /* 139 */ + "osf_adjtime", /* 140, not implemented */ + "getpeername", /* 141 */ + "osf_gethostid", /* 142, not implemented */ + "osf_sethostid", /* 143, not implemented */ + "getrlimit", /* 144 */ + "setrlimit", /* 145 */ + "osf_old_killpg", /* 146, not implemented */ + "setsid", /* 147 */ + "quotactl", /* 148 */ + "osf_oldquota", /* 149, not implemented */ + "getsockname", /* 150 */ + "SYS_151", /* 151 */ + "SYS_152", /* 152 */ + "osf_pid_block", /* 153, not implemented */ + "osf_pid_unblock", /* 154, not implemented */ + "SYS_155", /* 155 */ + "sigaction", /* 156 */ + "osf_sigwaitprim", /* 157, not implemented */ + "osf_nfssvc", /* 158, not implemented */ + "osf_getdirentries", /* 159 */ + "osf_statfs", /* 160 */ + "osf_fstatfs", /* 161 */ + "SYS_162", /* 162 */ + "osf_asynch_daemon", /* 163, not implemented */ + "osf_getfh", /* 164, not implemented */ + "osf_getdomainname", /* 165 */ + "setdomainname", /* 166 */ + "SYS_167", /* 167 */ + "SYS_168", /* 168 */ + "osf_exportfs", /* 169, not implemented */ + "SYS_170", /* 170 */ + "SYS_171", /* 171 */ + "SYS_172", /* 172 */ + "SYS_173", /* 173 */ + "SYS_174", /* 174 */ + "SYS_175", /* 175 */ + "SYS_176", /* 176 */ + "SYS_177", /* 177 */ + "SYS_178", /* 178 */ + "SYS_179", /* 179 */ + "SYS_180", /* 180 */ + "osf_alt_plock", /* 181, not implemented */ + "SYS_182", /* 182 */ + "SYS_183", /* 183 */ + "osf_getmnt", /* 184, not implemented */ + "SYS_185", /* 185 */ + "SYS_186", /* 186 */ + "osf_alt_sigpending", /* 187, not implemented */ + "osf_alt_setsid", /* 188, not implemented */ + "SYS_189", /* 189 */ + "SYS_190", /* 190 */ + "SYS_191", /* 191 */ + "SYS_192", /* 192 */ + "SYS_193", /* 193 */ + "SYS_194", /* 194 */ + "SYS_195", /* 195 */ + "SYS_196", /* 196 */ + "SYS_197", /* 197 */ + "SYS_198", /* 198 */ + "osf_swapon", /* 199 */ + "msgctl", /* 200 */ + "msgget", /* 201 */ + "msgrcv", /* 202 */ + "msgsnd", /* 203 */ + "semctl", /* 204 */ + "semget", /* 205 */ + "semop", /* 206 */ + "osf_utsname", /* 207 */ + "lchown", /* 208 */ + "osf_shmat", /* 209 */ + "shmctl", /* 210 */ + "shmdt", /* 211 */ + "shmget", /* 212 */ + "osf_mvalid", /* 213, not implemented */ + "osf_getaddressconf", /* 214, not implemented */ + "osf_msleep", /* 215, not implemented */ + "osf_mwakeup", /* 216, not implemented */ + "msync", /* 217 */ + "osf_signal", /* 218, not implemented */ + "osf_utc_gettime", /* 219, not implemented */ + "osf_utc_adjtime", /* 220, not implemented */ + "SYS_221", /* 221 */ + "osf_security", /* 222, not implemented */ + "osf_kloadcall", /* 223, not implemented */ + "SYS_224", /* 224 */ + "SYS_225", /* 225 */ + "SYS_226", /* 226 */ + "SYS_227", /* 227 */ + "SYS_228", /* 228 */ + "SYS_229", /* 229 */ + "SYS_230", /* 230 */ + "SYS_231", /* 231 */ + "SYS_232", /* 232 */ + "getpgid", /* 233 */ + "getsid", /* 234 */ + "sigaltstack", /* 235 */ + "osf_waitid", /* 236, not implemented */ + "osf_priocntlset", /* 237, not implemented */ + "osf_sigsendset", /* 238, not implemented */ + "osf_set_speculative", /* 239, not implemented */ + "osf_msfs_syscall", /* 240, not implemented */ + "osf_sysinfo", /* 241 */ + "osf_uadmin", /* 242, not implemented */ + "osf_fuser", /* 243, not implemented */ + "osf_proplist_syscall", /* 244 */ + "osf_ntp_adjtime", /* 245, not implemented */ + "osf_ntp_gettime", /* 246, not implemented */ + "osf_pathconf", /* 247, not implemented */ + "osf_fpathconf", /* 248, not implemented */ + "SYS_249", /* 249 */ + "osf_uswitch", /* 250, not implemented */ + "osf_usleep_thread", /* 251 */ + "osf_audcntl", /* 252, not implemented */ + "osf_audgen", /* 253, not implemented */ + "sysfs", /* 254 */ + "osf_subsysinfo", /* 255, not implemented */ + "osf_getsysinfo", /* 256 */ + "osf_setsysinfo", /* 257 */ + "osf_afs_syscall", /* 258, not implemented */ + "osf_swapctl", /* 259, not implemented */ + "osf_memcntl", /* 260, not implemented */ + "osf_fdatasync", /* 261, not implemented */ + "SYS_262", /* 262 */ + "SYS_263", /* 263 */ + "SYS_264", /* 264 */ + "SYS_265", /* 265 */ + "SYS_266", /* 266 */ + "SYS_267", /* 267 */ + "SYS_268", /* 268 */ + "SYS_269", /* 269 */ + "SYS_270", /* 270 */ + "SYS_271", /* 271 */ + "SYS_272", /* 272 */ + "SYS_273", /* 273 */ + "SYS_274", /* 274 */ + "SYS_275", /* 275 */ + "SYS_276", /* 276 */ + "SYS_277", /* 277 */ + "SYS_278", /* 278 */ + "SYS_279", /* 279 */ + "SYS_280", /* 280 */ + "SYS_281", /* 281 */ + "SYS_282", /* 282 */ + "SYS_283", /* 283 */ + "SYS_284", /* 284 */ + "SYS_285", /* 285 */ + "SYS_286", /* 286 */ + "SYS_287", /* 287 */ + "SYS_288", /* 288 */ + "SYS_289", /* 289 */ + "SYS_290", /* 290 */ + "SYS_291", /* 291 */ + "SYS_292", /* 292 */ + "SYS_293", /* 293 */ + "SYS_294", /* 294 */ + "SYS_295", /* 295 */ + "SYS_296", /* 296 */ + "SYS_297", /* 297 */ + "SYS_298", /* 298 */ + "SYS_299", /* 299 */ + "bdflush", /* 300 */ + "sethae", /* 301 */ + "mount", /* 302 */ + "adjtimex32", /* 303 */ + "swapoff", /* 304 */ + "getdents", /* 305 */ + "create_module", /* 306 */ + "init_module", /* 307 */ + "delete_module", /* 308 */ + "get_kernel_syms", /* 309 */ + "syslog", /* 310 */ + "reboot", /* 311 */ + "clone", /* 312 */ + "uselib", /* 313 */ + "mlock", /* 314 */ + "munlock", /* 315 */ + "mlockall", /* 316 */ + "munlockall", /* 317 */ + "sysinfo", /* 318 */ + "sysctl", /* 319 */ + "idle", /* 320 */ + "oldumount", /* 321 */ + "swapon", /* 322 */ + "times", /* 323 */ + "personality", /* 324 */ + "setfsuid", /* 325 */ + "setfsgid", /* 326 */ + "ustat", /* 327 */ + "statfs", /* 328 */ + "fstatfs", /* 329 */ + "sched_setparam", /* 330 */ + "sched_getparam", /* 331 */ + "sched_setscheduler", /* 332 */ + "sched_getscheduler", /* 333 */ + "sched_yield", /* 334 */ + "sched_get_priority_max", /* 335 */ + "sched_get_priority_min", /* 336 */ + "sched_rr_get_interval", /* 337 */ + "afs_syscall", /* 338 */ + "uname", /* 339 */ + "nanosleep", /* 340 */ + "mremap", /* 341 */ + "nfsservctl", /* 342 */ + "setresuid", /* 343 */ + "getresuid", /* 344 */ + "pciconfig_read", /* 345 */ + "pciconfig_write", /* 346 */ + "query_module", /* 347 */ + "prctl", /* 348 */ + "pread", /* 349 */ + "pwrite", /* 350 */ + "rt_sigreturn", /* 351 */ + "rt_sigaction", /* 352 */ + "rt_sigprocmask", /* 353 */ + "rt_sigpending", /* 354 */ + "rt_sigtimedwait", /* 355 */ + "rt_sigqueueinfo", /* 356 */ + "rt_sigsuspend", /* 357 */ + "select", /* 358 */ + "gettimeofday", /* 359 */ + "settimeofday", /* 360 */ + "getitimer", /* 361 */ + "setitimer", /* 362 */ + "utimes", /* 363 */ + "getrusage", /* 364 */ + "wait4", /* 365 */ + "adjtimex", /* 366 */ + "getcwd", /* 367 */ + "capget", /* 368 */ + "capset", /* 369 */ + "sendfile", /* 370 */ + "setresgid", /* 371 */ + "getresgid", /* 372 */ + "dipc", /* 373, not implemented */ + "pivot_root", /* 374 */ + "mincore", /* 375 */ + "pciconfig_iobase", /* 376 */ + "getdents64", /* 377 */ + "gettid", /* 378 */ + "readahead", /* 379 */ + "SYS_380", /* 380 */ + "tkill", /* 381 */ + "setxattr", /* 382 */ + "lsetxattr", /* 383 */ + "fsetxattr", /* 384 */ + "getxattr", /* 385 */ + "lgetxattr", /* 386 */ + "fgetxattr", /* 387 */ + "listxattr", /* 388 */ + "llistxattr", /* 389 */ + "flistxattr", /* 390 */ + "removexattr", /* 391 */ + "lremovexattr", /* 392 */ + "fremovexattr", /* 393 */ + "futex", /* 394 */ + "sched_setaffinity", /* 395 */ + "sched_getaffinity", /* 396 */ + "tuxcall", /* 397 */ + "io_setup", /* 398 */ + "io_destroy", /* 399 */ + "io_getevents", /* 400 */ + "io_submit", /* 401 */ + "io_cancel", /* 402 */ + "SYS_403", /* 403 */ + "SYS_404", /* 404 */ + "exit_group", /* 405 */ + "lookup_dcookie", /* 406 */ + "epoll_create", /* 407 */ + "epoll_ctl", /* 408 */ + "epoll_wait", /* 409 */ + "remap_file_pages", /* 410 */ + "set_tid_address", /* 411 */ + "restart_syscall", /* 412 */ + "fadvise", /* 413 */ + "timer_create", /* 414 */ + "timer_settime", /* 415 */ + "timer_gettime", /* 416 */ + "timer_getoverrun", /* 417 */ + "timer_delete", /* 418 */ + "clock_settime", /* 419 */ + "clock_gettime", /* 420 */ + "clock_getres", /* 421 */ + "clock_nanosleep", /* 422 */ + "semtimedop", /* 423 */ + "tgkill", /* 424 */ + "stat64", /* 425 */ + "lstat64", /* 426 */ + "fstat64", /* 427 */ + "vserver", /* 428 */ + "mbind", /* 429 */ + "get_mempolicy", /* 430 */ + "set_mempolicy", /* 431 */ + "mq_open", /* 432 */ + "mq_unlink", /* 433 */ + "mq_timedsend", /* 434 */ + "mq_timedreceive", /* 435 */ + "mq_notify", /* 436 */ + "mq_getsetattr", /* 437 */ + "waitid" /* 438 */ diff --git a/sysdeps/linux-gnu/alpha/trace.c b/sysdeps/linux-gnu/alpha/trace.c new file mode 100644 index 0000000..e4d4063 --- /dev/null +++ b/sysdeps/linux-gnu/alpha/trace.c @@ -0,0 +1,75 @@ +#include "config.h" + +#include <sys/types.h> +#include <sys/wait.h> +#include <signal.h> +#include <sys/ptrace.h> +#include <asm/ptrace.h> + +#include "common.h" +#include "debug.h" + +#if (!defined(PTRACE_PEEKUSER) && defined(PTRACE_PEEKUSR)) +# define PTRACE_PEEKUSER PTRACE_PEEKUSR +#endif + +#if (!defined(PTRACE_POKEUSER) && defined(PTRACE_POKEUSR)) +# define PTRACE_POKEUSER PTRACE_POKEUSR +#endif + +void +get_arch_dep(Process *proc) { +} + +/* Returns 1 if syscall, 2 if sysret, 0 otherwise. + */ +int +syscall_p(Process *proc, int status, int *sysnum) { + if (WIFSTOPPED(status) + && WSTOPSIG(status) == (SIGTRAP | proc->tracesysgood)) { + char *ip = get_instruction_pointer(proc) - 4; + long x = ptrace(PTRACE_PEEKTEXT, proc->pid, ip, 0); + debug(2, "instr: %016lx", x); + if ((x & 0xffffffff) != 0x00000083) + return 0; + *sysnum = + ptrace(PTRACE_PEEKUSER, proc->pid, 0 /* REG_R0 */ , 0); + if (proc->callstack_depth > 0 && + proc->callstack[proc->callstack_depth - 1].is_syscall && + proc->callstack[proc->callstack_depth - 1].c_un.syscall == *sysnum) { + return 2; + } + if (*sysnum >= 0 && *sysnum < 500) { + return 1; + } + } + return 0; +} + +long +gimme_arg(enum tof type, Process *proc, int arg_num, arg_type_info *info) { + if (arg_num == -1) { /* return value */ + return ptrace(PTRACE_PEEKUSER, proc->pid, 0 /* REG_R0 */ , 0); + } + + if (type == LT_TOF_FUNCTION || type == LT_TOF_FUNCTIONR) { + if (arg_num <= 5) + return ptrace(PTRACE_PEEKUSER, proc->pid, + arg_num + 16 /* REG_A0 */ , 0); + else + return ptrace(PTRACE_PEEKTEXT, proc->pid, + proc->stack_pointer + 8 * (arg_num - 6), + 0); + } else if (type == LT_TOF_SYSCALL || type == LT_TOF_SYSCALLR) { + return ptrace(PTRACE_PEEKUSER, proc->pid, + arg_num + 16 /* REG_A0 */ , 0); + } else { + fprintf(stderr, "gimme_arg called with wrong arguments\n"); + exit(1); + } + return 0; +} + +void +save_register_args(enum tof type, Process *proc) { +} diff --git a/sysdeps/linux-gnu/arch_mksyscallent b/sysdeps/linux-gnu/arch_mksyscallent new file mode 100644 index 0000000..853d62d --- /dev/null +++ b/sysdeps/linux-gnu/arch_mksyscallent @@ -0,0 +1,42 @@ +#!/usr/bin/awk -f + +# hack expression to generate arch_syscallent.h from <asm/unistd.h> +# It reads from stdin and writes to stdout +# Currently (linux-2.6.16), it works OK on arm +# It is untested in other architectures + +BEGIN { + max=0; + FS="[ \t\n()+]+"; +} + +{ +# printf("/%s/%s/%s/%s/\n", $1, $2, $3, $4); + if (($1 ~ /^#define$/) && ($2 ~ /^__[A-Z]+_NR_/)) { + sub(/^__[A-Z]+_NR_/,"",$2); + if (($3>=0) && ($3<=1000)) { + SYSCALL[$3]=$2; + if ($3 > max) { + max=$3; + } + } else if (($3 ~ /^__[A-Z]+_NR_BASE$/) && ($4>=0) && ($4<=1000)) { + SYSCALL[$4]=$2; + if ($4 > max) { + max=$4; + } + } + } +} + +END { + for(i=0; i<=max; i++) { + if (!SYSCALL[i]) { + SYSCALL[i] = i; + } + pad = 32 - length(SYSCALL[i]); + if (pad<1) { + pad=1; + } + printf("\t\"%s\",%*s/* %d */\n", SYSCALL[i], pad, "", i); + } +} diff --git a/sysdeps/linux-gnu/arm/Makefile b/sysdeps/linux-gnu/arm/Makefile new file mode 100644 index 0000000..f55ba48 --- /dev/null +++ b/sysdeps/linux-gnu/arm/Makefile @@ -0,0 +1,10 @@ +OBJ = trace.o regs.o plt.o breakpoint.o + +all: arch.o + +arch.o: $(OBJ) arch.h + $(CC) -nostdlib -r -o arch.o $(OBJ) + +clean: + $(RM) $(OBJ) arch.o + diff --git a/sysdeps/linux-gnu/arm/arch.h b/sysdeps/linux-gnu/arm/arch.h new file mode 100644 index 0000000..8f2dfb3 --- /dev/null +++ b/sysdeps/linux-gnu/arm/arch.h @@ -0,0 +1,11 @@ +#define ARCH_HAVE_ENABLE_BREAKPOINT 1 +#define ARCH_HAVE_DISABLE_BREAKPOINT 1 + +#define BREAKPOINT_VALUE { 0xf0, 0x01, 0xf0, 0xe7 } +#define BREAKPOINT_LENGTH 4 +#define THUMB_BREAKPOINT_VALUE { 0x01, 0xde } +#define THUMB_BREAKPOINT_LENGTH 2 +#define DECR_PC_AFTER_BREAK 0 + +#define LT_ELFCLASS ELFCLASS32 +#define LT_ELF_MACHINE EM_ARM diff --git a/sysdeps/linux-gnu/arm/arch_syscallent.h b/sysdeps/linux-gnu/arm/arch_syscallent.h new file mode 100644 index 0000000..ce1e844 --- /dev/null +++ b/sysdeps/linux-gnu/arm/arch_syscallent.h @@ -0,0 +1,6 @@ + "0", /* 0 */ + "breakpoint", /* 1 */ + "cacheflush", /* 2 */ + "usr26", /* 3 */ + "usr32", /* 4 */ + "set_tls", /* 5 */ diff --git a/sysdeps/linux-gnu/arm/breakpoint.c b/sysdeps/linux-gnu/arm/breakpoint.c new file mode 100644 index 0000000..4c20260 --- /dev/null +++ b/sysdeps/linux-gnu/arm/breakpoint.c @@ -0,0 +1,77 @@ +/* + * This file is part of ltrace. + * + * Copyright (C) 2007 by Instituto Nokia de Tecnologia (INdT) + * + * Author: Anderson Lizardo <anderson.lizardo@indt.org.br> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + * Modified from sysdeps/linux-gnu/breakpoint.c and added ARM Thumb support. + */ + +#include <sys/ptrace.h> +#include "config.h" +#include "arch.h" +#include "options.h" +#include "output.h" +#include "debug.h" + +void +arch_enable_breakpoint(pid_t pid, Breakpoint *sbp) { + unsigned int i, j; + const unsigned char break_insn[] = BREAKPOINT_VALUE; + const unsigned char thumb_break_insn[] = THUMB_BREAKPOINT_VALUE; + + debug(1, "arch_enable_breakpoint(%d,%p)", pid, sbp->addr); + + for (i = 0; i < 1 + ((BREAKPOINT_LENGTH - 1) / sizeof(long)); i++) { + long a = ptrace(PTRACE_PEEKTEXT, pid, sbp->addr + i * sizeof(long), 0); + unsigned char *bytes = (unsigned char *)&a; + + debug(2, "current = 0x%lx, orig_value = 0x%lx, thumb_mode = %d", a, *(long *)&sbp->orig_value, sbp->thumb_mode); + for (j = 0; j < sizeof(long) && i * sizeof(long) + j < BREAKPOINT_LENGTH; j++) { + + sbp->orig_value[i * sizeof(long) + j] = bytes[j]; + if (!sbp->thumb_mode) { + bytes[j] = break_insn[i * sizeof(long) + j]; + } + else if (j < THUMB_BREAKPOINT_LENGTH) { + bytes[j] = thumb_break_insn[i * sizeof(long) + j]; + } + } + ptrace(PTRACE_POKETEXT, pid, sbp->addr + i * sizeof(long), a); + } +} + +void +arch_disable_breakpoint(pid_t pid, const Breakpoint *sbp) { + unsigned int i, j; + const unsigned char break_insn[] = BREAKPOINT_VALUE; + const unsigned char thumb_break_insn[] = THUMB_BREAKPOINT_VALUE; + + debug(1, "arch_disable_breakpoint(%d,%p)", pid, sbp->addr); + + for (i = 0; i < 1 + ((BREAKPOINT_LENGTH - 1) / sizeof(long)); i++) { + long a = ptrace(PTRACE_PEEKTEXT, pid, sbp->addr + i * sizeof(long), 0); + unsigned char *bytes = (unsigned char *)&a; + + debug(2, "current = 0x%lx, orig_value = 0x%lx, thumb_mode = %d", a, *(long *)&sbp->orig_value, sbp->thumb_mode); + for (j = 0; j < sizeof(long) && i * sizeof(long) + j < BREAKPOINT_LENGTH; j++) { + bytes[j] = sbp->orig_value[i * sizeof(long) + j]; + } + ptrace(PTRACE_POKETEXT, pid, sbp->addr + i * sizeof(long), a); + } +} diff --git a/sysdeps/linux-gnu/arm/plt.c b/sysdeps/linux-gnu/arm/plt.c new file mode 100644 index 0000000..bd92a63 --- /dev/null +++ b/sysdeps/linux-gnu/arm/plt.c @@ -0,0 +1,12 @@ +#include <gelf.h> +#include "common.h" + +GElf_Addr +arch_plt_sym_val(struct ltelf *lte, size_t ndx, GElf_Rela * rela) { + return lte->plt_addr + 20 + ndx * 12; +} + +void * +sym2addr(Process *proc, struct library_symbol *sym) { + return sym->enter_addr; +} diff --git a/sysdeps/linux-gnu/arm/ptrace.h b/sysdeps/linux-gnu/arm/ptrace.h new file mode 100644 index 0000000..52215bc --- /dev/null +++ b/sysdeps/linux-gnu/arm/ptrace.h @@ -0,0 +1,9 @@ +#include <sys/ptrace.h> +#include <asm/ptrace.h> + +typedef struct { + int valid; + struct pt_regs regs; + long func_arg[5]; + long sysc_arg[5]; +} proc_archdep; diff --git a/sysdeps/linux-gnu/arm/regs.c b/sysdeps/linux-gnu/arm/regs.c new file mode 100644 index 0000000..2488b0a --- /dev/null +++ b/sysdeps/linux-gnu/arm/regs.c @@ -0,0 +1,51 @@ +#include "config.h" + +#include <sys/types.h> +#include <sys/ptrace.h> +#include <asm/ptrace.h> + +#include "common.h" + +#if (!defined(PTRACE_PEEKUSER) && defined(PTRACE_PEEKUSR)) +# define PTRACE_PEEKUSER PTRACE_PEEKUSR +#endif + +#if (!defined(PTRACE_POKEUSER) && defined(PTRACE_POKEUSR)) +# define PTRACE_POKEUSER PTRACE_POKEUSR +#endif + +#define off_pc 60 +#define off_lr 56 +#define off_sp 52 + +void * +get_instruction_pointer(Process *proc) { + return (void *)ptrace(PTRACE_PEEKUSER, proc->pid, off_pc, 0); +} + +void +set_instruction_pointer(Process *proc, void *addr) { + ptrace(PTRACE_POKEUSER, proc->pid, off_pc, addr); +} + +void * +get_stack_pointer(Process *proc) { + return (void *)ptrace(PTRACE_PEEKUSER, proc->pid, off_sp, 0); +} + +/* really, this is given the *stack_pointer expecting + * a CISC architecture; in our case, we don't need that */ +void * +get_return_addr(Process *proc, void *stack_pointer) { + long addr = ptrace(PTRACE_PEEKUSER, proc->pid, off_lr, 0); + + proc->thumb_mode = addr & 1; + if (proc->thumb_mode) + addr &= ~1; + return (void *)addr; +} + +void +set_return_addr(Process *proc, void *addr) { + ptrace(PTRACE_POKEUSER, proc->pid, off_lr, addr); +} diff --git a/sysdeps/linux-gnu/arm/signalent.h b/sysdeps/linux-gnu/arm/signalent.h new file mode 100644 index 0000000..0afb004 --- /dev/null +++ b/sysdeps/linux-gnu/arm/signalent.h @@ -0,0 +1,33 @@ + "SIG_0", /* 0 */ + "SIGHUP", /* 1 */ + "SIGINT", /* 2 */ + "SIGQUIT", /* 3 */ + "SIGILL", /* 4 */ + "SIGTRAP", /* 5 */ + "SIGABRT", /* 6 */ + "SIGBUS", /* 7 */ + "SIGFPE", /* 8 */ + "SIGKILL", /* 9 */ + "SIGUSR1", /* 10 */ + "SIGSEGV", /* 11 */ + "SIGUSR2", /* 12 */ + "SIGPIPE", /* 13 */ + "SIGALRM", /* 14 */ + "SIGTERM", /* 15 */ + "SIGSTKFLT", /* 16 */ + "SIGCHLD", /* 17 */ + "SIGCONT", /* 18 */ + "SIGSTOP", /* 19 */ + "SIGTSTP", /* 20 */ + "SIGTTIN", /* 21 */ + "SIGTTOU", /* 22 */ + "SIGURG", /* 23 */ + "SIGXCPU", /* 24 */ + "SIGXFSZ", /* 25 */ + "SIGVTALRM", /* 26 */ + "SIGPROF", /* 27 */ + "SIGWINCH", /* 28 */ + "SIGIO", /* 29 */ + "SIGPWR", /* 30 */ + "SIGSYS", /* 31 */ + "SIGSWI", /* 32 */ diff --git a/sysdeps/linux-gnu/arm/syscallent.h b/sysdeps/linux-gnu/arm/syscallent.h new file mode 100644 index 0000000..4113234 --- /dev/null +++ b/sysdeps/linux-gnu/arm/syscallent.h @@ -0,0 +1,322 @@ + "restart_syscall", /* 0 */ + "exit", /* 1 */ + "fork", /* 2 */ + "read", /* 3 */ + "write", /* 4 */ + "open", /* 5 */ + "close", /* 6 */ + "7", /* 7 */ + "creat", /* 8 */ + "link", /* 9 */ + "unlink", /* 10 */ + "execve", /* 11 */ + "chdir", /* 12 */ + "time", /* 13 */ + "mknod", /* 14 */ + "chmod", /* 15 */ + "lchown", /* 16 */ + "17", /* 17 */ + "18", /* 18 */ + "lseek", /* 19 */ + "getpid", /* 20 */ + "mount", /* 21 */ + "umount", /* 22 */ + "setuid", /* 23 */ + "getuid", /* 24 */ + "stime", /* 25 */ + "ptrace", /* 26 */ + "alarm", /* 27 */ + "28", /* 28 */ + "pause", /* 29 */ + "utime", /* 30 */ + "31", /* 31 */ + "32", /* 32 */ + "access", /* 33 */ + "nice", /* 34 */ + "35", /* 35 */ + "sync", /* 36 */ + "kill", /* 37 */ + "rename", /* 38 */ + "mkdir", /* 39 */ + "rmdir", /* 40 */ + "dup", /* 41 */ + "pipe", /* 42 */ + "times", /* 43 */ + "44", /* 44 */ + "brk", /* 45 */ + "setgid", /* 46 */ + "getgid", /* 47 */ + "48", /* 48 */ + "geteuid", /* 49 */ + "getegid", /* 50 */ + "acct", /* 51 */ + "umount2", /* 52 */ + "53", /* 53 */ + "ioctl", /* 54 */ + "fcntl", /* 55 */ + "56", /* 56 */ + "setpgid", /* 57 */ + "58", /* 58 */ + "59", /* 59 */ + "umask", /* 60 */ + "chroot", /* 61 */ + "ustat", /* 62 */ + "dup2", /* 63 */ + "getppid", /* 64 */ + "getpgrp", /* 65 */ + "setsid", /* 66 */ + "sigaction", /* 67 */ + "68", /* 68 */ + "69", /* 69 */ + "setreuid", /* 70 */ + "setregid", /* 71 */ + "sigsuspend", /* 72 */ + "sigpending", /* 73 */ + "sethostname", /* 74 */ + "setrlimit", /* 75 */ + "getrlimit", /* 76 */ + "getrusage", /* 77 */ + "gettimeofday", /* 78 */ + "settimeofday", /* 79 */ + "getgroups", /* 80 */ + "setgroups", /* 81 */ + "select", /* 82 */ + "symlink", /* 83 */ + "84", /* 84 */ + "readlink", /* 85 */ + "uselib", /* 86 */ + "swapon", /* 87 */ + "reboot", /* 88 */ + "readdir", /* 89 */ + "mmap", /* 90 */ + "munmap", /* 91 */ + "truncate", /* 92 */ + "ftruncate", /* 93 */ + "fchmod", /* 94 */ + "fchown", /* 95 */ + "getpriority", /* 96 */ + "setpriority", /* 97 */ + "98", /* 98 */ + "statfs", /* 99 */ + "fstatfs", /* 100 */ + "101", /* 101 */ + "socketcall", /* 102 */ + "syslog", /* 103 */ + "setitimer", /* 104 */ + "getitimer", /* 105 */ + "stat", /* 106 */ + "lstat", /* 107 */ + "fstat", /* 108 */ + "109", /* 109 */ + "110", /* 110 */ + "vhangup", /* 111 */ + "112", /* 112 */ + "syscall", /* 113 */ + "wait4", /* 114 */ + "swapoff", /* 115 */ + "sysinfo", /* 116 */ + "ipc", /* 117 */ + "fsync", /* 118 */ + "sigreturn", /* 119 */ + "clone", /* 120 */ + "setdomainname", /* 121 */ + "uname", /* 122 */ + "123", /* 123 */ + "adjtimex", /* 124 */ + "mprotect", /* 125 */ + "sigprocmask", /* 126 */ + "127", /* 127 */ + "init_module", /* 128 */ + "delete_module", /* 129 */ + "130", /* 130 */ + "quotactl", /* 131 */ + "getpgid", /* 132 */ + "fchdir", /* 133 */ + "bdflush", /* 134 */ + "sysfs", /* 135 */ + "personality", /* 136 */ + "137", /* 137 */ + "setfsuid", /* 138 */ + "setfsgid", /* 139 */ + "_llseek", /* 140 */ + "getdents", /* 141 */ + "_newselect", /* 142 */ + "flock", /* 143 */ + "msync", /* 144 */ + "readv", /* 145 */ + "writev", /* 146 */ + "getsid", /* 147 */ + "fdatasync", /* 148 */ + "_sysctl", /* 149 */ + "mlock", /* 150 */ + "munlock", /* 151 */ + "mlockall", /* 152 */ + "munlockall", /* 153 */ + "sched_setparam", /* 154 */ + "sched_getparam", /* 155 */ + "sched_setscheduler", /* 156 */ + "sched_getscheduler", /* 157 */ + "sched_yield", /* 158 */ + "sched_get_priority_max", /* 159 */ + "sched_get_priority_min", /* 160 */ + "sched_rr_get_interval", /* 161 */ + "nanosleep", /* 162 */ + "mremap", /* 163 */ + "setresuid", /* 164 */ + "getresuid", /* 165 */ + "166", /* 166 */ + "167", /* 167 */ + "poll", /* 168 */ + "nfsservctl", /* 169 */ + "setresgid", /* 170 */ + "getresgid", /* 171 */ + "prctl", /* 172 */ + "rt_sigreturn", /* 173 */ + "rt_sigaction", /* 174 */ + "rt_sigprocmask", /* 175 */ + "rt_sigpending", /* 176 */ + "rt_sigtimedwait", /* 177 */ + "rt_sigqueueinfo", /* 178 */ + "rt_sigsuspend", /* 179 */ + "pread64", /* 180 */ + "pwrite64", /* 181 */ + "chown", /* 182 */ + "getcwd", /* 183 */ + "capget", /* 184 */ + "capset", /* 185 */ + "sigaltstack", /* 186 */ + "sendfile", /* 187 */ + "188", /* 188 */ + "189", /* 189 */ + "vfork", /* 190 */ + "ugetrlimit", /* 191 */ + "mmap2", /* 192 */ + "truncate64", /* 193 */ + "ftruncate64", /* 194 */ + "stat64", /* 195 */ + "lstat64", /* 196 */ + "fstat64", /* 197 */ + "lchown32", /* 198 */ + "getuid32", /* 199 */ + "getgid32", /* 200 */ + "geteuid32", /* 201 */ + "getegid32", /* 202 */ + "setreuid32", /* 203 */ + "setregid32", /* 204 */ + "getgroups32", /* 205 */ + "setgroups32", /* 206 */ + "fchown32", /* 207 */ + "setresuid32", /* 208 */ + "getresuid32", /* 209 */ + "setresgid32", /* 210 */ + "getresgid32", /* 211 */ + "chown32", /* 212 */ + "setuid32", /* 213 */ + "setgid32", /* 214 */ + "setfsuid32", /* 215 */ + "setfsgid32", /* 216 */ + "getdents64", /* 217 */ + "pivot_root", /* 218 */ + "mincore", /* 219 */ + "madvise", /* 220 */ + "fcntl64", /* 221 */ + "222", /* 222 */ + "223", /* 223 */ + "gettid", /* 224 */ + "readahead", /* 225 */ + "setxattr", /* 226 */ + "lsetxattr", /* 227 */ + "fsetxattr", /* 228 */ + "getxattr", /* 229 */ + "lgetxattr", /* 230 */ + "fgetxattr", /* 231 */ + "listxattr", /* 232 */ + "llistxattr", /* 233 */ + "flistxattr", /* 234 */ + "removexattr", /* 235 */ + "lremovexattr", /* 236 */ + "fremovexattr", /* 237 */ + "tkill", /* 238 */ + "sendfile64", /* 239 */ + "futex", /* 240 */ + "sched_setaffinity", /* 241 */ + "sched_getaffinity", /* 242 */ + "io_setup", /* 243 */ + "io_destroy", /* 244 */ + "io_getevents", /* 245 */ + "io_submit", /* 246 */ + "io_cancel", /* 247 */ + "exit_group", /* 248 */ + "lookup_dcookie", /* 249 */ + "epoll_create", /* 250 */ + "epoll_ctl", /* 251 */ + "epoll_wait", /* 252 */ + "remap_file_pages", /* 253 */ + "254", /* 254 */ + "255", /* 255 */ + "set_tid_address", /* 256 */ + "timer_create", /* 257 */ + "timer_settime", /* 258 */ + "timer_gettime", /* 259 */ + "timer_getoverrun", /* 260 */ + "timer_delete", /* 261 */ + "clock_settime", /* 262 */ + "clock_gettime", /* 263 */ + "clock_getres", /* 264 */ + "clock_nanosleep", /* 265 */ + "statfs64", /* 266 */ + "fstatfs64", /* 267 */ + "tgkill", /* 268 */ + "utimes", /* 269 */ + "arm_fadvise64_64", /* 270 */ + "pciconfig_iobase", /* 271 */ + "pciconfig_read", /* 272 */ + "pciconfig_write", /* 273 */ + "mq_open", /* 274 */ + "mq_unlink", /* 275 */ + "mq_timedsend", /* 276 */ + "mq_timedreceive", /* 277 */ + "mq_notify", /* 278 */ + "mq_getsetattr", /* 279 */ + "waitid", /* 280 */ + "socket", /* 281 */ + "bind", /* 282 */ + "connect", /* 283 */ + "listen", /* 284 */ + "accept", /* 285 */ + "getsockname", /* 286 */ + "getpeername", /* 287 */ + "socketpair", /* 288 */ + "send", /* 289 */ + "sendto", /* 290 */ + "recv", /* 291 */ + "recvfrom", /* 292 */ + "shutdown", /* 293 */ + "setsockopt", /* 294 */ + "getsockopt", /* 295 */ + "sendmsg", /* 296 */ + "recvmsg", /* 297 */ + "semop", /* 298 */ + "semget", /* 299 */ + "semctl", /* 300 */ + "msgsnd", /* 301 */ + "msgrcv", /* 302 */ + "msgget", /* 303 */ + "msgctl", /* 304 */ + "shmat", /* 305 */ + "shmdt", /* 306 */ + "shmget", /* 307 */ + "shmctl", /* 308 */ + "add_key", /* 309 */ + "request_key", /* 310 */ + "keyctl", /* 311 */ + "semtimedop", /* 312 */ + "vserver", /* 313 */ + "ioprio_set", /* 314 */ + "ioprio_get", /* 315 */ + "inotify_init", /* 316 */ + "inotify_add_watch", /* 317 */ + "inotify_rm_watch", /* 318 */ + "mbind", /* 319 */ + "get_mempolicy", /* 320 */ + "set_mempolicy", /* 321 */ diff --git a/sysdeps/linux-gnu/arm/trace.c b/sysdeps/linux-gnu/arm/trace.c new file mode 100644 index 0000000..10f7cc4 --- /dev/null +++ b/sysdeps/linux-gnu/arm/trace.c @@ -0,0 +1,131 @@ +#include "config.h" + +#include <string.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <signal.h> +#include <sys/ptrace.h> +#include <asm/ptrace.h> + +#include "common.h" +#include "output.h" +#include "ptrace.h" + +#if (!defined(PTRACE_PEEKUSER) && defined(PTRACE_PEEKUSR)) +# define PTRACE_PEEKUSER PTRACE_PEEKUSR +#endif + +#if (!defined(PTRACE_POKEUSER) && defined(PTRACE_POKEUSR)) +# define PTRACE_POKEUSER PTRACE_POKEUSR +#endif + +#define off_r0 0 +#define off_r7 28 +#define off_ip 48 +#define off_pc 60 + +void +get_arch_dep(Process *proc) { + proc_archdep *a; + + if (!proc->arch_ptr) + proc->arch_ptr = (void *)malloc(sizeof(proc_archdep)); + a = (proc_archdep *) (proc->arch_ptr); + a->valid = (ptrace(PTRACE_GETREGS, proc->pid, 0, &a->regs) >= 0); +} + +/* Returns 0 if not a syscall, + * 1 if syscall entry, 2 if syscall exit, + * 3 if arch-specific syscall entry, 4 if arch-specific syscall exit, + * -1 on error. + */ +int +syscall_p(Process *proc, int status, int *sysnum) { + if (WIFSTOPPED(status) + && WSTOPSIG(status) == (SIGTRAP | proc->tracesysgood)) { + /* get the user's pc (plus 8) */ + int pc = ptrace(PTRACE_PEEKUSER, proc->pid, off_pc, 0); + /* fetch the SWI instruction */ + int insn = ptrace(PTRACE_PEEKTEXT, proc->pid, pc - 4, 0); + int ip = ptrace(PTRACE_PEEKUSER, proc->pid, off_ip, 0); + + if (insn == 0xef000000 || insn == 0x0f000000) { + /* EABI syscall */ + *sysnum = ptrace(PTRACE_PEEKUSER, proc->pid, off_r7, 0); + } else if ((insn & 0xfff00000) == 0xef900000) { + /* old ABI syscall */ + *sysnum = insn & 0xfffff; + } else { + /* TODO: handle swi<cond> variations */ + /* one possible reason for getting in here is that we + * are coming from a signal handler, so the current + * PC does not point to the instruction just after the + * "swi" one. */ + output_line(proc, "unexpected instruction 0x%x at %p", insn, pc - 4); + return -1; + } + if ((*sysnum & 0xf0000) == 0xf0000) { + /* arch-specific syscall */ + *sysnum &= ~0xf0000; + return ip ? 4 : 3; + } + /* ARM syscall convention: on syscall entry, ip is zero; + * on syscall exit, ip is non-zero */ + return ip ? 2 : 1; + } + return 0; +} + +long +gimme_arg(enum tof type, Process *proc, int arg_num, arg_type_info *info) { + proc_archdep *a = (proc_archdep *) proc->arch_ptr; + + if (arg_num == -1) { /* return value */ + return ptrace(PTRACE_PEEKUSER, proc->pid, off_r0, 0); + } + + /* deal with the ARM calling conventions */ + if (type == LT_TOF_FUNCTION || type == LT_TOF_FUNCTIONR) { + if (arg_num < 4) { + if (a->valid && type == LT_TOF_FUNCTION) + return a->regs.uregs[arg_num]; + if (a->valid && type == LT_TOF_FUNCTIONR) + return a->func_arg[arg_num]; + return ptrace(PTRACE_PEEKUSER, proc->pid, 4 * arg_num, + 0); + } else { + return ptrace(PTRACE_PEEKDATA, proc->pid, + proc->stack_pointer + 4 * (arg_num - 4), + 0); + } + } else if (type == LT_TOF_SYSCALL || type == LT_TOF_SYSCALLR) { + if (arg_num < 5) { + if (a->valid && type == LT_TOF_SYSCALL) + return a->regs.uregs[arg_num]; + if (a->valid && type == LT_TOF_SYSCALLR) + return a->sysc_arg[arg_num]; + return ptrace(PTRACE_PEEKUSER, proc->pid, 4 * arg_num, + 0); + } else { + return ptrace(PTRACE_PEEKDATA, proc->pid, + proc->stack_pointer + 4 * (arg_num - 5), + 0); + } + } else { + fprintf(stderr, "gimme_arg called with wrong arguments\n"); + exit(1); + } + + return 0; +} + +void +save_register_args(enum tof type, Process *proc) { + proc_archdep *a = (proc_archdep *) proc->arch_ptr; + if (a->valid) { + if (type == LT_TOF_FUNCTION) + memcpy(a->func_arg, a->regs.uregs, sizeof(a->func_arg)); + else + memcpy(a->sysc_arg, a->regs.uregs, sizeof(a->sysc_arg)); + } +} diff --git a/sysdeps/linux-gnu/breakpoint.c b/sysdeps/linux-gnu/breakpoint.c new file mode 100644 index 0000000..5ca131a --- /dev/null +++ b/sysdeps/linux-gnu/breakpoint.c @@ -0,0 +1,86 @@ +#include "config.h" + +#include <sys/ptrace.h> +#include <string.h> + +#include "common.h" +#include "arch.h" + +static unsigned char break_insn[] = BREAKPOINT_VALUE; + +#ifdef ARCH_HAVE_ENABLE_BREAKPOINT +extern void arch_enable_breakpoint(pid_t, Breakpoint *); +void +enable_breakpoint(pid_t pid, Breakpoint *sbp) { + if (sbp->libsym) { + debug(DEBUG_PROCESS, "enable_breakpoint: pid=%d, addr=%p, symbol=%s", pid, sbp->addr, sbp->libsym->name); + } else { + debug(DEBUG_PROCESS, "enable_breakpoint: pid=%d, addr=%p", pid, sbp->addr); + } + arch_enable_breakpoint(pid, sbp); +} +#else +void +enable_breakpoint(pid_t pid, Breakpoint *sbp) { + unsigned int i, j; + + if (sbp->libsym) { + debug(DEBUG_PROCESS, "enable_breakpoint: pid=%d, addr=%p, symbol=%s", pid, sbp->addr, sbp->libsym->name); + } else { + debug(DEBUG_PROCESS, "enable_breakpoint: pid=%d, addr=%p", pid, sbp->addr); + } + + for (i = 0; i < 1 + ((BREAKPOINT_LENGTH - 1) / sizeof(long)); i++) { + long a = + ptrace(PTRACE_PEEKTEXT, pid, sbp->addr + i * sizeof(long), + 0); + for (j = 0; + j < sizeof(long) + && i * sizeof(long) + j < BREAKPOINT_LENGTH; j++) { + unsigned char *bytes = (unsigned char *)&a; + + sbp->orig_value[i * sizeof(long) + j] = bytes[j]; + bytes[j] = break_insn[i * sizeof(long) + j]; + } + ptrace(PTRACE_POKETEXT, pid, sbp->addr + i * sizeof(long), a); + } +} +#endif /* ARCH_HAVE_ENABLE_BREAKPOINT */ + +#ifdef ARCH_HAVE_DISABLE_BREAKPOINT +extern void arch_disable_breakpoint(pid_t, const Breakpoint *sbp); +void +disable_breakpoint(pid_t pid, const Breakpoint *sbp) { + if (sbp->libsym) { + debug(DEBUG_PROCESS, "disable_breakpoint: pid=%d, addr=%p, symbol=%s", pid, sbp->addr, sbp->libsym->name); + } else { + debug(DEBUG_PROCESS, "disable_breakpoint: pid=%d, addr=%p", pid, sbp->addr); + } + arch_disable_breakpoint(pid, sbp); +} +#else +void +disable_breakpoint(pid_t pid, const Breakpoint *sbp) { + unsigned int i, j; + + if (sbp->libsym) { + debug(DEBUG_PROCESS, "disable_breakpoint: pid=%d, addr=%p, symbol=%s", pid, sbp->addr, sbp->libsym->name); + } else { + debug(DEBUG_PROCESS, "disable_breakpoint: pid=%d, addr=%p", pid, sbp->addr); + } + + for (i = 0; i < 1 + ((BREAKPOINT_LENGTH - 1) / sizeof(long)); i++) { + long a = + ptrace(PTRACE_PEEKTEXT, pid, sbp->addr + i * sizeof(long), + 0); + for (j = 0; + j < sizeof(long) + && i * sizeof(long) + j < BREAKPOINT_LENGTH; j++) { + unsigned char *bytes = (unsigned char *)&a; + + bytes[j] = sbp->orig_value[i * sizeof(long) + j]; + } + ptrace(PTRACE_POKETEXT, pid, sbp->addr + i * sizeof(long), a); + } +} +#endif /* ARCH_HAVE_DISABLE_BREAKPOINT */ diff --git a/sysdeps/linux-gnu/events.c b/sysdeps/linux-gnu/events.c new file mode 100644 index 0000000..a1e2a14 --- /dev/null +++ b/sysdeps/linux-gnu/events.c @@ -0,0 +1,161 @@ +#include "config.h" + +#define _GNU_SOURCE 1 +#include <stdlib.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <errno.h> +#include <signal.h> +#include <string.h> +#include <sys/ptrace.h> + +#include "common.h" + +static Event event; + +Event * +next_event(void) { + pid_t pid; + int status; + int tmp; + int stop_signal; + + debug(DEBUG_FUNCTION, "next_event()"); + if (!list_of_processes) { + debug(DEBUG_EVENT, "event: No more traced programs: exiting"); + exit(0); + } + pid = waitpid(-1, &status, __WALL); + if (pid == -1) { + if (errno == ECHILD) { + debug(DEBUG_EVENT, "event: No more traced programs: exiting"); + exit(0); + } else if (errno == EINTR) { + debug(DEBUG_EVENT, "event: none (wait received EINTR?)"); + event.type = EVENT_NONE; + return &event; + } + perror("wait"); + exit(1); + } + event.proc = pid2proc(pid); + if (!event.proc || event.proc->state == STATE_BEING_CREATED) { + event.type = EVENT_NEW; + event.e_un.newpid = pid; + debug(DEBUG_EVENT, "event: NEW: pid=%d", pid); + return &event; + } + get_arch_dep(event.proc); + event.proc->instruction_pointer = NULL; + debug(3, "event from pid %u", pid); + if (event.proc->breakpoints_enabled == -1) { + enable_all_breakpoints(event.proc); + event.type = EVENT_NONE; + trace_set_options(event.proc, event.proc->pid); + continue_process(event.proc->pid); + debug(DEBUG_EVENT, "event: NONE: pid=%d (enabling breakpoints)", pid); + return &event; + } + if (opt_i) { + event.proc->instruction_pointer = + get_instruction_pointer(event.proc); + } + switch (syscall_p(event.proc, status, &tmp)) { + case 1: + event.type = EVENT_SYSCALL; + event.e_un.sysnum = tmp; + debug(DEBUG_EVENT, "event: SYSCALL: pid=%d, sysnum=%d", pid, tmp); + return &event; + case 2: + event.type = EVENT_SYSRET; + event.e_un.sysnum = tmp; + debug(DEBUG_EVENT, "event: SYSRET: pid=%d, sysnum=%d", pid, tmp); + return &event; + case 3: + event.type = EVENT_ARCH_SYSCALL; + event.e_un.sysnum = tmp; + debug(DEBUG_EVENT, "event: ARCH_SYSCALL: pid=%d, sysnum=%d", pid, tmp); + return &event; + case 4: + event.type = EVENT_ARCH_SYSRET; + event.e_un.sysnum = tmp; + debug(DEBUG_EVENT, "event: ARCH_SYSRET: pid=%d, sysnum=%d", pid, tmp); + return &event; + case -1: + event.type = EVENT_NONE; + continue_process(event.proc->pid); + debug(DEBUG_EVENT, "event: NONE: pid=%d (syscall_p returned -1)", pid); + return &event; + } + if (WIFSTOPPED(status) && ((status>>16 == PTRACE_EVENT_FORK) || (status>>16 == PTRACE_EVENT_VFORK) || (status>>16 == PTRACE_EVENT_CLONE))) { + unsigned long data; + ptrace(PTRACE_GETEVENTMSG, pid, NULL, &data); + event.type = EVENT_CLONE; + event.e_un.newpid = data; + debug(DEBUG_EVENT, "event: CLONE: pid=%d, newpid=%d", pid, (int)data); + return &event; + } + if (WIFSTOPPED(status) && (status>>16 == PTRACE_EVENT_EXEC)) { + event.type = EVENT_EXEC; + debug(DEBUG_EVENT, "event: EXEC: pid=%d", pid); + return &event; + } + if (WIFEXITED(status)) { + event.type = EVENT_EXIT; + event.e_un.ret_val = WEXITSTATUS(status); + debug(DEBUG_EVENT, "event: EXIT: pid=%d, status=%d", pid, event.e_un.ret_val); + return &event; + } + if (WIFSIGNALED(status)) { + event.type = EVENT_EXIT_SIGNAL; + event.e_un.signum = WTERMSIG(status); + debug(DEBUG_EVENT, "event: EXIT_SIGNAL: pid=%d, signum=%d", pid, event.e_un.signum); + return &event; + } + if (!WIFSTOPPED(status)) { + /* should never happen */ + event.type = EVENT_NONE; + debug(DEBUG_EVENT, "event: NONE: pid=%d (wait error?)", pid); + return &event; + } + + stop_signal = WSTOPSIG(status); + + /* On some targets, breakpoints are signalled not using + SIGTRAP, but also with SIGILL, SIGSEGV or SIGEMT. Check + for these. (TODO: is this true?) */ + if (stop_signal == SIGSEGV + || stop_signal == SIGILL +#ifdef SIGEMT + || stop_signal == SIGEMT +#endif + ) { + if (!event.proc->instruction_pointer) { + event.proc->instruction_pointer = + get_instruction_pointer(event.proc); + } + + if (address2bpstruct(event.proc, event.proc->instruction_pointer)) + stop_signal = SIGTRAP; + } + + if (stop_signal != (SIGTRAP | event.proc->tracesysgood) + && stop_signal != SIGTRAP) { + event.type = EVENT_SIGNAL; + event.e_un.signum = stop_signal; + debug(DEBUG_EVENT, "event: SIGNAL: pid=%d, signum=%d", pid, stop_signal); + return &event; + } + + /* last case [by exhaustion] */ + event.type = EVENT_BREAKPOINT; + + if (!event.proc->instruction_pointer) { + event.proc->instruction_pointer = + get_instruction_pointer(event.proc); + } + event.e_un.brk_addr = + event.proc->instruction_pointer - DECR_PC_AFTER_BREAK; + debug(DEBUG_EVENT, "event: BREAKPOINT: pid=%d, addr=%p", pid, event.e_un.brk_addr); + return &event; +} diff --git a/sysdeps/linux-gnu/i386/Makefile b/sysdeps/linux-gnu/i386/Makefile new file mode 100644 index 0000000..60d7531 --- /dev/null +++ b/sysdeps/linux-gnu/i386/Makefile @@ -0,0 +1,10 @@ +OBJ = trace.o regs.o plt.o + +all: arch.o + +arch.o: $(OBJ) + $(CC) -nostdlib -r -o arch.o $(OBJ) + +clean: + $(RM) $(OBJ) arch.o + diff --git a/sysdeps/linux-gnu/i386/arch.h b/sysdeps/linux-gnu/i386/arch.h new file mode 100644 index 0000000..dc7383f --- /dev/null +++ b/sysdeps/linux-gnu/i386/arch.h @@ -0,0 +1,6 @@ +#define BREAKPOINT_VALUE {0xcc} +#define BREAKPOINT_LENGTH 1 +#define DECR_PC_AFTER_BREAK 1 + +#define LT_ELFCLASS ELFCLASS32 +#define LT_ELF_MACHINE EM_386 diff --git a/sysdeps/linux-gnu/i386/plt.c b/sysdeps/linux-gnu/i386/plt.c new file mode 100644 index 0000000..b53ff44 --- /dev/null +++ b/sysdeps/linux-gnu/i386/plt.c @@ -0,0 +1,12 @@ +#include <gelf.h> +#include "common.h" + +GElf_Addr +arch_plt_sym_val(struct ltelf *lte, size_t ndx, GElf_Rela * rela) { + return lte->plt_addr + (ndx + 1) * 16; +} + +void * +sym2addr(Process *proc, struct library_symbol *sym) { + return sym->enter_addr; +} diff --git a/sysdeps/linux-gnu/i386/ptrace.h b/sysdeps/linux-gnu/i386/ptrace.h new file mode 100644 index 0000000..c3cbcb6 --- /dev/null +++ b/sysdeps/linux-gnu/i386/ptrace.h @@ -0,0 +1 @@ +#include <sys/ptrace.h> diff --git a/sysdeps/linux-gnu/i386/regs.c b/sysdeps/linux-gnu/i386/regs.c new file mode 100644 index 0000000..6777f17 --- /dev/null +++ b/sysdeps/linux-gnu/i386/regs.c @@ -0,0 +1,40 @@ +#include "config.h" + +#include <sys/types.h> +#include <sys/ptrace.h> +#include <asm/ptrace.h> + +#include "common.h" + +#if (!defined(PTRACE_PEEKUSER) && defined(PTRACE_PEEKUSR)) +# define PTRACE_PEEKUSER PTRACE_PEEKUSR +#endif + +#if (!defined(PTRACE_POKEUSER) && defined(PTRACE_POKEUSR)) +# define PTRACE_POKEUSER PTRACE_POKEUSR +#endif + +void * +get_instruction_pointer(Process *proc) { + return (void *)ptrace(PTRACE_PEEKUSER, proc->pid, 4 * EIP, 0); +} + +void +set_instruction_pointer(Process *proc, void *addr) { + ptrace(PTRACE_POKEUSER, proc->pid, 4 * EIP, (long)addr); +} + +void * +get_stack_pointer(Process *proc) { + return (void *)ptrace(PTRACE_PEEKUSER, proc->pid, 4 * UESP, 0); +} + +void * +get_return_addr(Process *proc, void *stack_pointer) { + return (void *)ptrace(PTRACE_PEEKTEXT, proc->pid, stack_pointer, 0); +} + +void +set_return_addr(Process *proc, void *addr) { + ptrace(PTRACE_POKETEXT, proc->pid, proc->stack_pointer, (long)addr); +} diff --git a/sysdeps/linux-gnu/i386/signalent.h b/sysdeps/linux-gnu/i386/signalent.h new file mode 100644 index 0000000..5395f82 --- /dev/null +++ b/sysdeps/linux-gnu/i386/signalent.h @@ -0,0 +1,32 @@ + "SIG_0", /* 0 */ + "SIGHUP", /* 1 */ + "SIGINT", /* 2 */ + "SIGQUIT", /* 3 */ + "SIGILL", /* 4 */ + "SIGTRAP", /* 5 */ + "SIGABRT", /* 6 */ + "SIGBUS", /* 7 */ + "SIGFPE", /* 8 */ + "SIGKILL", /* 9 */ + "SIGUSR1", /* 10 */ + "SIGSEGV", /* 11 */ + "SIGUSR2", /* 12 */ + "SIGPIPE", /* 13 */ + "SIGALRM", /* 14 */ + "SIGTERM", /* 15 */ + "SIGSTKFLT", /* 16 */ + "SIGCHLD", /* 17 */ + "SIGCONT", /* 18 */ + "SIGSTOP", /* 19 */ + "SIGTSTP", /* 20 */ + "SIGTTIN", /* 21 */ + "SIGTTOU", /* 22 */ + "SIGURG", /* 23 */ + "SIGXCPU", /* 24 */ + "SIGXFSZ", /* 25 */ + "SIGVTALRM", /* 26 */ + "SIGPROF", /* 27 */ + "SIGWINCH", /* 28 */ + "SIGIO", /* 29 */ + "SIGPWR", /* 30 */ + "SIGSYS", /* 31 */ diff --git a/sysdeps/linux-gnu/i386/syscallent.h b/sysdeps/linux-gnu/i386/syscallent.h new file mode 100644 index 0000000..8f4c887 --- /dev/null +++ b/sysdeps/linux-gnu/i386/syscallent.h @@ -0,0 +1,317 @@ + "restart_syscall", /* 0 */ + "exit", /* 1 */ + "fork", /* 2 */ + "read", /* 3 */ + "write", /* 4 */ + "open", /* 5 */ + "close", /* 6 */ + "waitpid", /* 7 */ + "creat", /* 8 */ + "link", /* 9 */ + "unlink", /* 10 */ + "execve", /* 11 */ + "chdir", /* 12 */ + "time", /* 13 */ + "mknod", /* 14 */ + "chmod", /* 15 */ + "lchown", /* 16 */ + "break", /* 17 */ + "oldstat", /* 18 */ + "lseek", /* 19 */ + "getpid", /* 20 */ + "mount", /* 21 */ + "umount", /* 22 */ + "setuid", /* 23 */ + "getuid", /* 24 */ + "stime", /* 25 */ + "ptrace", /* 26 */ + "alarm", /* 27 */ + "oldfstat", /* 28 */ + "pause", /* 29 */ + "utime", /* 30 */ + "stty", /* 31 */ + "gtty", /* 32 */ + "access", /* 33 */ + "nice", /* 34 */ + "ftime", /* 35 */ + "sync", /* 36 */ + "kill", /* 37 */ + "rename", /* 38 */ + "mkdir", /* 39 */ + "rmdir", /* 40 */ + "dup", /* 41 */ + "pipe", /* 42 */ + "times", /* 43 */ + "prof", /* 44 */ + "brk", /* 45 */ + "setgid", /* 46 */ + "getgid", /* 47 */ + "signal", /* 48 */ + "geteuid", /* 49 */ + "getegid", /* 50 */ + "acct", /* 51 */ + "umount2", /* 52 */ + "lock", /* 53 */ + "ioctl", /* 54 */ + "fcntl", /* 55 */ + "mpx", /* 56 */ + "setpgid", /* 57 */ + "ulimit", /* 58 */ + "oldolduname", /* 59 */ + "umask", /* 60 */ + "chroot", /* 61 */ + "ustat", /* 62 */ + "dup2", /* 63 */ + "getppid", /* 64 */ + "getpgrp", /* 65 */ + "setsid", /* 66 */ + "sigaction", /* 67 */ + "sgetmask", /* 68 */ + "ssetmask", /* 69 */ + "setreuid", /* 70 */ + "setregid", /* 71 */ + "sigsuspend", /* 72 */ + "sigpending", /* 73 */ + "sethostname", /* 74 */ + "setrlimit", /* 75 */ + "getrlimit", /* 76 */ + "getrusage", /* 77 */ + "gettimeofday", /* 78 */ + "settimeofday", /* 79 */ + "getgroups", /* 80 */ + "setgroups", /* 81 */ + "select", /* 82 */ + "symlink", /* 83 */ + "oldlstat", /* 84 */ + "readlink", /* 85 */ + "uselib", /* 86 */ + "swapon", /* 87 */ + "reboot", /* 88 */ + "readdir", /* 89 */ + "mmap", /* 90 */ + "munmap", /* 91 */ + "truncate", /* 92 */ + "ftruncate", /* 93 */ + "fchmod", /* 94 */ + "fchown", /* 95 */ + "getpriority", /* 96 */ + "setpriority", /* 97 */ + "profil", /* 98 */ + "statfs", /* 99 */ + "fstatfs", /* 100 */ + "ioperm", /* 101 */ + "socketcall", /* 102 */ + "syslog", /* 103 */ + "setitimer", /* 104 */ + "getitimer", /* 105 */ + "stat", /* 106 */ + "lstat", /* 107 */ + "fstat", /* 108 */ + "olduname", /* 109 */ + "iopl", /* 110 */ + "vhangup", /* 111 */ + "idle", /* 112 */ + "vm86old", /* 113 */ + "wait4", /* 114 */ + "swapoff", /* 115 */ + "sysinfo", /* 116 */ + "ipc", /* 117 */ + "fsync", /* 118 */ + "sigreturn", /* 119 */ + "clone", /* 120 */ + "setdomainname", /* 121 */ + "uname", /* 122 */ + "modify_ldt", /* 123 */ + "adjtimex", /* 124 */ + "mprotect", /* 125 */ + "sigprocmask", /* 126 */ + "create_module", /* 127 */ + "init_module", /* 128 */ + "delete_module", /* 129 */ + "get_kernel_syms", /* 130 */ + "quotactl", /* 131 */ + "getpgid", /* 132 */ + "fchdir", /* 133 */ + "bdflush", /* 134 */ + "sysfs", /* 135 */ + "personality", /* 136 */ + "afs_syscall", /* 137 */ + "setfsuid", /* 138 */ + "setfsgid", /* 139 */ + "_llseek", /* 140 */ + "getdents", /* 141 */ + "_newselect", /* 142 */ + "flock", /* 143 */ + "msync", /* 144 */ + "readv", /* 145 */ + "writev", /* 146 */ + "getsid", /* 147 */ + "fdatasync", /* 148 */ + "_sysctl", /* 149 */ + "mlock", /* 150 */ + "munlock", /* 151 */ + "mlockall", /* 152 */ + "munlockall", /* 153 */ + "sched_setparam", /* 154 */ + "sched_getparam", /* 155 */ + "sched_setscheduler", /* 156 */ + "sched_getscheduler", /* 157 */ + "sched_yield", /* 158 */ + "sched_get_priority_max", /* 159 */ + "sched_get_priority_min", /* 160 */ + "sched_rr_get_interval", /* 161 */ + "nanosleep", /* 162 */ + "mremap", /* 163 */ + "setresuid", /* 164 */ + "getresuid", /* 165 */ + "vm86", /* 166 */ + "query_module", /* 167 */ + "poll", /* 168 */ + "nfsservctl", /* 169 */ + "setresgid", /* 170 */ + "getresgid", /* 171 */ + "prctl", /* 172 */ + "rt_sigreturn", /* 173 */ + "rt_sigaction", /* 174 */ + "rt_sigprocmask", /* 175 */ + "rt_sigpending", /* 176 */ + "rt_sigtimedwait", /* 177 */ + "rt_sigqueueinfo", /* 178 */ + "rt_sigsuspend", /* 179 */ + "pread64", /* 180 */ + "pwrite64", /* 181 */ + "chown", /* 182 */ + "getcwd", /* 183 */ + "capget", /* 184 */ + "capset", /* 185 */ + "sigaltstack", /* 186 */ + "sendfile", /* 187 */ + "getpmsg", /* 188 */ + "putpmsg", /* 189 */ + "vfork", /* 190 */ + "ugetrlimit", /* 191 */ + "mmap2", /* 192 */ + "truncate64", /* 193 */ + "ftruncate64", /* 194 */ + "stat64", /* 195 */ + "lstat64", /* 196 */ + "fstat64", /* 197 */ + "lchown32", /* 198 */ + "getuid32", /* 199 */ + "getgid32", /* 200 */ + "geteuid32", /* 201 */ + "getegid32", /* 202 */ + "setreuid32", /* 203 */ + "setregid32", /* 204 */ + "getgroups32", /* 205 */ + "setgroups32", /* 206 */ + "fchown32", /* 207 */ + "setresuid32", /* 208 */ + "getresuid32", /* 209 */ + "setresgid32", /* 210 */ + "getresgid32", /* 211 */ + "chown32", /* 212 */ + "setuid32", /* 213 */ + "setgid32", /* 214 */ + "setfsuid32", /* 215 */ + "setfsgid32", /* 216 */ + "pivot_root", /* 217 */ + "mincore", /* 218 */ + "madvise1", /* 219 */ + "getdents64", /* 220 */ + "fcntl64", /* 221 */ + "222", /* 222 */ + "223", /* 223 */ + "gettid", /* 224 */ + "readahead", /* 225 */ + "setxattr", /* 226 */ + "lsetxattr", /* 227 */ + "fsetxattr", /* 228 */ + "getxattr", /* 229 */ + "lgetxattr", /* 230 */ + "fgetxattr", /* 231 */ + "listxattr", /* 232 */ + "llistxattr", /* 233 */ + "flistxattr", /* 234 */ + "removexattr", /* 235 */ + "lremovexattr", /* 236 */ + "fremovexattr", /* 237 */ + "tkill", /* 238 */ + "sendfile64", /* 239 */ + "futex", /* 240 */ + "sched_setaffinity", /* 241 */ + "sched_getaffinity", /* 242 */ + "set_thread_area", /* 243 */ + "get_thread_area", /* 244 */ + "io_setup", /* 245 */ + "io_destroy", /* 246 */ + "io_getevents", /* 247 */ + "io_submit", /* 248 */ + "io_cancel", /* 249 */ + "fadvise64", /* 250 */ + "251", /* 251 */ + "exit_group", /* 252 */ + "lookup_dcookie", /* 253 */ + "epoll_create", /* 254 */ + "epoll_ctl", /* 255 */ + "epoll_wait", /* 256 */ + "remap_file_pages", /* 257 */ + "set_tid_address", /* 258 */ + "timer_create", /* 259 */ + "260", /* 260 */ + "261", /* 261 */ + "262", /* 262 */ + "263", /* 263 */ + "264", /* 264 */ + "265", /* 265 */ + "266", /* 266 */ + "267", /* 267 */ + "statfs64", /* 268 */ + "fstatfs64", /* 269 */ + "tgkill", /* 270 */ + "utimes", /* 271 */ + "fadvise64_64", /* 272 */ + "vserver", /* 273 */ + "mbind", /* 274 */ + "get_mempolicy", /* 275 */ + "set_mempolicy", /* 276 */ + "mq_open", /* 277 */ + "278", /* 278 */ + "279", /* 279 */ + "280", /* 280 */ + "281", /* 281 */ + "282", /* 282 */ + "kexec_load", /* 283 */ + "waitid", /* 284 */ + "285", /* 285 */ + "add_key", /* 286 */ + "request_key", /* 287 */ + "keyctl", /* 288 */ + "ioprio_set", /* 289 */ + "ioprio_get", /* 290 */ + "inotify_init", /* 291 */ + "inotify_add_watch", /* 292 */ + "inotify_rm_watch", /* 293 */ + "migrate_pages", /* 294 */ + "openat", /* 295 */ + "mkdirat", /* 296 */ + "mknodat", /* 297 */ + "fchownat", /* 298 */ + "futimesat", /* 299 */ + "fstatat64", /* 300 */ + "unlinkat", /* 301 */ + "renameat", /* 302 */ + "linkat", /* 303 */ + "symlinkat", /* 304 */ + "readlinkat", /* 305 */ + "fchmodat", /* 306 */ + "faccessat", /* 307 */ + "pselect6", /* 308 */ + "ppoll", /* 309 */ + "unshare", /* 310 */ + "set_robust_list", /* 311 */ + "get_robust_list", /* 312 */ + "splice", /* 313 */ + "sync_file_range", /* 314 */ + "tee", /* 315 */ + "vmsplice", /* 316 */ diff --git a/sysdeps/linux-gnu/i386/trace.c b/sysdeps/linux-gnu/i386/trace.c new file mode 100644 index 0000000..76f1105 --- /dev/null +++ b/sysdeps/linux-gnu/i386/trace.c @@ -0,0 +1,85 @@ +#include "config.h" + +#include <stdlib.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <signal.h> +#include <sys/ptrace.h> +#include <asm/ptrace.h> + +#include "common.h" + +#if (!defined(PTRACE_PEEKUSER) && defined(PTRACE_PEEKUSR)) +# define PTRACE_PEEKUSER PTRACE_PEEKUSR +#endif + +#if (!defined(PTRACE_POKEUSER) && defined(PTRACE_POKEUSR)) +# define PTRACE_POKEUSER PTRACE_POKEUSR +#endif + +void +get_arch_dep(Process *proc) { +} + +/* Returns 1 if syscall, 2 if sysret, 0 otherwise. + */ +int +syscall_p(Process *proc, int status, int *sysnum) { + if (WIFSTOPPED(status) + && WSTOPSIG(status) == (SIGTRAP | proc->tracesysgood)) { + *sysnum = ptrace(PTRACE_PEEKUSER, proc->pid, 4 * ORIG_EAX, 0); + + if (proc->callstack_depth > 0 && + proc->callstack[proc->callstack_depth - 1].is_syscall && + proc->callstack[proc->callstack_depth - 1].c_un.syscall == *sysnum) { + return 2; + } + + if (*sysnum >= 0) { + return 1; + } + } + return 0; +} + +long +gimme_arg(enum tof type, Process *proc, int arg_num, arg_type_info *info) { + if (arg_num == -1) { /* return value */ + return ptrace(PTRACE_PEEKUSER, proc->pid, 4 * EAX, 0); + } + + if (type == LT_TOF_FUNCTION || type == LT_TOF_FUNCTIONR) { + return ptrace(PTRACE_PEEKTEXT, proc->pid, + proc->stack_pointer + 4 * (arg_num + 1), 0); + } else if (type == LT_TOF_SYSCALL || type == LT_TOF_SYSCALLR) { +#if 0 + switch (arg_num) { + case 0: + return ptrace(PTRACE_PEEKUSER, proc->pid, 4 * EBX, 0); + case 1: + return ptrace(PTRACE_PEEKUSER, proc->pid, 4 * ECX, 0); + case 2: + return ptrace(PTRACE_PEEKUSER, proc->pid, 4 * EDX, 0); + case 3: + return ptrace(PTRACE_PEEKUSER, proc->pid, 4 * ESI, 0); + case 4: + return ptrace(PTRACE_PEEKUSER, proc->pid, 4 * EDI, 0); + default: + fprintf(stderr, + "gimme_arg called with wrong arguments\n"); + exit(2); + } +#else + return ptrace(PTRACE_PEEKUSER, proc->pid, 4 * arg_num, 0); +#endif + } else { + fprintf(stderr, "gimme_arg called with wrong arguments\n"); + exit(1); + } + + return 0; +} + +void +save_register_args(enum tof type, Process *proc) { +} diff --git a/sysdeps/linux-gnu/ia64/Makefile b/sysdeps/linux-gnu/ia64/Makefile new file mode 100644 index 0000000..4f0ef63 --- /dev/null +++ b/sysdeps/linux-gnu/ia64/Makefile @@ -0,0 +1,10 @@ +OBJ = trace.o regs.o plt.o breakpoint.o + +all: arch.o + +arch.o: $(OBJ) + $(LD) -r -o arch.o $(OBJ) + +clean: + $(RM) $(OBJ) arch.o + diff --git a/sysdeps/linux-gnu/ia64/arch.h b/sysdeps/linux-gnu/ia64/arch.h new file mode 100644 index 0000000..673047c --- /dev/null +++ b/sysdeps/linux-gnu/ia64/arch.h @@ -0,0 +1,13 @@ +#define ARCH_HAVE_DISABLE_BREAKPOINT 1 +#define ARCH_HAVE_ENABLE_BREAKPOINT 1 + +#define BREAKPOINT_LENGTH 16 +#define BREAKPOINT_VALUE {0} +#define DECR_PC_AFTER_BREAK 0 + +#define LT_ELFCLASS ELFCLASS64 +#define LT_ELF_MACHINE EM_IA_64 + +// ia64 actually does use .opd, but we don't need to do the +// translation manually. +#undef ARCH_SUPPORTS_OPD diff --git a/sysdeps/linux-gnu/ia64/breakpoint.c b/sysdeps/linux-gnu/ia64/breakpoint.c new file mode 100644 index 0000000..4f09173 --- /dev/null +++ b/sysdeps/linux-gnu/ia64/breakpoint.c @@ -0,0 +1,212 @@ +/* IA64 breakpoint support. Much of this clagged from gdb + * -Ian Wienand <ianw@gelato.unsw.edu.au> 10/3/2005 + */ + +#include "config.h" + +#include <sys/ptrace.h> +#include <string.h> +#include "arch.h" +#include "options.h" +#include "output.h" +#include "debug.h" + +static long long +extract_bit_field(char *bundle, int from, int len) { + long long result = 0LL; + int to = from + len; + int from_byte = from / 8; + int to_byte = to / 8; + unsigned char *b = (unsigned char *)bundle; + unsigned char c; + int lshift; + int i; + + c = b[from_byte]; + if (from_byte == to_byte) + c = ((unsigned char)(c << (8 - to % 8))) >> (8 - to % 8); + result = c >> (from % 8); + lshift = 8 - (from % 8); + + for (i = from_byte + 1; i < to_byte; i++) { + result |= ((long long)b[i]) << lshift; + lshift += 8; + } + + if (from_byte < to_byte && (to % 8 != 0)) { + c = b[to_byte]; + c = ((unsigned char)(c << (8 - to % 8))) >> (8 - to % 8); + result |= ((long long)c) << lshift; + } + + return result; +} + +/* Replace the specified bits in an instruction bundle */ +static void +replace_bit_field(char *bundle, long long val, int from, int len) { + int to = from + len; + int from_byte = from / 8; + int to_byte = to / 8; + unsigned char *b = (unsigned char *)bundle; + unsigned char c; + + if (from_byte == to_byte) { + unsigned char left, right; + c = b[from_byte]; + left = (c >> (to % 8)) << (to % 8); + right = + ((unsigned char)(c << (8 - from % 8))) >> (8 - from % 8); + c = (unsigned char)(val & 0xff); + c = (unsigned char)(c << (from % 8 + 8 - to % 8)) >> (8 - + to % 8); + c |= right | left; + b[from_byte] = c; + } else { + int i; + c = b[from_byte]; + c = ((unsigned char)(c << (8 - from % 8))) >> (8 - from % 8); + c = c | (val << (from % 8)); + b[from_byte] = c; + val >>= 8 - from % 8; + + for (i = from_byte + 1; i < to_byte; i++) { + c = val & 0xff; + val >>= 8; + b[i] = c; + } + + if (to % 8 != 0) { + unsigned char cv = (unsigned char)val; + c = b[to_byte]; + c = c >> (to % 8) << (to % 8); + c |= ((unsigned char)(cv << (8 - to % 8))) >> (8 - + to % 8); + b[to_byte] = c; + } + } +} + +/* Return the contents of slot N (for N = 0, 1, or 2) in + and instruction bundle */ +static long long +slotN_contents(char *bundle, int slotnum) { + return extract_bit_field(bundle, 5 + 41 * slotnum, 41); +} + +/* Store an instruction in an instruction bundle */ + +static void +replace_slotN_contents(char *bundle, long long instr, int slotnum) { + replace_bit_field(bundle, instr, 5 + 41 * slotnum, 41); +} + +typedef enum instruction_type { + A, /* Integer ALU ; I-unit or M-unit */ + I, /* Non-ALU integer; I-unit */ + M, /* Memory ; M-unit */ + F, /* Floating-point ; F-unit */ + B, /* Branch ; B-unit */ + L, /* Extended (L+X) ; I-unit */ + X, /* Extended (L+X) ; I-unit */ + undefined /* undefined or reserved */ +} instruction_type; + +static enum instruction_type template_encoding_table[32][3] = { + {M, I, I}, /* 00 */ + {M, I, I}, /* 01 */ + {M, I, I}, /* 02 */ + {M, I, I}, /* 03 */ + {M, L, X}, /* 04 */ + {M, L, X}, /* 05 */ + {undefined, undefined, undefined}, /* 06 */ + {undefined, undefined, undefined}, /* 07 */ + {M, M, I}, /* 08 */ + {M, M, I}, /* 09 */ + {M, M, I}, /* 0A */ + {M, M, I}, /* 0B */ + {M, F, I}, /* 0C */ + {M, F, I}, /* 0D */ + {M, M, F}, /* 0E */ + {M, M, F}, /* 0F */ + {M, I, B}, /* 10 */ + {M, I, B}, /* 11 */ + {M, B, B}, /* 12 */ + {M, B, B}, /* 13 */ + {undefined, undefined, undefined}, /* 14 */ + {undefined, undefined, undefined}, /* 15 */ + {B, B, B}, /* 16 */ + {B, B, B}, /* 17 */ + {M, M, B}, /* 18 */ + {M, M, B}, /* 19 */ + {undefined, undefined, undefined}, /* 1A */ + {undefined, undefined, undefined}, /* 1B */ + {M, F, B}, /* 1C */ + {M, F, B}, /* 1D */ + {undefined, undefined, undefined}, /* 1E */ + {undefined, undefined, undefined}, /* 1F */ +}; + +union bundle_t { + char cbundle[16]; + unsigned long ubundle[2]; +}; + +void +arch_enable_breakpoint(pid_t pid, Breakpoint *sbp) { + + unsigned long addr = (unsigned long)sbp->addr; + union bundle_t bundle; + int slotnum = (int)(addr & 0x0f) & 0x3; + long long instr; + int template; + + debug(1, "Enable Breakpoint at %p)", sbp->addr); + + if (slotnum > 2) + printf + ("Can't insert breakpoint for slot numbers greater than 2."); + + addr &= ~0x0f; + bundle.ubundle[0] = ptrace(PTRACE_PEEKTEXT, pid, addr, 0); + bundle.ubundle[1] = ptrace(PTRACE_PEEKTEXT, pid, addr + 8, 0); + + /* Check for L type instruction in 2nd slot, if present then + bump up the slot number to the 3rd slot */ + template = extract_bit_field(bundle.cbundle, 0, 5); + if (slotnum == 1 && template_encoding_table[template][1] == L) { + slotnum = 2; + } + + instr = slotN_contents(bundle.cbundle, slotnum); + + memcpy(sbp->orig_value, &instr, sizeof(instr)); + + replace_slotN_contents(bundle.cbundle, 0x00002000040LL, slotnum); + + ptrace(PTRACE_POKETEXT, pid, addr, bundle.ubundle[0]); + ptrace(PTRACE_POKETEXT, pid, addr + 8, bundle.ubundle[1]); + +} + +void +arch_disable_breakpoint(pid_t pid, const Breakpoint *sbp) { + + unsigned long addr = (unsigned long)sbp->addr; + int slotnum = (int)(addr & 0x0f) & 0x3; + union bundle_t bundle; + unsigned long instr; + + debug(1, "Disable Breakpoint at %p", sbp->addr); + + addr &= ~0x0f; + + bundle.ubundle[0] = ptrace(PTRACE_PEEKTEXT, pid, addr, 0); + bundle.ubundle[1] = ptrace(PTRACE_PEEKTEXT, pid, addr + 8, 0); + + memcpy(&instr, sbp->orig_value, sizeof(instr)); + + replace_slotN_contents(bundle.cbundle, instr, slotnum); + ptrace(PTRACE_POKETEXT, pid, addr, bundle.ubundle[0]); + ptrace(PTRACE_POKETEXT, pid, addr + 8, bundle.ubundle[1]); +} diff --git a/sysdeps/linux-gnu/ia64/plt.c b/sysdeps/linux-gnu/ia64/plt.c new file mode 100644 index 0000000..7fd451b --- /dev/null +++ b/sysdeps/linux-gnu/ia64/plt.c @@ -0,0 +1,46 @@ +#include <gelf.h> +#include "common.h" + +/* A bundle is 128 bits */ +#define BUNDLE_SIZE 16 + +/* + + The PLT has + + ] 3 bundles as a header + + ] The special reserved entry + + ] Following that, each PLT entry has it's initial code that the GOT entry + points to. Each PLT entry has one bundle allocated. + + ] Following that, each PLT entry has two bundles of actual PLT code, + i.e. load up the address from the GOT and jump to it. This is the + point we want to insert the breakpoint, as this will be captured + every time we jump to the PLT entry in the code. + +*/ + +GElf_Addr +arch_plt_sym_val(struct ltelf *lte, size_t ndx, GElf_Rela * rela) { + /* Find number of entires by removing header and special + * entry, dividing total size by three, since each PLT entry + * will have 3 bundles (1 for inital entry and two for the PLT + * code). */ + int entries = (lte->plt_size - 4 * BUNDLE_SIZE) / (3 * BUNDLE_SIZE); + + /* Now the point we want to break on is the PLT entry after + * all the header stuff */ + unsigned long addr = + lte->plt_addr + (4 * BUNDLE_SIZE) + (BUNDLE_SIZE * entries) + + (2 * ndx * BUNDLE_SIZE); + debug(3, "Found PLT %d entry at %lx\n", ndx, addr); + + return addr; +} + +void * +sym2addr(Process *proc, struct library_symbol *sym) { + return sym->enter_addr; +} diff --git a/sysdeps/linux-gnu/ia64/ptrace.h b/sysdeps/linux-gnu/ia64/ptrace.h new file mode 100644 index 0000000..c3cbcb6 --- /dev/null +++ b/sysdeps/linux-gnu/ia64/ptrace.h @@ -0,0 +1 @@ +#include <sys/ptrace.h> diff --git a/sysdeps/linux-gnu/ia64/regs.c b/sysdeps/linux-gnu/ia64/regs.c new file mode 100644 index 0000000..d161d34 --- /dev/null +++ b/sysdeps/linux-gnu/ia64/regs.c @@ -0,0 +1,51 @@ +#include "config.h" + +#include <sys/types.h> +#include <sys/ptrace.h> + +#include <asm/ptrace_offsets.h> +#include <asm/rse.h> + +#include <stddef.h> +#include "debug.h" +#include "common.h" + +void * +get_instruction_pointer(Process *proc) { + unsigned long ip = ptrace(PTRACE_PEEKUSER, proc->pid, PT_CR_IIP, 0); + unsigned long slot = + (ptrace(PTRACE_PEEKUSER, proc->pid, PT_CR_IPSR, 0) >> 41) & 3; + + return (void *)(ip | slot); +} + +void +set_instruction_pointer(Process *proc, void *addr) { + + unsigned long newip = (unsigned long)addr; + unsigned long slot = (unsigned long)addr & 0xf; + unsigned long psr = ptrace(PTRACE_PEEKUSER, proc->pid, PT_CR_IPSR, 0); + + psr &= ~(3UL << 41); + psr |= (slot & 0x3) << 41; + + newip &= ~0xfUL; + + ptrace(PTRACE_POKEUSER, proc->pid, PT_CR_IIP, (long)newip); + ptrace(PTRACE_POKEUSER, proc->pid, PT_CR_IPSR, psr); +} + +void * +get_stack_pointer(Process *proc) { + return (void *)ptrace(PTRACE_PEEKUSER, proc->pid, PT_R12, 0); +} + +void * +get_return_addr(Process *proc, void *stack_pointer) { + return (void *)ptrace(PTRACE_PEEKUSER, proc->pid, PT_B0, 0); +} + +void +set_return_addr(Process *proc, void *addr) { + ptrace(PTRACE_POKEUSER, proc->pid, PT_B0, addr); +} diff --git a/sysdeps/linux-gnu/ia64/signalent.h b/sysdeps/linux-gnu/ia64/signalent.h new file mode 100644 index 0000000..5395f82 --- /dev/null +++ b/sysdeps/linux-gnu/ia64/signalent.h @@ -0,0 +1,32 @@ + "SIG_0", /* 0 */ + "SIGHUP", /* 1 */ + "SIGINT", /* 2 */ + "SIGQUIT", /* 3 */ + "SIGILL", /* 4 */ + "SIGTRAP", /* 5 */ + "SIGABRT", /* 6 */ + "SIGBUS", /* 7 */ + "SIGFPE", /* 8 */ + "SIGKILL", /* 9 */ + "SIGUSR1", /* 10 */ + "SIGSEGV", /* 11 */ + "SIGUSR2", /* 12 */ + "SIGPIPE", /* 13 */ + "SIGALRM", /* 14 */ + "SIGTERM", /* 15 */ + "SIGSTKFLT", /* 16 */ + "SIGCHLD", /* 17 */ + "SIGCONT", /* 18 */ + "SIGSTOP", /* 19 */ + "SIGTSTP", /* 20 */ + "SIGTTIN", /* 21 */ + "SIGTTOU", /* 22 */ + "SIGURG", /* 23 */ + "SIGXCPU", /* 24 */ + "SIGXFSZ", /* 25 */ + "SIGVTALRM", /* 26 */ + "SIGPROF", /* 27 */ + "SIGWINCH", /* 28 */ + "SIGIO", /* 29 */ + "SIGPWR", /* 30 */ + "SIGSYS", /* 31 */ diff --git a/sysdeps/linux-gnu/ia64/syscallent.h b/sysdeps/linux-gnu/ia64/syscallent.h new file mode 100644 index 0000000..9bb4fb8 --- /dev/null +++ b/sysdeps/linux-gnu/ia64/syscallent.h @@ -0,0 +1,1303 @@ + "0", /* 0 */ + "1", /* 1 */ + "2", /* 2 */ + "3", /* 3 */ + "4", /* 4 */ + "5", /* 5 */ + "6", /* 6 */ + "7", /* 7 */ + "8", /* 8 */ + "9", /* 9 */ + "10", /* 10 */ + "11", /* 11 */ + "12", /* 12 */ + "13", /* 13 */ + "14", /* 14 */ + "15", /* 15 */ + "16", /* 16 */ + "17", /* 17 */ + "18", /* 18 */ + "19", /* 19 */ + "20", /* 20 */ + "21", /* 21 */ + "22", /* 22 */ + "23", /* 23 */ + "24", /* 24 */ + "25", /* 25 */ + "26", /* 26 */ + "27", /* 27 */ + "28", /* 28 */ + "29", /* 29 */ + "30", /* 30 */ + "31", /* 31 */ + "32", /* 32 */ + "33", /* 33 */ + "34", /* 34 */ + "35", /* 35 */ + "36", /* 36 */ + "37", /* 37 */ + "38", /* 38 */ + "39", /* 39 */ + "40", /* 40 */ + "41", /* 41 */ + "42", /* 42 */ + "43", /* 43 */ + "44", /* 44 */ + "45", /* 45 */ + "46", /* 46 */ + "47", /* 47 */ + "48", /* 48 */ + "49", /* 49 */ + "50", /* 50 */ + "51", /* 51 */ + "52", /* 52 */ + "53", /* 53 */ + "54", /* 54 */ + "55", /* 55 */ + "56", /* 56 */ + "57", /* 57 */ + "58", /* 58 */ + "59", /* 59 */ + "60", /* 60 */ + "61", /* 61 */ + "62", /* 62 */ + "63", /* 63 */ + "64", /* 64 */ + "65", /* 65 */ + "66", /* 66 */ + "67", /* 67 */ + "68", /* 68 */ + "69", /* 69 */ + "70", /* 70 */ + "71", /* 71 */ + "72", /* 72 */ + "73", /* 73 */ + "74", /* 74 */ + "75", /* 75 */ + "76", /* 76 */ + "77", /* 77 */ + "78", /* 78 */ + "79", /* 79 */ + "80", /* 80 */ + "81", /* 81 */ + "82", /* 82 */ + "83", /* 83 */ + "84", /* 84 */ + "85", /* 85 */ + "86", /* 86 */ + "87", /* 87 */ + "88", /* 88 */ + "89", /* 89 */ + "90", /* 90 */ + "91", /* 91 */ + "92", /* 92 */ + "93", /* 93 */ + "94", /* 94 */ + "95", /* 95 */ + "96", /* 96 */ + "97", /* 97 */ + "98", /* 98 */ + "99", /* 99 */ + "100", /* 100 */ + "101", /* 101 */ + "102", /* 102 */ + "103", /* 103 */ + "104", /* 104 */ + "105", /* 105 */ + "106", /* 106 */ + "107", /* 107 */ + "108", /* 108 */ + "109", /* 109 */ + "110", /* 110 */ + "111", /* 111 */ + "112", /* 112 */ + "113", /* 113 */ + "114", /* 114 */ + "115", /* 115 */ + "116", /* 116 */ + "117", /* 117 */ + "118", /* 118 */ + "119", /* 119 */ + "120", /* 120 */ + "121", /* 121 */ + "122", /* 122 */ + "123", /* 123 */ + "124", /* 124 */ + "125", /* 125 */ + "126", /* 126 */ + "127", /* 127 */ + "128", /* 128 */ + "129", /* 129 */ + "130", /* 130 */ + "131", /* 131 */ + "132", /* 132 */ + "133", /* 133 */ + "134", /* 134 */ + "135", /* 135 */ + "136", /* 136 */ + "137", /* 137 */ + "138", /* 138 */ + "139", /* 139 */ + "140", /* 140 */ + "141", /* 141 */ + "142", /* 142 */ + "143", /* 143 */ + "144", /* 144 */ + "145", /* 145 */ + "146", /* 146 */ + "147", /* 147 */ + "148", /* 148 */ + "149", /* 149 */ + "150", /* 150 */ + "151", /* 151 */ + "152", /* 152 */ + "153", /* 153 */ + "154", /* 154 */ + "155", /* 155 */ + "156", /* 156 */ + "157", /* 157 */ + "158", /* 158 */ + "159", /* 159 */ + "160", /* 160 */ + "161", /* 161 */ + "162", /* 162 */ + "163", /* 163 */ + "164", /* 164 */ + "165", /* 165 */ + "166", /* 166 */ + "167", /* 167 */ + "168", /* 168 */ + "169", /* 169 */ + "170", /* 170 */ + "171", /* 171 */ + "172", /* 172 */ + "173", /* 173 */ + "174", /* 174 */ + "175", /* 175 */ + "176", /* 176 */ + "177", /* 177 */ + "178", /* 178 */ + "179", /* 179 */ + "180", /* 180 */ + "181", /* 181 */ + "182", /* 182 */ + "183", /* 183 */ + "184", /* 184 */ + "185", /* 185 */ + "186", /* 186 */ + "187", /* 187 */ + "188", /* 188 */ + "189", /* 189 */ + "190", /* 190 */ + "191", /* 191 */ + "192", /* 192 */ + "193", /* 193 */ + "194", /* 194 */ + "195", /* 195 */ + "196", /* 196 */ + "197", /* 197 */ + "198", /* 198 */ + "199", /* 199 */ + "200", /* 200 */ + "201", /* 201 */ + "202", /* 202 */ + "203", /* 203 */ + "204", /* 204 */ + "205", /* 205 */ + "206", /* 206 */ + "207", /* 207 */ + "208", /* 208 */ + "209", /* 209 */ + "210", /* 210 */ + "211", /* 211 */ + "212", /* 212 */ + "213", /* 213 */ + "214", /* 214 */ + "215", /* 215 */ + "216", /* 216 */ + "217", /* 217 */ + "218", /* 218 */ + "219", /* 219 */ + "220", /* 220 */ + "221", /* 221 */ + "222", /* 222 */ + "223", /* 223 */ + "224", /* 224 */ + "225", /* 225 */ + "226", /* 226 */ + "227", /* 227 */ + "228", /* 228 */ + "229", /* 229 */ + "230", /* 230 */ + "231", /* 231 */ + "232", /* 232 */ + "233", /* 233 */ + "234", /* 234 */ + "235", /* 235 */ + "236", /* 236 */ + "237", /* 237 */ + "238", /* 238 */ + "239", /* 239 */ + "240", /* 240 */ + "241", /* 241 */ + "242", /* 242 */ + "243", /* 243 */ + "244", /* 244 */ + "245", /* 245 */ + "246", /* 246 */ + "247", /* 247 */ + "248", /* 248 */ + "249", /* 249 */ + "250", /* 250 */ + "251", /* 251 */ + "252", /* 252 */ + "253", /* 253 */ + "254", /* 254 */ + "255", /* 255 */ + "256", /* 256 */ + "257", /* 257 */ + "258", /* 258 */ + "259", /* 259 */ + "260", /* 260 */ + "261", /* 261 */ + "262", /* 262 */ + "263", /* 263 */ + "264", /* 264 */ + "265", /* 265 */ + "266", /* 266 */ + "267", /* 267 */ + "268", /* 268 */ + "269", /* 269 */ + "270", /* 270 */ + "271", /* 271 */ + "272", /* 272 */ + "273", /* 273 */ + "274", /* 274 */ + "275", /* 275 */ + "276", /* 276 */ + "277", /* 277 */ + "278", /* 278 */ + "279", /* 279 */ + "280", /* 280 */ + "281", /* 281 */ + "282", /* 282 */ + "283", /* 283 */ + "284", /* 284 */ + "285", /* 285 */ + "286", /* 286 */ + "287", /* 287 */ + "288", /* 288 */ + "289", /* 289 */ + "290", /* 290 */ + "291", /* 291 */ + "292", /* 292 */ + "293", /* 293 */ + "294", /* 294 */ + "295", /* 295 */ + "296", /* 296 */ + "297", /* 297 */ + "298", /* 298 */ + "299", /* 299 */ + "300", /* 300 */ + "301", /* 301 */ + "302", /* 302 */ + "303", /* 303 */ + "304", /* 304 */ + "305", /* 305 */ + "306", /* 306 */ + "307", /* 307 */ + "308", /* 308 */ + "309", /* 309 */ + "310", /* 310 */ + "311", /* 311 */ + "312", /* 312 */ + "313", /* 313 */ + "314", /* 314 */ + "315", /* 315 */ + "316", /* 316 */ + "317", /* 317 */ + "318", /* 318 */ + "319", /* 319 */ + "320", /* 320 */ + "321", /* 321 */ + "322", /* 322 */ + "323", /* 323 */ + "324", /* 324 */ + "325", /* 325 */ + "326", /* 326 */ + "327", /* 327 */ + "328", /* 328 */ + "329", /* 329 */ + "330", /* 330 */ + "331", /* 331 */ + "332", /* 332 */ + "333", /* 333 */ + "334", /* 334 */ + "335", /* 335 */ + "336", /* 336 */ + "337", /* 337 */ + "338", /* 338 */ + "339", /* 339 */ + "340", /* 340 */ + "341", /* 341 */ + "342", /* 342 */ + "343", /* 343 */ + "344", /* 344 */ + "345", /* 345 */ + "346", /* 346 */ + "347", /* 347 */ + "348", /* 348 */ + "349", /* 349 */ + "350", /* 350 */ + "351", /* 351 */ + "352", /* 352 */ + "353", /* 353 */ + "354", /* 354 */ + "355", /* 355 */ + "356", /* 356 */ + "357", /* 357 */ + "358", /* 358 */ + "359", /* 359 */ + "360", /* 360 */ + "361", /* 361 */ + "362", /* 362 */ + "363", /* 363 */ + "364", /* 364 */ + "365", /* 365 */ + "366", /* 366 */ + "367", /* 367 */ + "368", /* 368 */ + "369", /* 369 */ + "370", /* 370 */ + "371", /* 371 */ + "372", /* 372 */ + "373", /* 373 */ + "374", /* 374 */ + "375", /* 375 */ + "376", /* 376 */ + "377", /* 377 */ + "378", /* 378 */ + "379", /* 379 */ + "380", /* 380 */ + "381", /* 381 */ + "382", /* 382 */ + "383", /* 383 */ + "384", /* 384 */ + "385", /* 385 */ + "386", /* 386 */ + "387", /* 387 */ + "388", /* 388 */ + "389", /* 389 */ + "390", /* 390 */ + "391", /* 391 */ + "392", /* 392 */ + "393", /* 393 */ + "394", /* 394 */ + "395", /* 395 */ + "396", /* 396 */ + "397", /* 397 */ + "398", /* 398 */ + "399", /* 399 */ + "400", /* 400 */ + "401", /* 401 */ + "402", /* 402 */ + "403", /* 403 */ + "404", /* 404 */ + "405", /* 405 */ + "406", /* 406 */ + "407", /* 407 */ + "408", /* 408 */ + "409", /* 409 */ + "410", /* 410 */ + "411", /* 411 */ + "412", /* 412 */ + "413", /* 413 */ + "414", /* 414 */ + "415", /* 415 */ + "416", /* 416 */ + "417", /* 417 */ + "418", /* 418 */ + "419", /* 419 */ + "420", /* 420 */ + "421", /* 421 */ + "422", /* 422 */ + "423", /* 423 */ + "424", /* 424 */ + "425", /* 425 */ + "426", /* 426 */ + "427", /* 427 */ + "428", /* 428 */ + "429", /* 429 */ + "430", /* 430 */ + "431", /* 431 */ + "432", /* 432 */ + "433", /* 433 */ + "434", /* 434 */ + "435", /* 435 */ + "436", /* 436 */ + "437", /* 437 */ + "438", /* 438 */ + "439", /* 439 */ + "440", /* 440 */ + "441", /* 441 */ + "442", /* 442 */ + "443", /* 443 */ + "444", /* 444 */ + "445", /* 445 */ + "446", /* 446 */ + "447", /* 447 */ + "448", /* 448 */ + "449", /* 449 */ + "450", /* 450 */ + "451", /* 451 */ + "452", /* 452 */ + "453", /* 453 */ + "454", /* 454 */ + "455", /* 455 */ + "456", /* 456 */ + "457", /* 457 */ + "458", /* 458 */ + "459", /* 459 */ + "460", /* 460 */ + "461", /* 461 */ + "462", /* 462 */ + "463", /* 463 */ + "464", /* 464 */ + "465", /* 465 */ + "466", /* 466 */ + "467", /* 467 */ + "468", /* 468 */ + "469", /* 469 */ + "470", /* 470 */ + "471", /* 471 */ + "472", /* 472 */ + "473", /* 473 */ + "474", /* 474 */ + "475", /* 475 */ + "476", /* 476 */ + "477", /* 477 */ + "478", /* 478 */ + "479", /* 479 */ + "480", /* 480 */ + "481", /* 481 */ + "482", /* 482 */ + "483", /* 483 */ + "484", /* 484 */ + "485", /* 485 */ + "486", /* 486 */ + "487", /* 487 */ + "488", /* 488 */ + "489", /* 489 */ + "490", /* 490 */ + "491", /* 491 */ + "492", /* 492 */ + "493", /* 493 */ + "494", /* 494 */ + "495", /* 495 */ + "496", /* 496 */ + "497", /* 497 */ + "498", /* 498 */ + "499", /* 499 */ + "500", /* 500 */ + "501", /* 501 */ + "502", /* 502 */ + "503", /* 503 */ + "504", /* 504 */ + "505", /* 505 */ + "506", /* 506 */ + "507", /* 507 */ + "508", /* 508 */ + "509", /* 509 */ + "510", /* 510 */ + "511", /* 511 */ + "512", /* 512 */ + "513", /* 513 */ + "514", /* 514 */ + "515", /* 515 */ + "516", /* 516 */ + "517", /* 517 */ + "518", /* 518 */ + "519", /* 519 */ + "520", /* 520 */ + "521", /* 521 */ + "522", /* 522 */ + "523", /* 523 */ + "524", /* 524 */ + "525", /* 525 */ + "526", /* 526 */ + "527", /* 527 */ + "528", /* 528 */ + "529", /* 529 */ + "530", /* 530 */ + "531", /* 531 */ + "532", /* 532 */ + "533", /* 533 */ + "534", /* 534 */ + "535", /* 535 */ + "536", /* 536 */ + "537", /* 537 */ + "538", /* 538 */ + "539", /* 539 */ + "540", /* 540 */ + "541", /* 541 */ + "542", /* 542 */ + "543", /* 543 */ + "544", /* 544 */ + "545", /* 545 */ + "546", /* 546 */ + "547", /* 547 */ + "548", /* 548 */ + "549", /* 549 */ + "550", /* 550 */ + "551", /* 551 */ + "552", /* 552 */ + "553", /* 553 */ + "554", /* 554 */ + "555", /* 555 */ + "556", /* 556 */ + "557", /* 557 */ + "558", /* 558 */ + "559", /* 559 */ + "560", /* 560 */ + "561", /* 561 */ + "562", /* 562 */ + "563", /* 563 */ + "564", /* 564 */ + "565", /* 565 */ + "566", /* 566 */ + "567", /* 567 */ + "568", /* 568 */ + "569", /* 569 */ + "570", /* 570 */ + "571", /* 571 */ + "572", /* 572 */ + "573", /* 573 */ + "574", /* 574 */ + "575", /* 575 */ + "576", /* 576 */ + "577", /* 577 */ + "578", /* 578 */ + "579", /* 579 */ + "580", /* 580 */ + "581", /* 581 */ + "582", /* 582 */ + "583", /* 583 */ + "584", /* 584 */ + "585", /* 585 */ + "586", /* 586 */ + "587", /* 587 */ + "588", /* 588 */ + "589", /* 589 */ + "590", /* 590 */ + "591", /* 591 */ + "592", /* 592 */ + "593", /* 593 */ + "594", /* 594 */ + "595", /* 595 */ + "596", /* 596 */ + "597", /* 597 */ + "598", /* 598 */ + "599", /* 599 */ + "600", /* 600 */ + "601", /* 601 */ + "602", /* 602 */ + "603", /* 603 */ + "604", /* 604 */ + "605", /* 605 */ + "606", /* 606 */ + "607", /* 607 */ + "608", /* 608 */ + "609", /* 609 */ + "610", /* 610 */ + "611", /* 611 */ + "612", /* 612 */ + "613", /* 613 */ + "614", /* 614 */ + "615", /* 615 */ + "616", /* 616 */ + "617", /* 617 */ + "618", /* 618 */ + "619", /* 619 */ + "620", /* 620 */ + "621", /* 621 */ + "622", /* 622 */ + "623", /* 623 */ + "624", /* 624 */ + "625", /* 625 */ + "626", /* 626 */ + "627", /* 627 */ + "628", /* 628 */ + "629", /* 629 */ + "630", /* 630 */ + "631", /* 631 */ + "632", /* 632 */ + "633", /* 633 */ + "634", /* 634 */ + "635", /* 635 */ + "636", /* 636 */ + "637", /* 637 */ + "638", /* 638 */ + "639", /* 639 */ + "640", /* 640 */ + "641", /* 641 */ + "642", /* 642 */ + "643", /* 643 */ + "644", /* 644 */ + "645", /* 645 */ + "646", /* 646 */ + "647", /* 647 */ + "648", /* 648 */ + "649", /* 649 */ + "650", /* 650 */ + "651", /* 651 */ + "652", /* 652 */ + "653", /* 653 */ + "654", /* 654 */ + "655", /* 655 */ + "656", /* 656 */ + "657", /* 657 */ + "658", /* 658 */ + "659", /* 659 */ + "660", /* 660 */ + "661", /* 661 */ + "662", /* 662 */ + "663", /* 663 */ + "664", /* 664 */ + "665", /* 665 */ + "666", /* 666 */ + "667", /* 667 */ + "668", /* 668 */ + "669", /* 669 */ + "670", /* 670 */ + "671", /* 671 */ + "672", /* 672 */ + "673", /* 673 */ + "674", /* 674 */ + "675", /* 675 */ + "676", /* 676 */ + "677", /* 677 */ + "678", /* 678 */ + "679", /* 679 */ + "680", /* 680 */ + "681", /* 681 */ + "682", /* 682 */ + "683", /* 683 */ + "684", /* 684 */ + "685", /* 685 */ + "686", /* 686 */ + "687", /* 687 */ + "688", /* 688 */ + "689", /* 689 */ + "690", /* 690 */ + "691", /* 691 */ + "692", /* 692 */ + "693", /* 693 */ + "694", /* 694 */ + "695", /* 695 */ + "696", /* 696 */ + "697", /* 697 */ + "698", /* 698 */ + "699", /* 699 */ + "700", /* 700 */ + "701", /* 701 */ + "702", /* 702 */ + "703", /* 703 */ + "704", /* 704 */ + "705", /* 705 */ + "706", /* 706 */ + "707", /* 707 */ + "708", /* 708 */ + "709", /* 709 */ + "710", /* 710 */ + "711", /* 711 */ + "712", /* 712 */ + "713", /* 713 */ + "714", /* 714 */ + "715", /* 715 */ + "716", /* 716 */ + "717", /* 717 */ + "718", /* 718 */ + "719", /* 719 */ + "720", /* 720 */ + "721", /* 721 */ + "722", /* 722 */ + "723", /* 723 */ + "724", /* 724 */ + "725", /* 725 */ + "726", /* 726 */ + "727", /* 727 */ + "728", /* 728 */ + "729", /* 729 */ + "730", /* 730 */ + "731", /* 731 */ + "732", /* 732 */ + "733", /* 733 */ + "734", /* 734 */ + "735", /* 735 */ + "736", /* 736 */ + "737", /* 737 */ + "738", /* 738 */ + "739", /* 739 */ + "740", /* 740 */ + "741", /* 741 */ + "742", /* 742 */ + "743", /* 743 */ + "744", /* 744 */ + "745", /* 745 */ + "746", /* 746 */ + "747", /* 747 */ + "748", /* 748 */ + "749", /* 749 */ + "750", /* 750 */ + "751", /* 751 */ + "752", /* 752 */ + "753", /* 753 */ + "754", /* 754 */ + "755", /* 755 */ + "756", /* 756 */ + "757", /* 757 */ + "758", /* 758 */ + "759", /* 759 */ + "760", /* 760 */ + "761", /* 761 */ + "762", /* 762 */ + "763", /* 763 */ + "764", /* 764 */ + "765", /* 765 */ + "766", /* 766 */ + "767", /* 767 */ + "768", /* 768 */ + "769", /* 769 */ + "770", /* 770 */ + "771", /* 771 */ + "772", /* 772 */ + "773", /* 773 */ + "774", /* 774 */ + "775", /* 775 */ + "776", /* 776 */ + "777", /* 777 */ + "778", /* 778 */ + "779", /* 779 */ + "780", /* 780 */ + "781", /* 781 */ + "782", /* 782 */ + "783", /* 783 */ + "784", /* 784 */ + "785", /* 785 */ + "786", /* 786 */ + "787", /* 787 */ + "788", /* 788 */ + "789", /* 789 */ + "790", /* 790 */ + "791", /* 791 */ + "792", /* 792 */ + "793", /* 793 */ + "794", /* 794 */ + "795", /* 795 */ + "796", /* 796 */ + "797", /* 797 */ + "798", /* 798 */ + "799", /* 799 */ + "800", /* 800 */ + "801", /* 801 */ + "802", /* 802 */ + "803", /* 803 */ + "804", /* 804 */ + "805", /* 805 */ + "806", /* 806 */ + "807", /* 807 */ + "808", /* 808 */ + "809", /* 809 */ + "810", /* 810 */ + "811", /* 811 */ + "812", /* 812 */ + "813", /* 813 */ + "814", /* 814 */ + "815", /* 815 */ + "816", /* 816 */ + "817", /* 817 */ + "818", /* 818 */ + "819", /* 819 */ + "820", /* 820 */ + "821", /* 821 */ + "822", /* 822 */ + "823", /* 823 */ + "824", /* 824 */ + "825", /* 825 */ + "826", /* 826 */ + "827", /* 827 */ + "828", /* 828 */ + "829", /* 829 */ + "830", /* 830 */ + "831", /* 831 */ + "832", /* 832 */ + "833", /* 833 */ + "834", /* 834 */ + "835", /* 835 */ + "836", /* 836 */ + "837", /* 837 */ + "838", /* 838 */ + "839", /* 839 */ + "840", /* 840 */ + "841", /* 841 */ + "842", /* 842 */ + "843", /* 843 */ + "844", /* 844 */ + "845", /* 845 */ + "846", /* 846 */ + "847", /* 847 */ + "848", /* 848 */ + "849", /* 849 */ + "850", /* 850 */ + "851", /* 851 */ + "852", /* 852 */ + "853", /* 853 */ + "854", /* 854 */ + "855", /* 855 */ + "856", /* 856 */ + "857", /* 857 */ + "858", /* 858 */ + "859", /* 859 */ + "860", /* 860 */ + "861", /* 861 */ + "862", /* 862 */ + "863", /* 863 */ + "864", /* 864 */ + "865", /* 865 */ + "866", /* 866 */ + "867", /* 867 */ + "868", /* 868 */ + "869", /* 869 */ + "870", /* 870 */ + "871", /* 871 */ + "872", /* 872 */ + "873", /* 873 */ + "874", /* 874 */ + "875", /* 875 */ + "876", /* 876 */ + "877", /* 877 */ + "878", /* 878 */ + "879", /* 879 */ + "880", /* 880 */ + "881", /* 881 */ + "882", /* 882 */ + "883", /* 883 */ + "884", /* 884 */ + "885", /* 885 */ + "886", /* 886 */ + "887", /* 887 */ + "888", /* 888 */ + "889", /* 889 */ + "890", /* 890 */ + "891", /* 891 */ + "892", /* 892 */ + "893", /* 893 */ + "894", /* 894 */ + "895", /* 895 */ + "896", /* 896 */ + "897", /* 897 */ + "898", /* 898 */ + "899", /* 899 */ + "900", /* 900 */ + "901", /* 901 */ + "902", /* 902 */ + "903", /* 903 */ + "904", /* 904 */ + "905", /* 905 */ + "906", /* 906 */ + "907", /* 907 */ + "908", /* 908 */ + "909", /* 909 */ + "910", /* 910 */ + "911", /* 911 */ + "912", /* 912 */ + "913", /* 913 */ + "914", /* 914 */ + "915", /* 915 */ + "916", /* 916 */ + "917", /* 917 */ + "918", /* 918 */ + "919", /* 919 */ + "920", /* 920 */ + "921", /* 921 */ + "922", /* 922 */ + "923", /* 923 */ + "924", /* 924 */ + "925", /* 925 */ + "926", /* 926 */ + "927", /* 927 */ + "928", /* 928 */ + "929", /* 929 */ + "930", /* 930 */ + "931", /* 931 */ + "932", /* 932 */ + "933", /* 933 */ + "934", /* 934 */ + "935", /* 935 */ + "936", /* 936 */ + "937", /* 937 */ + "938", /* 938 */ + "939", /* 939 */ + "940", /* 940 */ + "941", /* 941 */ + "942", /* 942 */ + "943", /* 943 */ + "944", /* 944 */ + "945", /* 945 */ + "946", /* 946 */ + "947", /* 947 */ + "948", /* 948 */ + "949", /* 949 */ + "950", /* 950 */ + "951", /* 951 */ + "952", /* 952 */ + "953", /* 953 */ + "954", /* 954 */ + "955", /* 955 */ + "956", /* 956 */ + "957", /* 957 */ + "958", /* 958 */ + "959", /* 959 */ + "960", /* 960 */ + "961", /* 961 */ + "962", /* 962 */ + "963", /* 963 */ + "964", /* 964 */ + "965", /* 965 */ + "966", /* 966 */ + "967", /* 967 */ + "968", /* 968 */ + "969", /* 969 */ + "970", /* 970 */ + "971", /* 971 */ + "972", /* 972 */ + "973", /* 973 */ + "974", /* 974 */ + "975", /* 975 */ + "976", /* 976 */ + "977", /* 977 */ + "978", /* 978 */ + "979", /* 979 */ + "980", /* 980 */ + "981", /* 981 */ + "982", /* 982 */ + "983", /* 983 */ + "984", /* 984 */ + "985", /* 985 */ + "986", /* 986 */ + "987", /* 987 */ + "988", /* 988 */ + "989", /* 989 */ + "990", /* 990 */ + "991", /* 991 */ + "992", /* 992 */ + "993", /* 993 */ + "994", /* 994 */ + "995", /* 995 */ + "996", /* 996 */ + "997", /* 997 */ + "998", /* 998 */ + "999", /* 999 */ + "1000", /* 1000 */ + "1001", /* 1001 */ + "1002", /* 1002 */ + "1003", /* 1003 */ + "1004", /* 1004 */ + "1005", /* 1005 */ + "1006", /* 1006 */ + "1007", /* 1007 */ + "1008", /* 1008 */ + "1009", /* 1009 */ + "1010", /* 1010 */ + "1011", /* 1011 */ + "1012", /* 1012 */ + "1013", /* 1013 */ + "1014", /* 1014 */ + "1015", /* 1015 */ + "1016", /* 1016 */ + "1017", /* 1017 */ + "1018", /* 1018 */ + "1019", /* 1019 */ + "1020", /* 1020 */ + "1021", /* 1021 */ + "1022", /* 1022 */ + "1023", /* 1023 */ + "ni_syscall", /* 1024 */ + "exit", /* 1025 */ + "read", /* 1026 */ + "write", /* 1027 */ + "open", /* 1028 */ + "close", /* 1029 */ + "creat", /* 1030 */ + "link", /* 1031 */ + "unlink", /* 1032 */ + "execve", /* 1033 */ + "chdir", /* 1034 */ + "fchdir", /* 1035 */ + "utimes", /* 1036 */ + "mknod", /* 1037 */ + "chmod", /* 1038 */ + "chown", /* 1039 */ + "lseek", /* 1040 */ + "getpid", /* 1041 */ + "getppid", /* 1042 */ + "mount", /* 1043 */ + "umount", /* 1044 */ + "setuid", /* 1045 */ + "getuid", /* 1046 */ + "geteuid", /* 1047 */ + "ptrace", /* 1048 */ + "access", /* 1049 */ + "sync", /* 1050 */ + "fsync", /* 1051 */ + "fdatasync", /* 1052 */ + "kill", /* 1053 */ + "rename", /* 1054 */ + "mkdir", /* 1055 */ + "rmdir", /* 1056 */ + "dup", /* 1057 */ + "pipe", /* 1058 */ + "times", /* 1059 */ + "brk", /* 1060 */ + "setgid", /* 1061 */ + "getgid", /* 1062 */ + "getegid", /* 1063 */ + "acct", /* 1064 */ + "ioctl", /* 1065 */ + "fcntl", /* 1066 */ + "umask", /* 1067 */ + "chroot", /* 1068 */ + "ustat", /* 1069 */ + "dup2", /* 1070 */ + "setreuid", /* 1071 */ + "setregid", /* 1072 */ + "getresuid", /* 1073 */ + "setresuid", /* 1074 */ + "getresgid", /* 1075 */ + "setresgid", /* 1076 */ + "getgroups", /* 1077 */ + "setgroups", /* 1078 */ + "getpgid", /* 1079 */ + "setpgid", /* 1080 */ + "setsid", /* 1081 */ + "getsid", /* 1082 */ + "sethostname", /* 1083 */ + "setrlimit", /* 1084 */ + "getrlimit", /* 1085 */ + "getrusage", /* 1086 */ + "gettimeofday", /* 1087 */ + "settimeofday", /* 1088 */ + "select", /* 1089 */ + "poll", /* 1090 */ + "symlink", /* 1091 */ + "readlink", /* 1092 */ + "uselib", /* 1093 */ + "swapon", /* 1094 */ + "swapoff", /* 1095 */ + "reboot", /* 1096 */ + "truncate", /* 1097 */ + "ftruncate", /* 1098 */ + "fchmod", /* 1099 */ + "fchown", /* 1100 */ + "getpriority", /* 1101 */ + "setpriority", /* 1102 */ + "statfs", /* 1103 */ + "fstatfs", /* 1104 */ + "gettid", /* 1105 */ + "semget", /* 1106 */ + "semop", /* 1107 */ + "semctl", /* 1108 */ + "msgget", /* 1109 */ + "msgsnd", /* 1110 */ + "msgrcv", /* 1111 */ + "msgctl", /* 1112 */ + "shmget", /* 1113 */ + "shmat", /* 1114 */ + "shmdt", /* 1115 */ + "shmctl", /* 1116 */ + "syslog", /* 1117 */ + "setitimer", /* 1118 */ + "getitimer", /* 1119 */ + "1120", /* 1120 */ + "1121", /* 1121 */ + "1122", /* 1122 */ + "vhangup", /* 1123 */ + "lchown", /* 1124 */ + "remap_file_pages", /* 1125 */ + "wait4", /* 1126 */ + "sysinfo", /* 1127 */ + "clone", /* 1128 */ + "setdomainname", /* 1129 */ + "uname", /* 1130 */ + "adjtimex", /* 1131 */ + "1132", /* 1132 */ + "init_module", /* 1133 */ + "delete_module", /* 1134 */ + "1135", /* 1135 */ + "1136", /* 1136 */ + "quotactl", /* 1137 */ + "bdflush", /* 1138 */ + "sysfs", /* 1139 */ + "personality", /* 1140 */ + "afs_syscall", /* 1141 */ + "setfsuid", /* 1142 */ + "setfsgid", /* 1143 */ + "getdents", /* 1144 */ + "flock", /* 1145 */ + "readv", /* 1146 */ + "writev", /* 1147 */ + "pread64", /* 1148 */ + "pwrite64", /* 1149 */ + "_sysctl", /* 1150 */ + "mmap", /* 1151 */ + "munmap", /* 1152 */ + "mlock", /* 1153 */ + "mlockall", /* 1154 */ + "mprotect", /* 1155 */ + "mremap", /* 1156 */ + "msync", /* 1157 */ + "munlock", /* 1158 */ + "munlockall", /* 1159 */ + "sched_getparam", /* 1160 */ + "sched_setparam", /* 1161 */ + "sched_getscheduler", /* 1162 */ + "sched_setscheduler", /* 1163 */ + "sched_yield", /* 1164 */ + "sched_get_priority_max", /* 1165 */ + "sched_get_priority_min", /* 1166 */ + "sched_rr_get_interval", /* 1167 */ + "nanosleep", /* 1168 */ + "nfsservctl", /* 1169 */ + "prctl", /* 1170 */ + "1171", /* 1171 */ + "mmap2", /* 1172 */ + "pciconfig_read", /* 1173 */ + "pciconfig_write", /* 1174 */ + "perfmonctl", /* 1175 */ + "sigaltstack", /* 1176 */ + "rt_sigaction", /* 1177 */ + "rt_sigpending", /* 1178 */ + "rt_sigprocmask", /* 1179 */ + "rt_sigqueueinfo", /* 1180 */ + "rt_sigreturn", /* 1181 */ + "rt_sigsuspend", /* 1182 */ + "rt_sigtimedwait", /* 1183 */ + "getcwd", /* 1184 */ + "capget", /* 1185 */ + "capset", /* 1186 */ + "sendfile", /* 1187 */ + "getpmsg", /* 1188 */ + "putpmsg", /* 1189 */ + "socket", /* 1190 */ + "bind", /* 1191 */ + "connect", /* 1192 */ + "listen", /* 1193 */ + "accept", /* 1194 */ + "getsockname", /* 1195 */ + "getpeername", /* 1196 */ + "socketpair", /* 1197 */ + "send", /* 1198 */ + "sendto", /* 1199 */ + "recv", /* 1200 */ + "recvfrom", /* 1201 */ + "shutdown", /* 1202 */ + "setsockopt", /* 1203 */ + "getsockopt", /* 1204 */ + "sendmsg", /* 1205 */ + "recvmsg", /* 1206 */ + "pivot_root", /* 1207 */ + "mincore", /* 1208 */ + "madvise", /* 1209 */ + "stat", /* 1210 */ + "lstat", /* 1211 */ + "fstat", /* 1212 */ + "clone2", /* 1213 */ + "getdents64", /* 1214 */ + "getunwind", /* 1215 */ + "readahead", /* 1216 */ + "setxattr", /* 1217 */ + "lsetxattr", /* 1218 */ + "fsetxattr", /* 1219 */ + "getxattr", /* 1220 */ + "lgetxattr", /* 1221 */ + "fgetxattr", /* 1222 */ + "listxattr", /* 1223 */ + "llistxattr", /* 1224 */ + "flistxattr", /* 1225 */ + "removexattr", /* 1226 */ + "lremovexattr", /* 1227 */ + "fremovexattr", /* 1228 */ + "tkill", /* 1229 */ + "futex", /* 1230 */ + "sched_setaffinity", /* 1231 */ + "sched_getaffinity", /* 1232 */ + "set_tid_address", /* 1233 */ + "fadvise64", /* 1234 */ + "tgkill", /* 1235 */ + "exit_group", /* 1236 */ + "lookup_dcookie", /* 1237 */ + "io_setup", /* 1238 */ + "io_destroy", /* 1239 */ + "io_getevents", /* 1240 */ + "io_submit", /* 1241 */ + "io_cancel", /* 1242 */ + "epoll_create", /* 1243 */ + "epoll_ctl", /* 1244 */ + "epoll_wait", /* 1245 */ + "restart_syscall", /* 1246 */ + "semtimedop", /* 1247 */ + "timer_create", /* 1248 */ + "timer_settime", /* 1249 */ + "timer_gettime", /* 1250 */ + "timer_getoverrun", /* 1251 */ + "timer_delete", /* 1252 */ + "clock_settime", /* 1253 */ + "clock_gettime", /* 1254 */ + "clock_getres", /* 1255 */ + "clock_nanosleep", /* 1256 */ + "fstatfs64", /* 1257 */ + "statfs64", /* 1258 */ + "mbind", /* 1259 */ + "get_mempolicy", /* 1260 */ + "set_mempolicy", /* 1261 */ + "mq_open", /* 1262 */ + "mq_unlink", /* 1263 */ + "mq_timedsend", /* 1264 */ + "mq_timedreceive", /* 1265 */ + "mq_notify", /* 1266 */ + "mq_getsetattr", /* 1267 */ + "kexec_load", /* 1268 */ + "vserver", /* 1269 */ + "waitid", /* 1270 */ + "add_key", /* 1271 */ + "request_key", /* 1272 */ + "keyctl", /* 1273 */ + "ioprio_set", /* 1274 */ + "ioprio_get", /* 1275 */ + "1276", /* 1276 */ + "inotify_init", /* 1277 */ + "inotify_add_watch", /* 1278 */ + "inotify_rm_watch", /* 1279 */ + "migrate_pages", /* 1280 */ + "openat", /* 1281 */ + "mkdirat", /* 1282 */ + "mknodat", /* 1283 */ + "fchownat", /* 1284 */ + "futimesat", /* 1285 */ + "newfstatat", /* 1286 */ + "unlinkat", /* 1287 */ + "renameat", /* 1288 */ + "linkat", /* 1289 */ + "symlinkat", /* 1290 */ + "readlinkat", /* 1291 */ + "fchmodat", /* 1292 */ + "faccessat", /* 1293 */ + "1294", /* 1294 */ + "1295", /* 1295 */ + "unshare", /* 1296 */ + "splice", /* 1297 */ + "set_robust_list", /* 1298 */ + "get_robust_list", /* 1299 */ + "sync_file_range", /* 1300 */ + "tee", /* 1301 */ + "vmsplice", /* 1302 */ diff --git a/sysdeps/linux-gnu/ia64/trace.c b/sysdeps/linux-gnu/ia64/trace.c new file mode 100644 index 0000000..799e0ff --- /dev/null +++ b/sysdeps/linux-gnu/ia64/trace.c @@ -0,0 +1,268 @@ +#include "config.h" + +#include <stdlib.h> +#include <stddef.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <signal.h> +#include <sys/ptrace.h> +#include <string.h> +#include <asm/ptrace_offsets.h> +#include <asm/rse.h> + +#include "common.h" + +/* What we think of as a bundle, ptrace thinks of it as two unsigned + * longs */ +union bundle_t { + /* An IA64 instruction bundle has a 5 bit header describing the + * type of bundle, then 3 41 bit instructions + */ + struct { + struct { + unsigned long template:5; + unsigned long slot0:41; + unsigned long bot_slot1:18; + } word0; + struct { + unsigned long top_slot1:23; + unsigned long slot2:41; + } word1; + } bitmap; + unsigned long code[2]; +}; + +union cfm_t { + struct { + unsigned long sof:7; + unsigned long sol:7; + unsigned long sor:4; + unsigned long rrb_gr:7; + unsigned long rrb_fr:7; + unsigned long rrb_pr:6; + } cfm; + unsigned long value; +}; + +int +syscall_p(Process *proc, int status, int *sysnum) { + if (WIFSTOPPED(status) + && WSTOPSIG(status) == (SIGTRAP | proc->tracesysgood)) { + unsigned long slot = + (ptrace(PTRACE_PEEKUSER, proc->pid, PT_CR_IPSR, 0) >> 41) & + 0x3; + unsigned long ip = + ptrace(PTRACE_PEEKUSER, proc->pid, PT_CR_IIP, 0); + + /* r15 holds the system call number */ + unsigned long r15 = + ptrace(PTRACE_PEEKUSER, proc->pid, PT_R15, 0); + unsigned long insn; + + union bundle_t bundle; + + /* On fault, the IP has moved forward to the next + * slot. If that is zero, then the actual place we + * broke was in the previous bundle, so wind back the + * IP. + */ + if (slot == 0) + ip = ip - 16; + bundle.code[0] = ptrace(PTRACE_PEEKTEXT, proc->pid, ip, 0); + bundle.code[1] = ptrace(PTRACE_PEEKTEXT, proc->pid, ip + 8, 0); + + unsigned long bot = 0UL | bundle.bitmap.word0.bot_slot1; + unsigned long top = 0UL | bundle.bitmap.word1.top_slot1; + + /* handle the rollback, slot 0 is actually slot 2 of + * the previous instruction (see above) */ + switch (slot) { + case 0: + insn = bundle.bitmap.word1.slot2; + break; + case 1: + insn = bundle.bitmap.word0.slot0; + break; + case 2: + /* make sure we're shifting about longs */ + insn = 0UL | bot | (top << 18UL); + break; + default: + printf("Ummm, can't find instruction slot?\n"); + exit(1); + } + + /* We need to support both the older break instruction + * type syscalls, and the new epc type ones. + * + * Bit 20 of the break constant is encoded in the "i" + * bit (bit 36) of the instruction, hence you should + * see 0x1000000000. + * + * An EPC call is just 0x1ffffffffff + */ + if (insn == 0x1000000000 || insn == 0x1ffffffffff) { + *sysnum = r15; + if (proc->callstack_depth > 0 && + proc->callstack[proc->callstack_depth - 1].is_syscall && + proc->callstack[proc->callstack_depth - 1].c_un.syscall == *sysnum) { + return 2; + } + return 1; + } + } + return 0; +} + +/* Stolen from David Mosberger's utrace tool, which he released under + the GPL + (http://www.gelato.unsw.edu.au/archives/linux-ia64/0104/1405.html) */ +static inline double +fpreg_to_double (struct ia64_fpreg *fp) { + double result; + + asm ("ldf.fill %0=%1" : "=f"(result) : "m"(*fp)); + return result; +} + +static long +gimme_long_arg(enum tof type, Process *proc, int arg_num) { + union cfm_t cfm; + unsigned long bsp; + + bsp = ptrace(PTRACE_PEEKUSER, proc->pid, PT_AR_BSP, 0); + cfm.value = ptrace(PTRACE_PEEKUSER, proc->pid, PT_CFM, 0); + + if (arg_num == -1) /* return value */ + return ptrace(PTRACE_PEEKUSER, proc->pid, PT_R8, 0); + + /* First 8 arguments are passed in registers on the register + * stack, the following arguments are passed on the stack + * after a 16 byte scratch area + * + * If the function has returned, the ia64 register window has + * been reverted to the caller's configuration. So although in + * the callee, the first parameter is in R32, in the caller + * the first parameter comes in the registers after the local + * registers (really, input parameters plus locals, but the + * hardware doesn't track the distinction.) So we have to add + * in the size of the local area (sol) to find the first + * parameter passed to the callee. */ + if (type == LT_TOF_FUNCTION || type == LT_TOF_FUNCTIONR) { + if (arg_num < 8) { + if (type == LT_TOF_FUNCTIONR) + arg_num += cfm.cfm.sol; + + return ptrace(PTRACE_PEEKDATA, proc->pid, + (long)ia64_rse_skip_regs((unsigned long *)bsp, + -cfm.cfm.sof + arg_num), + 0); + } else { + unsigned long sp = + ptrace(PTRACE_PEEKUSER, proc->pid, PT_R12, 0) + 16; + return ptrace(PTRACE_PEEKDATA, proc->pid, + sp + (8 * (arg_num - 8))); + } + } + + if (type == LT_TOF_SYSCALL || LT_TOF_SYSCALLR) + return ptrace(PTRACE_PEEKDATA, proc->pid, + (long)ia64_rse_skip_regs((unsigned long *)bsp, arg_num), + 0); + + /* error if we get here */ + fprintf(stderr, "gimme_arg called with wrong arguments\n"); + exit(1); +} + +static long float_regs[8] = { PT_F8, PT_F9, PT_F10, PT_F11, + PT_F12, PT_F13, PT_F14, PT_F15 }; +static double +gimme_float_arg(enum tof type, Process *proc, int arg_num) { + union cfm_t cfm; + unsigned long bsp; + struct ia64_fpreg reg; + + if (arg_num == -1) { /* return value */ + reg.u.bits[0] = ptrace(PTRACE_PEEKUSER, proc->pid, + PT_F8, 0); + reg.u.bits[1] = ptrace(PTRACE_PEEKUSER, proc->pid, + PT_F8 + 0x8, 0); + return fpreg_to_double(®); + } + + bsp = ptrace(PTRACE_PEEKUSER, proc->pid, PT_AR_BSP, 0); + cfm.value = ptrace(PTRACE_PEEKUSER, proc->pid, PT_CFM, 0); + + /* The first 8 arguments are passed in regular registers + * (counting from R32), unless they are floating point values + * (the case in question here). In that case, up to the first + * 8 regular registers are still "allocated" for each of the + * first 8 parameters, but if a parameter is floating point, + * then the register is left unset and the parameter is passed + * in the first available floating-point register, counting + * from F8. + * + * Take func(int a, float f, int b, double d), for example. + * a - passed in R32 + * f - R33 left unset, value passed in F8 + * b - passed in R34 + * d - R35 left unset, value passed in F9 + * + * ltrace handles this by counting floating point arguments + * while parsing declarations. The "arg_num" in this routine + * (which is only called for floating point values) really + * means which floating point parameter we're looking for, + * ignoring everything else. + * + * Following the first 8 arguments, the remaining arguments + * are passed on the stack after a 16 byte scratch area + */ + if (type == LT_TOF_FUNCTION || type == LT_TOF_FUNCTIONR) { + if (arg_num < 8) { + reg.u.bits[0] = ptrace(PTRACE_PEEKUSER, proc->pid, + float_regs[arg_num], 0); + reg.u.bits[1] = ptrace(PTRACE_PEEKUSER, proc->pid, + float_regs[arg_num] + 0x8, 0); + return fpreg_to_double(®); + } else { + unsigned long sp = + ptrace(PTRACE_PEEKUSER, proc->pid, PT_R12, 0) + 16; + reg.u.bits[0] = ptrace(PTRACE_PEEKDATA, proc->pid, + sp + (8 * (arg_num - 8))); + reg.u.bits[0] = ptrace(PTRACE_PEEKDATA, proc->pid, + sp + (8 * (arg_num - 8)) + 0x8); + return fpreg_to_double(®); + } + } + + /* error if we get here */ + fprintf(stderr, "gimme_arg called with wrong arguments\n"); + exit(1); +} + +long +gimme_arg(enum tof type, Process *proc, int arg_num, arg_type_info *info) { + union { + long l; + float f; + double d; + } cvt; + + if (info->type == ARGTYPE_FLOAT) + cvt.f = gimme_float_arg(type, proc, info->u.float_info.float_index); + else if (info->type == ARGTYPE_DOUBLE) + cvt.d = gimme_float_arg(type, proc, info->u.double_info.float_index); + else + cvt.l = gimme_long_arg(type, proc, arg_num); + + return cvt.l; +} + +void +save_register_args(enum tof type, Process *proc) { +} + +void +get_arch_dep(Process *proc) { +} diff --git a/sysdeps/linux-gnu/m68k/Makefile b/sysdeps/linux-gnu/m68k/Makefile new file mode 100644 index 0000000..60d7531 --- /dev/null +++ b/sysdeps/linux-gnu/m68k/Makefile @@ -0,0 +1,10 @@ +OBJ = trace.o regs.o plt.o + +all: arch.o + +arch.o: $(OBJ) + $(CC) -nostdlib -r -o arch.o $(OBJ) + +clean: + $(RM) $(OBJ) arch.o + diff --git a/sysdeps/linux-gnu/m68k/arch.h b/sysdeps/linux-gnu/m68k/arch.h new file mode 100644 index 0000000..1790d09 --- /dev/null +++ b/sysdeps/linux-gnu/m68k/arch.h @@ -0,0 +1,6 @@ +#define BREAKPOINT_VALUE { 0x4e, 0x4f } +#define BREAKPOINT_LENGTH 2 +#define DECR_PC_AFTER_BREAK 2 + +#define LT_ELFCLASS ELFCLASS32 +#define LT_ELF_MACHINE EM_68K diff --git a/sysdeps/linux-gnu/m68k/plt.c b/sysdeps/linux-gnu/m68k/plt.c new file mode 100644 index 0000000..508d7fc --- /dev/null +++ b/sysdeps/linux-gnu/m68k/plt.c @@ -0,0 +1,13 @@ +#include <gelf.h> +#include "common.h" + +GElf_Addr +arch_plt_sym_val(struct ltelf *lte, size_t ndx, GElf_Rela * rela) { + return lte->plt_addr + (ndx + 1) + * ((lte->ehdr.e_flags & EF_CPU32) ? 24 : 12); +} + +void * +sym2addr(Process *proc, struct library_symbol *sym) { + return sym->enter_addr; +} diff --git a/sysdeps/linux-gnu/m68k/ptrace.h b/sysdeps/linux-gnu/m68k/ptrace.h new file mode 100644 index 0000000..c3cbcb6 --- /dev/null +++ b/sysdeps/linux-gnu/m68k/ptrace.h @@ -0,0 +1 @@ +#include <sys/ptrace.h> diff --git a/sysdeps/linux-gnu/m68k/regs.c b/sysdeps/linux-gnu/m68k/regs.c new file mode 100644 index 0000000..959a60e --- /dev/null +++ b/sysdeps/linux-gnu/m68k/regs.c @@ -0,0 +1,40 @@ +#include "config.h" + +#include <sys/types.h> +#include <sys/ptrace.h> +#include <asm/ptrace.h> + +#include "common.h" + +#if (!defined(PTRACE_PEEKUSER) && defined(PTRACE_PEEKUSR)) +# define PTRACE_PEEKUSER PTRACE_PEEKUSR +#endif + +#if (!defined(PTRACE_POKEUSER) && defined(PTRACE_POKEUSR)) +# define PTRACE_POKEUSER PTRACE_POKEUSR +#endif + +void * +get_instruction_pointer(Process *proc) { + return (void *)ptrace(PTRACE_PEEKUSER, proc->pid, 4 * PT_PC, 0); +} + +void +set_instruction_pointer(Process *proc, void *addr) { + ptrace(PTRACE_POKEUSER, proc->pid, 4 * PT_PC, addr); +} + +void * +get_stack_pointer(Process *proc) { + return (void *)ptrace(PTRACE_PEEKUSER, proc->pid, 4 * PT_USP, 0); +} + +void * +get_return_addr(Process *proc, void *stack_pointer) { + return (void *)ptrace(PTRACE_PEEKTEXT, proc->pid, stack_pointer, 0); +} + +void +set_return_addr(Process *proc, void *addr) { + ptrace(PTRACE_POKETEXT, proc->pid, proc->stack_pointer, addr); +} diff --git a/sysdeps/linux-gnu/m68k/signalent.h b/sysdeps/linux-gnu/m68k/signalent.h new file mode 100644 index 0000000..5395f82 --- /dev/null +++ b/sysdeps/linux-gnu/m68k/signalent.h @@ -0,0 +1,32 @@ + "SIG_0", /* 0 */ + "SIGHUP", /* 1 */ + "SIGINT", /* 2 */ + "SIGQUIT", /* 3 */ + "SIGILL", /* 4 */ + "SIGTRAP", /* 5 */ + "SIGABRT", /* 6 */ + "SIGBUS", /* 7 */ + "SIGFPE", /* 8 */ + "SIGKILL", /* 9 */ + "SIGUSR1", /* 10 */ + "SIGSEGV", /* 11 */ + "SIGUSR2", /* 12 */ + "SIGPIPE", /* 13 */ + "SIGALRM", /* 14 */ + "SIGTERM", /* 15 */ + "SIGSTKFLT", /* 16 */ + "SIGCHLD", /* 17 */ + "SIGCONT", /* 18 */ + "SIGSTOP", /* 19 */ + "SIGTSTP", /* 20 */ + "SIGTTIN", /* 21 */ + "SIGTTOU", /* 22 */ + "SIGURG", /* 23 */ + "SIGXCPU", /* 24 */ + "SIGXFSZ", /* 25 */ + "SIGVTALRM", /* 26 */ + "SIGPROF", /* 27 */ + "SIGWINCH", /* 28 */ + "SIGIO", /* 29 */ + "SIGPWR", /* 30 */ + "SIGSYS", /* 31 */ diff --git a/sysdeps/linux-gnu/m68k/syscallent.h b/sysdeps/linux-gnu/m68k/syscallent.h new file mode 100644 index 0000000..4d65b39 --- /dev/null +++ b/sysdeps/linux-gnu/m68k/syscallent.h @@ -0,0 +1,282 @@ + "0", /* 0 */ + "exit", /* 1 */ + "fork", /* 2 */ + "read", /* 3 */ + "write", /* 4 */ + "open", /* 5 */ + "close", /* 6 */ + "waitpid", /* 7 */ + "creat", /* 8 */ + "link", /* 9 */ + "unlink", /* 10 */ + "execve", /* 11 */ + "chdir", /* 12 */ + "time", /* 13 */ + "mknod", /* 14 */ + "chmod", /* 15 */ + "chown", /* 16 */ + "break", /* 17 */ + "oldstat", /* 18 */ + "lseek", /* 19 */ + "getpid", /* 20 */ + "mount", /* 21 */ + "umount", /* 22 */ + "setuid", /* 23 */ + "getuid", /* 24 */ + "stime", /* 25 */ + "ptrace", /* 26 */ + "alarm", /* 27 */ + "oldfstat", /* 28 */ + "pause", /* 29 */ + "utime", /* 30 */ + "stty", /* 31 */ + "gtty", /* 32 */ + "access", /* 33 */ + "nice", /* 34 */ + "ftime", /* 35 */ + "sync", /* 36 */ + "kill", /* 37 */ + "rename", /* 38 */ + "mkdir", /* 39 */ + "rmdir", /* 40 */ + "dup", /* 41 */ + "pipe", /* 42 */ + "times", /* 43 */ + "prof", /* 44 */ + "brk", /* 45 */ + "setgid", /* 46 */ + "getgid", /* 47 */ + "signal", /* 48 */ + "geteuid", /* 49 */ + "getegid", /* 50 */ + "acct", /* 51 */ + "umount2", /* 52 */ + "lock", /* 53 */ + "ioctl", /* 54 */ + "fcntl", /* 55 */ + "mpx", /* 56 */ + "setpgid", /* 57 */ + "ulimit", /* 58 */ + "oldolduname", /* 59 */ + "umask", /* 60 */ + "chroot", /* 61 */ + "ustat", /* 62 */ + "dup2", /* 63 */ + "getppid", /* 64 */ + "getpgrp", /* 65 */ + "setsid", /* 66 */ + "sigaction", /* 67 */ + "sgetmask", /* 68 */ + "ssetmask", /* 69 */ + "setreuid", /* 70 */ + "setregid", /* 71 */ + "sigsuspend", /* 72 */ + "sigpending", /* 73 */ + "sethostname", /* 74 */ + "setrlimit", /* 75 */ + "getrlimit", /* 76 */ + "getrusage", /* 77 */ + "gettimeofday", /* 78 */ + "settimeofday", /* 79 */ + "getgroups", /* 80 */ + "setgroups", /* 81 */ + "select", /* 82 */ + "symlink", /* 83 */ + "oldlstat", /* 84 */ + "readlink", /* 85 */ + "uselib", /* 86 */ + "swapon", /* 87 */ + "reboot", /* 88 */ + "readdir", /* 89 */ + "mmap", /* 90 */ + "munmap", /* 91 */ + "truncate", /* 92 */ + "ftruncate", /* 93 */ + "fchmod", /* 94 */ + "fchown", /* 95 */ + "getpriority", /* 96 */ + "setpriority", /* 97 */ + "profil", /* 98 */ + "statfs", /* 99 */ + "fstatfs", /* 100 */ + "ioperm", /* 101 */ + "socketcall", /* 102 */ + "syslog", /* 103 */ + "setitimer", /* 104 */ + "getitimer", /* 105 */ + "stat", /* 106 */ + "lstat", /* 107 */ + "fstat", /* 108 */ + "olduname", /* 109 */ + "110", /* 110 */ + "vhangup", /* 111 */ + "112", /* 112 */ + "113", /* 113 */ + "wait4", /* 114 */ + "swapoff", /* 115 */ + "sysinfo", /* 116 */ + "ipc", /* 117 */ + "fsync", /* 118 */ + "sigreturn", /* 119 */ + "clone", /* 120 */ + "setdomainname", /* 121 */ + "uname", /* 122 */ + "cacheflush", /* 123 */ + "adjtimex", /* 124 */ + "mprotect", /* 125 */ + "sigprocmask", /* 126 */ + "create_module", /* 127 */ + "init_module", /* 128 */ + "delete_module", /* 129 */ + "get_kernel_syms", /* 130 */ + "quotactl", /* 131 */ + "getpgid", /* 132 */ + "fchdir", /* 133 */ + "bdflush", /* 134 */ + "sysfs", /* 135 */ + "personality", /* 136 */ + "afs_syscall", /* 137 */ + "setfsuid", /* 138 */ + "setfsgid", /* 139 */ + "_llseek", /* 140 */ + "getdents", /* 141 */ + "_newselect", /* 142 */ + "flock", /* 143 */ + "msync", /* 144 */ + "readv", /* 145 */ + "writev", /* 146 */ + "getsid", /* 147 */ + "fdatasync", /* 148 */ + "_sysctl", /* 149 */ + "mlock", /* 150 */ + "munlock", /* 151 */ + "mlockall", /* 152 */ + "munlockall", /* 153 */ + "sched_setparam", /* 154 */ + "sched_getparam", /* 155 */ + "sched_setscheduler", /* 156 */ + "sched_getscheduler", /* 157 */ + "sched_yield", /* 158 */ + "sched_get_priority_max", /* 159 */ + "sched_get_priority_min", /* 160 */ + "sched_rr_get_interval", /* 161 */ + "nanosleep", /* 162 */ + "mremap", /* 163 */ + "setresuid", /* 164 */ + "getresuid", /* 165 */ + "getpagesize", /* 166 */ + "query_module", /* 167 */ + "poll", /* 168 */ + "nfsservctl", /* 169 */ + "setresgid", /* 170 */ + "getresgid", /* 171 */ + "prctl", /* 172 */ + "rt_sigreturn", /* 173 */ + "rt_sigaction", /* 174 */ + "rt_sigprocmask", /* 175 */ + "rt_sigpending", /* 176 */ + "rt_sigtimedwait", /* 177 */ + "rt_sigqueueinfo", /* 178 */ + "rt_sigsuspend", /* 179 */ + "pread64", /* 180 */ + "pwrite64", /* 181 */ + "lchown", /* 182 */ + "getcwd", /* 183 */ + "capget", /* 184 */ + "capset", /* 185 */ + "sigaltstack", /* 186 */ + "sendfile", /* 187 */ + "getpmsg", /* 188 */ + "putpmsg", /* 189 */ + "vfork", /* 190 */ + "ugetrlimit", /* 191 */ + "mmap2", /* 192 */ + "truncate64", /* 193 */ + "ftruncate64", /* 194 */ + "stat64", /* 195 */ + "lstat64", /* 196 */ + "fstat64", /* 197 */ + "chown32", /* 198 */ + "getuid32", /* 199 */ + "getgid32", /* 200 */ + "geteuid32", /* 201 */ + "getegid32", /* 202 */ + "setreuid32", /* 203 */ + "setregid32", /* 204 */ + "getgroups32", /* 205 */ + "setgroups32", /* 206 */ + "fchown32", /* 207 */ + "setresuid32", /* 208 */ + "getresuid32", /* 209 */ + "setresgid32", /* 210 */ + "getresgid32", /* 211 */ + "lchown32", /* 212 */ + "setuid32", /* 213 */ + "setgid32", /* 214 */ + "setfsuid32", /* 215 */ + "setfsgid32", /* 216 */ + "pivot_root", /* 217 */ + "218", /* 218 */ + "219", /* 219 */ + "getdents64", /* 220 */ + "gettid", /* 221 */ + "tkill", /* 222 */ + "setxattr", /* 223 */ + "lsetxattr", /* 224 */ + "fsetxattr", /* 225 */ + "getxattr", /* 226 */ + "lgetxattr", /* 227 */ + "fgetxattr", /* 228 */ + "listxattr", /* 229 */ + "llistxattr", /* 230 */ + "flistxattr", /* 231 */ + "removexattr", /* 232 */ + "lremovexattr", /* 233 */ + "fremovexattr", /* 234 */ + "futex", /* 235 */ + "sendfile64", /* 236 */ + "mincore", /* 237 */ + "madvise", /* 238 */ + "fcntl64", /* 239 */ + "readahead", /* 240 */ + "io_setup", /* 241 */ + "io_destroy", /* 242 */ + "io_getevents", /* 243 */ + "io_submit", /* 244 */ + "io_cancel", /* 245 */ + "fadvise64", /* 246 */ + "exit_group", /* 247 */ + "lookup_dcookie", /* 248 */ + "epoll_create", /* 249 */ + "epoll_ctl", /* 250 */ + "epoll_wait", /* 251 */ + "remap_file_pages", /* 252 */ + "set_tid_address", /* 253 */ + "timer_create", /* 254 */ + "timer_settime", /* 255 */ + "timer_gettime", /* 256 */ + "timer_getoverrun", /* 257 */ + "timer_delete", /* 258 */ + "clock_settime", /* 259 */ + "clock_gettime", /* 260 */ + "clock_getres", /* 261 */ + "clock_nanosleep", /* 262 */ + "statfs64", /* 263 */ + "fstatfs64", /* 264 */ + "tgkill", /* 265 */ + "utimes", /* 266 */ + "fadvise64_64", /* 267 */ + "mbind", /* 268 */ + "get_mempolicy", /* 269 */ + "set_mempolicy", /* 270 */ + "mq_open", /* 271 */ + "mq_unlink", /* 272 */ + "mq_timedsend", /* 273 */ + "mq_timedreceive", /* 274 */ + "mq_notify", /* 275 */ + "mq_getsetattr", /* 276 */ + "waitid", /* 277 */ + "vserver", /* 278 */ + "add_key", /* 279 */ + "request_key", /* 280 */ + "keyctl", /* 281 */ diff --git a/sysdeps/linux-gnu/m68k/trace.c b/sysdeps/linux-gnu/m68k/trace.c new file mode 100644 index 0000000..2f89fdf --- /dev/null +++ b/sysdeps/linux-gnu/m68k/trace.c @@ -0,0 +1,89 @@ +#include "config.h" + +#include <sys/types.h> +#include <sys/wait.h> +#include <signal.h> +#include <sys/ptrace.h> +#include <asm/ptrace.h> + +#include "common.h" + +#if (!defined(PTRACE_PEEKUSER) && defined(PTRACE_PEEKUSR)) +# define PTRACE_PEEKUSER PTRACE_PEEKUSR +#endif + +#if (!defined(PTRACE_POKEUSER) && defined(PTRACE_POKEUSR)) +# define PTRACE_POKEUSER PTRACE_POKEUSR +#endif + +void +get_arch_dep(Process *proc) { +} + +/* Returns 1 if syscall, 2 if sysret, 0 otherwise. + */ +int +syscall_p(Process *proc, int status, int *sysnum) { + int depth; + + if (WIFSTOPPED(status) + && WSTOPSIG(status) == (SIGTRAP | proc->tracesysgood)) { + *sysnum = ptrace(PTRACE_PEEKUSER, proc->pid, 4 * PT_ORIG_D0, 0); + if (*sysnum == -1) + return 0; + if (*sysnum >= 0) { + depth = proc->callstack_depth; + if (depth > 0 && + proc->callstack[depth - 1].is_syscall && + proc->callstack[depth - 1].c_un.syscall == *sysnum) { + return 2; + } else { + return 1; + } + } + } + return 0; +} + +long +gimme_arg(enum tof type, Process *proc, int arg_num, arg_type_info *info) { + if (arg_num == -1) { /* return value */ + return ptrace(PTRACE_PEEKUSER, proc->pid, 4 * PT_D0, 0); + } + + if (type == LT_TOF_FUNCTION || type == LT_TOF_FUNCTIONR) { + return ptrace(PTRACE_PEEKTEXT, proc->pid, + proc->stack_pointer + 4 * (arg_num + 1), 0); + } else if (type == LT_TOF_SYSCALL || type == LT_TOF_SYSCALLR) { +#if 0 + switch (arg_num) { + case 0: + return ptrace(PTRACE_PEEKUSER, proc->pid, 4 * PT_D1, 0); + case 1: + return ptrace(PTRACE_PEEKUSER, proc->pid, 4 * PT_D2, 0); + case 2: + return ptrace(PTRACE_PEEKUSER, proc->pid, 4 * PT_D3, 0); + case 3: + return ptrace(PTRACE_PEEKUSER, proc->pid, 4 * PT_D4, 0); + case 4: + return ptrace(PTRACE_PEEKUSER, proc->pid, 4 * PT_D5, 0); + default: + fprintf(stderr, + "gimme_arg called with wrong arguments\n"); + exit(2); + } +#else + /* That hack works on m68k, too */ + return ptrace(PTRACE_PEEKUSER, proc->pid, 4 * arg_num, 0); +#endif + } else { + fprintf(stderr, "gimme_arg called with wrong arguments\n"); + exit(1); + } + + return 0; +} + +void +save_register_args(enum tof type, Process *proc) { +} diff --git a/sysdeps/linux-gnu/mipsel/Doxyfile b/sysdeps/linux-gnu/mipsel/Doxyfile new file mode 100644 index 0000000..b4f5eb2 --- /dev/null +++ b/sysdeps/linux-gnu/mipsel/Doxyfile @@ -0,0 +1,275 @@ +# Doxyfile 1.5.1 + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- +PROJECT_NAME = ltrace-mipsel +PROJECT_NUMBER = +OUTPUT_DIRECTORY = +CREATE_SUBDIRS = NO +OUTPUT_LANGUAGE = English +USE_WINDOWS_ENCODING = NO +BRIEF_MEMBER_DESC = YES +REPEAT_BRIEF = YES +ABBREVIATE_BRIEF = "The $name class" \ + "The $name widget" \ + "The $name file" \ + is \ + provides \ + specifies \ + contains \ + represents \ + a \ + an \ + the +ALWAYS_DETAILED_SEC = NO +INLINE_INHERITED_MEMB = NO +FULL_PATH_NAMES = YES +STRIP_FROM_PATH = +STRIP_FROM_INC_PATH = +SHORT_NAMES = NO +JAVADOC_AUTOBRIEF = NO +MULTILINE_CPP_IS_BRIEF = NO +DETAILS_AT_TOP = NO +INHERIT_DOCS = YES +SEPARATE_MEMBER_PAGES = NO +TAB_SIZE = 8 +ALIASES = +OPTIMIZE_OUTPUT_FOR_C = NO +OPTIMIZE_OUTPUT_JAVA = NO +BUILTIN_STL_SUPPORT = NO +DISTRIBUTE_GROUP_DOC = NO +SUBGROUPING = YES +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- +EXTRACT_ALL = YES +EXTRACT_PRIVATE = YES +EXTRACT_STATIC = YES +EXTRACT_LOCAL_CLASSES = YES +EXTRACT_LOCAL_METHODS = YES +HIDE_UNDOC_MEMBERS = NO +HIDE_UNDOC_CLASSES = NO +HIDE_FRIEND_COMPOUNDS = NO +HIDE_IN_BODY_DOCS = NO +INTERNAL_DOCS = NO +CASE_SENSE_NAMES = YES +HIDE_SCOPE_NAMES = NO +SHOW_INCLUDE_FILES = YES +INLINE_INFO = YES +SORT_MEMBER_DOCS = YES +SORT_BRIEF_DOCS = YES +SORT_BY_SCOPE_NAME = YES +GENERATE_TODOLIST = YES +GENERATE_TESTLIST = YES +GENERATE_BUGLIST = YES +GENERATE_DEPRECATEDLIST= YES +ENABLED_SECTIONS = +MAX_INITIALIZER_LINES = 30 +SHOW_USED_FILES = YES +SHOW_DIRECTORIES = NO +FILE_VERSION_FILTER = +#--------------------------------------------------------------------------- +# configuration options related to warning and progress messages +#--------------------------------------------------------------------------- +QUIET = NO +WARNINGS = YES +WARN_IF_UNDOCUMENTED = YES +WARN_IF_DOC_ERROR = YES +WARN_NO_PARAMDOC = NO +WARN_FORMAT = "$file:$line: $text" +WARN_LOGFILE = +#--------------------------------------------------------------------------- +# configuration options related to the input files +#--------------------------------------------------------------------------- +INPUT = . +FILE_PATTERNS = *.c \ + *.cc \ + *.cxx \ + *.cpp \ + *.c++ \ + *.d \ + *.java \ + *.ii \ + *.ixx \ + *.ipp \ + *.i++ \ + *.inl \ + *.h \ + *.hh \ + *.hxx \ + *.hpp \ + *.h++ \ + *.idl \ + *.odl \ + *.cs \ + *.php \ + *.php3 \ + *.inc \ + *.m \ + *.mm \ + *.dox \ + *.py \ + *.C \ + *.CC \ + *.C++ \ + *.II \ + *.I++ \ + *.H \ + *.HH \ + *.H++ \ + *.CS \ + *.PHP \ + *.PHP3 \ + *.M \ + *.MM \ + *.PY +RECURSIVE = NO +EXCLUDE = +EXCLUDE_SYMLINKS = NO +EXCLUDE_PATTERNS = +EXAMPLE_PATH = +EXAMPLE_PATTERNS = * +EXAMPLE_RECURSIVE = NO +IMAGE_PATH = +INPUT_FILTER = +FILTER_PATTERNS = +FILTER_SOURCE_FILES = NO +#--------------------------------------------------------------------------- +# configuration options related to source browsing +#--------------------------------------------------------------------------- +SOURCE_BROWSER = YES +INLINE_SOURCES = YES +STRIP_CODE_COMMENTS = YES +REFERENCED_BY_RELATION = YES +REFERENCES_RELATION = YES +REFERENCES_LINK_SOURCE = YES +USE_HTAGS = NO +VERBATIM_HEADERS = YES +#--------------------------------------------------------------------------- +# configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- +ALPHABETICAL_INDEX = NO +COLS_IN_ALPHA_INDEX = 5 +IGNORE_PREFIX = +#--------------------------------------------------------------------------- +# configuration options related to the HTML output +#--------------------------------------------------------------------------- +GENERATE_HTML = YES +HTML_OUTPUT = html +HTML_FILE_EXTENSION = .html +HTML_HEADER = +HTML_FOOTER = +HTML_STYLESHEET = +HTML_ALIGN_MEMBERS = YES +GENERATE_HTMLHELP = NO +CHM_FILE = +HHC_LOCATION = +GENERATE_CHI = NO +BINARY_TOC = NO +TOC_EXPAND = NO +DISABLE_INDEX = NO +ENUM_VALUES_PER_LINE = 4 +GENERATE_TREEVIEW = NO +TREEVIEW_WIDTH = 250 +#--------------------------------------------------------------------------- +# configuration options related to the LaTeX output +#--------------------------------------------------------------------------- +GENERATE_LATEX = NO +LATEX_OUTPUT = latex +LATEX_CMD_NAME = latex +MAKEINDEX_CMD_NAME = makeindex +COMPACT_LATEX = NO +PAPER_TYPE = a4wide +EXTRA_PACKAGES = +LATEX_HEADER = +PDF_HYPERLINKS = NO +USE_PDFLATEX = NO +LATEX_BATCHMODE = NO +LATEX_HIDE_INDICES = NO +#--------------------------------------------------------------------------- +# configuration options related to the RTF output +#--------------------------------------------------------------------------- +GENERATE_RTF = NO +RTF_OUTPUT = rtf +COMPACT_RTF = NO +RTF_HYPERLINKS = NO +RTF_STYLESHEET_FILE = +RTF_EXTENSIONS_FILE = +#--------------------------------------------------------------------------- +# configuration options related to the man page output +#--------------------------------------------------------------------------- +GENERATE_MAN = NO +MAN_OUTPUT = man +MAN_EXTENSION = .3 +MAN_LINKS = NO +#--------------------------------------------------------------------------- +# configuration options related to the XML output +#--------------------------------------------------------------------------- +GENERATE_XML = NO +XML_OUTPUT = xml +XML_SCHEMA = +XML_DTD = +XML_PROGRAMLISTING = YES +#--------------------------------------------------------------------------- +# configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- +GENERATE_AUTOGEN_DEF = NO +#--------------------------------------------------------------------------- +# configuration options related to the Perl module output +#--------------------------------------------------------------------------- +GENERATE_PERLMOD = NO +PERLMOD_LATEX = NO +PERLMOD_PRETTY = YES +PERLMOD_MAKEVAR_PREFIX = +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- +ENABLE_PREPROCESSING = YES +MACRO_EXPANSION = NO +EXPAND_ONLY_PREDEF = NO +SEARCH_INCLUDES = YES +INCLUDE_PATH = +INCLUDE_FILE_PATTERNS = +PREDEFINED = +EXPAND_AS_DEFINED = +SKIP_FUNCTION_MACROS = YES +#--------------------------------------------------------------------------- +# Configuration::additions related to external references +#--------------------------------------------------------------------------- +TAGFILES = +GENERATE_TAGFILE = +ALLEXTERNALS = NO +EXTERNAL_GROUPS = YES +PERL_PATH = /usr/bin/perl +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- +CLASS_DIAGRAMS = YES +HIDE_UNDOC_RELATIONS = YES +HAVE_DOT = YES +CLASS_GRAPH = YES +COLLABORATION_GRAPH = YES +GROUP_GRAPHS = YES +UML_LOOK = YES +TEMPLATE_RELATIONS = YES +INCLUDE_GRAPH = YES +INCLUDED_BY_GRAPH = YES +CALL_GRAPH = YES +CALLER_GRAPH = YES +GRAPHICAL_HIERARCHY = YES +DIRECTORY_GRAPH = YES +DOT_IMAGE_FORMAT = png +DOT_PATH = +DOTFILE_DIRS = +MAX_DOT_GRAPH_WIDTH = 1024 +MAX_DOT_GRAPH_HEIGHT = 1024 +MAX_DOT_GRAPH_DEPTH = 1000 +DOT_TRANSPARENT = NO +DOT_MULTI_TARGETS = NO +GENERATE_LEGEND = YES +DOT_CLEANUP = NO +#--------------------------------------------------------------------------- +# Configuration::additions related to the search engine +#--------------------------------------------------------------------------- +SEARCHENGINE = NO diff --git a/sysdeps/linux-gnu/mipsel/Makefile b/sysdeps/linux-gnu/mipsel/Makefile new file mode 100644 index 0000000..44f4aae --- /dev/null +++ b/sysdeps/linux-gnu/mipsel/Makefile @@ -0,0 +1,22 @@ +.PHONY: all deps clean docs + +OBJ = trace.o regs.o plt.o +SRC=$(OBJ:.o=.c) + +all: arch.o + +deps: + $(CC) $(CFLAGS) $(CPPFLAGS) -M $(SRC) > .depends + +arch.o: $(OBJ) arch.h + $(CC) -nostdlib -r -o arch.o $(OBJ) + +clean: + -rm -f $(OBJ) arch.o + -rm -rf html + +docs: + doxygen + + +-include .depends diff --git a/sysdeps/linux-gnu/mipsel/arch.h b/sysdeps/linux-gnu/mipsel/arch.h new file mode 100644 index 0000000..dd0ca35 --- /dev/null +++ b/sysdeps/linux-gnu/mipsel/arch.h @@ -0,0 +1,9 @@ +#define BREAKPOINT_VALUE { 0x0d, 0x00, 0x00, 0x00 } +#define BREAKPOINT_LENGTH 4 +#define DECR_PC_AFTER_BREAK 0 + +#define LT_ELFCLASS ELFCLASS32 +#define LT_ELF_MACHINE EM_MIPS + +#define PLTs_INIT_BY_HERE "_start" +#define E_ENTRY_NAME "_start" diff --git a/sysdeps/linux-gnu/mipsel/mipsel.h b/sysdeps/linux-gnu/mipsel/mipsel.h new file mode 100644 index 0000000..a63cafd --- /dev/null +++ b/sysdeps/linux-gnu/mipsel/mipsel.h @@ -0,0 +1,11 @@ +#ifndef MIPSEL_h +#define MIPSEL_h +// linux-2.4.30/arch/mips/kernel/ptrace.c for these offsets. +#define off_v0 2 +#define off_pc 64 +#define off_a0 4 +#define off_a3 7 +#define off_lr 31 +#define off_sp 29 + +#endif // MIPSEL_h diff --git a/sysdeps/linux-gnu/mipsel/plt.c b/sysdeps/linux-gnu/mipsel/plt.c new file mode 100644 index 0000000..003171b --- /dev/null +++ b/sysdeps/linux-gnu/mipsel/plt.c @@ -0,0 +1,72 @@ +#include <debug.h> +#include <gelf.h> +#include <sys/ptrace.h> +#include "common.h" + +/** + \addtogroup mipsel + @{ + */ + +/** + \param lte Structure containing link table entry information + \param ndx Index into .dynsym + \param rela Not used. + \return Address of GOT table entry + + MIPS ABI Supplement: + + DT_PLTGOT This member holds the address of the .got section. + + DT_MIPS_SYMTABNO This member holds the number of entries in the + .dynsym section. + + DT_MIPS_LOCAL_GOTNO This member holds the number of local global + offset table entries. + + DT_MIPS_GOTSYM This member holds the index of the first dyamic + symbol table entry that corresponds to an entry in the gobal offset + table. + + Called by read_elf when building the symbol table. + + */ +GElf_Addr +arch_plt_sym_val(struct ltelf *lte, size_t ndx, GElf_Rela * rela) { + debug(1,"plt_addr %x ndx %#x",lte->pltgot_addr, ndx); + return lte->pltgot_addr + + sizeof(void *) * (lte->mips_local_gotno + (ndx - lte->mips_gotsym)); +} +/** + \param proc The process to work on. + \param sym The library symbol. + \return What is at the got table address + + The return value should be the address to put the breakpoint at. + + On the mips the library_symbol.enter_addr is the .got addr for the + symbol and the breakpoint.addr is the actual breakpoint address. + + Other processors use a plt, the mips is "special" in that is uses + the .got for both function and data relocations. Prior to program + startup, return 0. + + \warning MIPS relocations are lazy. This means that the breakpoint + may move after the first call. Ltrace dictionary routines don't + have a delete and symbol is one to one with breakpoint, so if the + breakpoint changes I just add a new breakpoint for the new address. + */ +void * +sym2addr(Process *proc, struct library_symbol *sym) { + long ret; + if(!proc->pid){ + return 0; + } + ret=ptrace(PTRACE_PEEKTEXT, proc->pid, sym->enter_addr, 0); + if(ret==-1){ + ret =0; + } + return (void *)ret;; +} + +/**@}*/ diff --git a/sysdeps/linux-gnu/mipsel/ptrace.h b/sysdeps/linux-gnu/mipsel/ptrace.h new file mode 100644 index 0000000..c3cbcb6 --- /dev/null +++ b/sysdeps/linux-gnu/mipsel/ptrace.h @@ -0,0 +1 @@ +#include <sys/ptrace.h> diff --git a/sysdeps/linux-gnu/mipsel/regs.c b/sysdeps/linux-gnu/mipsel/regs.c new file mode 100644 index 0000000..a7a6de1 --- /dev/null +++ b/sysdeps/linux-gnu/mipsel/regs.c @@ -0,0 +1,76 @@ +#include "config.h" + +#include <stddef.h> +#include <sys/types.h> +#include <sys/ptrace.h> +#include <asm/ptrace.h> +#include <linux/user.h> + +#include "common.h" +#include "mipsel.h" + +#if (!defined(PTRACE_PEEKUSER) && defined(PTRACE_PEEKUSR)) +# define PTRACE_PEEKUSER PTRACE_PEEKUSR +#endif + +#if (!defined(PTRACE_POKEUSER) && defined(PTRACE_POKEUSR)) +# define PTRACE_POKEUSER PTRACE_POKEUSR +#endif + +/** + \addtogroup mipsel + @{ + */ + + +/** + \param proc The process to work on. + \return The current instruction pointer. + */ +void * +get_instruction_pointer(Process *proc) { + return (void *)ptrace(PTRACE_PEEKUSER, proc->pid, off_pc, 0); +} + +/** + \param proc The process to work on. + \param addr The address to set to. + + Called by \c continue_after_breakpoint(). + + \todo Our mips kernel ptrace doesn't support PTRACE_SINGLESTEP, so + we \c continue_process() after a breakpoint. Check if this is OK. + */ +void +set_instruction_pointer(Process *proc, void *addr) { + ptrace(PTRACE_POKEUSER, proc->pid, off_pc, addr); +} + +/** + \param proc The process to work on. + \return The current stack pointer. + */ +void * +get_stack_pointer(Process *proc) { + return (void *)ptrace(PTRACE_PEEKUSER, proc->pid, off_sp, 0); +} + +/** + \param proc The process to work on. + \param stack_pointer The current stack pointer for proc + \return The current return address. + + Called by \c handle_breakpoint(). + + Mips uses r31 for the return address, so the stack_pointer is + unused. + */ +void * +get_return_addr(Process *proc, void *stack_pointer) { + return (void *)ptrace(PTRACE_PEEKUSER, proc->pid, off_lr, 0); +} + +void +set_return_addr(Process *proc, void *addr) { + ptrace(PTRACE_POKEUSER, proc->pid, off_lr, addr); +} diff --git a/sysdeps/linux-gnu/mipsel/signalent.h b/sysdeps/linux-gnu/mipsel/signalent.h new file mode 100644 index 0000000..6bb1ff6 --- /dev/null +++ b/sysdeps/linux-gnu/mipsel/signalent.h @@ -0,0 +1,32 @@ +"SIG_0", /* 0 */ + "SIGHUP", // 1 /* Hangup (POSIX). */ + "SIGINT", // 2 /* Interrupt (ANSI). */ + "SIGQUIT", // 3 /* Quit (POSIX). */ + "SIGILL", // 4 /* Illegal instruction (ANSI). */ + "SIGTRAP", // 5 /* Trace trap (POSIX). */ + "SIGIOT", // 6 /* IOT trap (4.2 BSD). */ + "SIGEMT", // 7 + "SIGFPE", // 8 /* Floating-point exception (ANSI). */ + "SIGKILL", // 9 /* Kill, unblockable (POSIX). */ + "SIGBUS", // 10 /* BUS error (4.2 BSD). */ + "SIGSEGV", // 11 /* Segmentation violation (ANSI). */ + "SIGSYS", // 12 + "SIGPIPE", // 13 /* Broken pipe (POSIX). */ + "SIGALRM", // 14 /* Alarm clock (POSIX). */ + "SIGTERM", // 15 /* Termination (ANSI). */ + "SIGUSR1", // 16 /* User-defined signal 1 (POSIX). */ + "SIGUSR2", // 17 /* User-defined signal 2 (POSIX). */ + "SIGCHLD", // 18 /* Child status has changed (POSIX). */ + "SIGPWR", // 19 /* Power failure restart (System V). */ + "SIGWINCH", // 20 /* Window size change (4.3 BSD, Sun). */ + "SIGURG", // 21 /* Urgent condition on socket (4.2 BSD). */ + "SIGIO", // 22 /* I/O now possible (4.2 BSD). */ + "SIGSTOP", // 23 /* Stop, unblockable (POSIX). */ + "SIGTSTP", // 24 /* Keyboard stop (POSIX). */ + "SIGCONT", // 25 /* Continue (POSIX). */ + "SIGTTIN", // 26 /* Background read from tty (POSIX). */ + "SIGTTOU", // 27 /* Background write to tty (POSIX). */ + "SIGVTALRM", // 28 /* Virtual alarm clock (4.2 BSD). */ + "SIGPROF", // 29 /* Profiling alarm clock (4.2 BSD). */ + "SIGXCPU", // 30 /* CPU limit exceeded (4.2 BSD). */ + "SIGXFSZ", // 31 /* File size limit exceeded (4.2 BSD). */ diff --git a/sysdeps/linux-gnu/mipsel/syscallent.h b/sysdeps/linux-gnu/mipsel/syscallent.h new file mode 100644 index 0000000..bed43fe --- /dev/null +++ b/sysdeps/linux-gnu/mipsel/syscallent.h @@ -0,0 +1,241 @@ + "0", /*Linux + 0*/ + "exit", /*Linux + 1*/ + "fork", /*Linux + 2*/ + "read", /*Linux + 3*/ + "write", /*Linux + 4*/ + "open", /*Linux + 5*/ + "close", /*Linux + 6*/ + "waitpid", /*Linux + 7*/ + "creat", /*Linux + 8*/ + "link", /*Linux + 9*/ + "unlink", /*Linux + 10*/ + "execve", /*Linux + 11*/ + "chdir", /*Linux + 12*/ + "time", /*Linux + 13*/ + "mknod", /*Linux + 14*/ + "chmod", /*Linux + 15*/ + "lchown", /*Linux + 16*/ + "break", /*Linux + 17*/ + "unused18", /*Linux + 18*/ + "lseek", /*Linux + 19*/ + "getpid", /*Linux + 20*/ + "mount", /*Linux + 21*/ + "umount", /*Linux + 22*/ + "setuid", /*Linux + 23*/ + "getuid", /*Linux + 24*/ + "stime", /*Linux + 25*/ + "ptrace", /*Linux + 26*/ + "alarm", /*Linux + 27*/ + "unused28", /*Linux + 28*/ + "pause", /*Linux + 29*/ + "utime", /*Linux + 30*/ + "stty", /*Linux + 31*/ + "gtty", /*Linux + 32*/ + "access", /*Linux + 33*/ + "nice", /*Linux + 34*/ + "ftime", /*Linux + 35*/ + "sync", /*Linux + 36*/ + "kill", /*Linux + 37*/ + "rename", /*Linux + 38*/ + "mkdir", /*Linux + 39*/ + "rmdir", /*Linux + 40*/ + "dup", /*Linux + 41*/ + "pipe", /*Linux + 42*/ + "times", /*Linux + 43*/ + "prof", /*Linux + 44*/ + "brk", /*Linux + 45*/ + "setgid", /*Linux + 46*/ + "getgid", /*Linux + 47*/ + "signal", /*Linux + 48*/ + "geteuid", /*Linux + 49*/ + "getegid", /*Linux + 50*/ + "acct", /*Linux + 51*/ + "umount2", /*Linux + 52*/ + "lock", /*Linux + 53*/ + "ioctl", /*Linux + 54*/ + "fcntl", /*Linux + 55*/ + "mpx", /*Linux + 56*/ + "setpgid", /*Linux + 57*/ + "ulimit", /*Linux + 58*/ + "unused59", /*Linux + 59*/ + "umask", /*Linux + 60*/ + "chroot", /*Linux + 61*/ + "ustat", /*Linux + 62*/ + "dup2", /*Linux + 63*/ + "getppid", /*Linux + 64*/ + "getpgrp", /*Linux + 65*/ + "setsid", /*Linux + 66*/ + "sigaction", /*Linux + 67*/ + "sgetmask", /*Linux + 68*/ + "ssetmask", /*Linux + 69*/ + "setreuid", /*Linux + 70*/ + "setregid", /*Linux + 71*/ + "sigsuspend", /*Linux + 72*/ + "sigpending", /*Linux + 73*/ + "sethostname", /*Linux + 74*/ + "setrlimit", /*Linux + 75*/ + "getrlimit", /*Linux + 76*/ + "getrusage", /*Linux + 77*/ + "gettimeofday", /*Linux + 78*/ + "settimeofday", /*Linux + 79*/ + "getgroups", /*Linux + 80*/ + "setgroups", /*Linux + 81*/ + "reserved82", /*Linux + 82*/ + "symlink", /*Linux + 83*/ + "unused84", /*Linux + 84*/ + "readlink", /*Linux + 85*/ + "uselib", /*Linux + 86*/ + "swapon", /*Linux + 87*/ + "reboot", /*Linux + 88*/ + "readdir", /*Linux + 89*/ + "mmap", /*Linux + 90*/ + "munmap", /*Linux + 91*/ + "truncate", /*Linux + 92*/ + "ftruncate", /*Linux + 93*/ + "fchmod", /*Linux + 94*/ + "fchown", /*Linux + 95*/ + "getpriority", /*Linux + 96*/ + "setpriority", /*Linux + 97*/ + "profil", /*Linux + 98*/ + "statfs", /*Linux + 99*/ + "fstatfs", /*Linux + 100*/ + "ioperm", /*Linux + 101*/ + "socketcall", /*Linux + 102*/ + "syslog", /*Linux + 103*/ + "setitimer", /*Linux + 104*/ + "getitimer", /*Linux + 105*/ + "stat", /*Linux + 106*/ + "lstat", /*Linux + 107*/ + "fstat", /*Linux + 108*/ + "unused109", /*Linux + 109*/ + "iopl", /*Linux + 110*/ + "vhangup", /*Linux + 111*/ + "idle", /*Linux + 112*/ + "vm86", /*Linux + 113*/ + "wait4", /*Linux + 114*/ + "swapoff", /*Linux + 115*/ + "sysinfo", /*Linux + 116*/ + "ipc", /*Linux + 117*/ + "fsync", /*Linux + 118*/ + "sigreturn", /*Linux + 119*/ + "clone", /*Linux + 120*/ + "setdomainname", /*Linux + 121*/ + "uname", /*Linux + 122*/ + "modify_ldt", /*Linux + 123*/ + "adjtimex", /*Linux + 124*/ + "mprotect", /*Linux + 125*/ + "sigprocmask", /*Linux + 126*/ + "create_module", /*Linux + 127*/ + "init_module", /*Linux + 128*/ + "delete_module", /*Linux + 129*/ + "get_kernel_syms", /*Linux + 130*/ + "quotactl", /*Linux + 131*/ + "getpgid", /*Linux + 132*/ + "fchdir", /*Linux + 133*/ + "bdflush", /*Linux + 134*/ + "sysfs", /*Linux + 135*/ + "personality", /*Linux + 136*/ + "afs_syscall", /*Linux + 137*/ /* Syscall for Andrew File System */ + "setfsuid", /*Linux + 138*/ + "setfsgid", /*Linux + 139*/ + "_llseek", /*Linux + 140*/ + "getdents", /*Linux + 141*/ + "_newselect", /*Linux + 142*/ + "flock", /*Linux + 143*/ + "msync", /*Linux + 144*/ + "readv", /*Linux + 145*/ + "writev", /*Linux + 146*/ + "cacheflush", /*Linux + 147*/ + "cachectl", /*Linux + 148*/ + "sysmips", /*Linux + 149*/ + "unused150", /*Linux + 150*/ + "getsid", /*Linux + 151*/ + "fdatasync", /*Linux + 152*/ + "_sysctl", /*Linux + 153*/ + "mlock", /*Linux + 154*/ + "munlock", /*Linux + 155*/ + "mlockall", /*Linux + 156*/ + "munlockall", /*Linux + 157*/ + "sched_setparam", /*Linux + 158*/ + "sched_getparam", /*Linux + 159*/ + "sched_setscheduler", /*Linux + 160*/ + "sched_getscheduler", /*Linux + 161*/ + "sched_yield", /*Linux + 162*/ + "sched_get_priority_max", /*Linux + 163*/ + "sched_get_priority_min", /*Linux + 164*/ + "sched_rr_get_interval", /*Linux + 165*/ + "nanosleep", /*Linux + 166*/ + "mremap", /*Linux + 167*/ + "accept", /*Linux + 168*/ + "bind", /*Linux + 169*/ + "connect", /*Linux + 170*/ + "getpeername", /*Linux + 171*/ + "getsockname", /*Linux + 172*/ + "getsockopt", /*Linux + 173*/ + "listen", /*Linux + 174*/ + "recv", /*Linux + 175*/ + "recvfrom", /*Linux + 176*/ + "recvmsg", /*Linux + 177*/ + "send", /*Linux + 178*/ + "sendmsg", /*Linux + 179*/ + "sendto", /*Linux + 180*/ + "setsockopt", /*Linux + 181*/ + "shutdown", /*Linux + 182*/ + "socket", /*Linux + 183*/ + "socketpair", /*Linux + 184*/ + "setresuid", /*Linux + 185*/ + "getresuid", /*Linux + 186*/ + "query_module", /*Linux + 187*/ + "poll", /*Linux + 188*/ + "nfsservctl", /*Linux + 189*/ + "setresgid", /*Linux + 190*/ + "getresgid", /*Linux + 191*/ + "prctl", /*Linux + 192*/ + "rt_sigreturn", /*Linux + 193*/ + "rt_sigaction", /*Linux + 194*/ + "rt_sigprocmask", /*Linux + 195*/ + "rt_sigpending", /*Linux + 196*/ + "rt_sigtimedwait", /*Linux + 197*/ + "rt_sigqueueinfo", /*Linux + 198*/ + "rt_sigsuspend", /*Linux + 199*/ + "pread", /*Linux + 200*/ + "pwrite", /*Linux + 201*/ + "chown", /*Linux + 202*/ + "getcwd", /*Linux + 203*/ + "capget", /*Linux + 204*/ + "capset", /*Linux + 205*/ + "sigaltstack", /*Linux + 206*/ + "sendfile", /*Linux + 207*/ + "getpmsg", /*Linux + 208*/ + "putpmsg", /*Linux + 209*/ + "mmap2", /*Linux + 210*/ + "truncate64", /*Linux + 211*/ + "ftruncate64", /*Linux + 212*/ + "stat64", /*Linux + 213*/ + "lstat64", /*Linux + 214*/ + "fstat64", /*Linux + 215*/ + "pivot_root", /*Linux + 216*/ + "mincore", /*Linux + 217*/ + "madvise", /*Linux + 218*/ + "getdents64", /*Linux + 219*/ + "fcntl64", /*Linux + 220*/ + "security", /*Linux + 221*/ + "gettid", /*Linux + 222*/ + "readahead", /*Linux + 223*/ + "setxattr", /*Linux + 224*/ + "lsetxattr", /*Linux + 225*/ + "fsetxattr", /*Linux + 226*/ + "getxattr", /*Linux + 227*/ + "lgetxattr", /*Linux + 228*/ + "fgetxattr", /*Linux + 229*/ + "listxattr", /*Linux + 230*/ + "llistxattr", /*Linux + 231*/ + "flistxattr", /*Linux + 232*/ + "removexattr", /*Linux + 233*/ + "lremovexattr", /*Linux + 234*/ + "fremovexattr", /*Linux + 235*/ + "tkill", /*Linux + 236*/ + "sendfile64", /*Linux + 237*/ + "futex", /*Linux + 238*/ + "sched_setaffinity", /*Linux + 239*/ + "sched_getaffinity", /*Linux + 240*/ diff --git a/sysdeps/linux-gnu/mipsel/trace.c b/sysdeps/linux-gnu/mipsel/trace.c new file mode 100644 index 0000000..ff94930 --- /dev/null +++ b/sysdeps/linux-gnu/mipsel/trace.c @@ -0,0 +1,167 @@ +#include "config.h" + +#include <sys/types.h> +#include <sys/wait.h> +#include <signal.h> +#include <sys/ptrace.h> +#include <asm/ptrace.h> +#include "debug.h" +#include "common.h" +#include "mipsel.h" +#if (!defined(PTRACE_PEEKUSER) && defined(PTRACE_PEEKUSR)) +# define PTRACE_PEEKUSER PTRACE_PEEKUSR +#endif + +#if (!defined(PTRACE_POKEUSER) && defined(PTRACE_POKEUSR)) +# define PTRACE_POKEUSER PTRACE_POKEUSR +#endif + + +/** + \addtogroup mipsel Mipsel specific functions. + + These are the functions that it looks like I need to implement in + order to get ltrace to work on our target. + + @{ + */ + +/** + \param proc The process that had an event. + + Called by \c next_event() right after the return from wait. + + Most targets just return here. A couple use proc->arch_ptr for a + private data area. + */ +void +get_arch_dep(Process *proc) { +} + +/** + \param proc Process that had event. + \param status From \c wait() + \param sysnum 0-based syscall number. + \return 1 if syscall, 2 if sysret, 0 otherwise. + + Called by \c next_event() after the call to get_arch_dep() + + It seems that the ptrace call trips twice on a system call, once + just before the system call and once when it returns. Both times, + the pc points at the instruction just after the mipsel "syscall" + instruction. + + There are several possiblities for system call sets, each is offset + by a base from the others. On our system, it looks like the base + for the system calls is 4000. + */ +int +syscall_p(Process *proc, int status, int *sysnum) { + if (WIFSTOPPED(status) + && WSTOPSIG(status) == (SIGTRAP | proc->tracesysgood)) { + /* get the user's pc (plus 8) */ + long pc = (long)get_instruction_pointer(proc); + /* fetch the SWI instruction */ + int insn = ptrace(PTRACE_PEEKTEXT, proc->pid, pc - 4, 0); + int num = ptrace(PTRACE_PEEKTEXT, proc->pid, pc - 8, 0); + + /* + On a mipsel, syscall looks like: + 24040fa1 li v0, 0x0fa1 # 4001 --> _exit syscall + 0000000c syscall + */ + if(insn!=0x0000000c){ + return 0; + } + + *sysnum = (num & 0xFFFF) - 4000; + /* if it is a syscall, return 1 or 2 */ + if (proc->callstack_depth > 0 && + proc->callstack[proc->callstack_depth - 1].is_syscall && + proc->callstack[proc->callstack_depth - 1].c_un.syscall == *sysnum) { + return 2; + } + + if (*sysnum >= 0) { + return 1; + } + } + return 0; +} +/** + \param type Function/syscall call or return. + \param proc The process that had an event. + \param arg_num -1 for return value, + \return The argument to fetch. + + A couple of assumptions. + +- Type is LT_TOF_FUNCTIONR or LT_TOF_SYSCALLR if arg_num==-1. These + types are only used in calls for output_right(), which only uses -1 + for arg_num. +- Type is LT_TOF_FUNCTION or LT_TOF_SYSCALL for args 0...4. +- I'm only displaying the first 4 args (Registers a0..a3). Good + enough for now. + + Mipsel conventions seem to be: +- syscall parameters: r4...r9 +- syscall return: if(!a3){ return v0;} else{ errno=v0;return -1;} +- function call: r4..r7. Not sure how to get arg number 5. +- function return: v0 + +The argument registers are wiped by a call, so it is a mistake to ask +for arguments on a return. If ltrace does this, we will need to cache +arguments somewhere on the call. + +I'm not doing any floating point support here. + +*/ +long +gimme_arg(enum tof type, Process *proc, int arg_num, arg_type_info *info) { + long ret; + debug(2,"type %d arg %d",type,arg_num); + if (type == LT_TOF_FUNCTION || type == LT_TOF_SYSCALL){ + if(arg_num <4){ + ret=ptrace(PTRACE_PEEKUSER,proc->pid,off_a0+arg_num,0); + debug(2,"ret = %#lx",ret); + return ret; + } else { + // If we need this, I think we can look at [sp+16] for arg_num==4. + CP; + return 0; + } + } + if(arg_num>=0){ + fprintf(stderr,"args on return?"); + } + if(type == LT_TOF_FUNCTIONR) { + return ptrace(PTRACE_PEEKUSER,proc->pid,off_v0,0); + } + if (type == LT_TOF_SYSCALLR) { + unsigned a3=ptrace(PTRACE_PEEKUSER, proc->pid,off_a3,0); + unsigned v0=ptrace(PTRACE_PEEKUSER, proc->pid,off_v0,0); + if(!a3){ + return v0; + } + return -1; + } + fprintf(stderr, "gimme_arg called with wrong arguments\n"); + return 0; +} + +/** + \param type Type of call/return + \param proc Process to work with. + + Called by \c output_left(), which is called on a syscall or + function. + + The other architectures stub this out, but seems to be the place to + stash off the arguments on a call so we have them on the return. + +*/ +void +save_register_args(enum tof type, Process *proc) { +} + +/**@}*/ diff --git a/sysdeps/linux-gnu/mksignalent b/sysdeps/linux-gnu/mksignalent new file mode 100755 index 0000000..316d81f --- /dev/null +++ b/sysdeps/linux-gnu/mksignalent @@ -0,0 +1,33 @@ +#!/usr/bin/awk -f + +# hack expression to generate arch/signalent.h from <asm/signal.h> +# It reads from stdin and writes to stdout + +BEGIN { + max=0; +} + +{ + if (($1 ~ /^#define$/) && (!SIGNAL[$3]) && ($2 ~ /^SIG[A-Z]/) \ + && ($2 !~ /^SIGRTMIN$/) && ($2 !~ /^SIGRTMAX$/) && ($2 !~ /^SIGSTKSZ$/) \ + && ($3>=0) && ($3<=1000)) { + SIGNAL[$3]=$2; + if ($3 > max) { + max=$3; + } + } +} + +END { + for(i=0; i<=max; i++) { + if (!SIGNAL[i]) { + SIGNAL[i] = "SIG_" i; + } + pad = 16 - length(SIGNAL[i]); + if (pad<1) { + pad=1; + } + printf("\t\"%s\",%*s/* %d */\n", SIGNAL[i], pad, "", i); + } +} + diff --git a/sysdeps/linux-gnu/mksyscallent b/sysdeps/linux-gnu/mksyscallent new file mode 100755 index 0000000..e0c3ec7 --- /dev/null +++ b/sysdeps/linux-gnu/mksyscallent @@ -0,0 +1,45 @@ +#!/usr/bin/awk -f + +# hack expression to generate arch/syscallent.h from <asm/unistd.h> +# It reads from stdin and writes to stdout +# It should work OK on i386,m68k,arm,ia64 +# It does NOT work in mips, s390 +# It is untested in other architectures + +BEGIN { + max=0; + FS="[ \t\n()+]+"; +} + +{ + #debug + #printf("/%s/%s/%s/%s/\n", $1, $2, $3, $4); + if (($1 ~ /^#define$/) && ($2 ~ /^__NR_/)) { + #ia64 syscalls are > 1000 (lower for x86 compat) + if (($3>=0) && ($3<=2000)) { + SYSCALL[$3]=substr($2,6); + if ($3 > max) { + max=$3; + } + } else if (($3 ~ /^__NR_SYSCALL_BASE$/) && ($4>=0) && ($4<=1000)) { + SYSCALL[$4]=substr($2,6); + if ($4 > max) { + max=$4; + } + } + } +} + +END { + for(i=0; i<=max; i++) { + if (!SYSCALL[i]) { + SYSCALL[i] = i; + } + pad = 32 - length(SYSCALL[i]); + if (pad<1) { + pad=1; + } + printf("\t\"%s\",%*s/* %d */\n", SYSCALL[i], pad, "", i); + } +} + diff --git a/sysdeps/linux-gnu/mksyscallent_s390 b/sysdeps/linux-gnu/mksyscallent_s390 new file mode 100644 index 0000000..73416d9 --- /dev/null +++ b/sysdeps/linux-gnu/mksyscallent_s390 @@ -0,0 +1,38 @@ +#!/usr/bin/perl +# +# Generate syscall table for s390/s390x +# +# Use this on arch/s390/kernel/syscall.s after removing the first few +# nonsyscall lines. +# +# cat syscall.s | mksyscallent_s390 > syscalls31.h +# cat syscall.s | mksyscallent_s390 -x > syscalls64.h +# + +use Getopt::Std; +use integer; +getopts('x'); +$i = 0; +$s390x = 0; +$opt_x and $s390x = 1; + +while (<>) { + chomp; + + if ($s390x==1) { + s/^SYSCALL\([^,]*,//; + } else { + s/^SYSCALL\(//; + } + + s/,.*//; + s/^sys_//; + s/^s390[x]*_//; + s/_glue$//; + s/^ni_syscall.*/$i/i; + $len = 32 - length(); + $tab = $len / 8; + $space = $len % 8; + print " \"$_\"," ," " x $space , "\t" x $tab, " \/* $i \*\/\n"; + $i++; +} diff --git a/sysdeps/linux-gnu/ppc/Makefile b/sysdeps/linux-gnu/ppc/Makefile new file mode 100644 index 0000000..60d7531 --- /dev/null +++ b/sysdeps/linux-gnu/ppc/Makefile @@ -0,0 +1,10 @@ +OBJ = trace.o regs.o plt.o + +all: arch.o + +arch.o: $(OBJ) + $(CC) -nostdlib -r -o arch.o $(OBJ) + +clean: + $(RM) $(OBJ) arch.o + diff --git a/sysdeps/linux-gnu/ppc/arch.h b/sysdeps/linux-gnu/ppc/arch.h new file mode 100644 index 0000000..711b4a3 --- /dev/null +++ b/sysdeps/linux-gnu/ppc/arch.h @@ -0,0 +1,24 @@ +#define BREAKPOINT_VALUE { 0x7f, 0xe0, 0x00, 0x08 } +#define BREAKPOINT_LENGTH 4 +#define DECR_PC_AFTER_BREAK 0 + +#define LT_ELFCLASS ELFCLASS32 +#define LT_ELF_MACHINE EM_PPC + +#ifdef __powerpc64__ // Says 'ltrace' is 64 bits, says nothing about target. +#define LT_ELFCLASS2 ELFCLASS64 +#define LT_ELF_MACHINE2 EM_PPC64 +#define ARCH_SUPPORTS_OPD +#endif + +#define PLT_REINITALISATION_BP "_start" + +/* Start of arch-specific functions. */ +#define ARCH_HAVE_UMOVELONG + +#define PPC_NOP { 0x60, 0x00, 0x00, 0x00 } +#define PPC_NOP_LENGTH 4 + +#if (PPC_NOP_LENGTH != BREAKPOINT_LENGTH) +#error "Length of the breakpoint value not equal to the length of a nop instruction" +#endif diff --git a/sysdeps/linux-gnu/ppc/plt.c b/sysdeps/linux-gnu/ppc/plt.c new file mode 100644 index 0000000..31830fb --- /dev/null +++ b/sysdeps/linux-gnu/ppc/plt.c @@ -0,0 +1,54 @@ +#include <gelf.h> +#include "common.h" + +GElf_Addr +arch_plt_sym_val(struct ltelf *lte, size_t ndx, GElf_Rela * rela) { + return rela->r_offset; +} + +void * +sym2addr(Process *proc, struct library_symbol *sym) { + void *addr = sym->enter_addr; + long pt_ret; + + debug(3, 0); + + if (sym->plt_type != LS_TOPLT_POINT) { + return addr; + } + + if (proc->pid == 0) { + return 0; + } + + if (options.debug >= 3) { + xinfdump(proc->pid, (void *)(((long)addr-32)&0xfffffff0), + sizeof(void*)*8); + } + + // On a PowerPC-64 system, a plt is three 64-bit words: the first is the + // 64-bit address of the routine. Before the PLT has been initialized, + // this will be 0x0. In fact, the symbol table won't have the plt's + // address even. Ater the PLT has been initialized, but before it has + // been resolved, the first word will be the address of the function in + // the dynamic linker that will reslove the PLT. After the PLT is + // resolved, this will will be the address of the routine whose symbol + // is in the symbol table. + + // On a PowerPC-32 system, there are two types of PLTs: secure (new) and + // non-secure (old). For the secure case, the PLT is simply a pointer + // and we can treat it much as we do for the PowerPC-64 case. For the + // non-secure case, the PLT is executable code and we can put the + // break-point right in the PLT. + + pt_ret = ptrace(PTRACE_PEEKTEXT, proc->pid, addr, 0); + + if (proc->mask_32bit) { + // Assume big-endian. + addr = (void *)((pt_ret >> 32) & 0xffffffff); + } else { + addr = (void *)pt_ret; + } + + return addr; +} diff --git a/sysdeps/linux-gnu/ppc/ptrace.h b/sysdeps/linux-gnu/ppc/ptrace.h new file mode 100644 index 0000000..c3cbcb6 --- /dev/null +++ b/sysdeps/linux-gnu/ppc/ptrace.h @@ -0,0 +1 @@ +#include <sys/ptrace.h> diff --git a/sysdeps/linux-gnu/ppc/regs.c b/sysdeps/linux-gnu/ppc/regs.c new file mode 100644 index 0000000..eca58ff --- /dev/null +++ b/sysdeps/linux-gnu/ppc/regs.c @@ -0,0 +1,47 @@ +#include "config.h" + +#include <sys/types.h> +#include <sys/ptrace.h> +#include <asm/ptrace.h> + +#include "common.h" + +#if (!defined(PTRACE_PEEKUSER) && defined(PTRACE_PEEKUSR)) +# define PTRACE_PEEKUSER PTRACE_PEEKUSR +#endif + +#if (!defined(PTRACE_POKEUSER) && defined(PTRACE_POKEUSR)) +# define PTRACE_POKEUSER PTRACE_POKEUSR +#endif + +void * +get_instruction_pointer(Process *proc) { + return (void *)ptrace(PTRACE_PEEKUSER, proc->pid, sizeof(long)*PT_NIP, 0); +} + +void +set_instruction_pointer(Process *proc, void *addr) { + ptrace(PTRACE_POKEUSER, proc->pid, sizeof(long)*PT_NIP, addr); +} + +void * +get_stack_pointer(Process *proc) { + return (void *)ptrace(PTRACE_PEEKUSER, proc->pid, sizeof(long)*PT_R1, 0); +} + +void * +get_return_addr(Process *proc, void *stack_pointer) { + return (void *)ptrace(PTRACE_PEEKUSER, proc->pid, sizeof(long)*PT_LNK, 0); +} + +void +set_return_addr(Process *proc, void *addr) { + ptrace(PTRACE_POKEUSER, proc->pid, sizeof(long)*PT_LNK, addr); +} + +/* Grab the value of CTR registers. */ +void * +get_count_register (Process *proc) { + return (void *) ptrace (PTRACE_PEEKUSER, proc->pid, + sizeof (long) * PT_CTR, 0); +} diff --git a/sysdeps/linux-gnu/ppc/signalent.h b/sysdeps/linux-gnu/ppc/signalent.h new file mode 100644 index 0000000..d58a36c --- /dev/null +++ b/sysdeps/linux-gnu/ppc/signalent.h @@ -0,0 +1,32 @@ +"SIG_0", /* 0 */ + "SIGHUP", /* 1 */ + "SIGINT", /* 2 */ + "SIGQUIT", /* 3 */ + "SIGILL", /* 4 */ + "SIGTRAP", /* 5 */ + "SIGABRT", /* 6 */ + "SIGBUS", /* 7 */ + "SIGFPE", /* 8 */ + "SIGKILL", /* 9 */ + "SIGUSR1", /* 10 */ + "SIGSEGV", /* 11 */ + "SIGUSR2", /* 12 */ + "SIGPIPE", /* 13 */ + "SIGALRM", /* 14 */ + "SIGTERM", /* 15 */ + "SIGSTKFLT", /* 16 */ + "SIGCHLD", /* 17 */ + "SIGCONT", /* 18 */ + "SIGSTOP", /* 19 */ + "SIGTSTP", /* 20 */ + "SIGTTIN", /* 21 */ + "SIGTTOU", /* 22 */ + "SIGURG", /* 23 */ + "SIGXCPU", /* 24 */ + "SIGXFSZ", /* 25 */ + "SIGVTALRM", /* 26 */ + "SIGPROF", /* 27 */ + "SIGWINCH", /* 28 */ + "SIGIO", /* 29 */ + "SIGPWR", /* 30 */ + "SIGSYS", /* 31 */ diff --git a/sysdeps/linux-gnu/ppc/syscallent.h b/sysdeps/linux-gnu/ppc/syscallent.h new file mode 100644 index 0000000..5ce5739 --- /dev/null +++ b/sysdeps/linux-gnu/ppc/syscallent.h @@ -0,0 +1,272 @@ +"0", /* 0 */ + "exit", /* 1 */ + "fork", /* 2 */ + "read", /* 3 */ + "write", /* 4 */ + "open", /* 5 */ + "close", /* 6 */ + "waitpid", /* 7 */ + "creat", /* 8 */ + "link", /* 9 */ + "unlink", /* 10 */ + "execve", /* 11 */ + "chdir", /* 12 */ + "time", /* 13 */ + "mknod", /* 14 */ + "chmod", /* 15 */ + "lchown", /* 16 */ + "break", /* 17 */ + "oldstat", /* 18 */ + "lseek", /* 19 */ + "getpid", /* 20 */ + "mount", /* 21 */ + "umount", /* 22 */ + "setuid", /* 23 */ + "getuid", /* 24 */ + "stime", /* 25 */ + "ptrace", /* 26 */ + "alarm", /* 27 */ + "oldfstat", /* 28 */ + "pause", /* 29 */ + "utime", /* 30 */ + "stty", /* 31 */ + "gtty", /* 32 */ + "access", /* 33 */ + "nice", /* 34 */ + "ftime", /* 35 */ + "sync", /* 36 */ + "kill", /* 37 */ + "rename", /* 38 */ + "mkdir", /* 39 */ + "rmdir", /* 40 */ + "dup", /* 41 */ + "pipe", /* 42 */ + "times", /* 43 */ + "prof", /* 44 */ + "brk", /* 45 */ + "setgid", /* 46 */ + "getgid", /* 47 */ + "signal", /* 48 */ + "geteuid", /* 49 */ + "getegid", /* 50 */ + "acct", /* 51 */ + "umount2", /* 52 */ + "lock", /* 53 */ + "ioctl", /* 54 */ + "fcntl", /* 55 */ + "mpx", /* 56 */ + "setpgid", /* 57 */ + "ulimit", /* 58 */ + "oldolduname", /* 59 */ + "umask", /* 60 */ + "chroot", /* 61 */ + "ustat", /* 62 */ + "dup2", /* 63 */ + "getppid", /* 64 */ + "getpgrp", /* 65 */ + "setsid", /* 66 */ + "sigaction", /* 67 */ + "sgetmask", /* 68 */ + "ssetmask", /* 69 */ + "setreuid", /* 70 */ + "setregid", /* 71 */ + "sigsuspend", /* 72 */ + "sigpending", /* 73 */ + "sethostname", /* 74 */ + "setrlimit", /* 75 */ + "getrlimit", /* 76 */ + "getrusage", /* 77 */ + "gettimeofday", /* 78 */ + "settimeofday", /* 79 */ + "getgroups", /* 80 */ + "setgroups", /* 81 */ + "select", /* 82 */ + "symlink", /* 83 */ + "oldlstat", /* 84 */ + "readlink", /* 85 */ + "uselib", /* 86 */ + "swapon", /* 87 */ + "reboot", /* 88 */ + "readdir", /* 89 */ + "mmap", /* 90 */ + "munmap", /* 91 */ + "truncate", /* 92 */ + "ftruncate", /* 93 */ + "fchmod", /* 94 */ + "fchown", /* 95 */ + "getpriority", /* 96 */ + "setpriority", /* 97 */ + "profil", /* 98 */ + "statfs", /* 99 */ + "fstatfs", /* 100 */ + "ioperm", /* 101 */ + "socketcall", /* 102 */ + "syslog", /* 103 */ + "setitimer", /* 104 */ + "getitimer", /* 105 */ + "stat", /* 106 */ + "lstat", /* 107 */ + "fstat", /* 108 */ + "olduname", /* 109 */ + "iopl", /* 110 */ + "vhangup", /* 111 */ + "idle", /* 112 */ + "vm86", /* 113 */ + "wait4", /* 114 */ + "swapoff", /* 115 */ + "sysinfo", /* 116 */ + "ipc", /* 117 */ + "fsync", /* 118 */ + "sigreturn", /* 119 */ + "clone", /* 120 */ + "setdomainname", /* 121 */ + "uname", /* 122 */ + "modify_ldt", /* 123 */ + "adjtimex", /* 124 */ + "mprotect", /* 125 */ + "sigprocmask", /* 126 */ + "create_module", /* 127 */ + "init_module", /* 128 */ + "delete_module", /* 129 */ + "get_kernel_syms", /* 130 */ + "quotactl", /* 131 */ + "getpgid", /* 132 */ + "fchdir", /* 133 */ + "bdflush", /* 134 */ + "sysfs", /* 135 */ + "personality", /* 136 */ + "afs_syscall", /* 137 */ + "setfsuid", /* 138 */ + "setfsgid", /* 139 */ + "_llseek", /* 140 */ + "getdents", /* 141 */ + "_newselect", /* 142 */ + "flock", /* 143 */ + "msync", /* 144 */ + "readv", /* 145 */ + "writev", /* 146 */ + "getsid", /* 147 */ + "fdatasync", /* 148 */ + "_sysctl", /* 149 */ + "mlock", /* 150 */ + "munlock", /* 151 */ + "mlockall", /* 152 */ + "munlockall", /* 153 */ + "sched_setparam", /* 154 */ + "sched_getparam", /* 155 */ + "sched_setscheduler", /* 156 */ + "sched_getscheduler", /* 157 */ + "sched_yield", /* 158 */ + "sched_get_priority_max", /* 159 */ + "sched_get_priority_min", /* 160 */ + "sched_rr_get_interval", /* 161 */ + "nanosleep", /* 162 */ + "mremap", /* 163 */ + "setresuid", /* 164 */ + "getresuid", /* 165 */ + "query_module", /* 166 */ + "poll", /* 167 */ + "nfsservctl", /* 168 */ + "setresgid", /* 169 */ + "getresgid", /* 170 */ + "prctl", /* 171 */ + "rt_sigreturn", /* 172 */ + "rt_sigaction", /* 173 */ + "rt_sigprocmask", /* 174 */ + "rt_sigpending", /* 175 */ + "rt_sigtimedwait", /* 176 */ + "rt_sigqueueinfo", /* 177 */ + "rt_sigsuspend", /* 178 */ + "pread", /* 179 */ + "pwrite", /* 180 */ + "chown", /* 181 */ + "getcwd", /* 182 */ + "capget", /* 183 */ + "capset", /* 184 */ + "sigaltstack", /* 185 */ + "sendfile", /* 186 */ + "getpmsg", /* 187 */ + "putpmsg", /* 188 */ + "vfork", /* 189 */ + "ugetrlimit", /* 190 */ + "readahead", /* 191 */ + "mmap2", /* 192 */ + "truncate64", /* 193 */ + "ftruncate64", /* 194 */ + "stat64", /* 195 */ + "lstat64", /* 196 */ + "fstat64", /* 197 */ + "pciconfig_read", /* 198 */ + "pciconfig_write", /* 199 */ + "pciconfig_iobase", /* 200 */ + "multiplexer", /* 201 */ + "getdents64", /* 202 */ + "pivot_root", /* 203 */ + "fcntl64", /* 204 */ + "madvise", /* 205 */ + "mincore", /* 206 */ + "gettid", /* 207 */ + "tkill", /* 208 */ + "setxattr", /* 209 */ + "lsetxattr", /* 210 */ + "fsetxattr", /* 211 */ + "getxattr", /* 212 */ + "lgetxattr", /* 213 */ + "fgetxattr", /* 214 */ + "listxattr", /* 215 */ + "llistxattr", /* 216 */ + "flistxattr", /* 217 */ + "removexattr", /* 218 */ + "lremovexattr", /* 219 */ + "fremovexattr", /* 220 */ + "futex", /* 221 */ + "sched_setaffinity", /* 222 */ + "sched_getaffinity", /* 223 */ + "224", /* 224 */ + "tuxcall", /* 225 */ + "sendfile64", /* 226 */ + "io_setup", /* 227 */ + "io_destroy", /* 228 */ + "io_getevents", /* 229 */ + "io_submit", /* 230 */ + "io_cancel", /* 231 */ + "set_tid_address", /* 232 */ + "fadvise64", /* 233 */ + "exit_group", /* 234 */ + "lookup_dcookie", /* 235 */ + "epoll_create", /* 236 */ + "epoll_ctl", /* 237 */ + "epoll_wait", /* 238 */ + "remap_file_pages", /* 239 */ + "timer_create", /* 240 */ + "timer_settime", /* 241 */ + "timer_gettime", /* 242 */ + "timer_getoverrun", /* 243 */ + "timer_delete", /* 244 */ + "clock_settime", /* 245 */ + "clock_gettime", /* 246 */ + "clock_getres", /* 247 */ + "clock_nanosleep", /* 248 */ + "swapcontext", /* 249 */ + "tgkill", /* 250 */ + "utimes", /* 251 */ + "statfs64", /* 252 */ + "fstatfs64", /* 253 */ + "fadvise64_64", /* 254 */ + "rtas", /* 255 */ + "mq_open", /* 262 */ + "mq_unlink", /* 263 */ + "mq_timedsend", /* 264 */ + "mq_timedreceive", /* 265 */ + "mq_notify", /* 266 */ + "mq_getsetattr", /* 267 */ + "kexec_load", /* 268 */ + "add_key", /* 269 */ + "request_key", /* 270 */ + "keyctl", /* 271 */ + "waitid", /* 272 */ + "ioprio_set", /* 273 */ + "ioprio_get", /* 274 */ + "inotify_init", /* 275 */ + "inotify_add_watch", /* 276 */ + "inotify_rm_watch", /* 277 */ diff --git a/sysdeps/linux-gnu/ppc/trace.c b/sysdeps/linux-gnu/ppc/trace.c new file mode 100644 index 0000000..81fb71d --- /dev/null +++ b/sysdeps/linux-gnu/ppc/trace.c @@ -0,0 +1,155 @@ +#include "config.h" + +#include <sys/types.h> +#include <sys/wait.h> +#include <signal.h> +#include <sys/ptrace.h> +#include <asm/ptrace.h> +#include <elf.h> +#include <errno.h> + +#include "common.h" + +#if (!defined(PTRACE_PEEKUSER) && defined(PTRACE_PEEKUSR)) +# define PTRACE_PEEKUSER PTRACE_PEEKUSR +#endif + +#if (!defined(PTRACE_POKEUSER) && defined(PTRACE_POKEUSR)) +# define PTRACE_POKEUSER PTRACE_POKEUSR +#endif + +void +get_arch_dep(Process *proc) { +#ifdef __powerpc64__ + if (proc->arch_ptr) + return; + proc->mask_32bit = (proc->e_machine == EM_PPC); + proc->arch_ptr = (void *)1; +#endif +} + +/* Returns 1 if syscall, 2 if sysret, 0 otherwise. */ +#define SYSCALL_INSN 0x44000002 + +unsigned int greg = 3; +unsigned int freg = 1; +unsigned int vreg = 2; + +int +syscall_p(Process *proc, int status, int *sysnum) { + if (WIFSTOPPED(status) + && WSTOPSIG(status) == (SIGTRAP | proc->tracesysgood)) { + long pc = (long)get_instruction_pointer(proc); + int insn = + (int)ptrace(PTRACE_PEEKTEXT, proc->pid, pc - sizeof(long), + 0); + + if (insn == SYSCALL_INSN) { + *sysnum = + (int)ptrace(PTRACE_PEEKUSER, proc->pid, + sizeof(long) * PT_R0, 0); + if (proc->callstack_depth > 0 && + proc->callstack[proc->callstack_depth - 1].is_syscall && + proc->callstack[proc->callstack_depth - 1].c_un.syscall == *sysnum) { + return 2; + } + return 1; + } + } + return 0; +} + +/* Grab functions arguments based on the PPC64 ABI. */ +long +gimme_arg(enum tof type, Process *proc, int arg_num, arg_type_info *info) { + long data; + + if (type == LT_TOF_FUNCTIONR) { + if (info->type == ARGTYPE_FLOAT || info->type == ARGTYPE_DOUBLE) + return ptrace (PTRACE_PEEKUSER, proc->pid, + sizeof (long) * (PT_FPR0 + 1), 0); + else + return ptrace (PTRACE_PEEKUSER, proc->pid, + sizeof (long) * PT_R3, 0); + } + + /* Check if we're entering a new function call to list parameters. If + so, initialize the register control variables to keep track of where + the parameters were stored. */ + if (type == LT_TOF_FUNCTION && arg_num == 0) { + /* Initialize the set of registrers for parameter passing. */ + greg = 3; + freg = 1; + vreg = 2; + } + + if (info->type == ARGTYPE_FLOAT || info->type == ARGTYPE_DOUBLE) { + if (freg <= 13 || (proc->mask_32bit && freg <= 8)) { + data = ptrace (PTRACE_PEEKUSER, proc->pid, + sizeof (long) * (PT_FPR0 + freg), 0); + + if (info->type == ARGTYPE_FLOAT) { + /* float values passed in FP registers are automatically + promoted to double. We need to convert it back to float + before printing. */ + union { long val; float fval; double dval; } cvt; + cvt.val = data; + cvt.fval = (float) cvt.dval; + data = cvt.val; + } + + freg++; + greg++; + + return data; + } + } + else if (greg <= 10) { + data = ptrace (PTRACE_PEEKUSER, proc->pid, + sizeof (long) * greg, 0); + greg++; + + return data; + } + else + return ptrace (PTRACE_PEEKDATA, proc->pid, + proc->stack_pointer + sizeof (long) * + (arg_num - 8), 0); + + return 0; +} + +void +save_register_args(enum tof type, Process *proc) { +} + +/* Read a single long from the process's memory address 'addr'. */ +int +arch_umovelong (Process *proc, void *addr, long *result, arg_type_info *info) { + long pointed_to; + + errno = 0; + + pointed_to = ptrace (PTRACE_PEEKTEXT, proc->pid, addr, 0); + + if (pointed_to == -1 && errno) + return -errno; + + /* Since int's are 4-bytes (long is 8-bytes) in length for ppc64, we + need to shift the long values returned by ptrace to end up with + the correct value. */ + + if (info) { + if (info->type == ARGTYPE_INT || (proc->mask_32bit && (info->type == ARGTYPE_POINTER + || info->type == ARGTYPE_STRING))) { + pointed_to = pointed_to >> 32; + + /* Make sure we have nothing in the upper word so we can + do a explicit cast from long to int later in the code. */ + pointed_to &= 0x00000000ffffffff; + } + } + + *result = pointed_to; + return 0; +} diff --git a/sysdeps/linux-gnu/proc.c b/sysdeps/linux-gnu/proc.c new file mode 100644 index 0000000..4251c1d --- /dev/null +++ b/sysdeps/linux-gnu/proc.c @@ -0,0 +1,36 @@ +#include "config.h" + +#include <sys/types.h> +#include <stdio.h> +#include <string.h> +#include <signal.h> +#include <unistd.h> + +/* /proc/pid doesn't exist just after the fork, and sometimes `ltrace' + * couldn't open it to find the executable. So it may be necessary to + * have a bit delay + */ + +#define MAX_DELAY 100000 /* 100000 microseconds = 0.1 seconds */ + +/* + * Returns a (malloc'd) file name corresponding to a running pid + */ +char * +pid2name(pid_t pid) { + char proc_exe[1024]; + + if (!kill(pid, 0)) { + int delay = 0; + + sprintf(proc_exe, "/proc/%d/exe", pid); + + while (delay < MAX_DELAY) { + if (!access(proc_exe, F_OK)) { + return strdup(proc_exe); + } + delay += 1000; /* 1 milisecond */ + } + } + return NULL; +} diff --git a/sysdeps/linux-gnu/s390/Makefile b/sysdeps/linux-gnu/s390/Makefile new file mode 100644 index 0000000..cea1b45 --- /dev/null +++ b/sysdeps/linux-gnu/s390/Makefile @@ -0,0 +1,13 @@ +# +# S/390 version +# Copyright (C) 2001 IBM Poughkeepsie, IBM Corporation +# +OBJ = trace.o regs.o plt.o + +all: arch.o + +arch.o: $(OBJ) + $(CC) -nostdlib -r -o arch.o $(OBJ) + +clean: + $(RM) $(OBJ) arch.o diff --git a/sysdeps/linux-gnu/s390/arch.h b/sysdeps/linux-gnu/s390/arch.h new file mode 100644 index 0000000..03690b8 --- /dev/null +++ b/sysdeps/linux-gnu/s390/arch.h @@ -0,0 +1,18 @@ +/* +** S/390 version +** (C) Copyright 2001 IBM Poughkeepsie, IBM Corporation +*/ + +#define BREAKPOINT_VALUE { 0x00, 0x01 } +#define BREAKPOINT_LENGTH 2 +#define DECR_PC_AFTER_BREAK 2 + +#ifdef __s390x__ +#define LT_ELFCLASS ELFCLASS64 +#define LT_ELF_MACHINE EM_S390 +#define LT_ELFCLASS2 ELFCLASS32 +#define LT_ELF_MACHINE2 EM_S390 +#else +#define LT_ELFCLASS ELFCLASS32 +#define LT_ELF_MACHINE EM_S390 +#endif diff --git a/sysdeps/linux-gnu/s390/plt.c b/sysdeps/linux-gnu/s390/plt.c new file mode 100644 index 0000000..85a1dd1 --- /dev/null +++ b/sysdeps/linux-gnu/s390/plt.c @@ -0,0 +1,12 @@ +#include <gelf.h> +#include "common.h" + +GElf_Addr +arch_plt_sym_val(struct ltelf *lte, size_t ndx, GElf_Rela * rela) { + return lte->plt_addr + (ndx + 1) * 32; +} + +void * +sym2addr(Process *proc, struct library_symbol *sym) { + return sym->enter_addr; +} diff --git a/sysdeps/linux-gnu/s390/ptrace.h b/sysdeps/linux-gnu/s390/ptrace.h new file mode 100644 index 0000000..c3cbcb6 --- /dev/null +++ b/sysdeps/linux-gnu/s390/ptrace.h @@ -0,0 +1 @@ +#include <sys/ptrace.h> diff --git a/sysdeps/linux-gnu/s390/regs.c b/sysdeps/linux-gnu/s390/regs.c new file mode 100644 index 0000000..169893e --- /dev/null +++ b/sysdeps/linux-gnu/s390/regs.c @@ -0,0 +1,75 @@ +/* +** S/390 version +** Copyright (C) 2001 IBM Poughkeepsie, IBM Corporation +*/ + +#include "config.h" + +#include <sys/types.h> +#include <sys/ptrace.h> +#include <asm/ptrace.h> + +#include "common.h" + +#if (!defined(PTRACE_PEEKUSER) && defined(PTRACE_PEEKUSR)) +# define PTRACE_PEEKUSER PTRACE_PEEKUSR +#endif + +#if (!defined(PTRACE_POKEUSER) && defined(PTRACE_POKEUSR)) +# define PTRACE_POKEUSER PTRACE_POKEUSR +#endif + +#ifdef __s390x__ +#define PSW_MASK 0xffffffffffffffff +#define PSW_MASK31 0x7fffffff +#else +#define PSW_MASK 0x7fffffff +#endif + +void * +get_instruction_pointer(Process *proc) { + long ret = ptrace(PTRACE_PEEKUSER, proc->pid, PT_PSWADDR, 0) & PSW_MASK; +#ifdef __s390x__ + if (proc->mask_32bit) + ret &= PSW_MASK31; +#endif + return (void *)ret; +} + +void +set_instruction_pointer(Process *proc, void *addr) { +#ifdef __s390x__ + if (proc->mask_32bit) + addr = (void *)((long)addr & PSW_MASK31); +#endif + ptrace(PTRACE_POKEUSER, proc->pid, PT_PSWADDR, addr); +} + +void * +get_stack_pointer(Process *proc) { + long ret = ptrace(PTRACE_PEEKUSER, proc->pid, PT_GPR15, 0) & PSW_MASK; +#ifdef __s390x__ + if (proc->mask_32bit) + ret &= PSW_MASK31; +#endif + return (void *)ret; +} + +void * +get_return_addr(Process *proc, void *stack_pointer) { + long ret = ptrace(PTRACE_PEEKUSER, proc->pid, PT_GPR14, 0) & PSW_MASK; +#ifdef __s390x__ + if (proc->mask_32bit) + ret &= PSW_MASK31; +#endif + return (void *)ret; +} + +void +set_return_addr(Process *proc, void *addr) { +#ifdef __s390x__ + if (proc->mask_32bit) + addr = (void *)((long)addr & PSW_MASK31); +#endif + ptrace(PTRACE_POKEUSER, proc->pid, PT_GPR14, addr); +} diff --git a/sysdeps/linux-gnu/s390/signalent.h b/sysdeps/linux-gnu/s390/signalent.h new file mode 100644 index 0000000..35b74f1 --- /dev/null +++ b/sysdeps/linux-gnu/s390/signalent.h @@ -0,0 +1,33 @@ +"SIG_0", /* 0 */ + "SIGHUP", /* 1 */ + "SIGINT", /* 2 */ + "SIGQUIT", /* 3 */ + "SIGILL", /* 4 */ + "SIGTRAP", /* 5 */ + "SIGABRT", /* 6 */ + "SIGBUS", /* 7 */ + "SIGFPE", /* 8 */ + "SIGKILL", /* 9 */ + "SIGUSR1", /* 10 */ + "SIGSEGV", /* 11 */ + "SIGUSR2", /* 12 */ + "SIGPIPE", /* 13 */ + "SIGALRM", /* 14 */ + "SIGTERM", /* 15 */ + "SIGSTKFLT", /* 16 */ + "SIGCHLD", /* 17 */ + "SIGCONT", /* 18 */ + "SIGSTOP", /* 19 */ + "SIGTSTP", /* 20 */ + "SIGTTIN", /* 21 */ + "SIGTTOU", /* 22 */ + "SIGURG", /* 23 */ + "SIGXCPU", /* 24 */ + "SIGXFSZ", /* 25 */ + "SIGVTALRM", /* 26 */ + "SIGPROF", /* 27 */ + "SIGWINCH", /* 28 */ + "SIGIO", /* 29 */ + "SIGPWR", /* 30 */ + "SIGUNUSED", /* 31 */ + "SIGRTMIN", /* 32 */ diff --git a/sysdeps/linux-gnu/s390/signalent1.h b/sysdeps/linux-gnu/s390/signalent1.h new file mode 100644 index 0000000..b5b6ca8 --- /dev/null +++ b/sysdeps/linux-gnu/s390/signalent1.h @@ -0,0 +1 @@ +#include "s390/signalent.h" diff --git a/sysdeps/linux-gnu/s390/syscallent.h b/sysdeps/linux-gnu/s390/syscallent.h new file mode 100644 index 0000000..5a35d93 --- /dev/null +++ b/sysdeps/linux-gnu/s390/syscallent.h @@ -0,0 +1,5 @@ +#ifdef __s390x__ +#include "s390/syscalls64.h" +#else +#include "s390/syscalls31.h" +#endif diff --git a/sysdeps/linux-gnu/s390/syscallent1.h b/sysdeps/linux-gnu/s390/syscallent1.h new file mode 100644 index 0000000..c9fdc81 --- /dev/null +++ b/sysdeps/linux-gnu/s390/syscallent1.h @@ -0,0 +1 @@ +#include "s390/syscalls31.h" diff --git a/sysdeps/linux-gnu/s390/syscalls31.h b/sysdeps/linux-gnu/s390/syscalls31.h new file mode 100644 index 0000000..42631eb --- /dev/null +++ b/sysdeps/linux-gnu/s390/syscalls31.h @@ -0,0 +1,310 @@ + "0", /* 0 */ + "exit", /* 1 */ + "fork", /* 2 */ + "read", /* 3 */ + "write", /* 4 */ + "open", /* 5 */ + "close", /* 6 */ + "restart_syscall", /* 7 */ + "creat", /* 8 */ + "link", /* 9 */ + "unlink", /* 10 */ + "execve", /* 11 */ + "chdir", /* 12 */ + "time", /* 13 */ + "mknod", /* 14 */ + "chmod", /* 15 */ + "lchown16", /* 16 */ + "17", /* 17 */ + "18", /* 18 */ + "lseek", /* 19 */ + "getpid", /* 20 */ + "mount", /* 21 */ + "oldumount", /* 22 */ + "setuid16", /* 23 */ + "getuid16", /* 24 */ + "stime", /* 25 */ + "ptrace", /* 26 */ + "alarm", /* 27 */ + "28", /* 28 */ + "pause", /* 29 */ + "utime", /* 30 */ + "31", /* 31 */ + "32", /* 32 */ + "access", /* 33 */ + "nice", /* 34 */ + "35", /* 35 */ + "sync", /* 36 */ + "kill", /* 37 */ + "rename", /* 38 */ + "mkdir", /* 39 */ + "rmdir", /* 40 */ + "dup", /* 41 */ + "pipe", /* 42 */ + "times", /* 43 */ + "44", /* 44 */ + "brk", /* 45 */ + "setgid16", /* 46 */ + "getgid16", /* 47 */ + "signal", /* 48 */ + "geteuid16", /* 49 */ + "getegid16", /* 50 */ + "acct", /* 51 */ + "umount", /* 52 */ + "53", /* 53 */ + "ioctl", /* 54 */ + "fcntl", /* 55 */ + "56", /* 56 */ + "setpgid", /* 57 */ + "58", /* 58 */ + "59", /* 59 */ + "umask", /* 60 */ + "chroot", /* 61 */ + "ustat", /* 62 */ + "dup2", /* 63 */ + "getppid", /* 64 */ + "getpgrp", /* 65 */ + "setsid", /* 66 */ + "sigaction", /* 67 */ + "68", /* 68 */ + "69", /* 69 */ + "setreuid16", /* 70 */ + "setregid16", /* 71 */ + "sigsuspend", /* 72 */ + "sigpending", /* 73 */ + "sethostname", /* 74 */ + "setrlimit", /* 75 */ + "old_getrlimit", /* 76 */ + "getrusage", /* 77 */ + "gettimeofday", /* 78 */ + "settimeofday", /* 79 */ + "getgroups16", /* 80 */ + "setgroups16", /* 81 */ + "82", /* 82 */ + "symlink", /* 83 */ + "84", /* 84 */ + "readlink", /* 85 */ + "uselib", /* 86 */ + "swapon", /* 87 */ + "reboot", /* 88 */ + "89", /* 89 */ + "old_mmap", /* 90 */ + "munmap", /* 91 */ + "truncate", /* 92 */ + "ftruncate", /* 93 */ + "fchmod", /* 94 */ + "fchown16", /* 95 */ + "getpriority", /* 96 */ + "setpriority", /* 97 */ + "98", /* 98 */ + "statfs", /* 99 */ + "fstatfs", /* 100 */ + "101", /* 101 */ + "socketcall", /* 102 */ + "syslog", /* 103 */ + "setitimer", /* 104 */ + "getitimer", /* 105 */ + "newstat", /* 106 */ + "newlstat", /* 107 */ + "newfstat", /* 108 */ + "109", /* 109 */ + "lookup_dcookie", /* 110 */ + "vhangup", /* 111 */ + "112", /* 112 */ + "113", /* 113 */ + "wait4", /* 114 */ + "swapoff", /* 115 */ + "sysinfo", /* 116 */ + "ipc", /* 117 */ + "fsync", /* 118 */ + "sigreturn", /* 119 */ + "clone", /* 120 */ + "setdomainname", /* 121 */ + "newuname", /* 122 */ + "123", /* 123 */ + "adjtimex", /* 124 */ + "mprotect", /* 125 */ + "sigprocmask", /* 126 */ + "127", /* 127 */ + "init_module", /* 128 */ + "delete_module", /* 129 */ + "130", /* 130 */ + "quotactl", /* 131 */ + "getpgid", /* 132 */ + "fchdir", /* 133 */ + "bdflush", /* 134 */ + "sysfs", /* 135 */ + "personality", /* 136 */ + "137", /* 137 */ + "setfsuid16", /* 138 */ + "setfsgid16", /* 139 */ + "llseek", /* 140 */ + "getdents", /* 141 */ + "select", /* 142 */ + "flock", /* 143 */ + "msync", /* 144 */ + "readv", /* 145 */ + "writev", /* 146 */ + "getsid", /* 147 */ + "fdatasync", /* 148 */ + "sysctl", /* 149 */ + "mlock", /* 150 */ + "munlock", /* 151 */ + "mlockall", /* 152 */ + "munlockall", /* 153 */ + "sched_setparam", /* 154 */ + "sched_getparam", /* 155 */ + "sched_setscheduler", /* 156 */ + "sched_getscheduler", /* 157 */ + "sched_yield", /* 158 */ + "sched_get_priority_max", /* 159 */ + "sched_get_priority_min", /* 160 */ + "sched_rr_get_interval", /* 161 */ + "nanosleep", /* 162 */ + "mremap", /* 163 */ + "setresuid16", /* 164 */ + "getresuid16", /* 165 */ + "166", /* 166 */ + "167", /* 167 */ + "poll", /* 168 */ + "nfsservctl", /* 169 */ + "setresgid16", /* 170 */ + "getresgid16", /* 171 */ + "prctl", /* 172 */ + "rt_sigreturn", /* 173 */ + "rt_sigaction", /* 174 */ + "rt_sigprocmask", /* 175 */ + "rt_sigpending", /* 176 */ + "rt_sigtimedwait", /* 177 */ + "rt_sigqueueinfo", /* 178 */ + "rt_sigsuspend", /* 179 */ + "pread64", /* 180 */ + "pwrite64", /* 181 */ + "chown16", /* 182 */ + "getcwd", /* 183 */ + "capget", /* 184 */ + "capset", /* 185 */ + "sigaltstack", /* 186 */ + "sendfile", /* 187 */ + "188", /* 188 */ + "189", /* 189 */ + "vfork", /* 190 */ + "getrlimit", /* 191 */ + "mmap2", /* 192 */ + "truncate64", /* 193 */ + "ftruncate64", /* 194 */ + "stat64", /* 195 */ + "lstat64", /* 196 */ + "fstat64", /* 197 */ + "lchown", /* 198 */ + "getuid", /* 199 */ + "getgid", /* 200 */ + "geteuid", /* 201 */ + "getegid", /* 202 */ + "setreuid", /* 203 */ + "setregid", /* 204 */ + "getgroups", /* 205 */ + "setgroups", /* 206 */ + "fchown", /* 207 */ + "setresuid", /* 208 */ + "getresuid", /* 209 */ + "setresgid", /* 210 */ + "getresgid", /* 211 */ + "chown", /* 212 */ + "setuid", /* 213 */ + "setgid", /* 214 */ + "setfsuid", /* 215 */ + "setfsgid", /* 216 */ + "pivot_root", /* 217 */ + "mincore", /* 218 */ + "madvise", /* 219 */ + "getdents64", /* 220 */ + "fcntl64", /* 221 */ + "readahead", /* 222 */ + "sendfile64", /* 223 */ + "setxattr", /* 224 */ + "lsetxattr", /* 225 */ + "fsetxattr", /* 226 */ + "getxattr", /* 227 */ + "lgetxattr", /* 228 */ + "fgetxattr", /* 229 */ + "listxattr", /* 230 */ + "llistxattr", /* 231 */ + "flistxattr", /* 232 */ + "removexattr", /* 233 */ + "lremovexattr", /* 234 */ + "fremovexattr", /* 235 */ + "gettid", /* 236 */ + "tkill", /* 237 */ + "futex", /* 238 */ + "sched_setaffinity", /* 239 */ + "sched_getaffinity", /* 240 */ + "tgkill", /* 241 */ + "242", /* 242 */ + "io_setup", /* 243 */ + "io_destroy", /* 244 */ + "io_getevents", /* 245 */ + "io_submit", /* 246 */ + "io_cancel", /* 247 */ + "exit_group", /* 248 */ + "epoll_create", /* 249 */ + "epoll_ctl", /* 250 */ + "epoll_wait", /* 251 */ + "set_tid_address", /* 252 */ + "fadvise64", /* 253 */ + "timer_create", /* 254 */ + "timer_settime", /* 255 */ + "timer_gettime", /* 256 */ + "timer_getoverrun", /* 257 */ + "timer_delete", /* 258 */ + "clock_settime", /* 259 */ + "clock_gettime", /* 260 */ + "clock_getres", /* 261 */ + "clock_nanosleep", /* 262 */ + "263", /* 263 */ + "fadvise64_64", /* 264 */ + "statfs64", /* 265 */ + "fstatfs64", /* 266 */ + "remap_file_pages", /* 267 */ + "268", /* 268 */ + "269", /* 269 */ + "270", /* 270 */ + "mq_open", /* 271 */ + "mq_unlink", /* 272 */ + "mq_timedsend", /* 273 */ + "mq_timedreceive", /* 274 */ + "mq_notify", /* 275 */ + "mq_getsetattr", /* 276 */ + "kexec_load", /* 277 */ + "add_key", /* 278 */ + "request_key", /* 279 */ + "keyctl", /* 280 */ + "waitid", /* 281 */ + "ioprio_set", /* 282 */ + "ioprio_get", /* 283 */ + "inotify_init", /* 284 */ + "inotify_add_watch", /* 285 */ + "inotify_rm_watch", /* 286 */ + "287", /* 287 */ + "openat", /* 288 */ + "mkdirat", /* 289 */ + "mknodat", /* 290 */ + "fchownat", /* 291 */ + "futimesat", /* 292 */ + "fstatat64", /* 293 */ + "unlinkat", /* 294 */ + "renameat", /* 295 */ + "linkat", /* 296 */ + "symlinkat", /* 297 */ + "readlinkat", /* 298 */ + "fchmodat", /* 299 */ + "faccessat", /* 300 */ + "pselect6", /* 301 */ + "ppoll", /* 302 */ + "unshare", /* 303 */ + "set_robust_list", /* 304 */ + "get_robust_list", /* 305 */ + "splice", /* 306 */ + "sync_file_range", /* 307 */ + "tee", /* 308 */ + "vmsplice", /* 309 */ diff --git a/sysdeps/linux-gnu/s390/syscalls64.h b/sysdeps/linux-gnu/s390/syscalls64.h new file mode 100644 index 0000000..97be52c --- /dev/null +++ b/sysdeps/linux-gnu/s390/syscalls64.h @@ -0,0 +1,310 @@ + "0", /* 0 */ + "exit", /* 1 */ + "fork", /* 2 */ + "read", /* 3 */ + "write", /* 4 */ + "open", /* 5 */ + "close", /* 6 */ + "restart_syscall", /* 7 */ + "creat", /* 8 */ + "link", /* 9 */ + "unlink", /* 10 */ + "execve", /* 11 */ + "chdir", /* 12 */ + "13", /* 13 */ + "mknod", /* 14 */ + "chmod", /* 15 */ + "16", /* 16 */ + "17", /* 17 */ + "18", /* 18 */ + "lseek", /* 19 */ + "getpid", /* 20 */ + "mount", /* 21 */ + "oldumount", /* 22 */ + "23", /* 23 */ + "24", /* 24 */ + "25", /* 25 */ + "ptrace", /* 26 */ + "alarm", /* 27 */ + "28", /* 28 */ + "pause", /* 29 */ + "utime", /* 30 */ + "31", /* 31 */ + "32", /* 32 */ + "access", /* 33 */ + "nice", /* 34 */ + "35", /* 35 */ + "sync", /* 36 */ + "kill", /* 37 */ + "rename", /* 38 */ + "mkdir", /* 39 */ + "rmdir", /* 40 */ + "dup", /* 41 */ + "pipe", /* 42 */ + "times", /* 43 */ + "44", /* 44 */ + "brk", /* 45 */ + "46", /* 46 */ + "47", /* 47 */ + "signal", /* 48 */ + "49", /* 49 */ + "50", /* 50 */ + "acct", /* 51 */ + "umount", /* 52 */ + "53", /* 53 */ + "ioctl", /* 54 */ + "fcntl", /* 55 */ + "56", /* 56 */ + "setpgid", /* 57 */ + "58", /* 58 */ + "59", /* 59 */ + "umask", /* 60 */ + "chroot", /* 61 */ + "ustat", /* 62 */ + "dup2", /* 63 */ + "getppid", /* 64 */ + "getpgrp", /* 65 */ + "setsid", /* 66 */ + "sigaction", /* 67 */ + "68", /* 68 */ + "69", /* 69 */ + "70", /* 70 */ + "71", /* 71 */ + "sigsuspend", /* 72 */ + "sigpending", /* 73 */ + "sethostname", /* 74 */ + "setrlimit", /* 75 */ + "getrlimit", /* 76 */ + "getrusage", /* 77 */ + "gettimeofday", /* 78 */ + "settimeofday", /* 79 */ + "80", /* 80 */ + "81", /* 81 */ + "82", /* 82 */ + "symlink", /* 83 */ + "84", /* 84 */ + "readlink", /* 85 */ + "uselib", /* 86 */ + "swapon", /* 87 */ + "reboot", /* 88 */ + "89", /* 89 */ + "old_mmap", /* 90 */ + "munmap", /* 91 */ + "truncate", /* 92 */ + "ftruncate", /* 93 */ + "fchmod", /* 94 */ + "95", /* 95 */ + "getpriority", /* 96 */ + "setpriority", /* 97 */ + "98", /* 98 */ + "statfs", /* 99 */ + "fstatfs", /* 100 */ + "101", /* 101 */ + "socketcall", /* 102 */ + "syslog", /* 103 */ + "setitimer", /* 104 */ + "getitimer", /* 105 */ + "newstat", /* 106 */ + "newlstat", /* 107 */ + "newfstat", /* 108 */ + "109", /* 109 */ + "lookup_dcookie", /* 110 */ + "vhangup", /* 111 */ + "112", /* 112 */ + "113", /* 113 */ + "wait4", /* 114 */ + "swapoff", /* 115 */ + "sysinfo", /* 116 */ + "ipc", /* 117 */ + "fsync", /* 118 */ + "sigreturn", /* 119 */ + "clone", /* 120 */ + "setdomainname", /* 121 */ + "newuname", /* 122 */ + "123", /* 123 */ + "adjtimex", /* 124 */ + "mprotect", /* 125 */ + "sigprocmask", /* 126 */ + "127", /* 127 */ + "init_module", /* 128 */ + "delete_module", /* 129 */ + "130", /* 130 */ + "quotactl", /* 131 */ + "getpgid", /* 132 */ + "fchdir", /* 133 */ + "bdflush", /* 134 */ + "sysfs", /* 135 */ + "personality", /* 136 */ + "137", /* 137 */ + "138", /* 138 */ + "139", /* 139 */ + "llseek", /* 140 */ + "getdents", /* 141 */ + "select", /* 142 */ + "flock", /* 143 */ + "msync", /* 144 */ + "readv", /* 145 */ + "writev", /* 146 */ + "getsid", /* 147 */ + "fdatasync", /* 148 */ + "sysctl", /* 149 */ + "mlock", /* 150 */ + "munlock", /* 151 */ + "mlockall", /* 152 */ + "munlockall", /* 153 */ + "sched_setparam", /* 154 */ + "sched_getparam", /* 155 */ + "sched_setscheduler", /* 156 */ + "sched_getscheduler", /* 157 */ + "sched_yield", /* 158 */ + "sched_get_priority_max", /* 159 */ + "sched_get_priority_min", /* 160 */ + "sched_rr_get_interval", /* 161 */ + "nanosleep", /* 162 */ + "mremap", /* 163 */ + "164", /* 164 */ + "165", /* 165 */ + "166", /* 166 */ + "167", /* 167 */ + "poll", /* 168 */ + "nfsservctl", /* 169 */ + "170", /* 170 */ + "171", /* 171 */ + "prctl", /* 172 */ + "rt_sigreturn", /* 173 */ + "rt_sigaction", /* 174 */ + "rt_sigprocmask", /* 175 */ + "rt_sigpending", /* 176 */ + "rt_sigtimedwait", /* 177 */ + "rt_sigqueueinfo", /* 178 */ + "rt_sigsuspend", /* 179 */ + "pread64", /* 180 */ + "pwrite64", /* 181 */ + "182", /* 182 */ + "getcwd", /* 183 */ + "capget", /* 184 */ + "capset", /* 185 */ + "sigaltstack", /* 186 */ + "sendfile64", /* 187 */ + "188", /* 188 */ + "189", /* 189 */ + "vfork", /* 190 */ + "getrlimit", /* 191 */ + "mmap2", /* 192 */ + "193", /* 193 */ + "194", /* 194 */ + "195", /* 195 */ + "196", /* 196 */ + "197", /* 197 */ + "lchown", /* 198 */ + "getuid", /* 199 */ + "getgid", /* 200 */ + "geteuid", /* 201 */ + "getegid", /* 202 */ + "setreuid", /* 203 */ + "setregid", /* 204 */ + "getgroups", /* 205 */ + "setgroups", /* 206 */ + "fchown", /* 207 */ + "setresuid", /* 208 */ + "getresuid", /* 209 */ + "setresgid", /* 210 */ + "getresgid", /* 211 */ + "chown", /* 212 */ + "setuid", /* 213 */ + "setgid", /* 214 */ + "setfsuid", /* 215 */ + "setfsgid", /* 216 */ + "pivot_root", /* 217 */ + "mincore", /* 218 */ + "madvise", /* 219 */ + "getdents64", /* 220 */ + "221", /* 221 */ + "readahead", /* 222 */ + "223", /* 223 */ + "setxattr", /* 224 */ + "lsetxattr", /* 225 */ + "fsetxattr", /* 226 */ + "getxattr", /* 227 */ + "lgetxattr", /* 228 */ + "fgetxattr", /* 229 */ + "listxattr", /* 230 */ + "llistxattr", /* 231 */ + "flistxattr", /* 232 */ + "removexattr", /* 233 */ + "lremovexattr", /* 234 */ + "fremovexattr", /* 235 */ + "gettid", /* 236 */ + "tkill", /* 237 */ + "futex", /* 238 */ + "sched_setaffinity", /* 239 */ + "sched_getaffinity", /* 240 */ + "tgkill", /* 241 */ + "242", /* 242 */ + "io_setup", /* 243 */ + "io_destroy", /* 244 */ + "io_getevents", /* 245 */ + "io_submit", /* 246 */ + "io_cancel", /* 247 */ + "exit_group", /* 248 */ + "epoll_create", /* 249 */ + "epoll_ctl", /* 250 */ + "epoll_wait", /* 251 */ + "set_tid_address", /* 252 */ + "fadvise64_64", /* 253 */ + "timer_create", /* 254 */ + "timer_settime", /* 255 */ + "timer_gettime", /* 256 */ + "timer_getoverrun", /* 257 */ + "timer_delete", /* 258 */ + "clock_settime", /* 259 */ + "clock_gettime", /* 260 */ + "clock_getres", /* 261 */ + "clock_nanosleep", /* 262 */ + "263", /* 263 */ + "264", /* 264 */ + "statfs64", /* 265 */ + "fstatfs64", /* 266 */ + "remap_file_pages", /* 267 */ + "268", /* 268 */ + "269", /* 269 */ + "270", /* 270 */ + "mq_open", /* 271 */ + "mq_unlink", /* 272 */ + "mq_timedsend", /* 273 */ + "mq_timedreceive", /* 274 */ + "mq_notify", /* 275 */ + "mq_getsetattr", /* 276 */ + "kexec_load", /* 277 */ + "add_key", /* 278 */ + "request_key", /* 279 */ + "keyctl", /* 280 */ + "waitid", /* 281 */ + "ioprio_set", /* 282 */ + "ioprio_get", /* 283 */ + "inotify_init", /* 284 */ + "inotify_add_watch", /* 285 */ + "inotify_rm_watch", /* 286 */ + "287", /* 287 */ + "openat", /* 288 */ + "mkdirat", /* 289 */ + "mknodat", /* 290 */ + "fchownat", /* 291 */ + "futimesat", /* 292 */ + "newfstatat", /* 293 */ + "unlinkat", /* 294 */ + "renameat", /* 295 */ + "linkat", /* 296 */ + "symlinkat", /* 297 */ + "readlinkat", /* 298 */ + "fchmodat", /* 299 */ + "faccessat", /* 300 */ + "pselect6", /* 301 */ + "ppoll", /* 302 */ + "unshare", /* 303 */ + "set_robust_list", /* 304 */ + "get_robust_list", /* 305 */ + "splice", /* 306 */ + "sync_file_range", /* 307 */ + "tee", /* 308 */ + "vmsplice", /* 309 */ diff --git a/sysdeps/linux-gnu/s390/trace.c b/sysdeps/linux-gnu/s390/trace.c new file mode 100644 index 0000000..9df2437 --- /dev/null +++ b/sysdeps/linux-gnu/s390/trace.c @@ -0,0 +1,198 @@ +/* +** S390 specific part of trace.c +** +** Other routines are in ../trace.c and need to be combined +** at link time with this code. +** +** Copyright (C) 2001,2005 IBM Corp. +*/ + +#include "config.h" + +#include <errno.h> +#include <stdlib.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <signal.h> +#include <sys/ptrace.h> +#include <asm/ptrace.h> + +#include "common.h" + +#if (!defined(PTRACE_PEEKUSER) && defined(PTRACE_PEEKUSR)) +# define PTRACE_PEEKUSER PTRACE_PEEKUSR +#endif + +#if (!defined(PTRACE_POKEUSER) && defined(PTRACE_POKEUSR)) +# define PTRACE_POKEUSER PTRACE_POKEUSR +#endif + +void +get_arch_dep(Process *proc) { +#ifdef __s390x__ + unsigned long psw; + + if (proc->arch_ptr) + return; + + psw = ptrace(PTRACE_PEEKUSER, proc->pid, PT_PSWMASK, 0); + + if ((psw & 0x000000180000000) == 0x000000080000000) { + proc->mask_32bit = 1; + proc->personality = 1; + } + + proc->arch_ptr = (void *)1; +#endif +} + +/* Returns 1 if syscall, 2 if sysret, 0 otherwise. + */ +int +syscall_p(Process *proc, int status, int *sysnum) { + long pc, opcode, offset_reg, scno, tmp; + void *svc_addr; + int gpr_offset[16] = { PT_GPR0, PT_GPR1, PT_ORIGGPR2, PT_GPR3, + PT_GPR4, PT_GPR5, PT_GPR6, PT_GPR7, + PT_GPR8, PT_GPR9, PT_GPR10, PT_GPR11, + PT_GPR12, PT_GPR13, PT_GPR14, PT_GPR15 + }; + + if (WIFSTOPPED(status) + && WSTOPSIG(status) == (SIGTRAP | proc->tracesysgood)) { + + /* + * If we have PTRACE_O_TRACESYSGOOD and we have the new style + * of passing the system call number to user space via PT_GPR2 + * then the task is quite easy. + */ + + *sysnum = ptrace(PTRACE_PEEKUSER, proc->pid, PT_GPR2, 0); + + if (proc->tracesysgood) { + /* System call was encountered... */ + if (proc->callstack_depth > 0 && + proc->callstack[proc->callstack_depth - + 1].is_syscall) { + /* syscall exit */ + *sysnum = + proc->callstack[proc->callstack_depth - + 1].c_un.syscall; + return 2; + } else { + /* syscall enter */ + if (*sysnum != -ENOSYS) + return 1; + } + } + + /* + * At least one of the two requirements mentioned above is not + * met. Therefore the fun part starts here: + * We try to do some instruction decoding without even knowing + * the instruction code length of the last instruction executed. + * Needs to be done to get the system call number or to decide + * if we reached a breakpoint or even checking for a completely + * unrelated instruction. + * Just a heuristic that most of the time appears to work... + */ + + pc = ptrace(PTRACE_PEEKUSER, proc->pid, PT_PSWADDR, 0); + opcode = ptrace(PTRACE_PEEKTEXT, proc->pid, + (char *)(pc - sizeof(long)), 0); + + if ((opcode & 0xffff) == 0x0001) { + /* Breakpoint */ + return 0; + } else if ((opcode & 0xff00) == 0x0a00) { + /* SVC opcode */ + scno = opcode & 0xff; + } else if ((opcode & 0xff000000) == 0x44000000) { + /* Instruction decoding of EXECUTE... */ + svc_addr = (void *)(opcode & 0xfff); + + offset_reg = (opcode & 0x000f0000) >> 16; + if (offset_reg) + svc_addr += ptrace(PTRACE_PEEKUSER, proc->pid, + gpr_offset[offset_reg], 0); + + offset_reg = (opcode & 0x0000f000) >> 12; + if (offset_reg) + svc_addr += ptrace(PTRACE_PEEKUSER, proc->pid, + gpr_offset[offset_reg], 0); + + scno = ptrace(PTRACE_PEEKTEXT, proc->pid, svc_addr, 0); +#ifdef __s390x__ + scno >>= 48; +#else + scno >>= 16; +#endif + if ((scno & 0xff00) != 0x0a000) + return 0; + + tmp = 0; + offset_reg = (opcode & 0x00f00000) >> 20; + if (offset_reg) + tmp = ptrace(PTRACE_PEEKUSER, proc->pid, + gpr_offset[offset_reg], 0); + + scno = (scno | tmp) & 0xff; + } else { + /* No opcode related to syscall handling */ + return 0; + } + + if (scno == 0) + scno = ptrace(PTRACE_PEEKUSER, proc->pid, PT_GPR1, 0); + + *sysnum = scno; + + /* System call was encountered... */ + if (proc->callstack_depth > 0 && + proc->callstack[proc->callstack_depth - 1].is_syscall) { + return 2; + } else { + return 1; + } + } + /* Unknown status... */ + return 0; +} + +long +gimme_arg(enum tof type, Process *proc, int arg_num, arg_type_info *info) { + long ret; + + switch (arg_num) { + case -1: /* return value */ + ret = ptrace(PTRACE_PEEKUSER, proc->pid, PT_GPR2, 0); + break; + case 0: + ret = ptrace(PTRACE_PEEKUSER, proc->pid, PT_ORIGGPR2, 0); + break; + case 1: + ret = ptrace(PTRACE_PEEKUSER, proc->pid, PT_GPR3, 0); + break; + case 2: + ret = ptrace(PTRACE_PEEKUSER, proc->pid, PT_GPR4, 0); + break; + case 3: + ret = ptrace(PTRACE_PEEKUSER, proc->pid, PT_GPR5, 0); + break; + case 4: + ret = ptrace(PTRACE_PEEKUSER, proc->pid, PT_GPR6, 0); + break; + default: + fprintf(stderr, "gimme_arg called with wrong arguments\n"); + exit(2); + } +#ifdef __s390x__ + if (proc->mask_32bit) + ret &= 0xffffffff; +#endif + return ret; +} + +void +save_register_args(enum tof type, Process *proc) { +} diff --git a/sysdeps/linux-gnu/sparc/Makefile b/sysdeps/linux-gnu/sparc/Makefile new file mode 100644 index 0000000..b3914e5 --- /dev/null +++ b/sysdeps/linux-gnu/sparc/Makefile @@ -0,0 +1,9 @@ +OBJ = regs.o trace.o plt.o + +all: arch.o + +arch.o: $(OBJ) + $(CC) -nostdlib -r -o arch.o $(OBJ) + +clean: + $(RM) $(OBJ) arch.o diff --git a/sysdeps/linux-gnu/sparc/arch.h b/sysdeps/linux-gnu/sparc/arch.h new file mode 100644 index 0000000..75251b8 --- /dev/null +++ b/sysdeps/linux-gnu/sparc/arch.h @@ -0,0 +1,8 @@ +#define BREAKPOINT_VALUE {0x91, 0xd0, 0x20, 0x01} +#define BREAKPOINT_LENGTH 4 +#define DECR_PC_AFTER_BREAK 0 + +#define LT_ELFCLASS ELFCLASS32 +#define LT_ELF_MACHINE EM_SPARC +#define LT_ELFCLASS2 ELFCLASS32 +#define LT_ELF_MACHINE2 EM_SPARC32PLUS diff --git a/sysdeps/linux-gnu/sparc/plt.c b/sysdeps/linux-gnu/sparc/plt.c new file mode 100644 index 0000000..f9e6d80 --- /dev/null +++ b/sysdeps/linux-gnu/sparc/plt.c @@ -0,0 +1,12 @@ +#include <gelf.h> +#include "common.h" + +GElf_Addr +arch_plt_sym_val(struct ltelf *lte, size_t ndx, GElf_Rela * rela) { + return rela->r_offset + 4; +} + +void * +sym2addr(Process *proc, struct library_symbol *sym) { + return sym->enter_addr; +} diff --git a/sysdeps/linux-gnu/sparc/ptrace.h b/sysdeps/linux-gnu/sparc/ptrace.h new file mode 100644 index 0000000..bbaf01a --- /dev/null +++ b/sysdeps/linux-gnu/sparc/ptrace.h @@ -0,0 +1,21 @@ +#undef PTRACE_GETREGS +#undef PTRACE_SETREGS +#undef PTRACE_GETFPREGS +#undef PTRACE_SETFPREGS +#include <sys/ptrace.h> +#ifndef PTRACE_SUNDETACH +#define PTRACE_SUNDETACH 11 +#endif +#undef PT_DETACH +#undef PTRACE_DETACH +#define PT_DETACH PTRACE_SUNDETACH +#define PTRACE_DETACH PTRACE_SUNDETACH + +#include <asm/ptrace.h> + +typedef struct { + int valid; + struct pt_regs regs; + unsigned int func_arg[6]; + unsigned int sysc_arg[6]; +} proc_archdep; diff --git a/sysdeps/linux-gnu/sparc/regs.c b/sysdeps/linux-gnu/sparc/regs.c new file mode 100644 index 0000000..49d2729 --- /dev/null +++ b/sysdeps/linux-gnu/sparc/regs.c @@ -0,0 +1,49 @@ +#include "config.h" + +#include <sys/types.h> +#include "ptrace.h" +#include "common.h" + +void * +get_instruction_pointer(Process *proc) { + proc_archdep *a = (proc_archdep *) (proc->arch_ptr); + if (a->valid) + return (void *)a->regs.pc; + return (void *)-1; +} + +void +set_instruction_pointer(Process *proc, void *addr) { + proc_archdep *a = (proc_archdep *) (proc->arch_ptr); + if (a->valid) + a->regs.pc = (long)addr; +} + +void * +get_stack_pointer(Process *proc) { + proc_archdep *a = (proc_archdep *) (proc->arch_ptr); + if (a->valid) + return (void *)a->regs.u_regs[UREG_I5]; + return (void *)-1; +} + +void * +get_return_addr(Process *proc, void *stack_pointer) { + proc_archdep *a = (proc_archdep *) (proc->arch_ptr); + unsigned int t; + if (!a->valid) + return (void *)-1; + /* Work around structure returns */ + t = ptrace(PTRACE_PEEKTEXT, proc->pid, a->regs.u_regs[UREG_I6] + 8, 0); + if (t < 0x400000) + return (void *)a->regs.u_regs[UREG_I6] + 12; + return (void *)a->regs.u_regs[UREG_I6] + 8; +} + +void +set_return_addr(Process *proc, void *addr) { + proc_archdep *a = (proc_archdep *) (proc->arch_ptr); + if (!a->valid) + return; + ptrace(PTRACE_POKETEXT, proc->pid, a->regs.u_regs[UREG_I6] + 8, addr); +} diff --git a/sysdeps/linux-gnu/sparc/signalent.h b/sysdeps/linux-gnu/sparc/signalent.h new file mode 100644 index 0000000..d30f69e --- /dev/null +++ b/sysdeps/linux-gnu/sparc/signalent.h @@ -0,0 +1,32 @@ +"SIG_0", /* 0 */ + "SIGHUP", /* 1 */ + "SIGINT", /* 2 */ + "SIGQUIT", /* 3 */ + "SIGILL", /* 4 */ + "SIGTRAP", /* 5 */ + "SIGABRT", /* 6 */ + "SIGEMT", /* 7 */ + "SIGFPE", /* 8 */ + "SIGKILL", /* 9 */ + "SIGBUS", /* 10 */ + "SIGSEGV", /* 11 */ + "SIGSYS", /* 12 */ + "SIGPIPE", /* 13 */ + "SIGALRM", /* 14 */ + "SIGTERM", /* 15 */ + "SIGURG", /* 16 */ + "SIGSTOP", /* 17 */ + "SIGTSTP", /* 18 */ + "SIGCONT", /* 19 */ + "SIGCHLD", /* 20 */ + "SIGTTIN", /* 21 */ + "SIGTTOU", /* 22 */ + "SIGIO", /* 23 */ + "SIGXCPU", /* 24 */ + "SIGXFSZ", /* 25 */ + "SIGVTALRM", /* 26 */ + "SIGPROF", /* 27 */ + "SIGWINCH", /* 28 */ + "SIGLOST", /* 29 */ + "SIGUSR1", /* 30 */ + "SIGUSR2", /* 31 */ diff --git a/sysdeps/linux-gnu/sparc/syscallent.h b/sysdeps/linux-gnu/sparc/syscallent.h new file mode 100644 index 0000000..96eeec9 --- /dev/null +++ b/sysdeps/linux-gnu/sparc/syscallent.h @@ -0,0 +1,284 @@ +"0", /* 0 */ + "exit", /* 1 */ + "fork", /* 2 */ + "read", /* 3 */ + "write", /* 4 */ + "open", /* 5 */ + "close", /* 6 */ + "wait4", /* 7 */ + "creat", /* 8 */ + "link", /* 9 */ + "unlink", /* 10 */ + "execv", /* 11 */ + "chdir", /* 12 */ + "chown", /* 13 */ + "mknod", /* 14 */ + "chmod", /* 15 */ + "lchown", /* 16 */ + "brk", /* 17 */ + "perfctr", /* 18 */ + "lseek", /* 19 */ + "getpid", /* 20 */ + "capget", /* 21 */ + "capset", /* 22 */ + "setuid", /* 23 */ + "getuid", /* 24 */ + "25", /* 25 */ + "ptrace", /* 26 */ + "alarm", /* 27 */ + "sigaltstack", /* 28 */ + "pause", /* 29 */ + "utime", /* 30 */ + "lchown32", /* 31 */ + "fchown32", /* 32 */ + "access", /* 33 */ + "nice", /* 34 */ + "chown32", /* 35 */ + "sync", /* 36 */ + "kill", /* 37 */ + "stat", /* 38 */ + "sendfile", /* 39 */ + "lstat", /* 40 */ + "dup", /* 41 */ + "pipe", /* 42 */ + "times", /* 43 */ + "getuid32", /* 44 */ + "umount2", /* 45 */ + "setgid", /* 46 */ + "getgid", /* 47 */ + "signal", /* 48 */ + "geteuid", /* 49 */ + "getegid", /* 50 */ + "acct", /* 51 */ + "memory_ordering", /* 52 */ + "getgid32", /* 53 */ + "ioctl", /* 54 */ + "reboot", /* 55 */ + "mmap2", /* 56 */ + "symlink", /* 57 */ + "readlink", /* 58 */ + "execve", /* 59 */ + "umask", /* 60 */ + "chroot", /* 61 */ + "fstat", /* 62 */ + "fstat64", /* 63 */ + "getpagesize", /* 64 */ + "msync", /* 65 */ + "vfork", /* 66 */ + "pread64", /* 67 */ + "pwrite64", /* 68 */ + "geteuid32", /* 69 */ + "getegid32", /* 70 */ + "mmap", /* 71 */ + "setreuid32", /* 72 */ + "munmap", /* 73 */ + "mprotect", /* 74 */ + "madvise", /* 75 */ + "vhangup", /* 76 */ + "truncate64", /* 77 */ + "mincore", /* 78 */ + "getgroups", /* 79 */ + "setgroups", /* 80 */ + "getpgrp", /* 81 */ + "setgroups32", /* 82 */ + "setitimer", /* 83 */ + "ftruncate64", /* 84 */ + "swapon", /* 85 */ + "getitimer", /* 86 */ + "setuid32", /* 87 */ + "sethostname", /* 88 */ + "setgid32", /* 89 */ + "dup2", /* 90 */ + "setfsuid32", /* 91 */ + "fcntl", /* 92 */ + "select", /* 93 */ + "setfsgid32", /* 94 */ + "fsync", /* 95 */ + "setpriority", /* 96 */ + "socket", /* 97 */ + "connect", /* 98 */ + "accept", /* 99 */ + "getpriority", /* 100 */ + "rt_sigreturn", /* 101 */ + "rt_sigaction", /* 102 */ + "rt_sigprocmask", /* 103 */ + "rt_sigpending", /* 104 */ + "rt_sigtimedwait", /* 105 */ + "rt_sigqueueinfo", /* 106 */ + "rt_sigsuspend", /* 107 */ + "setresuid32", /* 108 */ + "getresuid32", /* 109 */ + "setresgid32", /* 110 */ + "getresgid32", /* 111 */ + "setregid32", /* 112 */ + "recvmsg", /* 113 */ + "sendmsg", /* 114 */ + "getgroups32", /* 115 */ + "gettimeofday", /* 116 */ + "getrusage", /* 117 */ + "getsockopt", /* 118 */ + "getcwd", /* 119 */ + "readv", /* 120 */ + "writev", /* 121 */ + "settimeofday", /* 122 */ + "fchown", /* 123 */ + "fchmod", /* 124 */ + "recvfrom", /* 125 */ + "setreuid", /* 126 */ + "setregid", /* 127 */ + "rename", /* 128 */ + "truncate", /* 129 */ + "ftruncate", /* 130 */ + "flock", /* 131 */ + "lstat64", /* 132 */ + "sendto", /* 133 */ + "shutdown", /* 134 */ + "socketpair", /* 135 */ + "mkdir", /* 136 */ + "rmdir", /* 137 */ + "utimes", /* 138 */ + "stat64", /* 139 */ + "sendfile64", /* 140 */ + "getpeername", /* 141 */ + "futex", /* 142 */ + "gettid", /* 143 */ + "getrlimit", /* 144 */ + "setrlimit", /* 145 */ + "pivot_root", /* 146 */ + "prctl", /* 147 */ + "pciconfig_read", /* 148 */ + "pciconfig_write", /* 149 */ + "getsockname", /* 150 */ + "inotify_init", /* 151 */ + "inotify_add_watch", /* 152 */ + "poll", /* 153 */ + "getdents64", /* 154 */ + "fcntl64", /* 155 */ + "inotify_rm_watch", /* 156 */ + "statfs", /* 157 */ + "fstatfs", /* 158 */ + "umount", /* 159 */ + "sched_set_affinity", /* 160 */ + "sched_get_affinity", /* 161 */ + "getdomainname", /* 162 */ + "setdomainname", /* 163 */ + "utrap_install", /* 164 */ + "quotactl", /* 165 */ + "set_tid_address", /* 166 */ + "mount", /* 167 */ + "ustat", /* 168 */ + "setxattr", /* 169 */ + "lsetxattr", /* 170 */ + "fsetxattr", /* 171 */ + "getxattr", /* 172 */ + "lgetxattr", /* 173 */ + "getdents", /* 174 */ + "setsid", /* 175 */ + "fchdir", /* 176 */ + "fgetxattr", /* 177 */ + "listxattr", /* 178 */ + "llistxattr", /* 179 */ + "flistxattr", /* 180 */ + "removexattr", /* 181 */ + "lremovexattr", /* 182 */ + "sigpending", /* 183 */ + "query_module", /* 184 */ + "setpgid", /* 185 */ + "fremovexattr", /* 186 */ + "tkill", /* 187 */ + "exit_group", /* 188 */ + "uname", /* 189 */ + "init_module", /* 190 */ + "personality", /* 191 */ + "remap_file_pages", /* 192 */ + "epoll_create", /* 193 */ + "epoll_ctl", /* 194 */ + "epoll_wait", /* 195 */ + "ioprio_set", /* 196 */ + "getppid", /* 197 */ + "sigaction", /* 198 */ + "sgetmask", /* 199 */ + "ssetmask", /* 200 */ + "sigsuspend", /* 201 */ + "oldlstat", /* 202 */ + "uselib", /* 203 */ + "readdir", /* 204 */ + "readahead", /* 205 */ + "socketcall", /* 206 */ + "syslog", /* 207 */ + "lookup_dcookie", /* 208 */ + "fadvise64", /* 209 */ + "fadvise64_64", /* 210 */ + "tgkill", /* 211 */ + "waitpid", /* 212 */ + "swapoff", /* 213 */ + "sysinfo", /* 214 */ + "ipc", /* 215 */ + "sigreturn", /* 216 */ + "clone", /* 217 */ + "ioprio_get", /* 218 */ + "adjtimex", /* 219 */ + "sigprocmask", /* 220 */ + "create_module", /* 221 */ + "delete_module", /* 222 */ + "get_kernel_syms", /* 223 */ + "getpgid", /* 224 */ + "bdflush", /* 225 */ + "sysfs", /* 226 */ + "afs_syscall", /* 227 */ + "setfsuid", /* 228 */ + "setfsgid", /* 229 */ + "_newselect", /* 230 */ + "time", /* 231 */ + "232", /* 232 */ + "stime", /* 233 */ + "statfs64", /* 234 */ + "fstatfs64", /* 235 */ + "_llseek", /* 236 */ + "mlock", /* 237 */ + "munlock", /* 238 */ + "mlockall", /* 239 */ + "munlockall", /* 240 */ + "sched_setparam", /* 241 */ + "sched_getparam", /* 242 */ + "sched_setscheduler", /* 243 */ + "sched_getscheduler", /* 244 */ + "sched_yield", /* 245 */ + "sched_get_priority_max", /* 246 */ + "sched_get_priority_min", /* 247 */ + "sched_rr_get_interval", /* 248 */ + "nanosleep", /* 249 */ + "mremap", /* 250 */ + "_sysctl", /* 251 */ + "getsid", /* 252 */ + "fdatasync", /* 253 */ + "nfsservctl", /* 254 */ + "aplib", /* 255 */ + "clock_settime", /* 256 */ + "clock_gettime", /* 257 */ + "clock_getres", /* 258 */ + "clock_nanosleep", /* 259 */ + "sched_getaffinity", /* 260 */ + "sched_setaffinity", /* 261 */ + "timer_settime", /* 262 */ + "timer_gettime", /* 263 */ + "timer_getoverrun", /* 264 */ + "timer_delete", /* 265 */ + "timer_create", /* 266 */ + "vserver", /* 267 */ + "io_setup", /* 268 */ + "io_destroy", /* 269 */ + "io_submit", /* 270 */ + "io_cancel", /* 271 */ + "io_getevents", /* 272 */ + "mq_open", /* 273 */ + "mq_unlink", /* 274 */ + "mq_timedsend", /* 275 */ + "mq_timedreceive", /* 276 */ + "mq_notify", /* 277 */ + "mq_getsetattr", /* 278 */ + "waitid", /* 279 */ + "setaltroot", /* 280 */ + "add_key", /* 281 */ + "request_key", /* 282 */ + "keyctl", /* 283 */ diff --git a/sysdeps/linux-gnu/sparc/trace.c b/sysdeps/linux-gnu/sparc/trace.c new file mode 100644 index 0000000..7f05b55 --- /dev/null +++ b/sysdeps/linux-gnu/sparc/trace.c @@ -0,0 +1,81 @@ +#include "config.h" + +#include <stdlib.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <signal.h> +#include <string.h> +#include "ptrace.h" +#include "common.h" + +void +get_arch_dep(Process *proc) { + proc_archdep *a; + if (!proc->arch_ptr) + proc->arch_ptr = (void *)malloc(sizeof(proc_archdep)); + a = (proc_archdep *) (proc->arch_ptr); + a->valid = (ptrace(PTRACE_GETREGS, proc->pid, &a->regs, 0) >= 0); +} + +/* Returns syscall number if `pid' stopped because of a syscall. + * Returns -1 otherwise + */ +int +syscall_p(Process *proc, int status, int *sysnum) { + if (WIFSTOPPED(status) + && WSTOPSIG(status) == (SIGTRAP | proc->tracesysgood)) { + void *ip = get_instruction_pointer(proc); + unsigned int insn; + if (ip == (void *)-1) + return 0; + insn = ptrace(PTRACE_PEEKTEXT, proc->pid, ip, 0); + if ((insn & 0xc1f8007f) == 0x81d00010) { + *sysnum = ((proc_archdep *) proc->arch_ptr)->regs.u_regs[UREG_G0]; + if (proc->callstack_depth > 0 && + proc->callstack[proc->callstack_depth - 1].is_syscall && + proc->callstack[proc->callstack_depth - 1].c_un.syscall == *sysnum) { + return 2; + } else if (*sysnum >= 0) { + return 1; + } + } + } + return 0; +} + +long +gimme_arg(enum tof type, Process *proc, int arg_num, arg_type_info *info) { + proc_archdep *a = (proc_archdep *) proc->arch_ptr; + if (!a->valid) { + fprintf(stderr, "Could not get child registers\n"); + exit(1); + } + if (arg_num == -1) /* return value */ + return a->regs.u_regs[UREG_G7]; + + if (type == LT_TOF_FUNCTION || type == LT_TOF_SYSCALL || arg_num >= 6) { + if (arg_num < 6) + return ((int *)&a->regs.u_regs[UREG_G7])[arg_num]; + return ptrace(PTRACE_PEEKTEXT, proc->pid, + proc->stack_pointer + 64 * (arg_num + 1)); + } else if (type == LT_TOF_FUNCTIONR) + return a->func_arg[arg_num]; + else if (type == LT_TOF_SYSCALLR) + return a->sysc_arg[arg_num]; + else { + fprintf(stderr, "gimme_arg called with wrong arguments\n"); + exit(1); + } + return 0; +} + +void +save_register_args(enum tof type, Process *proc) { + proc_archdep *a = (proc_archdep *) proc->arch_ptr; + if (a->valid) { + if (type == LT_TOF_FUNCTION) + memcpy(a->func_arg, &a->regs.u_regs[UREG_G7], sizeof(a->func_arg)); + else + memcpy(a->sysc_arg, &a->regs.u_regs[UREG_G7], sizeof(a->sysc_arg)); + } +} diff --git a/sysdeps/linux-gnu/trace.c b/sysdeps/linux-gnu/trace.c new file mode 100644 index 0000000..df5b090 --- /dev/null +++ b/sysdeps/linux-gnu/trace.c @@ -0,0 +1,193 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/wait.h> +#include "ptrace.h" +#include <asm/unistd.h> + +#include "common.h" + +/* If the system headers did not provide the constants, hard-code the normal + values. */ +#ifndef PTRACE_EVENT_FORK + +#define PTRACE_OLDSETOPTIONS 21 +#define PTRACE_SETOPTIONS 0x4200 +#define PTRACE_GETEVENTMSG 0x4201 + +/* options set using PTRACE_SETOPTIONS */ +#define PTRACE_O_TRACESYSGOOD 0x00000001 +#define PTRACE_O_TRACEFORK 0x00000002 +#define PTRACE_O_TRACEVFORK 0x00000004 +#define PTRACE_O_TRACECLONE 0x00000008 +#define PTRACE_O_TRACEEXEC 0x00000010 +#define PTRACE_O_TRACEVFORKDONE 0x00000020 +#define PTRACE_O_TRACEEXIT 0x00000040 + +/* Wait extended result codes for the above trace options. */ +#define PTRACE_EVENT_FORK 1 +#define PTRACE_EVENT_VFORK 2 +#define PTRACE_EVENT_CLONE 3 +#define PTRACE_EVENT_EXEC 4 +#define PTRACE_EVENT_VFORK_DONE 5 +#define PTRACE_EVENT_EXIT 6 + +#endif /* PTRACE_EVENT_FORK */ + +#ifdef ARCH_HAVE_UMOVELONG +extern int arch_umovelong (Process *, void *, long *, arg_type_info *); +int +umovelong (Process *proc, void *addr, long *result, arg_type_info *info) { + return arch_umovelong (proc, addr, result, info); +} +#else +/* Read a single long from the process's memory address 'addr' */ +int +umovelong (Process *proc, void *addr, long *result, arg_type_info *info) { + long pointed_to; + + errno = 0; + pointed_to = ptrace (PTRACE_PEEKTEXT, proc->pid, addr, 0); + if (pointed_to == -1 && errno) + return -errno; + + *result = pointed_to; + return 0; +} +#endif + +void +trace_me(void) { + debug(DEBUG_PROCESS, "trace_me: pid=%d\n", getpid()); + if (ptrace(PTRACE_TRACEME, 0, 1, 0) < 0) { + perror("PTRACE_TRACEME"); + exit(1); + } +} + +int +trace_pid(pid_t pid) { + debug(DEBUG_PROCESS, "trace_pid: pid=%d\n", pid); + if (ptrace(PTRACE_ATTACH, pid, 1, 0) < 0) { + return -1; + } + + /* man ptrace: PTRACE_ATTACH attaches to the process specified + in pid. The child is sent a SIGSTOP, but will not + necessarily have stopped by the completion of this call; + use wait() to wait for the child to stop. */ + if (waitpid (pid, NULL, 0) != pid) { + perror ("trace_pid: waitpid"); + exit (1); + } + + return 0; +} + +void +trace_set_options(Process *proc, pid_t pid) { + if (proc->tracesysgood & 0x80) + return; + + debug(DEBUG_PROCESS, "trace_set_options: pid=%d\n", pid); + + long options = PTRACE_O_TRACESYSGOOD | PTRACE_O_TRACEFORK | + PTRACE_O_TRACEVFORK | PTRACE_O_TRACECLONE | + PTRACE_O_TRACEEXEC; + if (ptrace(PTRACE_SETOPTIONS, pid, 0, options) < 0 && + ptrace(PTRACE_OLDSETOPTIONS, pid, 0, options) < 0) { + perror("PTRACE_SETOPTIONS"); + return; + } + proc->tracesysgood |= 0x80; +} + +void +untrace_pid(pid_t pid) { + debug(DEBUG_PROCESS, "untrace_pid: pid=%d\n", pid); + ptrace(PTRACE_DETACH, pid, 1, 0); +} + +void +continue_after_signal(pid_t pid, int signum) { + Process *proc; + + debug(DEBUG_PROCESS, "continue_after_signal: pid=%d, signum=%d", pid, signum); + + proc = pid2proc(pid); + if (proc && proc->breakpoint_being_enabled) { +#if defined __sparc__ || defined __ia64___ + ptrace(PTRACE_SYSCALL, pid, 0, signum); +#else + ptrace(PTRACE_SINGLESTEP, pid, 0, signum); +#endif + } else { + ptrace(PTRACE_SYSCALL, pid, 0, signum); + } +} + +void +continue_process(pid_t pid) { + /* We always trace syscalls to control fork(), clone(), execve()... */ + + debug(DEBUG_PROCESS, "continue_process: pid=%d", pid); + + ptrace(PTRACE_SYSCALL, pid, 0, 0); +} + +void +continue_enabling_breakpoint(pid_t pid, Breakpoint *sbp) { + enable_breakpoint(pid, sbp); + continue_process(pid); +} + +void +continue_after_breakpoint(Process *proc, Breakpoint *sbp) { + if (sbp->enabled) + disable_breakpoint(proc->pid, sbp); + set_instruction_pointer(proc, sbp->addr); + if (sbp->enabled == 0) { + continue_process(proc->pid); + } else { + debug(DEBUG_PROCESS, "continue_after_breakpoint: pid=%d, addr=%p", proc->pid, sbp->addr); + proc->breakpoint_being_enabled = sbp; +#if defined __sparc__ || defined __ia64___ + /* we don't want to singlestep here */ + continue_process(proc->pid); +#else + ptrace(PTRACE_SINGLESTEP, proc->pid, 0, 0); +#endif + } +} + +/* Read a series of bytes starting at the process's memory address + 'addr' and continuing until a NUL ('\0') is seen or 'len' bytes + have been read. +*/ +int +umovestr(Process *proc, void *addr, int len, void *laddr) { + union { + long a; + char c[sizeof(long)]; + } a; + int i; + int offset = 0; + + while (offset < len) { + a.a = ptrace(PTRACE_PEEKTEXT, proc->pid, addr + offset, 0); + for (i = 0; i < sizeof(long); i++) { + if (a.c[i] && offset + (signed)i < len) { + *(char *)(laddr + offset + i) = a.c[i]; + } else { + *(char *)(laddr + offset + i) = '\0'; + return 0; + } + } + offset += sizeof(long); + } + *(char *)(laddr + offset) = '\0'; + return 0; +} diff --git a/sysdeps/linux-gnu/x86_64/Makefile b/sysdeps/linux-gnu/x86_64/Makefile new file mode 100644 index 0000000..0a19c97 --- /dev/null +++ b/sysdeps/linux-gnu/x86_64/Makefile @@ -0,0 +1,9 @@ +OBJ = trace.o regs.o plt.o + +all: arch.o + +arch.o: $(OBJ) + $(CC) -nostdlib -r -o arch.o $(OBJ) + +clean: + $(RM) $(OBJ) arch.o diff --git a/sysdeps/linux-gnu/x86_64/arch.h b/sysdeps/linux-gnu/x86_64/arch.h new file mode 100644 index 0000000..255395c --- /dev/null +++ b/sysdeps/linux-gnu/x86_64/arch.h @@ -0,0 +1,12 @@ +#define BREAKPOINT_VALUE {0xcc} +#define BREAKPOINT_LENGTH 1 +#define DECR_PC_AFTER_BREAK 1 + +#define LT_ELFCLASS ELFCLASS64 +#define LT_ELF_MACHINE EM_X86_64 +#define LT_ELFCLASS2 ELFCLASS32 +#define LT_ELF_MACHINE2 EM_386 + +/* __NR_fork, __NR_clone, __NR_clone2, __NR_vfork and __NR_execve + from asm-i386/unistd.h. */ +#define FORK_EXEC_SYSCALLS , { 2, 120, -1, 190, 11 } diff --git a/sysdeps/linux-gnu/x86_64/ffcheck.c b/sysdeps/linux-gnu/x86_64/ffcheck.c new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/sysdeps/linux-gnu/x86_64/ffcheck.c diff --git a/sysdeps/linux-gnu/x86_64/plt.c b/sysdeps/linux-gnu/x86_64/plt.c new file mode 100644 index 0000000..b53ff44 --- /dev/null +++ b/sysdeps/linux-gnu/x86_64/plt.c @@ -0,0 +1,12 @@ +#include <gelf.h> +#include "common.h" + +GElf_Addr +arch_plt_sym_val(struct ltelf *lte, size_t ndx, GElf_Rela * rela) { + return lte->plt_addr + (ndx + 1) * 16; +} + +void * +sym2addr(Process *proc, struct library_symbol *sym) { + return sym->enter_addr; +} diff --git a/sysdeps/linux-gnu/x86_64/ptrace.h b/sysdeps/linux-gnu/x86_64/ptrace.h new file mode 100644 index 0000000..c3cbcb6 --- /dev/null +++ b/sysdeps/linux-gnu/x86_64/ptrace.h @@ -0,0 +1 @@ +#include <sys/ptrace.h> diff --git a/sysdeps/linux-gnu/x86_64/regs.c b/sysdeps/linux-gnu/x86_64/regs.c new file mode 100644 index 0000000..ed1f118 --- /dev/null +++ b/sysdeps/linux-gnu/x86_64/regs.c @@ -0,0 +1,54 @@ +#include "config.h" + +#include <sys/types.h> +#include <sys/ptrace.h> +#include <sys/reg.h> + +#include "common.h" + +#if (!defined(PTRACE_PEEKUSER) && defined(PTRACE_PEEKUSR)) +# define PTRACE_PEEKUSER PTRACE_PEEKUSR +#endif + +#if (!defined(PTRACE_POKEUSER) && defined(PTRACE_POKEUSR)) +# define PTRACE_POKEUSER PTRACE_POKEUSR +#endif + +void * +get_instruction_pointer(Process *proc) { + long int ret = ptrace(PTRACE_PEEKUSER, proc->pid, 8 * RIP, 0); + if (proc->mask_32bit) + ret &= 0xffffffff; + return (void *)ret; +} + +void +set_instruction_pointer(Process *proc, void *addr) { + if (proc->mask_32bit) + addr = (void *)((long int)addr & 0xffffffff); + ptrace(PTRACE_POKEUSER, proc->pid, 8 * RIP, addr); +} + +void * +get_stack_pointer(Process *proc) { + long int ret = ptrace(PTRACE_PEEKUSER, proc->pid, 8 * RSP, 0); + if (proc->mask_32bit) + ret &= 0xffffffff; + return (void *)ret; +} + +void * +get_return_addr(Process *proc, void *stack_pointer) { + unsigned long int ret; + ret = ptrace(PTRACE_PEEKTEXT, proc->pid, stack_pointer, 0); + if (proc->mask_32bit) + ret &= 0xffffffff; + return (void *)ret; +} + +void +set_return_addr(Process *proc, void *addr) { + if (proc->mask_32bit) + addr = (void *)((long int)addr & 0xffffffff); + ptrace(PTRACE_POKETEXT, proc->pid, proc->stack_pointer, addr); +} diff --git a/sysdeps/linux-gnu/x86_64/signalent.h b/sysdeps/linux-gnu/x86_64/signalent.h new file mode 100644 index 0000000..d58a36c --- /dev/null +++ b/sysdeps/linux-gnu/x86_64/signalent.h @@ -0,0 +1,32 @@ +"SIG_0", /* 0 */ + "SIGHUP", /* 1 */ + "SIGINT", /* 2 */ + "SIGQUIT", /* 3 */ + "SIGILL", /* 4 */ + "SIGTRAP", /* 5 */ + "SIGABRT", /* 6 */ + "SIGBUS", /* 7 */ + "SIGFPE", /* 8 */ + "SIGKILL", /* 9 */ + "SIGUSR1", /* 10 */ + "SIGSEGV", /* 11 */ + "SIGUSR2", /* 12 */ + "SIGPIPE", /* 13 */ + "SIGALRM", /* 14 */ + "SIGTERM", /* 15 */ + "SIGSTKFLT", /* 16 */ + "SIGCHLD", /* 17 */ + "SIGCONT", /* 18 */ + "SIGSTOP", /* 19 */ + "SIGTSTP", /* 20 */ + "SIGTTIN", /* 21 */ + "SIGTTOU", /* 22 */ + "SIGURG", /* 23 */ + "SIGXCPU", /* 24 */ + "SIGXFSZ", /* 25 */ + "SIGVTALRM", /* 26 */ + "SIGPROF", /* 27 */ + "SIGWINCH", /* 28 */ + "SIGIO", /* 29 */ + "SIGPWR", /* 30 */ + "SIGSYS", /* 31 */ diff --git a/sysdeps/linux-gnu/x86_64/signalent1.h b/sysdeps/linux-gnu/x86_64/signalent1.h new file mode 100644 index 0000000..5ead946 --- /dev/null +++ b/sysdeps/linux-gnu/x86_64/signalent1.h @@ -0,0 +1 @@ +#include "i386/signalent.h" diff --git a/sysdeps/linux-gnu/x86_64/syscallent.h b/sysdeps/linux-gnu/x86_64/syscallent.h new file mode 100644 index 0000000..5e5f88a --- /dev/null +++ b/sysdeps/linux-gnu/x86_64/syscallent.h @@ -0,0 +1,256 @@ +"read", /* 0 */ + "write", /* 1 */ + "open", /* 2 */ + "close", /* 3 */ + "stat", /* 4 */ + "fstat", /* 5 */ + "lstat", /* 6 */ + "poll", /* 7 */ + "lseek", /* 8 */ + "mmap", /* 9 */ + "mprotect", /* 10 */ + "munmap", /* 11 */ + "brk", /* 12 */ + "rt_sigaction", /* 13 */ + "rt_sigprocmask", /* 14 */ + "rt_sigreturn", /* 15 */ + "ioctl", /* 16 */ + "pread", /* 17 */ + "pwrite", /* 18 */ + "readv", /* 19 */ + "writev", /* 20 */ + "access", /* 21 */ + "pipe", /* 22 */ + "select", /* 23 */ + "sched_yield", /* 24 */ + "mremap", /* 25 */ + "msync", /* 26 */ + "mincore", /* 27 */ + "madvise", /* 28 */ + "shmget", /* 29 */ + "shmat", /* 30 */ + "shmctl", /* 31 */ + "dup", /* 32 */ + "dup2", /* 33 */ + "pause", /* 34 */ + "nanosleep", /* 35 */ + "getitimer", /* 36 */ + "alarm", /* 37 */ + "setitimer", /* 38 */ + "getpid", /* 39 */ + "sendfile", /* 40 */ + "socket", /* 41 */ + "connect", /* 42 */ + "accept", /* 43 */ + "sendto", /* 44 */ + "recvfrom", /* 45 */ + "sendmsg", /* 46 */ + "recvmsg", /* 47 */ + "shutdown", /* 48 */ + "bind", /* 49 */ + "listen", /* 50 */ + "getsockname", /* 51 */ + "getpeername", /* 52 */ + "socketpair", /* 53 */ + "setsockopt", /* 54 */ + "getsockopt", /* 55 */ + "clone", /* 56 */ + "fork", /* 57 */ + "vfork", /* 58 */ + "execve", /* 59 */ + "exit", /* 60 */ + "wait4", /* 61 */ + "kill", /* 62 */ + "uname", /* 63 */ + "semget", /* 64 */ + "semop", /* 65 */ + "semctl", /* 66 */ + "shmdt", /* 67 */ + "msgget", /* 68 */ + "msgsnd", /* 69 */ + "msgrcv", /* 70 */ + "msgctl", /* 71 */ + "fcntl", /* 72 */ + "flock", /* 73 */ + "fsync", /* 74 */ + "fdatasync", /* 75 */ + "truncate", /* 76 */ + "ftruncate", /* 77 */ + "getdents", /* 78 */ + "getcwd", /* 79 */ + "chdir", /* 80 */ + "fchdir", /* 81 */ + "rename", /* 82 */ + "mkdir", /* 83 */ + "rmdir", /* 84 */ + "creat", /* 85 */ + "link", /* 86 */ + "unlink", /* 87 */ + "symlink", /* 88 */ + "readlink", /* 89 */ + "chmod", /* 90 */ + "fchmod", /* 91 */ + "chown", /* 92 */ + "fchown", /* 93 */ + "lchown", /* 94 */ + "umask", /* 95 */ + "gettimeofday", /* 96 */ + "getrlimit", /* 97 */ + "getrusage", /* 98 */ + "sysinfo", /* 99 */ + "times", /* 100 */ + "ptrace", /* 101 */ + "getuid", /* 102 */ + "syslog", /* 103 */ + "getgid", /* 104 */ + "setuid", /* 105 */ + "setgid", /* 106 */ + "geteuid", /* 107 */ + "getegid", /* 108 */ + "setpgid", /* 109 */ + "getppid", /* 110 */ + "getpgrp", /* 111 */ + "setsid", /* 112 */ + "setreuid", /* 113 */ + "setregid", /* 114 */ + "getgroups", /* 115 */ + "setgroups", /* 116 */ + "setresuid", /* 117 */ + "getresuid", /* 118 */ + "setresgid", /* 119 */ + "getresgid", /* 120 */ + "getpgid", /* 121 */ + "setfsuid", /* 122 */ + "setfsgid", /* 123 */ + "getsid", /* 124 */ + "capget", /* 125 */ + "capset", /* 126 */ + "rt_sigpending", /* 127 */ + "rt_sigtimedwait", /* 128 */ + "rt_sigqueueinfo", /* 129 */ + "rt_sigsuspend", /* 130 */ + "sigaltstack", /* 131 */ + "utime", /* 132 */ + "mknod", /* 133 */ + "uselib", /* 134 */ + "personality", /* 135 */ + "ustat", /* 136 */ + "statfs", /* 137 */ + "fstatfs", /* 138 */ + "sysfs", /* 139 */ + "getpriority", /* 140 */ + "setpriority", /* 141 */ + "sched_setparam", /* 142 */ + "sched_getparam", /* 143 */ + "sched_setscheduler", /* 144 */ + "sched_getscheduler", /* 145 */ + "sched_get_priority_max", /* 146 */ + "sched_get_priority_min", /* 147 */ + "sched_rr_get_interval", /* 148 */ + "mlock", /* 149 */ + "munlock", /* 150 */ + "mlockall", /* 151 */ + "munlockall", /* 152 */ + "vhangup", /* 153 */ + "modify_ldt", /* 154 */ + "pivot_root", /* 155 */ + "_sysctl", /* 156 */ + "prctl", /* 157 */ + "arch_prctl", /* 158 */ + "adjtimex", /* 159 */ + "setrlimit", /* 160 */ + "chroot", /* 161 */ + "sync", /* 162 */ + "acct", /* 163 */ + "settimeofday", /* 164 */ + "mount", /* 165 */ + "umount2", /* 166 */ + "swapon", /* 167 */ + "swapoff", /* 168 */ + "reboot", /* 169 */ + "sethostname", /* 170 */ + "setdomainname", /* 171 */ + "iopl", /* 172 */ + "ioperm", /* 173 */ + "create_module", /* 174 */ + "init_module", /* 175 */ + "delete_module", /* 176 */ + "get_kernel_syms", /* 177 */ + "query_module", /* 178 */ + "quotactl", /* 179 */ + "nfsservctl", /* 180 */ + "getpmsg", /* 181 */ + "putpmsg", /* 182 */ + "afs_syscall", /* 183 */ + "tuxcall", /* 184 */ + "security", /* 185 */ + "gettid", /* 186 */ + "readahead", /* 187 */ + "setxattr", /* 188 */ + "lsetxattr", /* 189 */ + "fsetxattr", /* 190 */ + "getxattr", /* 191 */ + "lgetxattr", /* 192 */ + "fgetxattr", /* 193 */ + "listxattr", /* 194 */ + "llistxattr", /* 195 */ + "flistxattr", /* 196 */ + "removexattr", /* 197 */ + "lremovexattr", /* 198 */ + "fremovexattr", /* 199 */ + "tkill", /* 200 */ + "time", /* 201 */ + "futex", /* 202 */ + "sched_setaffinity", /* 203 */ + "sched_getaffinity", /* 204 */ + "set_thread_area", /* 205 */ + "io_setup", /* 206 */ + "io_destroy", /* 207 */ + "io_getevents", /* 208 */ + "io_submit", /* 209 */ + "io_cancel", /* 210 */ + "get_thread_area", /* 211 */ + "lookup_dcookie", /* 212 */ + "epoll_create", /* 213 */ + "epoll_ctl", /* 214 */ + "epoll_wait", /* 215 */ + "remap_file_pages", /* 216 */ + "getdents64", /* 217 */ + "set_tid_address", /* 218 */ + "restart_syscall", /* 219 */ + "semtimedop", /* 220 */ + "fadvise64", /* 221 */ + "timer_create", /* 222 */ + "timer_settime", /* 223 */ + "timer_gettime", /* 224 */ + "timer_getoverrun", /* 225 */ + "timer_delete", /* 226 */ + "clock_settime", /* 227 */ + "clock_gettime", /* 228 */ + "clock_getres", /* 229 */ + "clock_nanosleep", /* 230 */ + "exit_group", /* 231 */ + "epoll_wait", /* 232 */ + "epoll_ctl", /* 233 */ + "tgkill", /* 234 */ + "utimes", /* 235 */ + "vserver", /* 236 */ + "mbind", /* 237 */ + "set_mempolicy", /* 238 */ + "get_mempolicy", /* 239 */ + "mq_open", /* 240 */ + "mq_unlink", /* 241 */ + "mq_timedsend", /* 242 */ + "mq_timedreceive", /* 243 */ + "mq_notify", /* 244 */ + "mq_getsetattr", /* 245 */ + "kexec_load", /* 246 */ + "waitid", /* 247 */ + "add_key", /* 248 */ + "request_key", /* 249 */ + "keyctl", /* 250 */ + "ioprio_set", /* 251 */ + "ioprio_get", /* 252 */ + "inotify_init", /* 253 */ + "inotify_add_watch", /* 254 */ + "inotify_rm_watch", /* 255 */ diff --git a/sysdeps/linux-gnu/x86_64/syscallent1.h b/sysdeps/linux-gnu/x86_64/syscallent1.h new file mode 100644 index 0000000..d8dd9f7 --- /dev/null +++ b/sysdeps/linux-gnu/x86_64/syscallent1.h @@ -0,0 +1 @@ +#include "i386/syscallent.h" diff --git a/sysdeps/linux-gnu/x86_64/trace.c b/sysdeps/linux-gnu/x86_64/trace.c new file mode 100644 index 0000000..189734d --- /dev/null +++ b/sysdeps/linux-gnu/x86_64/trace.c @@ -0,0 +1,144 @@ +#include "config.h" + +#include <sys/types.h> +#include <sys/wait.h> +#include <signal.h> +#include <stdlib.h> +#include <sys/ptrace.h> +#include <sys/reg.h> + +#include "common.h" + +#if (!defined(PTRACE_PEEKUSER) && defined(PTRACE_PEEKUSR)) +# define PTRACE_PEEKUSER PTRACE_PEEKUSR +#endif + +#if (!defined(PTRACE_POKEUSER) && defined(PTRACE_POKEUSR)) +# define PTRACE_POKEUSER PTRACE_POKEUSR +#endif + +void +get_arch_dep(Process *proc) { + unsigned long cs; + if (proc->arch_ptr) + return; + cs = ptrace(PTRACE_PEEKUSER, proc->pid, 8 * CS, 0); + if (cs == 0x23) { + proc->mask_32bit = 1; + proc->personality = 1; + } + proc->arch_ptr = (void *)1; +} + +/* Returns 1 if syscall, 2 if sysret, 0 otherwise. + */ +int +syscall_p(Process *proc, int status, int *sysnum) { + if (WIFSTOPPED(status) + && WSTOPSIG(status) == (SIGTRAP | proc->tracesysgood)) { + *sysnum = ptrace(PTRACE_PEEKUSER, proc->pid, 8 * ORIG_RAX, 0); + + if (proc->callstack_depth > 0 && + proc->callstack[proc->callstack_depth - 1].is_syscall && + proc->callstack[proc->callstack_depth - 1].c_un.syscall == *sysnum) { + return 2; + } + + if (*sysnum >= 0) { + return 1; + } + } + return 0; +} + +static unsigned int +gimme_arg32(enum tof type, Process *proc, int arg_num) { + if (arg_num == -1) { /* return value */ + return ptrace(PTRACE_PEEKUSER, proc->pid, 8 * RAX, 0); + } + + if (type == LT_TOF_FUNCTION || type == LT_TOF_FUNCTIONR) { + return ptrace(PTRACE_PEEKTEXT, proc->pid, + proc->stack_pointer + 4 * (arg_num + 1), 0); + } else if (type == LT_TOF_SYSCALL || type == LT_TOF_SYSCALLR) { + switch (arg_num) { + case 0: + return ptrace(PTRACE_PEEKUSER, proc->pid, 8 * RBX, 0); + case 1: + return ptrace(PTRACE_PEEKUSER, proc->pid, 8 * RCX, 0); + case 2: + return ptrace(PTRACE_PEEKUSER, proc->pid, 8 * RDX, 0); + case 3: + return ptrace(PTRACE_PEEKUSER, proc->pid, 8 * RSI, 0); + case 4: + return ptrace(PTRACE_PEEKUSER, proc->pid, 8 * RDI, 0); + case 5: + return ptrace(PTRACE_PEEKUSER, proc->pid, 8 * RBP, 0); + default: + fprintf(stderr, + "gimme_arg32 called with wrong arguments\n"); + exit(2); + } + } + fprintf(stderr, "gimme_arg called with wrong arguments\n"); + exit(1); +} + +long +gimme_arg(enum tof type, Process *proc, int arg_num, arg_type_info *info) { + if (proc->mask_32bit) + return (unsigned int)gimme_arg32(type, proc, arg_num); + + if (arg_num == -1) { /* return value */ + return ptrace(PTRACE_PEEKUSER, proc->pid, 8 * RAX, 0); + } + + if (type == LT_TOF_FUNCTION || type == LT_TOF_FUNCTIONR) { + switch (arg_num) { + case 0: + return ptrace(PTRACE_PEEKUSER, proc->pid, 8 * RDI, 0); + case 1: + return ptrace(PTRACE_PEEKUSER, proc->pid, 8 * RSI, 0); + case 2: + return ptrace(PTRACE_PEEKUSER, proc->pid, 8 * RDX, 0); + case 3: + return ptrace(PTRACE_PEEKUSER, proc->pid, 8 * RCX, 0); + case 4: + return ptrace(PTRACE_PEEKUSER, proc->pid, 8 * R8, 0); + case 5: + return ptrace(PTRACE_PEEKUSER, proc->pid, 8 * R9, 0); + default: + return ptrace(PTRACE_PEEKTEXT, proc->pid, + proc->stack_pointer + 8 * (arg_num - 6 + + 1), 0); + } + } else if (type == LT_TOF_SYSCALL || LT_TOF_SYSCALLR) { + switch (arg_num) { + case 0: + return ptrace(PTRACE_PEEKUSER, proc->pid, 8 * RDI, 0); + case 1: + return ptrace(PTRACE_PEEKUSER, proc->pid, 8 * RSI, 0); + case 2: + return ptrace(PTRACE_PEEKUSER, proc->pid, 8 * RDX, 0); + case 3: + return ptrace(PTRACE_PEEKUSER, proc->pid, 8 * R10, 0); + case 4: + return ptrace(PTRACE_PEEKUSER, proc->pid, 8 * R8, 0); + case 5: + return ptrace(PTRACE_PEEKUSER, proc->pid, 8 * R9, 0); + default: + fprintf(stderr, + "gimme_arg called with wrong arguments\n"); + exit(2); + } + } else { + fprintf(stderr, "gimme_arg called with wrong arguments\n"); + exit(1); + } + + return 0; +} + +void +save_register_args(enum tof type, Process *proc) { +} diff --git a/testsuite/Makefile b/testsuite/Makefile new file mode 100644 index 0000000..b78807e --- /dev/null +++ b/testsuite/Makefile @@ -0,0 +1,71 @@ +# Copyright (C) 1992 - 2001 Free Software Foundation, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 1, or (at your option) +# any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +# + +AUTOMAKE_OPTIONS = dejagnu +EXPECT = expect +RUNTEST = runtest +CC = @CC@ + +srcdir = . +RUNTESTDEFAULTFLAGS = --srcdir $(srcdir) + +CLEANFILES = *.log *.sum site.bak setval.tmp site.exp + +SUBDIRS = ltrace.main ltrace.minor ltrace.torture + +#all: all-recursive + +.SUFFIXES: + +check-DEJAGNU: site.exp + EXPECT=$(EXPECT); export EXPECT; + @$(RUNTEST) $(RUNTESTDEFAULTFLAGS) $(RUNTESTFLAGS); + +site.exp: + @echo 'Making a new site.exp file...' + @echo '## these variables are automatically generated by make ##' >site.tmp + @echo '# Do not edit here. If you wish to override these values' >>site.tmp + @echo '# edit the last section' >>site.tmp + @echo 'set srcdir $(srcdir)' >>site.tmp + @echo "set objdir `pwd`" >>site.tmp + @echo '## All variables above are generated by configure. Do Not Edit ##' >>site.tmp + @test ! -f site.exp || \ + sed '1,/^## All variables above are.*##/ d' site.exp >> site.tmp + @-rm -f site.bak + @test ! -f site.exp || mv site.exp site.bak + @mv site.tmp site.exp + +check: check-DEJAGNU + +clean: + list='$(SUBDIRS)'; for subdir in $$list; do \ + echo "Making clean in $$subdir"; \ + (cd $$subdir && $(MAKE) clean ) done; + + -rm -f $(CLEANFILES) + +distclean: + list='$(SUBDIRS)'; for subdir in $$list; do \ + echo "Making clean in $$subdir"; \ + (cd $$subdir && $(MAKE) distclean ) done; + -rm -f $(CLEANFILES) + +.PHONY: $(RECURSIVE_TARGETS) check clean distclean realclean + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/testsuite/README b/testsuite/README new file mode 100644 index 0000000..63d2b3b --- /dev/null +++ b/testsuite/README @@ -0,0 +1,244 @@ + README for ltrace testsuite + 18, October, 2005 by Yao Qi <qiyao@cn.ibm.com> + +This is the README file for ltrace testsuite. + +Quick Overview +============== + + This testsuite is based on the dejagnu framework, which is again +dependent on Expect and Tcl. So all these three package (tcl, expect +and dejagnu) should be installed on your system before running these +tests. + + After unpacking file ltrace-0.3.36.tar.gz: + + tar -zxvfm ltrace-0.3.36.tar.gz + + you'll find a directory named ltrace-0.3.36, which contains: + + debian etc testsuite sysdeps + +you can first build this package, then run the testsuite in the +following steps: + + 1 cd ltrace-0.3.36 + + 2 Confiugre ltrace for 32-bit mode or 64-bit mode. + ./configure + OR CC='gcc -m64' ./configure + + 3 Build ltace + make + + 4 Run all the test in default mode. + make check + + The default is to test the ltrace just built, using the default +compiler options. You can control this by adding a symbol to 'make check': + + To test the shipped ltrace tool (as opposed to the just built by "make") + + --tool_exec=/usr/bin/ltrace + + To change compiler switches for the target test cases + + CFLAGS_FOR_TARGET=-m64 + + To change the target compiler (instead of shipped gcc) + + CC_FOR_TARGET=/opt/gcc-4.0/bin/gcc + + + You can run all the tests in different mode respectively as follows, + + (1) ./run-my-tests.sh -m32 + OR make check + + (test ltrace in build tree and compile test cases in 32-bit mode) + + (2) ./run-my-tests.sh -m64 + OR make check RUNTESTFLAGS="CFLAGS_FOR_TARGET=-m64" + + (test ltrace in build tree and compile test cases in 64-bit mode) + + (3) ./run-my-tests.sh -m32 /usr/bin/ltrace + OR make check RUNTESTFLAGS="--tool_exec=/usr/bin/ltrace" + + (test shipped ltrace and compile test cases in 32-bit mode) + + (4) ./run-my-tests.sh -m64 /usr/bin/ltrace + OR make check RUNTESTFLAGS="--tool_exec=/usr/bin/ltrace CFLAGS_FOR_TARGET=-m64" + + (run shipped ltrace and compile test cases in 64-bit mode) + + (5) cd testsuite; make test + + (run ltrace in build tree and compile test cases same as ltrace itself) + + + (6) make check RUNTESTFLAGS="--tool_exec=/usr/bin/ltrace CFLAGS_FOR_TARGET=-m64 CC_FOR_TARGET=/opt/gcc-4.0/bin/gcc" + + (run shipped ltrace and compile test cases in 64 bit mode by /opt/gcc-4.0/bin/gcc) +Ltrace Testsuite +================ + + This testsuite for ltrace is a DejaGNU based testsuite that can +either be used to test your newly built ltrace, or for regression +testing a ltrace with local modifications. + + Running the testsuite requires the prior installation of DejaGNU. +The directory ftp://sources.redhat.com/pub/dejagnu/ will contain a +recent snapshot. Once DejaGNU is installed or built and add the +location of runtest into $PATH, you can run the tests in one of the +four ways it mentioned in Quick Overview. The DejaGNU framework could +be built in following steps: + + 1 Uppack these three packages. + tar zxvf dejagnu-1.4.4.tar.gz + tar zxvf tcl8.4.9-src.tar.gz + tar zxvf expect-5.43.0.tar.gz + + 2 Build them and install. + cd dejagnu-1.4.4 + ./configure + make + make install + cd .. + + cd tcl8.4.9/unix + ./configure + make + make install + cd .. + + cd expect-5.43 + ./configure + make + make install + cd .. + + See the DejaGNU documentation and dejagnu-1.4.4/README for further +details. + + +Componets in ltrace testsuite +============================= + + This testsuite include all the source code you need for ltrace +test in a single directory, which is "ltrace-0.3.36/testsuite". +This directory includes the following files and sub-directories: + +`config/unix.exp` + configuration file for dejagnu-based test. + +`lib/ltrace.exp` + some basic functions used in all the test cases. + +`ltrace.main/` + some basic tests for major fetures of ltrace. + + (1) ltrace.main/main.exp does tests on tracing a function +implemented in a shared library. + + (2) ltrace.main/main-internal.exp does tests on tracing a function +implemented in main executable. + + (3) ltrace.main/signals.exp do test on tracing user-defined signals +sent by program to itself. + + (4) ltrace.main/system_calls.exp do test on tracing all the system +calls in program. + +`ltrace.minor/` + some tests for minor fetures of ltrace. + + (1) ltrace.minor/attach-process.exp do test on attching a process. + + (2) ltrace.minor/count-record.exp do test on counting time and +calls. + + (3) ltrace.minor/demangle.exp do test on demangling the C++ symbols. + + (4) ltrace.minor/time-record-T.exp do test on showing the time spent +inside each call. + + (5) ltrace.minor/time-record-tt.exp + (6) ltrace.minor/time-record-ttt.exp do test on printing absolute +timestamps in different format. + + (7) ltrace.minor/trace-clone.exp do test on following clone to child +process. + + (8) ltrace.minor/trace-fork.exp do test on following fork to child +process. + +`ltrace.torture/` + some tests in extreme condations. + + (1) ltrace.torture/signals.exp do test on tracing flooded signals +send to program itself. + +Trouble shootings +================= + + (1) Running ltrace with -u option requires the superuser privilege. +You must make sure you are root or have already got root's password. + + (2) Check the *.ltrace files in each ltrace.* directories if there are +some FAILs in the output. They are informative. + + (3) Add --verbose option in RUNTESTFLAGS when 'make check' if you want +to see more details of these tests. + +Test case extension +=================== + + Current testsuite is quite basic. The framework of testsuite is +extendable and scalealbe, so you can add new testcases into it easily. +I will describe how to do that in different ways. + + (1) Add new test case in an existed testcase directory. + + It is simple. Just add a foo.exp script and a relevant foo.c if +necessary. The dejagnu framework can run that script automatically when +you run "make check". The executable and object file would be generate +in the test, please add them in 'clean' entry in Makefile.in to ensure +that they could be cleaned up automatically when run 'make clean'. + + (2) Add new test case in a new testcase directory. + + It is a little complicated. Fisrt create a new directory in +testsuite/ with the same pattern as others, for example ltrace.bar, +and then create a Makefile.in, an Expect script foo.exp, and relative +foo.c if necessary. Then modify the configure.ac in ltrace-0.3.36/, +and add "testsuite/ltrace.bar/Makefile" into macro AC_OUTPUT, +testsuite/ltrace.bar/Makefile will be generated when you configure +this package. + + Adding Makefile.in in the new directroy is just to remove +intermediate files and log files automatically later, such as foo.ltrace, +object files and executables. For example, if you want to remove A.ltrace, +B.ltrace, A and B at the time of cleanup, you can write Makefile.in +like this: + + clean: + -rm -f A B + -rm -f *.o + -rm -f *.ltrace + distclean: clean + -rm -f Makefile + + At last add the new directory 'ltrace.bar' into the macro SUBDIRS +in testsuite/Makefile.in. + + Rerun the autoconf and ./configure in ./ltrace-0.3.36, the Makefile +will be updated. + + + +^L +(this is for editing this file with GNU emacs) +Local Variables: +mode: text +End: diff --git a/testsuite/config/unix.exp b/testsuite/config/unix.exp new file mode 100644 index 0000000..29d28a2 --- /dev/null +++ b/testsuite/config/unix.exp @@ -0,0 +1 @@ +load_lib ltrace.exp diff --git a/testsuite/lib/compiler.c b/testsuite/lib/compiler.c new file mode 100644 index 0000000..e6b1ec6 --- /dev/null +++ b/testsuite/lib/compiler.c @@ -0,0 +1,58 @@ +/* This test file is part of GDB, the GNU debugger. + + Copyright 1995, 1997, 1999, 2003, 2004 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + */ + +/* Sometimes the behavior of a test depends upon the compiler used to + compile the test program. A test script can call get_compiler_info + to figure out the compiler version and test_compiler_info to test it. + + get_compiler_info runs the preprocessor on this file and then eval's + the result. This sets various symbols for use by test_compiler_info. + + TODO: make compiler_info a local variable for get_compiler_info and + test_compiler_info. + + TODO: all clients should use test_compiler_info and should not + use gcc_compiled, hp_cc_compiler, or hp_aCC_compiler. + + */ + +/* Note the semicolon at the end of this line. Older versions of + hp c++ have a bug in string preprocessing: if the last token on a + line is a string, then the preprocessor concatenates the next line + onto the current line and eats the newline! That messes up TCL of + course. That happens with HP aC++ A.03.13, but it no longer happens + with HP aC++ A.03.45. */ + +set compiler_info "unknown" ; + +#if defined (__GNUC__) +#if defined (__GNUC_PATCHLEVEL__) +/* Only GCC versions >= 3.0 define the __GNUC_PATCHLEVEL__ macro. */ +set compiler_info [join {gcc __GNUC__ __GNUC_MINOR__ __GNUC_PATCHLEVEL__} -] +#else +set compiler_info [join {gcc __GNUC__ __GNUC_MINOR__ "unknown"} -] +#endif +#endif + +#if defined (__xlc__) +/* IBM'x xlc compiler. NOTE: __xlc__ expands to a double quoted string of four + numbers seperated by '.'s: currently "7.0.0.0" */ +set need_a_set [regsub -all {\.} [join {xlc __xlc__} -] - compiler_info] +#endif diff --git a/testsuite/lib/compiler.cc b/testsuite/lib/compiler.cc new file mode 100644 index 0000000..7068462 --- /dev/null +++ b/testsuite/lib/compiler.cc @@ -0,0 +1,45 @@ +/* This test file is part of GDB, the GNU debugger. + + Copyright 1995, 1999, 2003, 2004 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + */ + +/* This file is exactly like compiler.c. I could just use compiler.c if + I could be sure that every C++ compiler accepted extensions of ".c". */ + +/* Note the semicolon at the end of this line. Older versions of + hp c++ have a bug in string preprocessing: if the last token on a + line is a string, then the preprocessor concatenates the next line + onto the current line and eats the newline! That messes up TCL of + course. That happens with HP aC++ A.03.13, but it no longer happens + with HP aC++ A.03.45. */ + +set compiler_info "unknown" ; + +#if defined (__GNUC__) +#if defined (__GNUC_PATCHLEVEL__) +/* Only GCC versions >= 3.0 define the __GNUC_PATCHLEVEL__ macro. */ +set compiler_info [join {gcc __GNUC__ __GNUC_MINOR__ __GNUC_PATCHLEVEL__} -] +#else +set compiler_info [join {gcc __GNUC__ __GNUC_MINOR__ "unknown"} -] +#endif +#endif + +#if defined (__xlC__) +/* xlC++ version like 800 */ +set compiler_info [join {xlc++ __IBMCPP__} -] +#endif diff --git a/testsuite/lib/ltrace.exp b/testsuite/lib/ltrace.exp new file mode 100644 index 0000000..b56d50d --- /dev/null +++ b/testsuite/lib/ltrace.exp @@ -0,0 +1,279 @@ +# This file was written by Yao Qi. (qiyao@cn.ibm.com) + +# Generic ltrace test subroutines that should work for any target. If these +# need to be modified for any target, it can be done with a variable +# or by passing arguments. + + +global LTRACE +if [info exists TOOL_EXECUTABLE] { + set LTRACE $TOOL_EXECUTABLE +} else { + set LTRACE $objdir/../ltrace +} + +global LTRACE_OPTIONS +set LTRACE_OPTIONS ""; + +# ltrace_compile SOURCE DEST TYPE OPTIONS +# +# Compile PUT(program under test) by native compiler. ltrace_compile runs +# the right compiler, and TCL captures the output, and I evaluate the output. +# +# SOURCE is the name of program under test, with full directory. +# DEST is the name of output of compilation, with full directory. +# TYPE is an enum-like variable to affect the format or result of compiler +# output. Values: +# executable if output is an executable. +# object if output is an object. +# OPTIONS is option to compiler in this compilation. +proc ltrace_compile {source dest type options} { + global LTRACE_TESTCASE_OPTIONS; + + # Add platform-specific options if a shared library was specified using + # "shlib=librarypath" in OPTIONS. + set new_options "" + set shlib_found 0 + + foreach opt $options { + if [regexp {^shlib=(.*)} $opt dummy_var shlib_name] { + if [test_compiler_info "xlc*"] { + # IBM xlc compiler doesn't accept shared library named other + # than .so: use "-Wl," to bypass this + lappend source "-Wl,$shlib_name" + } else { + lappend source $shlib_name + } + + if {$shlib_found == 0} { + set shlib_found 1 + + if { ([test_compiler_info "gcc-*"]&& ([istarget "powerpc*-*-aix*"]|| [istarget "rs6000*-*-aix*"] ))} { + lappend options "additional_flags=-L${objdir}/${subdir}" + } elseif { [istarget "mips-sgi-irix*"] } { + lappend options "additional_flags=-rpath ${objdir}/${subdir}" + } + } + + } else { + lappend new_options $opt + } + } + #end of for loop + set options $new_options + # dump some information for debug purpose. + verbose "options are $options" + verbose "source is $source $dest $type $options" + + set result [target_compile $source $dest $type $options]; + verbose "result is $result" + regsub "\[\r\n\]*$" "$result" "" result; + regsub "^\[\r\n\]*" "$result" "" result; + if { $result != "" && [lsearch $options quiet] == -1} { + clone_output "compile failed for ltrace test, $result" + } + return $result; +} + +proc get_compiler_info {binfile args} { + # For compiler.c and compiler.cc + global srcdir + + # I am going to play with the log to keep noise out. + global outdir + global tool + + # These come from compiler.c or compiler.cc + global compiler_info + + # Legacy global data symbols. + #global gcc_compiled + + # Choose which file to preprocess. + set ifile "${srcdir}/lib/compiler.c" + if { [llength $args] > 0 && [lindex $args 0] == "c++" } { + set ifile "${srcdir}/lib/compiler.cc" + } + + # Run $ifile through the right preprocessor. + # Toggle ltrace.log to keep the compiler output out of the log. + #log_file + set cppout [ ltrace_compile "${ifile}" "" preprocess [list "$args" quiet] ] + #log_file -a "$outdir/$tool.log" + + # Eval the output. + set unknown 0 + foreach cppline [ split "$cppout" "\n" ] { + if { [ regexp "^#" "$cppline" ] } { + # line marker + } elseif { [ regexp "^\[\n\r\t \]*$" "$cppline" ] } { + # blank line + } elseif { [ regexp "^\[\n\r\t \]*set\[\n\r\t \]" "$cppline" ] } { + # eval this line + verbose "get_compiler_info: $cppline" 2 + eval "$cppline" + } else { + # unknown line + verbose "get_compiler_info: $cppline" + set unknown 1 + } + } + + # Reset to unknown compiler if any diagnostics happened. + if { $unknown } { + set compiler_info "unknown" + } + return 0 +} + +proc test_compiler_info { {compiler ""} } { + global compiler_info + verbose "compiler_info=$compiler_info" + # if no arg, return the compiler_info string + + if [string match "" $compiler] { + if [info exists compiler_info] { + return $compiler_info + } else { + perror "No compiler info found." + } + } + + return [string match $compiler $compiler_info] +} + +proc ltrace_compile_shlib {sources dest options} { + set obj_options $options + verbose "+++++++ [test_compiler_info]" + switch -glob [test_compiler_info] { + "xlc-*" { + lappend obj_options "additional_flags=-qpic" + } + "gcc-*" { + if { !([istarget "powerpc*-*-aix*"] + || [istarget "rs6000*-*-aix*"]) } { + lappend obj_options "additional_flags=-fpic" + } + } + "xlc++-*" { + lappend obj_options "additional_flags=-qpic" + } + + default { + fail "Bad compiler!" + } + } + + set outdir [file dirname $dest] + set objects "" + foreach source $sources { + set sourcebase [file tail $source] + if {[ltrace_compile $source "${outdir}/${sourcebase}.o" object $obj_options] != ""} { + return -1 + } + lappend objects ${outdir}/${sourcebase}.o + } + + set link_options $options + if { [test_compiler_info "xlc-*"] || [test_compiler_info "xlc++-*"]} { + lappend link_options "additional_flags=-qmkshrobj" + } else { + lappend link_options "additional_flags=-shared" + } + if {[ltrace_compile "${objects}" "${dest}" executable $link_options] != ""} { + return -1 + } +} + +# +# ltrace_options OPTIONS_LIST +# Pass ltrace commandline options. +# +proc ltrace_options { args } { + + global LTRACE_OPTIONS + set LTRACE_OPTIONS $args +} + +# +# ltrace_runtest LD_LIBRARY_PATH BIN FILE +# Trace the execution of BIN and return result. +# +# BIN is program-under-test. +# LD_LIBRARY_PATH is the env for program-under-test to run. +# FILE is to save the output from ltrace with default name $BIN.ltrace. +# Retrun output from ltrace. +# +proc ltrace_runtest { args } { + + global LTRACE + global LTRACE_OPTIONS + + verbose "LTRACE = $LTRACE" + + set LD_LIBRARY_PATH_ [lindex $args 0] + set BIN [lindex $args 1] + + # specify the output file, the default one is $BIN.ltrace + if [llength $args]==3 then { + set file [lindex $args 2] + } else { + set file $BIN.ltrace + } + # append this option to LTRACE_OPTIONS. + lappend LTRACE_OPTIONS "-o" + lappend LTRACE_OPTIONS "$file" + verbose "LTRACE_OPTIONS = $LTRACE_OPTIONS" + #ltrace the PUT. + catch "exec sh -c {export LD_LIBRARY_PATH=$LD_LIBRARY_PATH_; $LTRACE $LTRACE_OPTIONS $BIN;exit}" output + + # return output from ltrace. + return $output +} + +# +# ltrace_saveoutput OUTPUT FILE +# Save OUTPUT from ltrace to file FILE. +# OUTPUT is output from ltrace or return value of ltrace_runtest. +# FILE is file save output. +# +proc ltrace_saveoutput { args } { + + set output [lindex $args 0] + set file [lindex $args 1] + + set fd [open $file w] + puts $fd $output + close $fd +} + + +# +# ltrace_verify_output FILE_TO_SEARCH PATTERN MAX_LINE +# Verify the ltrace output by comparing the number of PATTERN in +# FILE_TO_SEARCH with INSTANCE_NO. Do not specify INSTANCE_NO if +# instance number is ignored in this test. +# Reutrn: +# 0 = number of PATTERN in FILE_TO_SEARCH inqual to INSTANCE_NO. +# 1 = number of PATTERN in FILE_TO_SEARCH qual to INSTANCE_NO. +# +proc ltrace_verify_output { file_to_search pattern {instance_no 0}} { + + # compute the number of PATTERN in FILE_TO_SEARCH by grep and wc. + catch "exec sh -c {grep \"$pattern\" $file_to_search | wc -l ;exit}" output + verbose "output = $output" + + if [ regexp "syntax error" $output ] then { + fail "Invalid regular expression $pattern" + } elseif { $instance_no == 0 } then { + if { $output == 0 } then { + fail "Fail to find $pattern in $file_to_search" + } else { + pass "$pattern in $file_to_search" + } + } elseif { $output >= $instance_no } then { + pass "$pattern in $file_to_search for $output times" + } else { + fail "$pattern in $file_to_search for $output times ,should be $instance_no" + } +} diff --git a/testsuite/ltrace.main/Makefile b/testsuite/ltrace.main/Makefile new file mode 100644 index 0000000..449214a --- /dev/null +++ b/testsuite/ltrace.main/Makefile @@ -0,0 +1,33 @@ +# Copyright (C) 1992 - 2001 Free Software Foundation, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 1, or (at your option) +# any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +# + +CLEANFILES = *.log *.sum site.bak setval.tmp site.exp + +.SUFFIXES: +clean: + -rm -f main main-internal system_calls signals parameters + -rm -f *.o *.so + -rm -f *.ltrace + -rm -f $(CLEANFILES) +distclean: clean + + +.PHONY: $(RECURSIVE_TARGETS) check clean distclean realclean + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/testsuite/ltrace.main/main-internal-1.c b/testsuite/ltrace.main/main-internal-1.c new file mode 100644 index 0000000..2a5cd0c --- /dev/null +++ b/testsuite/ltrace.main/main-internal-1.c @@ -0,0 +1,8 @@ +#include<stdio.h> + +void +display ( char* s ) +{ + printf("%s\n",s); +} + diff --git a/testsuite/ltrace.main/main-internal.c b/testsuite/ltrace.main/main-internal.c new file mode 100644 index 0000000..7508d06 --- /dev/null +++ b/testsuite/ltrace.main/main-internal.c @@ -0,0 +1,19 @@ + /* Ltrace Test : main-internal.c. + Objectives : Verify that ltrace can trace call from main + executable within it. + This file was written by Yao Qi <qiyao@cn.ibm.com>. */ +#include<stdio.h> + +extern void display ( char* ); + +#define DISPLAY_LOOP 12 + +int +main () +{ + int i; + printf ("should not show up if '-X display' is used.\n"); + for (i=0; i< DISPLAY_LOOP; i++) + display ("Function call within executable."); +} + diff --git a/testsuite/ltrace.main/main-internal.exp b/testsuite/ltrace.main/main-internal.exp new file mode 100644 index 0000000..f3f0015 --- /dev/null +++ b/testsuite/ltrace.main/main-internal.exp @@ -0,0 +1,33 @@ +# This file was written by Yao Qi <qiyao@cn.ibm.com>. + +set testfile "main-internal" +set srcfile ${testfile}.c +set binfile ${testfile} + +verbose "compiling source file now....." +# Build the shared libraries this test case needs. +if { [ ltrace_compile "${srcdir}/${subdir}/${testfile}.c ${srcdir}/${subdir}/${testfile}-1.c" "${srcdir}/${subdir}/${binfile}" executable {debug} ] != "" } { + send_user "Testcase compile failed, so all tests in this file will automatically fail.\n" +} + +# set options for ltrace. +ltrace_options "-x" "display" + +# Run PUT for ltarce. +set exec_output [ltrace_runtest $srcdir/$subdir $srcdir/$subdir/$binfile] + +# Check the output of this program. +verbose "ltrace runtest output: $exec_output\n" +if [regexp {ELF from incompatible architecture} $exec_output] { + fail "32-bit ltrace can not perform on 64-bit PUTs and rebuild ltrace in 64 bit mode!" + return +} elseif [ regexp {Couldn't do static plt2addr} $exec_output ] { + fail "Couldn't do static plt2addr!" + return +} elseif [regexp {Couldn't get .hash data from} $exec_output ] { + fail "Couldn't get .hash data!" + return +} + +set pattern "display" +ltrace_verify_output ${srcdir}/${subdir}/${testfile}.ltrace $pattern 12 diff --git a/testsuite/ltrace.main/main-lib.c b/testsuite/ltrace.main/main-lib.c new file mode 100644 index 0000000..5fd53b3 --- /dev/null +++ b/testsuite/ltrace.main/main-lib.c @@ -0,0 +1,7 @@ +#include<stdio.h> + +void +print(char* s) +{ + printf("%s\n",s); +} diff --git a/testsuite/ltrace.main/main.c b/testsuite/ltrace.main/main.c new file mode 100644 index 0000000..0f270bf --- /dev/null +++ b/testsuite/ltrace.main/main.c @@ -0,0 +1,21 @@ +/* Ltrace Test : main.c. + Objectives : Verify that ltrace can trace call a library function + from main executable. + + This file was written by Yao Qi <qiyao@cn.ibm.com>. */ + +extern void print ( char* ); + +#define PRINT_LOOP 10 + +int +main () +{ + int i; + + for (i=0; i<PRINT_LOOP; i++) + print ("Library function call!"); + + return 0; +} + diff --git a/testsuite/ltrace.main/main.exp b/testsuite/ltrace.main/main.exp new file mode 100644 index 0000000..ca66deb --- /dev/null +++ b/testsuite/ltrace.main/main.exp @@ -0,0 +1,39 @@ +# This file was written by Yao Qi <qiyao@cn.ibm.com>. + +set testfile "main" +set srcfile ${testfile}.c +set binfile ${testfile} +set libfile "main-lib" +set libsrc $srcdir/$subdir/$libfile.c +set lib_sl $srcdir/$subdir/lib$testfile.so + + +if [get_compiler_info $binfile] { + return -1 +} + +verbose "compiling source file now....." +if { [ltrace_compile_shlib $libsrc $lib_sl debug ] != "" + || [ltrace_compile $srcdir/$subdir/$srcfile $srcdir/$subdir/$binfile executable [list debug shlib=$lib_sl] ] != ""} { + send_user "Testcase compile failed, so all tests in this file will automatically fail.\n" +} + +# set options for ltrace. +ltrace_options "-l" "$srcdir/$subdir/libmain.so" + +# Run PUT for ltarce. +set exec_output [ltrace_runtest $srcdir/$subdir $srcdir/$subdir/$binfile] + +# Check the output of this program. +verbose "ltrace runtest output: $exec_output\n" +if [regexp {ELF from incompatible architecture} $exec_output] { + fail "32-bit ltrace can not perform on 64-bit PUTs and rebuild ltrace in 64 bit mode!" + return +} elseif [ regexp {Couldn't get .hash data} $exec_output ] { + fail "Couldn't get .hash data!" + return +} + +# Verify the output by checking numbers of print in main.ltrace. +set pattern "print" +ltrace_verify_output ${srcdir}/${subdir}/${testfile}.ltrace $pattern 10 diff --git a/testsuite/ltrace.main/parameters-lib.c b/testsuite/ltrace.main/parameters-lib.c new file mode 100644 index 0000000..26e4c3b --- /dev/null +++ b/testsuite/ltrace.main/parameters-lib.c @@ -0,0 +1,117 @@ +#include <string.h> +#include <stdio.h> + +void func_ignore(int a, int b, int c) +{ + printf("%d\n", a + b + c); +} + +void func_intptr(int *i) +{ + printf("%d\n", *i); +} + +void func_intptr_ret(int *i) +{ + *i = 42; +} + +int func_strlen(char* p) +{ + strcpy(p, "Hello world"); + return strlen(p); +} + +void func_strfixed(char* p) +{ + strcpy(p, "Hello world"); +} + +void func_ppp(int*** ppp) +{ + printf("%d\n", ***ppp); +} + +void func_stringp(char** sP) +{ + printf("%s\n", *sP); +} + +void func_enum(int x) +{ + printf("enum: %d\n", x); +} + +void func_short(short x1, short x2) +{ + printf("short: %hd %hd\n", x1, x2); +} + +void func_ushort(unsigned short x1, unsigned short x2) +{ + printf("ushort: %hu %hu\n", x1, x2); +} + +void func_float(float f1, float f2) +{ + printf("%f %f\n", f1, f2); +} + +void func_typedef(int x) +{ + printf("typedef'd enum: %d\n", x); +} + +void func_arrayi(int* a, int N) +{ + int i; + printf("array[int]: "); + for (i = 0; i < N; i++) + printf("%d ", a[i]); + printf("\n"); +} + +void func_arrayf(float* a, int N) +{ + int i; + printf("array[float]: "); + for (i = 0; i < N; i++) + printf("%f ", a[i]); + printf("\n"); +} + +struct test_struct { + int simple; + int alen; + int slen; + struct { int a; int b; }* array; + struct { int a; int b; } seq[3]; + char* str; + char* outer_str; +}; + +void func_struct(struct test_struct* x) +{ + char buf[100]; + int i; + + printf("struct: "); + + printf("%d, [", x->simple); + for (i = 0; i < x->alen; i++) { + printf("%d/%d", x->array[i].a, x->array[i].b); + if (i < x->alen - 1) + printf(" "); + } + printf("] ["); + for (i = 0; i < 3; i++) { + printf("%d/%d", x->seq[i].a, x->seq[i].b); + if (i < 2) + printf(" "); + } + printf("] "); + + strncpy(buf, x->str, x->slen); + buf[x->slen] = '\0'; + printf("%s\n", buf); +} diff --git a/testsuite/ltrace.main/parameters.c b/testsuite/ltrace.main/parameters.c new file mode 100644 index 0000000..dd63612 --- /dev/null +++ b/testsuite/ltrace.main/parameters.c @@ -0,0 +1,120 @@ +/* Ltrace Test : parameters.c. + Objectives : Verify that Ltrace can handle all the different + parameter types + + This file was written by Steve Fink <sphink@gmail.com>. */ + +#include <stdio.h> +#include <unistd.h> +#include <sys/syscall.h> +#include <sys/stat.h> +#include <errno.h> +#include <string.h> +#include <stdlib.h> + +void func_intptr(int *i); +void func_intptr_ret(int *i); +int func_strlen(char*); +void func_strfixed(char*); +void func_ppp(int***); +void func_stringp(char**); +void func_short(short, short); +void func_ushort(unsigned short, unsigned short); +void func_float(float, float); +void func_arrayi(int*, int); +void func_arrayf(float*, int); +void func_struct(void*); + +typedef enum { + RED, + GREEN, + BLUE, + CHARTREUSE, + PETUNIA +} color_t; +void func_enum(color_t); +void func_typedef(color_t); + +int +main () +{ + int x = 17; + int *xP, **xPP; + char buf[200]; + char *s; + int *ai; + float *af; + + func_intptr(&x); + + func_intptr_ret(&x); + + func_strlen(buf); + printf("%s\n", buf); + + func_strfixed(buf); + printf("%s\n", buf); + + x = 80; + xP = &x; + xPP = &xP; + func_ppp(&xPP); + + s = (char*) malloc(100); + strcpy(s, "Dude"); + func_stringp(&s); + + func_enum(BLUE); + + func_short(-8, -9); + func_ushort(33, 34); + func_float(3.4, -3.4); + + func_typedef(BLUE); + + ai = (int*) calloc(sizeof(int), 8); + for (x = 0; x < 8; x++) + ai[x] = 10 + x; + func_arrayi(ai, 8); + func_arrayi(ai, 2); + + af = (float*) calloc(sizeof(float), 8); + for (x = 0; x < 8; x++) + af[x] = 10.1 + x; + func_arrayf(af, 8); + func_arrayf(af, 2); + + { + struct { + int simple; + int alen; + int slen; + struct { int a; int b; }* array; + struct { int a; int b; } seq[3]; + char* str; + } x; + + x.simple = 89; + + x.alen = 2; + x.array = malloc(800); + x.array[0].a = 1; + x.array[0].b = 10; + x.array[1].a = 3; + x.array[1].b = 30; + + x.seq[0].a = 4; + x.seq[0].b = 40; + x.seq[1].a = 5; + x.seq[1].b = 50; + x.seq[2].a = 6; + x.seq[2].b = 60; + + x.slen = 3; + x.str = "123junk"; + + func_struct(&x); + } + + return 0; +} diff --git a/testsuite/ltrace.main/parameters.conf b/testsuite/ltrace.main/parameters.conf new file mode 100644 index 0000000..33c1a1e --- /dev/null +++ b/testsuite/ltrace.main/parameters.conf @@ -0,0 +1,15 @@ +void func_intptr(int*) +void func_intptr_ret(+int*) +int func_strlen(+string[retval]) +void func_strfixed(string[4]) +void func_ppp(int***) +void func_stringp(string*) +void func_enum(enum (RED=0,GREEN=1,BLUE=2,CHARTREUSE=3,PETUNIA=4)) +void func_short(short,short) +void func_ushort(ushort, ushort) +void func_float(float,float) +typedef color = enum (RED=0,GREEN=1,BLUE=2,CHARTREUSE=3,PETUNIA=4) +void func_typedef(color) +void func_arrayi(array(int,arg2)*,void) +void func_arrayf(array(float,arg2)*,void) +void func_struct(struct(int,int,int,array(struct(int,int),elt2)*,array(struct(int,int),3),string[elt3])*) diff --git a/testsuite/ltrace.main/parameters.exp b/testsuite/ltrace.main/parameters.exp new file mode 100644 index 0000000..bb55e03 --- /dev/null +++ b/testsuite/ltrace.main/parameters.exp @@ -0,0 +1,72 @@ +# This file was written by Steve Fink <sphink@gmail.com>. +# Based on main.c by Yao Qi <qiyao@cn.ibm.com>. + +set testfile "parameters" +set srcfile ${testfile}.c +set binfile ${testfile} +set libfile "parameters-lib" +set libsrc $srcdir/$subdir/$libfile.c +set lib_sl $srcdir/$subdir/lib$testfile.so + + +if [get_compiler_info $binfile] { + return -1 +} + +verbose "compiling source file now....." +if { [ltrace_compile_shlib $libsrc $lib_sl debug ] != "" + || [ltrace_compile $srcdir/$subdir/$srcfile $srcdir/$subdir/$binfile executable [list debug shlib=$lib_sl] ] != ""} { + send_user "Testcase compile failed, so all tests in this file will automatically fail.\n" +} + +# set options for ltrace. +ltrace_options "-l" "$srcdir/$subdir/libparameters.so" "-F" "$srcdir/$subdir/parameters.conf" + +# Run PUT for ltarce. +set exec_output [ltrace_runtest $srcdir/$subdir $srcdir/$subdir/$binfile] + +# Check the output of this program. +verbose "ltrace runtest output: $exec_output\n" +if [regexp {ELF from incompatible architecture} $exec_output] { + fail "32-bit ltrace can not perform on 64-bit PUTs and rebuild ltrace in 64 bit mode!" + return +} elseif [ regexp {Couldn't get .hash data} $exec_output ] { + fail "Couldn't get .hash data!" + return +} + +# Verify the output +set pattern "func_intptr(17)" +ltrace_verify_output ${srcdir}/${subdir}/${testfile}.ltrace $pattern 1 +set pattern "func_intptr_ret(42)" +ltrace_verify_output ${srcdir}/${subdir}/${testfile}.ltrace $pattern 1 +set pattern "func_strlen(\\\"Hello world\\\") *= *11" +ltrace_verify_output ${srcdir}/${subdir}/${testfile}.ltrace $pattern 1 +set pattern "func_strfixed(\\\"Hell\\\")" +ltrace_verify_output ${srcdir}/${subdir}/${testfile}.ltrace $pattern 1 +set pattern "func_ppp(80)" +ltrace_verify_output ${srcdir}/${subdir}/${testfile}.ltrace $pattern 1 +set pattern "func_stringp(\\\"Dude\\\")" +ltrace_verify_output ${srcdir}/${subdir}/${testfile}.ltrace $pattern 1 +set pattern "func_enum(BLUE)" +ltrace_verify_output ${srcdir}/${subdir}/${testfile}.ltrace $pattern 1 +set pattern "func_short(-8, -9)" +ltrace_verify_output ${srcdir}/${subdir}/${testfile}.ltrace $pattern 1 +set pattern "func_ushort(33, 34)" +ltrace_verify_output ${srcdir}/${subdir}/${testfile}.ltrace $pattern 1 +set pattern "func_float(3.40*, -3.40*)" +ltrace_verify_output ${srcdir}/${subdir}/${testfile}.ltrace $pattern 1 +set pattern "func_typedef(BLUE)" +ltrace_verify_output ${srcdir}/${subdir}/${testfile}.ltrace $pattern 1 +set pattern "func_arrayi(. 10, 11, 12, 13\\.\\.\\. ., )" +ltrace_verify_output ${srcdir}/${subdir}/${testfile}.ltrace $pattern 1 +set pattern "func_arrayi(. 10, 11 ., )" +ltrace_verify_output ${srcdir}/${subdir}/${testfile}.ltrace $pattern 1 +set pattern "func_arrayf(. 10.10*, 11.10*, 12.10*, 13.10*\\.\\.\\. ., )" +ltrace_verify_output ${srcdir}/${subdir}/${testfile}.ltrace $pattern 1 +set pattern "func_arrayf(. 10.10*, 11.10* ., )" +ltrace_verify_output ${srcdir}/${subdir}/${testfile}.ltrace $pattern 1 +set pattern "exited (status 0)" +ltrace_verify_output ${srcdir}/${subdir}/${testfile}.ltrace $pattern 1 +set pattern "func_struct({ 89, 2, 3, . { 1, 10 }, { 3, 30 } ., . { 4, 40 }, { 5, 50 }, { 6, 60 } ., \\\"123\\\" })" +ltrace_verify_output ${srcdir}/${subdir}/${testfile}.ltrace $pattern 1 diff --git a/testsuite/ltrace.main/signals.c b/testsuite/ltrace.main/signals.c new file mode 100644 index 0000000..a02e795 --- /dev/null +++ b/testsuite/ltrace.main/signals.c @@ -0,0 +1,48 @@ +/* Ltrace Test : signals.c. + Objectives : Verify that ltrace can trace user defined signal. + This file was written by Yao Qi <qiyao@cn.ibm.com>. */ + +#include<stdio.h> +#include<signal.h> +#include <sys/types.h> + +#define LOOP 7 + +void +handler(int signum,siginfo_t *info,void *act) +{ + /* Trace printf in signal handler. */ + printf("sival_int = %d\n",info->si_value.sival_int); +} + +int +main () +{ + struct sigaction act; + union sigval mysigval; + int i; + int sig; + pid_t pid; + + mysigval.sival_int=0; + + /* Use an user-defined signal 1. */ + sig = SIGUSR1; + pid=getpid(); + + sigemptyset(&act.sa_mask); + act.sa_sigaction=handler; + act.sa_flags=SA_SIGINFO; + + if(sigaction(sig,&act,NULL) < 0) + { + printf("install sigal error\n"); + } + + for(i=0; i<LOOP; i++) + { + sleep(1); + sigqueue(pid,sig,mysigval); + } + return 0; +} diff --git a/testsuite/ltrace.main/signals.exp b/testsuite/ltrace.main/signals.exp new file mode 100644 index 0000000..f0708e9 --- /dev/null +++ b/testsuite/ltrace.main/signals.exp @@ -0,0 +1,39 @@ +# This file was written by Yao Qi <qiyao@cn.ibm.com>. + +set testfile "signals" +set srcfile ${testfile}.c +set binfile ${testfile} + + +verbose "compiling source file now....." +# Build the shared libraries this test case needs. +if { [ ltrace_compile "${srcdir}/${subdir}/${testfile}.c" "${srcdir}/${subdir}/${binfile}" executable {debug} ] != "" } { + send_user "Testcase compile failed, so all tests in this file will automatically fail\n." +} + +# set options for ltrace. +ltrace_options "-L" + +# Run PUT for ltarce. +set exec_output [ltrace_runtest $srcdir/$subdir $srcdir/$subdir/$binfile] + +# Check the output of this program. +verbose "ltrace runtest output: $exec_output\n" +if [regexp {ELF from incompatible architecture} $exec_output] { + fail "32-bit ltrace can not perform on 64-bit PUTs and rebuild ltrace in 64 bit mode!" + return +} elseif [ regexp {Couldn't get .hash data} $exec_output ] { + fail "Couldn't get .hash data!" + return +} + +# Extract LOOP from source file. +set fd [ open $srcdir/$subdir/$srcfile r] +while { [gets $fd line] >= 0 } { + regexp {define LOOP.*([0-9]+)} $line match count +} +close $fd + +# Verify the output of ltrace by checking the number of SIGUSR1 in signal.ltrace +set pattern "SIGUSR1" +ltrace_verify_output ${srcdir}/${subdir}/${testfile}.ltrace $pattern $count diff --git a/testsuite/ltrace.main/system_calls.c b/testsuite/ltrace.main/system_calls.c new file mode 100644 index 0000000..7be3d04 --- /dev/null +++ b/testsuite/ltrace.main/system_calls.c @@ -0,0 +1,68 @@ +/* Ltrace Test : system_calls.c. + Objectives : Verify that Ltrace can trace all the system calls in + execution. + + You can add new system calls in it and add its verification in + system_calls correspondingly. + + This file was written by Yao Qi <qiyao@cn.ibm.com>. */ + +#include <stdio.h> +#include <unistd.h> +#include <sys/syscall.h> +#include <sys/stat.h> +#include <errno.h> + +void exit (int); + +#define BUF_SIZE 100 + +int +main () +{ + FILE* fp; + char s[]="system_calls"; + char buffer[BUF_SIZE]; + struct stat state; + + /* SYS_open. */ + fp = fopen ("system_calls.tmp", "w"); + if (fp == NULL) + { + printf("Can not create system_calls.tmp\n"); + exit (0); + } + /* SYS_write. */ + fwrite(s, sizeof(s), 1, fp); + /* SYS_lseek. */ + fseek (fp, 0, SEEK_CUR); + /* SYS_read. */ + fread(buffer, sizeof(s), 1, fp); + /* SYS_close. */ + fclose(fp); + + /* SYS_getcwd. */ + getcwd (buffer, BUF_SIZE); + /* SYS_chdir. */ + chdir ("."); + /* SYS_symlink. */ + symlink ("system_calls.tmp", "system_calls.link"); + /* SYS_unlink. */ + remove("system_calls.link"); + /* SYS_rename. */ + rename ("system_calls.tmp", "system_calls.tmp1"); + /* SYS_stat. */ + stat ("system_calls.tmp", &state); + /* SYS_access. */ + access ("system_calls.tmp", R_OK); + remove("system_calls.tmp1"); + + /* SYS_mkdir. */ + mkdir ("system_call_mkdir", 0777); + /* SYS_rmdir. */ + rmdir ("system_call_mkdir"); + + return 0; +} + + diff --git a/testsuite/ltrace.main/system_calls.exp b/testsuite/ltrace.main/system_calls.exp new file mode 100644 index 0000000..b274db5 --- /dev/null +++ b/testsuite/ltrace.main/system_calls.exp @@ -0,0 +1,67 @@ +# This file was written by Yao Qi <qiyao@cn.ibm.com>. + +set testfile "system_calls" +set srcfile ${testfile}.c +set binfile ${testfile} + + +verbose "compiling source file now....." +# Build the shared libraries this test case needs. +if { [ ltrace_compile "${srcdir}/${subdir}/${testfile}.c" "${srcdir}/${subdir}/${binfile}" executable {debug} ] != "" } { + send_user "Testcase compile failed, so all tests in this file will automatically fail.\n" +} + +# set options for ltrace. +ltrace_options "-S" + +#Run PUT for ltarce. +set exec_output [ltrace_runtest $srcdir/$subdir $srcdir/$subdir/$binfile] + +#check the output of this program. +verbose "ltrace runtest output: $exec_output\n" + +if [regexp {ELF from incompatible architecture} $exec_output] { + fail "32-bit ltrace can not perform on 64-bit PUTs and rebuild ltrace in 64 bit mode!" + return +} elseif [ regexp {Couldn't get .hash data} $exec_output ] { + fail "Couldn't get .hash data!" + return +} + + +set pattern "SYS_munmap" +ltrace_verify_output ${srcdir}/${subdir}/${testfile}.ltrace $pattern 2 +set pattern "SYS_write" +ltrace_verify_output ${srcdir}/${subdir}/${testfile}.ltrace $pattern 1 +set pattern "SYS_unlink" +ltrace_verify_output ${srcdir}/${subdir}/${testfile}.ltrace $pattern 1 + +set pattern "SYS_brk" +ltrace_verify_output ${srcdir}/${subdir}/${testfile}.ltrace $pattern 1 +set pattern "SYS_open" +ltrace_verify_output ${srcdir}/${subdir}/${testfile}.ltrace $pattern 1 +set pattern "SYS_fstat" +ltrace_verify_output ${srcdir}/${subdir}/${testfile}.ltrace $pattern 1 +set pattern "SYS_mmap" +ltrace_verify_output ${srcdir}/${subdir}/${testfile}.ltrace $pattern 1 +set pattern "SYS_close" +ltrace_verify_output ${srcdir}/${subdir}/${testfile}.ltrace $pattern 1 + +set pattern "SYS_getcwd" +ltrace_verify_output ${srcdir}/${subdir}/${testfile}.ltrace $pattern 1 +set pattern "SYS_chdir" +ltrace_verify_output ${srcdir}/${subdir}/${testfile}.ltrace $pattern 1 +set pattern "SYS_symlink" +ltrace_verify_output ${srcdir}/${subdir}/${testfile}.ltrace $pattern 1 +set pattern "SYS_unlink" +ltrace_verify_output ${srcdir}/${subdir}/${testfile}.ltrace $pattern 1 +set pattern "SYS_stat" +ltrace_verify_output ${srcdir}/${subdir}/${testfile}.ltrace $pattern 1 +set pattern "SYS_access" +ltrace_verify_output ${srcdir}/${subdir}/${testfile}.ltrace $pattern 1 +set pattern "SYS_rename" +ltrace_verify_output ${srcdir}/${subdir}/${testfile}.ltrace $pattern 1 +set pattern "SYS_mkdir" +ltrace_verify_output ${srcdir}/${subdir}/${testfile}.ltrace $pattern 1 +set pattern "SYS_rmdir" +ltrace_verify_output ${srcdir}/${subdir}/${testfile}.ltrace $pattern 1 diff --git a/testsuite/ltrace.minor/Makefile b/testsuite/ltrace.minor/Makefile new file mode 100644 index 0000000..cd4914f --- /dev/null +++ b/testsuite/ltrace.minor/Makefile @@ -0,0 +1,36 @@ +# Copyright (C) 1992 - 2001 Free Software Foundation, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 1, or (at your option) +# any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +# + +CLEANFILES = *.log *.sum site.bak setval.tmp site.exp + +.SUFFIXES: +clean: + -rm -f demangle trace-fork trace-clone trace-exec trace-exec1 + -rm -f time-record-tt time-record-ttt time-record-T + -rm -f attach-process count-record + -rm -f print-instruction-pointer + -rm -f *.o *.so + -rm -f *.ltrace + -rm -f $(CLEANFILES) +distclean: clean + + +.PHONY: $(RECURSIVE_TARGETS) check clean distclean realclean + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/testsuite/ltrace.minor/attach-process.c b/testsuite/ltrace.minor/attach-process.c new file mode 100644 index 0000000..4c00bce --- /dev/null +++ b/testsuite/ltrace.minor/attach-process.c @@ -0,0 +1,16 @@ +/* Ltrace Test : attach-process.c. + Objectives : Verify that ltrace can attach to a running process + by its PID. + + This file was written by Yao Qi <qiyao@cn.ibm.com>. */ + +#include<unistd.h> +#include <sys/types.h> + +int +main () +{ + sleep (5); + sleep (1); + return 0; +} diff --git a/testsuite/ltrace.minor/attach-process.exp b/testsuite/ltrace.minor/attach-process.exp new file mode 100644 index 0000000..ab5460e --- /dev/null +++ b/testsuite/ltrace.minor/attach-process.exp @@ -0,0 +1,38 @@ +# This file was written by Yao Qi <qiyao@cn.ibm.com>. + +set testfile "attach-process" +set srcfile ${testfile}.c +set binfile ${testfile} + +verbose "compiling source file now....." +# Build the shared libraries this test case needs. +if { [ ltrace_compile "${srcdir}/${subdir}/${testfile}.c" "${srcdir}/${subdir}/${binfile}" executable {debug} ] != "" } { + send_user "Testcase compile failed, so all tests in this file will automatically fail.\n" +} + +# Run the program and get PID of it. +catch "exec $srcdir/$subdir/$binfile &" output + +# get PID from ps output. +regexp {([0-9]+)} $output match PID +verbose "PID = $PID" + +# Run PUT for ltrace. +global LTRACE +catch "exec $LTRACE -S -p $PID -o ${srcdir}/${subdir}/${testfile}.ltrace" exec_output + +# Check the output of this program. +verbose "ltrace runtest output: $exec_output\n" +if [regexp {ELF from incompatible architecture} $exec_output] { + fail "32-bit ltrace can not perform on 64-bit PUTs and rebuild ltrace in 64 bit mode!" + return +} elseif [ regexp {Couldn't get .hash data} $exec_output ] { + fail "Couldn't get .hash data!" + return +} + +# Verify the output in attach-process.ltrace by checking the number +# of sleep. +set pattern "sleep" +ltrace_verify_output ${srcdir}/${subdir}/${testfile}.ltrace $pattern 1 + diff --git a/testsuite/ltrace.minor/count-record.c b/testsuite/ltrace.minor/count-record.c new file mode 100644 index 0000000..3b609ad --- /dev/null +++ b/testsuite/ltrace.minor/count-record.c @@ -0,0 +1,51 @@ +/* Ltrace Test : count-record.c. + Objectives : Verify that Ltrace can count all the system calls in + execution and report a summary on exit. + + This file was written by Yao Qi <qiyao@cn.ibm.com>. */ + +#include <stdio.h> +#include <unistd.h> +#include <sys/syscall.h> +#include <sys/stat.h> +#include <errno.h> + +void exit (int); + +#define BUF_SIZE 100 + +/* Do as many operations as possible to record these calls. */ +int +main () +{ + FILE* fp; + char s[]="system_calls"; + char buffer[BUF_SIZE]; + struct stat state; + + fp = fopen ("system_calls.tmp", "w"); + if (fp == NULL) + { + printf("Can not create system_calls.tmp\n"); + exit (0); + } + + fwrite(s, sizeof(s), 1, fp); + fseek (fp, 0, SEEK_CUR); + fread(buffer, sizeof(s), 1, fp); + fclose(fp); + + getcwd (buffer, BUF_SIZE); + chdir ("."); + symlink ("system_calls.tmp", "system_calls.link"); + remove("system_calls.link"); + rename ("system_calls.tmp", "system_calls.tmp1"); + stat ("system_calls.tmp", &state); + access ("system_calls.tmp", R_OK); + remove("system_calls.tmp1"); + + mkdir ("system_call_mkdir", 0777); + rmdir ("system_call_mkdir"); + + return 0; +} diff --git a/testsuite/ltrace.minor/count-record.exp b/testsuite/ltrace.minor/count-record.exp new file mode 100644 index 0000000..f8c4923 --- /dev/null +++ b/testsuite/ltrace.minor/count-record.exp @@ -0,0 +1,77 @@ +# This file was written by Yao Qi <qiyao@cn.ibm.com>. + +set testfile "count-record" +set srcfile ${testfile}.c +set binfile ${testfile} + +verbose "compiling source file now....." +# Build the shared libraries this test case needs. +if { [ ltrace_compile "${srcdir}/${subdir}/${testfile}.c" "${srcdir}/${subdir}/${binfile}" executable {debug} ] != "" } { + send_user "Testcase compile failed, so all tests in this file will automatically fail.\n" +} + +# set options for ltrace. +ltrace_options "-c" + +# Run PUT for ltrace. +set exec_output [ltrace_runtest $srcdir/$subdir $srcdir/$subdir/$binfile] + +# Check the output of this program. +verbose "ltrace runtest output: $exec_output\n" +if [regexp {ELF from incompatible architecture} $exec_output] { + fail "32-bit ltrace can not perform on 64-bit PUTs and rebuild ltrace in 64 bit mode!" + return +} elseif [ regexp {Couldn't get .hash data} $exec_output ] { + fail "Couldn't get .hash data!" + return +} + + +ltrace_saveoutput $exec_output $srcdir/$subdir/$binfile.ltrace + +# +# This is a sample output and Verify the forth and fifth column. +# +# 13.31 0.001051 1051 1 rmdir +# 12.81 0.001012 1012 1 fopen +# 10.32 0.000815 407 2 remove +# 9.56 0.000755 755 1 mkdir +# 7.86 0.000621 621 1 fseek +# 6.86 0.000542 542 1 fwrite +# 6.60 0.000521 521 1 fclose +# 6.03 0.000476 476 1 rename +# 5.61 0.000443 443 1 symlink +# 5.05 0.000399 399 1 chdir +# 4.80 0.000379 379 1 access +# 4.00 0.000316 316 1 __xstat +# 3.81 0.000301 301 1 getcwd +# 3.39 0.000268 268 1 fread +# + +set pattern " 1 rmdir" +ltrace_verify_output ${srcdir}/${subdir}/${testfile}.ltrace $pattern +set pattern " 1 fopen" +ltrace_verify_output ${srcdir}/${subdir}/${testfile}.ltrace $pattern +set pattern " 2 remove" +ltrace_verify_output ${srcdir}/${subdir}/${testfile}.ltrace $pattern +set pattern " 1 mkdir" +ltrace_verify_output ${srcdir}/${subdir}/${testfile}.ltrace $pattern +set pattern " 1 fseek" +ltrace_verify_output ${srcdir}/${subdir}/${testfile}.ltrace $pattern +set pattern " 1 fwrite" +ltrace_verify_output ${srcdir}/${subdir}/${testfile}.ltrace $pattern +set pattern " 1 fclose" +ltrace_verify_output ${srcdir}/${subdir}/${testfile}.ltrace $pattern +set pattern " 1 rename" +ltrace_verify_output ${srcdir}/${subdir}/${testfile}.ltrace $pattern +set pattern " 1 symlink" +ltrace_verify_output ${srcdir}/${subdir}/${testfile}.ltrace $pattern +set pattern " 1 chdir" +ltrace_verify_output ${srcdir}/${subdir}/${testfile}.ltrace $pattern +set pattern " 1 access" +ltrace_verify_output ${srcdir}/${subdir}/${testfile}.ltrace $pattern +set pattern " 1 getcwd" +ltrace_verify_output ${srcdir}/${subdir}/${testfile}.ltrace $pattern +set pattern " 1 fread" +ltrace_verify_output ${srcdir}/${subdir}/${testfile}.ltrace $pattern + diff --git a/testsuite/ltrace.minor/demangle-lib.cpp b/testsuite/ltrace.minor/demangle-lib.cpp new file mode 100644 index 0000000..01ab18d --- /dev/null +++ b/testsuite/ltrace.minor/demangle-lib.cpp @@ -0,0 +1,97 @@ +#include<stddef.h> +#include<iostream> + +#include"demangle.h" + +/* Number of arguments */ +int Fi_i(int bar) { return 0; } +int Fi_s(short bar) {return 0; } +int Fii_i(int bar, int goo) { return 0; } +int Fiii_i(int bar, int goo, int hoo) { return 0; } +int Fie_i(int bar, ...) { return 0; } + +/* Return types */ +void Fv_v(void) { ; } +char Fv_c(void) { return 0; } +signed char Fv_Sc(void) { return 0; } +unsigned char Fv_Uc(void) { return 0; } +short Fv_s(void) { return 0; } +unsigned short Fv_Us(void) { return 0; } +int Fv_i(void) { return 0; } +const int Fv_Ci(void) { return 0; } +unsigned int Fv_Ui(void) { return 0; } +volatile int Fv_Vi(void) { return 0; } +long Fv_l(void) { return 0; } +unsigned long Fv_Ul(void) { return 0; } +float Fv_f(void) { return 0; } +double Fv_g(void) { return 0; } +long double Fv_Lg(void) { return 0; } + +/* Pointers */ +void *Fv_Pv(void) { return 0; } +void **Fv_PPv(void) { return 0; } + +/* References */ +int& Fv_Ri(void) { static int x; return x; } + +/* Argument types */ +int FPi_i(int *a) { return 0; } +int FA10_i_i(int a[10]) { return 0; } +int Fc_i(char bar) { return 0; } +int Ff_i(float bar) { return 0; } +int Fg_i(double bar) { return 0; } + +/* Function pointers */ +typedef int (*x)(int); +typedef int (*y)(short); + +int Fx_i(x fnptr) { return 0; } +int Fxx_i(x fnptr, x fnptr2) { return 0; } +int Fxxx_i(x fnptr, x fnptr2, + x fnptr3) { return 0; } +int Fxxi_i(x fnptr, x fnptr2, + x fnptr3, int i) { return 0; } +int Fxix_i(x fnptr, int i, + x fnptr3) { return 0; } +int Fxyxy_i(x fnptr, y fnptr2, + x fnptr3, y fnptr4) { return 0; } + +/* Class methods */ +class myclass; +myclass::myclass(void) { myint = 0; } +myclass::myclass(int x) { myint = x; } +myclass::~myclass() { ; } + +int myclass::Fi_i(int bar) { return myint; } +int myclass::Fis_i(int bar) { return bar; } + +void* myclass::operator new(size_t size) +{ + void* p = malloc(size);return p; +} +void myclass::operator delete(void *p) {free(p);} + +myclass myclass::operator++() { return myclass(++myint); } +myclass myclass::operator++(int) { return myclass(myint++); } + +/* Binary */ +myclass myclass::operator+(int x) { return myclass(myint + x); } + +/* Assignment */ +myclass& myclass::operator=(const myclass& from) +{ + myint = from.myint; + return *this; +} + +/* test clashes */ +class nested; + +nested::nested(void) { ; } +nested::~nested() { ; } +int nested::Fi_i(int bar) { return bar; } + +void Fmyclass_v(myclass m) { ; } +void Fmxmx_v(myclass arg1, x arg2, + myclass arg3, x arg4) { ; } + diff --git a/testsuite/ltrace.minor/demangle.cpp b/testsuite/ltrace.minor/demangle.cpp new file mode 100644 index 0000000..14cc9f6 --- /dev/null +++ b/testsuite/ltrace.minor/demangle.cpp @@ -0,0 +1,121 @@ +/* Ltrace Test : demangle.cpp + Objectives : Verify that ltrace can demangle C++ symbols. + + This file was written by Yao Qi <qiyao@cn.ibm.com>. */ + +#include<stddef.h> +#include"demangle.h" + +/* Number of arguments */ +extern int Fi_i(int); +extern int Fi_s (short); +extern int Fii_i(int , int); +extern int Fiii_i(int, int, int); +extern int Fie_i(int bar, ...); + +/* Return types */ +extern void Fv_v(void); +extern char Fv_c(void); +extern signed char Fv_Sc(void); +extern unsigned char Fv_Uc(void); +extern short Fv_s(void); +extern unsigned short Fv_Us(void); +extern int Fv_i(void); +extern const int Fv_Ci(void); +extern unsigned int Fv_Ui(void); +extern volatile int Fv_Vi(void); +extern long Fv_l(void); +extern unsigned long Fv_Ul(void); +extern float Fv_f(void) ; +extern double Fv_g(void); +extern long double Fv_Lg(void); + + +/* Pointers */ +extern void * Fv_Pv(void); +extern void ** Fv_PPv(void); + +/* References */ +extern int& Fv_Ri(void); + +/* Argument types */ +extern int FPi_i(int *a); +extern int FA10_i_i(int a[10]) ; +extern int Fc_i(char bar); +extern int Ff_i(float bar); +extern int Fg_i(double bar); + +/* Function pointers */ +typedef int (*x)(int); +typedef int (*y)(short); + +extern int Fx_i(x); +extern int Fxx_i(x fnptr, x fnptr2); +extern int Fxxx_i(x fnptr, x fnptr2, x fnptr3); +extern int Fxxi_i(x fnptr, x fnptr2, x fnptr3, int i); +extern int Fxix_i(x fnptr, int i, x fnptr3); +extern int Fxyxy_i(x fnptr, y fnptr2, x fnptr3, y fnptr4); + + +extern void Fmyclass_v(myclass m); +extern void Fmxmx_v(myclass arg1, x arg2, myclass arg3, x arg4); + +int main () +{ + int i; + + i = Fi_i (0); + i = Fii_i (0,0); + i = Fiii_i (0,0,0); + i = Fie_i (0); + + Fv_v (); + Fv_c (); + Fv_Sc (); + Fv_Uc (); + Fv_s (); + Fv_Us (); + Fv_i (); + Fv_Ci (); + Fv_Ui (); + Fv_Vi (); + Fv_l (); + Fv_Ul (); + Fv_f (); + Fv_g (); + Fv_Lg (); + + Fv_Pv (); + Fv_PPv (); + + Fv_Ri (); + + FPi_i (&i); + FA10_i_i (&i); + Fc_i ('a'); + Ff_i (1.1f); + Fg_i (1.1); + + Fx_i (Fi_i); + Fxx_i (Fi_i, Fi_i); + Fxxx_i (Fi_i, Fi_i, Fi_i); + Fxxi_i (Fi_i, Fi_i, Fi_i, 0); + Fxyxy_i (Fi_i, Fi_s, Fi_i, Fi_s); + + myclass a,c; + myclass* b; + + a.Fi_i (0); + a.Fis_i (0); + a++; + c = a + 2; + + nested n; + n.Fi_i (0); + + b = (myclass*) new myclass(0); + delete (b); + Fmyclass_v (a); + + return 0; +} diff --git a/testsuite/ltrace.minor/demangle.exp b/testsuite/ltrace.minor/demangle.exp new file mode 100644 index 0000000..5077ac0 --- /dev/null +++ b/testsuite/ltrace.minor/demangle.exp @@ -0,0 +1,63 @@ +# This file was written by Yao Qi <qiyao@cn.ibm.com>. + +set testfile "demangle" +set srcfile ${testfile}.cpp +set binfile ${testfile} +set libfile "demangle-lib" +set libsrc $srcdir/$subdir/$libfile.cpp +set lib_sl $srcdir/$subdir/lib$testfile.so + +verbose "compiling source file now....." +if [get_compiler_info $binfile "c++"] { + return -1 +} + +verbose "compiling source file now....." +if { [ltrace_compile_shlib $libsrc $lib_sl [list debug c++]] != "" + || [ltrace_compile $srcdir/$subdir/$srcfile $srcdir/$subdir/$binfile executable [list debug shlib=$lib_sl c++] ] != ""} { + send_user "Testcase compile failed, so all tests in this file will automatically fail.\n" +} + +# set options for ltrace. +ltrace_options "-C" + +# Run PUT for ltrace. +set exec_output [ltrace_runtest $srcdir/$subdir $srcdir/$subdir/$binfile] + +# Check the output of this program. +verbose "ltrace runtest output: $exec_output\n" +if [regexp {ELF from incompatible architecture} $exec_output] { + fail "32-bit ltrace can not perform on 64-bit PUTs and rebuild ltrace in 64 bit mode!" + return +} elseif [ regexp {Couldn't get .hash data} $exec_output ] { + fail "Couldn't get .hash data!" + return +} + +# read function declarations from demangle.cpp and verify them in demangle.ltrace. +set fd [ open $srcdir/$subdir/$srcfile r] +while { [gets $fd line] >= 0 } { + if [regexp {extern (double|float|void|char|int|short|long|void \*|void \*\*) ([^ ])\(} $line match type fun] { + ltrace_verify_output ${srcdir}/${subdir}/${testfile}.ltrace $fun + } +} +close $fd + +#read member fucntions of classs from demangle-lib.cpp and verify them in demagle.ltrace. +set fd [ open $srcdir/$subdir/$testfile-lib.cpp r] +while { [gets $fd line] >= 0 } { + if [ regexp {((myclass|nested)::[^\(]*)\(} $line match fun] { + # For Debug purpose. + verbose "fun = $fun" + # Extract new/delete for remove extra SPACE in $fun, for example, + # $fun = "myclass::operator delete" will confuse ltrace_verify_output if it + # was an argument to it. + if [regexp {(new|delete)} $fun match sub_fun] { + ltrace_verify_output ${srcdir}/${subdir}/${testfile}.ltrace $sub_fun + } else { + # Verify class member functions without SPACE. + ltrace_verify_output ${srcdir}/${subdir}/${testfile}.ltrace $fun + } + } +} +close $fd diff --git a/testsuite/ltrace.minor/demangle.h b/testsuite/ltrace.minor/demangle.h new file mode 100644 index 0000000..02aa156 --- /dev/null +++ b/testsuite/ltrace.minor/demangle.h @@ -0,0 +1,36 @@ + +class myclass { + int myint; + public: + myclass(int x); + myclass(void); + ~myclass(); + static int Fis_i(int bar); + int Fi_i(int bar); + /* Overloaded operators */ + void* operator new(size_t); + void operator delete(void *); + /* Unary operation. */ + myclass operator++();// Preincrement + myclass operator++(int);// Postincrement + + /* Binary operation. */ + myclass operator+(int); + + /* Assignment */ + myclass& operator=(const myclass& from); + /* Nested classes */ + class nested { + public: + nested(); + ~nested(); + int Fi_i(int bar); + }; +}; + +class nested { + public: + nested(); + ~nested(); + int Fi_i(int bar); +}; diff --git a/testsuite/ltrace.minor/print-instruction-pointer.c b/testsuite/ltrace.minor/print-instruction-pointer.c new file mode 100644 index 0000000..37b8441 --- /dev/null +++ b/testsuite/ltrace.minor/print-instruction-pointer.c @@ -0,0 +1,11 @@ +#include <stdio.h> + +int main () +{ + int i=0; + printf("%d\n",i); + i=1; + printf("%d\n",i); + + return 0; +} diff --git a/testsuite/ltrace.minor/print-instruction-pointer.exp b/testsuite/ltrace.minor/print-instruction-pointer.exp new file mode 100644 index 0000000..87cc8f7 --- /dev/null +++ b/testsuite/ltrace.minor/print-instruction-pointer.exp @@ -0,0 +1,42 @@ +# This file was written by Yao Qi <qiyao@cn.ibm.com>. + +set testfile "print-instruction-pointer" +set srcfile ${testfile}.c +set binfile ${testfile} + + +verbose "compiling source file now....." +# Build the shared libraries this test case needs. +if { [ltrace_compile "${srcdir}/${subdir}/${srcfile}" "${srcdir}/${subdir}/${binfile}" executable {debug} ] != "" } { + send_user "Testcase compile failed, so all tests in this file will automatically fail.\n" +} + +# set options for ltrace. +ltrace_options "-i" +# Run PUT for ltrace. +set exec_output [ltrace_runtest $srcdir/$subdir $srcdir/$subdir/$binfile] + +# Check the output of this program. +verbose "ltrace runtest output: $exec_output\n" +if [regexp {ELF from incompatible architecture} $exec_output] { + fail "32-bit ltrace can not perform on 64-bit PUTs and rebuild ltrace in 64 bit mode!" + return +} elseif [ regexp {Couldn't get .hash data} $exec_output ] { + fail "Couldn't get .hash data!" + return +} + +# Get the addrss by objdump and sed. +catch "exec sh -c {objdump -d $srcdir/$subdir/$binfile | sed -n '/^\[0-9a-fA-F\]\[0-9a-fA-F\]* <main>/,/^\[0-9a-fA-F\]\[0-9a-fA-F\]* </p'| grep -A 1 'call\\|bl' }" output +#verbose "output=$output" +catch "exec sh -c {echo \"$output\" | sed -n '2p'| awk -F' ' '{print \$1}'|awk -F: '{print \$1}'}" addr1 +catch "exec sh -c {echo \"$output\" | sed -n '5p'| awk -F' ' '{print \$1}'|awk -F: '{print \$1}'}" addr2 + +verbose "addr1 = $addr1" +verbose "addr2 = $addr2" +# Verify the output by checking numbers of print in main.ltrace. +set pattern "$addr1.*printf" +ltrace_verify_output ${srcdir}/${subdir}/${testfile}.ltrace $pattern 1 + +set pattern "$addr2.*printf" +ltrace_verify_output ${srcdir}/${subdir}/${testfile}.ltrace $pattern 1 diff --git a/testsuite/ltrace.minor/time-record-T.exp b/testsuite/ltrace.minor/time-record-T.exp new file mode 100644 index 0000000..b411add --- /dev/null +++ b/testsuite/ltrace.minor/time-record-T.exp @@ -0,0 +1,84 @@ +# This file was written by Yao Qi <qiyao@cn.ibm.com>. + +set testfile "time-record" +set srcfile ${testfile}.c +set binfile ${testfile}-T + +verbose "compiling source file now....." +# Build the shared libraries this test case needs. +if { [ ltrace_compile "${srcdir}/${subdir}/${testfile}.c" "${srcdir}/${subdir}/${binfile}" executable {debug} ] != "" } { + send_user "Testcase compile failed, so all tests in this file will automatically fail.\n" +} + +# set options for ltrace. +ltrace_options "-T" + +# Run PUT for ltrace. +set exec_output [ltrace_runtest $srcdir/$subdir $srcdir/$subdir/$binfile] +verbose "ltrace runtest output: $exec_output\n" + +# Check the output of this program. +if [regexp {ELF from incompatible architecture} $exec_output] { + fail "32-bit ltrace can not perform on 64-bit PUTs and rebuild ltrace in 64 bit mode!" + return +} elseif [ regexp {Couldn't get .hash data} $exec_output ] { + fail "Couldn't get .hash data!" + return +} + +# Get the time of nanosleep in C source file. +set fd [ open $srcdir/$subdir/$srcfile r] +while { [gets $fd line] >= 0 } { + if [ regexp {define NANOSLEEP_COUNT ([0-9]+)} $line match nanosleep_usec] then { + break + } +} +close $fd + + +# Verify the time for calling sleep. +set fd [ open $srcdir/$subdir/$binfile.ltrace r] +set FOUND 0 +while { [gets $fd line] >= 0 } { + # match the line with sleep and extract the spent time in sleep and sleep argument. + if [ regexp {sleep\(([0-9]+).*<([0-9]+\.[0-9]+)>} $line match sleep_sec sec ] then { + verbose "sleep_sec = $sleep_sec, sec = $sec" + + if { $sec >= $sleep_sec } then { + pass "Correct Time spent inside call." + } else { + fail "Spent $sec inside call, but PUT call sleep($sleep_sec)!" + } + set FOUND 1 + break + } +} +close $fd + +if {$FOUND != 1} then { + fail "Fail to find call sleep!" +} + +# Verify the time for calling nanosleep. +set FOUND 0 +set fd [ open $srcdir/$subdir/$binfile.ltrace r] +while { [gets $fd line] >= 0 } { + # match the line with nanosleep and extract spent time and nanosleep argument. + if [ regexp {nanosleep.*<([0-9]+\.[0-9]+)>} $line match usec] then { + verbose "nanosleep_usec = $nanosleep_usec, usec = $usec" + + if { $usec * 1000 >= $nanosleep_usec} then { + pass "Correct Time spent inside call." + } else { + fail "Spent $usec inside call, but PUT call nanosleep($nanosleep_usec)!" + } + set FOUND 1 + break + } +} + +if { $FOUND != 1} then { + fail "Fail to find nanosleep" +} +close $fd + diff --git a/testsuite/ltrace.minor/time-record-tt.exp b/testsuite/ltrace.minor/time-record-tt.exp new file mode 100644 index 0000000..332704a --- /dev/null +++ b/testsuite/ltrace.minor/time-record-tt.exp @@ -0,0 +1,107 @@ +# This file was written by Yao Qi <qiyao@cn.ibm.com>. + +set testfile "time-record" +set srcfile ${testfile}.c +set binfile ${testfile}-tt + +verbose "compiling source file now....." +# Build the shared libraries this test case needs. +if { [ ltrace_compile "${srcdir}/${subdir}/${testfile}.c" "${srcdir}/${subdir}/${binfile}" executable {debug} ] != "" } { + send_user "Testcase compile failed, so all tests in this file will automatically fail.\n" +} + +# Set options for ltrace. +ltrace_options "-tt" + +# Run PUT for ltrace. +set exec_output [ltrace_runtest $srcdir/$subdir $srcdir/$subdir/$binfile] + +# Check the output of this program. +verbose "ltrace runtest output: $exec_output\n" +if [regexp {ELF from incompatible architecture} $exec_output] { + fail "32-bit ltrace can not perform on 64-bit PUTs and rebuild ltrace in 64 bit mode!" + return +} elseif [ regexp {Couldn't get .hash data} $exec_output ] { + fail "Couldn't get .hash data!" + return +} + +# Verify the time for calling sleep. +set fd [ open $srcdir/$subdir/$binfile.ltrace r] +set FOUND 0 +while { [gets $fd line] >= 0 } { + # match the line with sleep and extract the strat time and sleep argument. + if [ regexp {[0-9]+:([0-9]+):([0-9]+)\.[0-9]+ sleep\(([0-9]+)} $line match start_min start_sec sleep_sec] then { + # Remove extra zero. + regexp {0([1-9])} $start_min match start_min + regexp {0([1-9])} $start_sec match start_sec + + verbose "start_sec = $start_sec, sleep_sec = $sleep_sec" + # get a new line for the end time of sleep + gets $fd line + regexp {[0-9]+:([0-9]+):([0-9]+)} $line match end_min end_sec + verbose "end_sec = $end_sec" + + # Remove extra zero. + regexp {0([1-9])} $end_min match end_min + regexp {0([1-9])} $end_sec match end_sec + + if { (($end_min - $start_min)*60 + $end_sec - $start_sec)== $sleep_sec } then { + pass "Correct Timestamp." + } else { + fail "Start at $start_sec, End at $end_sec, but PUT call sleep($sleep_sec)!" + } + set FOUND 1 + break + } +} +close $fd + +if {$FOUND != 1} then { + fail "Fail to find call sleep!" +} + +# Get the time of sleep and nanosleep in C source file. +set fd [ open $srcdir/$subdir/$srcfile r] +while { [gets $fd line] >= 0 } { + if [ regexp {define NANOSLEEP_COUNT ([0-9]+)} $line match nanosleep_usec] then { + break + } +} +close $fd + +# Verify the time for calling nanosleep. +set FOUND 0 +set fd [ open $srcdir/$subdir/$binfile.ltrace r] +while { [gets $fd line] >= 0 } { + # match the line with sleep and extract the strat time and sleep argument. + if [ regexp {[0-9]+:[0-9]+:([0-9]+)\.([0-9][0-9][0-9]).* nanosleep} $line match start_sec start_usec ] then { + # Remove extra zeros. + regexp {0([1-9])} $start_sec match start_sec + regexp {0*([1-9][0-9]*)} $start_usec match start_usec + + verbose "start_sec = $start_sec, start_usec = $start_usec, sleep_usec = $nanosleep_usec" + # get a new line for the end time of sleep + gets $fd line + regexp {[0-9]+:[0-9]+:([0-9]+)\.([0-9][0-9][0-9])} $line match end_sec end_usec + + # Remove extra zeros. + regexp {0([1-9])} $end_sec match end_sec + regexp {0*([1-9][0-9]*)} $end_usec match end_usec + + verbose "end_sec = $end_sec, end_usec = $end_usec" + if { (($end_sec - $start_sec)*1000 + $end_usec - $start_usec) >= $nanosleep_usec} then { + pass "Correct Timestamp." + } else { + fail "Start at $start_usec, End at $end_usec, but PUT call nanosleep($nanosleep_usec)!" + } + set FOUND 1 + break + } +} + +if { $FOUND != 1} then { + fail "Fail to find nanosleep" +} +close $fd + diff --git a/testsuite/ltrace.minor/time-record-ttt.exp b/testsuite/ltrace.minor/time-record-ttt.exp new file mode 100644 index 0000000..2aee9d3 --- /dev/null +++ b/testsuite/ltrace.minor/time-record-ttt.exp @@ -0,0 +1,112 @@ +# This file was written by Yao Qi <qiyao@cn.ibm.com>. + +set testfile "time-record" +set srcfile ${testfile}.c +set binfile ${testfile}-ttt + +verbose "compiling source file now....." +# Build the shared libraries this test case needs. +if { [ ltrace_compile "${srcdir}/${subdir}/${testfile}.c" "${srcdir}/${subdir}/${binfile}" executable {debug} ] != "" } { + send_user "Testcase compile failed, so all tests in this file will automatically fail.\n" +} + +# Set options for ltrace. +ltrace_options "-ttt" + +# Run PUT for ltrace. +set exec_output [ltrace_runtest $srcdir/$subdir $srcdir/$subdir/$binfile] + + +# Check the output of this program. +verbose "ltrace runtest output: $exec_output\n" +if [regexp {ELF from incompatible architecture} $exec_output] { + fail "32-bit ltrace can not perform on 64-bit PUTs and rebuild ltrace in 64 bit mode!" + return +} elseif [ regexp {Couldn't get .hash data} $exec_output ] { + fail "Couldn't get .hash data!" + return +} + +# Verify the time for calling sleep. +set fd [ open $srcdir/$subdir/$binfile.ltrace r] +set FOUND 0 +while { [gets $fd line] >= 0 } { + # match the line with sleep and extract the strat time and sleep argument. + if [ regexp {([0-9]+)\.([0-9][0-9][0-9]).* sleep\(([0-9]+)} $line match start_sec start_usec sleep_sec] then { + + # Remove extra zeros. + regexp {0*([1-9][0-9]*)} $start_sec match start_sec + regexp {0*([1-9][0-9]*)} $start_usec match start_usec + + verbose "start_sec = $start_sec, start_usec = $start_usec,sleep_sec = $sleep_sec" + # get a new line for the end time of sleep + gets $fd line + regexp {([0-9]+)\.([0-9][0-9][0-9])} $line match end_sec end_usec + verbose "end_sec = $end_sec, end_usec=$end_usec" + + # Remove extra zeros. + regexp {0*([1-9][0-9]*)} $end_sec match end_sec + regexp {0*([1-9][0-9]*)} $end_usec match end_usec + + if { $end_sec - $start_sec >= $sleep_sec } then { + pass "Correct Timestamp." + } else { + fail "Start at $start_sec, End at $end_sec, but PUT call sleep($sleep_sec)!" + } + set FOUND 1 + break + } +} + +close $fd + +if {$FOUND != 1} then { + fail "Fail to find call sleep!" +} + + +# Get the time of nanosleep in C source file. +set fd [ open $srcdir/$subdir/$srcfile r] +while { [gets $fd line] >= 0 } { + if [ regexp {define NANOSLEEP_COUNT ([0-9]+)} $line match nanosleep_usec] then { + break + } +} +close $fd + +# Verify the time for calling nanosleep. +set FOUND 0 +set fd [ open $srcdir/$subdir/$binfile.ltrace r] +while { [gets $fd line] >= 0 } { + # match the line with sleep and extract the strat time and sleep argument. + if [ regexp {([0-9]+)\.([0-9][0-9][0-9]).* nanosleep} $line match start_sec start_usec ] then { + + # Remove extra zeros. + regexp {0*([1-9][0-9]*)} $start_sec match start_sec + regexp {0*([1-9][0-9]*)} $start_usec match start_usec + + verbose "start_sec = $start_sec, start_usec = $start_usec, nanosleep_usec = $nanosleep_usec" + # get a new line for the end time of sleep + gets $fd line + regexp {([0-9]+)\.([0-9][0-9][0-9])} $line match end_sec end_usec + + # Remove extra zeros. + regexp {0*([1-9][0-9]*)} $end_sec match end_sec + regexp {0*([1-9][0-9]*)} $end_usec match end_usec + + verbose "end_sec = $end_sec, end_usec = $end_usec" + if { ($end_sec - $start_sec)*1000 + $end_usec - $start_usec >= $nanosleep_usec} then { + pass "Correct Timestamp." + } else { + fail "Start at $start_usec, End at $end_usec, but PUT call nanosleep($nanosleep_usec)!" + } + set FOUND 1 + break + } +} + +if { $FOUND != 1} then { + fail "Fail to find nanosleep" +} +close $fd + diff --git a/testsuite/ltrace.minor/time-record.c b/testsuite/ltrace.minor/time-record.c new file mode 100644 index 0000000..a66b838 --- /dev/null +++ b/testsuite/ltrace.minor/time-record.c @@ -0,0 +1,23 @@ +/* Ltrace Test : time-record.c. + Objectives : Verify that Ltrace can record timestamp and spent + time inside each call. + + This file was written by Yao Qi <qiyao@cn.ibm.com>. */ +#include <stdio.h> +#include <time.h> + +#define SLEEP_COUNT 2 +#define NANOSLEEP_COUNT 50 + +int +main () +{ + struct timespec request, remain; + request.tv_sec = 0; + request.tv_nsec = NANOSLEEP_COUNT * 1000000; + + sleep (SLEEP_COUNT); + nanosleep (&request, NULL); + + return 0; +} diff --git a/testsuite/ltrace.minor/trace-clone.c b/testsuite/ltrace.minor/trace-clone.c new file mode 100644 index 0000000..a1ccb22 --- /dev/null +++ b/testsuite/ltrace.minor/trace-clone.c @@ -0,0 +1,38 @@ +/* Ltrace Test : trace-clone.c. + Objectives : Verify that ltrace can trace to child process after + clone called. + + This file was written by Yao Qi <qiyao@cn.ibm.com>. */ + +#include <stdio.h> +#include <sys/types.h> +#include <stdlib.h> +#include <sched.h> + +int child () +{ + sleep(1); + return 0; +} + +typedef int (* myfunc)(); + +#define STACK_SIZE 1024 + +int main () +{ + pid_t pid; + static char stack[STACK_SIZE]; +#ifdef __ia64__ + pid = __clone2((myfunc)&child, stack, STACK_SIZE, CLONE_FS, NULL); +#else + pid = clone((myfunc)&child, stack + STACK_SIZE, CLONE_FS, NULL); +#endif + if (pid < 0) + { + perror("clone called failed"); + exit (1); + } + + return 0; +} diff --git a/testsuite/ltrace.minor/trace-clone.exp b/testsuite/ltrace.minor/trace-clone.exp new file mode 100644 index 0000000..bda036f --- /dev/null +++ b/testsuite/ltrace.minor/trace-clone.exp @@ -0,0 +1,44 @@ +# This file was written by Yao Qi <qiyao@cn.ibm.com>. + +set testfile "trace-clone" +set srcfile ${testfile}.c +set binfile ${testfile} + + +verbose "compiling source file now....." +# Build the shared libraries this test case needs. +if { [ ltrace_compile "${srcdir}/${subdir}/${testfile}.c" "${srcdir}/${subdir}/${binfile}" executable {debug} ] != "" } { + send_user "Testcase compile failed, so all tests in this file will automatically fail.\n" +} +global LTRACE + +#Run PUT for ltrace. +spawn $LTRACE -f $srcdir/$subdir/$binfile +set timeout 4 +expect timeout { + fail "Time out! Maybe caused by ltrace segment fault or improper timeout value here!" + return +} + +catch "exec $LTRACE -f $srcdir/$subdir/$binfile" exec_output +# Save the output +ltrace_saveoutput "${exec_output}" ${srcdir}/${subdir}/${testfile}.ltrace + +#check the output of this program. +verbose "ltrace runtest output: $exec_output\n" +if [regexp {ELF from incompatible architecture} $exec_output] { + fail "32-bit ltrace can not perform on 64-bit PUTs and rebuild ltrace in 64 bit mode!" + return +} elseif [ regexp {Operation not permitted} $exec_output ] { + fail "Operation not permitted, see testrun.log for details!" + return +} elseif [ regexp {killed by SIGKILL} $exec_output ] { + fail "killed by SIGKILL!" + return +} + + + +set pattern "clone" +ltrace_verify_output ${srcdir}/${subdir}/${testfile}.ltrace $pattern 1 + diff --git a/testsuite/ltrace.minor/trace-exec.c b/testsuite/ltrace.minor/trace-exec.c new file mode 100644 index 0000000..e798cde --- /dev/null +++ b/testsuite/ltrace.minor/trace-exec.c @@ -0,0 +1,8 @@ +#include <unistd.h> +#include <stdlib.h> + +int main (int argc, char ** argv) +{ + execl (argv[1], argv[1], NULL); + abort (); +} diff --git a/testsuite/ltrace.minor/trace-exec.exp b/testsuite/ltrace.minor/trace-exec.exp new file mode 100644 index 0000000..c9de6f3 --- /dev/null +++ b/testsuite/ltrace.minor/trace-exec.exp @@ -0,0 +1,45 @@ +set testfile "trace-exec" +set srcfile ${testfile}.c +set binfile ${testfile} + +verbose "compiling first source file now....." +if { [ ltrace_compile "${srcdir}/${subdir}/${testfile}.c" "${srcdir}/${subdir}/${binfile}" executable {debug} ] != "" } { + send_user "Testcase compile failed, so all tests in this file will automatically fail.\n" +} + +verbose "compiling second source file now....." +if { [ ltrace_compile "${srcdir}/${subdir}/${testfile}1.c" "${srcdir}/${subdir}/${binfile}1" executable {debug} ] != "" } { + send_user "Testcase compile failed, so all tests in this file will automatically fail.\n" +} + +global LTRACE +set exec_output "" + +#Run PUT for ltrace. +spawn $LTRACE $srcdir/$subdir/$testfile $srcdir/$subdir/${testfile}1 +set timeout 4 +expect timeout { + fail "Time out! Maybe caused by ltrace segment fault or improper timeout value here!" + return +} + +catch "exec $LTRACE $srcdir/$subdir/$testfile $srcdir/$subdir/${testfile}1" exec_output + +#check the output of this program. +verbose "ltrace runtest output: $exec_output\n" +if [regexp {ELF from incompatible architecture} $exec_output] { + fail "32-bit ltrace can not perform on 64-bit PUTs and rebuild ltrace in 64 bit mode!" + return +} elseif [ regexp {Couldn't get .hash data} $exec_output ] { + fail "Couldn't get .hash data!" + return +} + +ltrace_saveoutput "${exec_output}" ${srcdir}/${subdir}/${testfile}.ltrace + +# execl from first binary +ltrace_verify_output ${srcdir}/${subdir}/${testfile}.ltrace {"^execl"} 1 +# puts from second binary +ltrace_verify_output ${srcdir}/${subdir}/${testfile}.ltrace {"^puts"} 1 +# assume glibc and see we really trace both binaries +ltrace_verify_output ${srcdir}/${subdir}/${testfile}.ltrace {"^__libc_start_main"} 2 diff --git a/testsuite/ltrace.minor/trace-exec1.c b/testsuite/ltrace.minor/trace-exec1.c new file mode 100644 index 0000000..e234fa0 --- /dev/null +++ b/testsuite/ltrace.minor/trace-exec1.c @@ -0,0 +1,6 @@ +#include <stdio.h> +int main (void) +{ + puts("Hello, World."); + return 0; +} diff --git a/testsuite/ltrace.minor/trace-fork.c b/testsuite/ltrace.minor/trace-fork.c new file mode 100644 index 0000000..a2d8a56 --- /dev/null +++ b/testsuite/ltrace.minor/trace-fork.c @@ -0,0 +1,33 @@ +/* Ltrace Test : trace-fork.c + Objectives : Verify that ltrace can trace to child process after + fork called. + + This file was written by Yao Qi <qiyao@cn.ibm.com>. */ + +#include <stdio.h> +#include <sys/types.h> + +void +child () +{ + printf("Fork Child\n"); + sleep(1); +} + +int +main () +{ + pid_t pid; + pid = fork (); + + if (pid == -1) + printf("fork failed!\n"); + else if (pid == 0) + child(); + else + { + printf("My child pid is %d\n",pid); + wait(); + } + return 0; +} diff --git a/testsuite/ltrace.minor/trace-fork.exp b/testsuite/ltrace.minor/trace-fork.exp new file mode 100644 index 0000000..5f30061 --- /dev/null +++ b/testsuite/ltrace.minor/trace-fork.exp @@ -0,0 +1,40 @@ +# This file was written by Yao Qi <qiyao@cn.ibm.com>. + +set testfile "trace-fork" +set srcfile ${testfile}.c +set binfile ${testfile} + + +verbose "compiling source file now....." +# Build the shared libraries this test case needs. +if { [ ltrace_compile "${srcdir}/${subdir}/${testfile}.c" "${srcdir}/${subdir}/${binfile}" executable {debug} ] != "" } { + send_user "Testcase compile failed, so all tests in this file will automatically fail.\n" +} +global LTRACE +set exec_output "" + +#Run PUT for ltrace. +spawn $LTRACE -f $srcdir/$subdir/$binfile +set timeout 4 +expect timeout { + fail "Time out! Maybe caused by ltrace segment fault or improper timeout value here!" + return +} + +catch "exec $LTRACE -f $srcdir/$subdir/$binfile" exec_output + +#check the output of this program. +verbose "ltrace runtest output: $exec_output\n" +if [regexp {ELF from incompatible architecture} $exec_output] { + fail "32-bit ltrace can not perform on 64-bit PUTs and rebuild ltrace in 64 bit mode!" + return +} elseif [ regexp {Couldn't get .hash data} $exec_output ] { + fail "Couldn't get .hash data!" + return +} + +ltrace_saveoutput "${exec_output}" ${srcdir}/${subdir}/${testfile}.ltrace + +set pattern "fork" +ltrace_verify_output ${srcdir}/${subdir}/${testfile}.ltrace $pattern 1 + diff --git a/testsuite/ltrace.torture/Makefile b/testsuite/ltrace.torture/Makefile new file mode 100644 index 0000000..bb4baa0 --- /dev/null +++ b/testsuite/ltrace.torture/Makefile @@ -0,0 +1,33 @@ +# Copyright (C) 1992 - 2001 Free Software Foundation, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 1, or (at your option) +# any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +# + +CLEANFILES = *.log *.sum site.bak setval.tmp site.exp + +.SUFFIXES: +clean: + -rm -f signals + -rm -f *.o *.so + -rm -f *.ltrace + -rm -f $(CLEANFILES) +distclean: clean + + +.PHONY: $(RECURSIVE_TARGETS) check clean distclean realclean + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/testsuite/ltrace.torture/ia64-sigill.exp b/testsuite/ltrace.torture/ia64-sigill.exp new file mode 100644 index 0000000..c4cf5a2 --- /dev/null +++ b/testsuite/ltrace.torture/ia64-sigill.exp @@ -0,0 +1,33 @@ +# This file was written by Yao Qi <qiyao@cn.ibm.com>. + +set testfile "ia64-sigill" +set srcfile ${testfile}.s +set binfile ${testfile} + +if { [istarget ia64-*] } then { + verbose "compiling source file now....." + # Build the shared libraries this test case needs. + if { [ ltrace_compile "${srcdir}/${subdir}/${testfile}.s" "${srcdir}/${subdir}/${binfile}" executable {debug} ] != "" } { + send_user "Testcase compile failed, so all tests in this file will automatically fail\n." + } + + # Run PUT for ltarce. + set exec_output [ltrace_runtest $srcdir/$subdir $srcdir/$subdir/$binfile] + + # Check the output of this program. + verbose "ltrace runtest output: $exec_output\n" + if [regexp {ELF from incompatible architecture} $exec_output] { + fail "32-bit ltrace can not perform on 64-bit PUTs and rebuild ltrace in 64 bit mode!" + return + } elseif [ regexp {Couldn't get .hash data} $exec_output ] { + fail "Couldn't get .hash data!" + return + } + + catch "exec sh -c {grep SIGILL ${srcdir}/${subdir}/${testfile}.ltrace | wc -l ;exit}" output + if { $output == 0 } then { + pass "ltrace did interpret SIGILL as breakpoint." + } else { + fail "ltrace failed to interpret SIGILL as breakpoint." + } +} diff --git a/testsuite/ltrace.torture/ia64-sigill.s b/testsuite/ltrace.torture/ia64-sigill.s new file mode 100644 index 0000000..dccb674 --- /dev/null +++ b/testsuite/ltrace.torture/ia64-sigill.s @@ -0,0 +1,43 @@ + .file "pokus.c" + .pred.safe_across_calls p1-p5,p16-p63 + .section .rodata + .align 8 +.LC0: + stringz "" + .text + .align 16 + .global main# + .proc main# +main: + .prologue 14, 32 + .save ar.pfs, r33 + alloc r33 = ar.pfs, 0, 4, 1, 0 + .vframe r34 + mov r34 = r12 + mov r35 = r1 + .save rp, r32 + mov r32 = b0 + .body + addl r36 = @ltoffx(.LC0), r1 + ;; + ld8.mov r36 = [r36], .LC0 + br.call.sptk.many b0 = printf# + nop.b 0x0 + nop.b 0x1 + nop.b 0x2 + nop.b 0x0 + nop.b 0x1 + nop.b 0x2 + mov r1 = r35 + addl r14 = 234, r0 + ;; + mov r8 = r14 + mov ar.pfs = r33 + mov b0 = r32 + .restore sp + mov r12 = r34 + br.ret.sptk.many b0 + ;; + .endp main# + .section .note.GNU-stack,"",@progbits + .ident "GCC: (GNU) 3.4.6 20060404 (Red Hat 3.4.6-3)" diff --git a/testsuite/ltrace.torture/signals.c b/testsuite/ltrace.torture/signals.c new file mode 100644 index 0000000..439aba1 --- /dev/null +++ b/testsuite/ltrace.torture/signals.c @@ -0,0 +1,44 @@ +/* Ltrace Test : signals.c. + Objectives : Verify that ltrace can trace user defined signal. + This file was written by Yao Qi <qiyao@cn.ibm.com>. */ + +#include<stdio.h> +#include<signal.h> +#include <sys/types.h> + +#define LOOP 20 + +void +handler(int signum,siginfo_t *info,void *act) +{ +} + +int +main () +{ + struct sigaction act; + union sigval mysigval; + int i; + int sig; + pid_t pid; + + mysigval.sival_int=0; + sig = 10; + pid=getpid(); + + sigemptyset(&act.sa_mask); + act.sa_sigaction=handler; + act.sa_flags=SA_SIGINFO; + + if(sigaction(sig,&act,NULL) < 0) + { + printf("install sigal error\n"); + } + + for(i=0; i< LOOP; i++) + { + usleep(100); + sigqueue(pid,sig,mysigval); + } + return 0; +} diff --git a/testsuite/ltrace.torture/signals.exp b/testsuite/ltrace.torture/signals.exp new file mode 100644 index 0000000..56a2ec9 --- /dev/null +++ b/testsuite/ltrace.torture/signals.exp @@ -0,0 +1,37 @@ +# This file was written by Yao Qi <qiyao@cn.ibm.com>. + +set testfile "signals" +set srcfile ${testfile}.c +set binfile ${testfile} + + +verbose "compiling source file now....." +# Build the shared libraries this test case needs. +if { [ ltrace_compile "${srcdir}/${subdir}/${testfile}.c" "${srcdir}/${subdir}/${binfile}" executable {debug} ] != "" } { + send_user "Testcase compile failed, so all tests in this file will automatically fail\n." +} + +# Set options for ltrace. +ltrace_options "-L" + +# Run PUT for ltarce. +set exec_output [ltrace_runtest $srcdir/$subdir $srcdir/$subdir/$binfile] + +# Check the output of this program. +verbose "ltrace runtest output: $exec_output\n" +if [regexp {ELF from incompatible architecture} $exec_output] { + fail "32-bit ltrace can not perform on 64-bit PUTs and rebuild ltrace in 64 bit mode!" + return +} elseif [ regexp {Couldn't get .hash data} $exec_output ] { + fail "Couldn't get .hash data!" + return +} + +# Extract LOOP from source file. +set fd [ open $srcdir/$subdir/$srcfile r] +while { [gets $fd line] >= 0 } { + regexp {define LOOP.*([0-9]+)} $line match count +} +set pattern "SIGUSR1" +ltrace_verify_output ${srcdir}/${subdir}/${testfile}.ltrace $pattern $count + diff --git a/testsuite/run-my-tests.sh b/testsuite/run-my-tests.sh new file mode 100755 index 0000000..3882c79 --- /dev/null +++ b/testsuite/run-my-tests.sh @@ -0,0 +1,43 @@ +#! /bin/sh +bitmode="" + +# This shell script is used to run the ltrace test suite. It is possible to +# run it via 'make check' using RUNTESTFLAGS. This script just makes it easy. + +function usage +{ + echo usage: `basename $0` '-m32|-m64 [<tool> | ""] [<test.exp>]' +} + +# The first argument is not optional: it must either be -m32 or -m64. If the +# second argument is used, it specifies the file name of the ltrace to be +# tested. The third argument specifies a particular test case to run. If +# the third argument is omitted, then all test cases are run. If you wish to +# use the third argument, but not the second, specify the second as "". + +# there is a secret argument: if the name of this script is 'test', then +# the --verbose argument is added to RUNTESTFLAGS. + +if [ x"$1" == x -o x"$1" != x-m32 -a x"$1" != x-m64 ]; then + usage + exit 1 +fi + +flags='' + +if [ `basename $0` == test ]; then + flags="--verbose " +fi + +if [ x"$2" != x ]; then + flags="${flags}--tool_exec=$2 " +fi + +flags="${flags}CFLAGS_FOR_TARGET=$1" + +if [ x"$3" != x ]; then + flags="$flags $3" +fi + +set -o xtrace +make check RUNTESTFLAGS="$flags" |