From 7fbfe0a255eb7ff3f27efa5adc6ed69dab471ecf Mon Sep 17 00:00:00 2001 From: "mostang.com!davidm" Date: Fri, 15 Feb 2002 23:22:05 +0000 Subject: [PATCH] (Logical change 1.3) --- BitKeeper/etc/logging_ok | 1 + COPYING | 340 ++++++++++++ ChangeLog | 39 ++ NOTES | 74 +++ README | 46 ++ include/config.h | 1 + include/libunwind-config.h | 2 + include/libunwind-ia64.h | 99 ++++ include/libunwind.h | 189 +++++++ src/Makefile | 342 ++++++++++++ src/backtrace.c | 50 ++ src/ia64/NOTES | 62 +++ src/ia64/TODO | 3 + src/ia64/__ia64_install_context.S | 146 +++++ src/ia64/init.c | 250 +++++++++ src/ia64/parser.c | 875 ++++++++++++++++++++++++++++++ src/ia64/regs.c | 590 ++++++++++++++++++++ src/ia64/rse.h | 66 +++ src/ia64/script.c | 469 ++++++++++++++++ src/ia64/script.h | 73 +++ src/ia64/tables-glibc.c | 197 +++++++ src/ia64/ucontext_i.h | 59 ++ src/ia64/unw_get_reg.c | 32 ++ src/ia64/unw_init_local.c | 279 ++++++++++ src/ia64/unw_init_remote.c | 98 ++++ src/ia64/unw_is_signal_frame.c | 32 ++ src/ia64/unw_resume.c | 36 ++ src/ia64/unw_set_reg.c | 32 ++ src/ia64/unw_step.c | 134 +++++ src/ia64/unwind_decoder.c | 482 ++++++++++++++++ src/ia64/unwind_i.h | 485 +++++++++++++++++ src/tests/Makefile | 19 + src/tests/bt.c | 74 +++ src/tests/exc.c | 89 +++ src/tests/sig.c | 82 +++ src/tests/verify.c | 137 +++++ 36 files changed, 5984 insertions(+) diff --git a/BitKeeper/etc/logging_ok b/BitKeeper/etc/logging_ok index e69de29b..be9e31a3 100644 --- a/BitKeeper/etc/logging_ok +++ b/BitKeeper/etc/logging_ok @@ -0,0 +1 @@ +davidm@panda.mostang.com diff --git a/COPYING b/COPYING index e69de29b..5b6e7c66 100644 --- a/COPYING +++ b/COPYING @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + 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. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/ChangeLog b/ChangeLog index e69de29b..7e03bdb2 100644 --- a/ChangeLog +++ b/ChangeLog @@ -0,0 +1,39 @@ +2002-01-18 David Mosberger-Tang + + * src/ia64/parser.c (__ia64_unw_create_state_record): Set + IA64_FLAG_HAS_HANDLER if the unwind info descriptors indicate that + there a handler. + + * src/ia64/regs.c (__ia64_access_reg): Return zero for UNW_REG_HANDLER + in frames that don't have a personality routine. + + * src/ia64/unwind_i.h (IA64_FLAG_HAS_HANDLER): New flag. + + * src/ia64/regs.c (__ia64_access_reg): When reading UNW_REG_HANDLER, + account for the fact that the personality address is gp-relative. + + * src/ia64/parser.c (__ia64_unw_create_state_record): Fix + initialization of segbase and len. + +2002-01-17 David Mosberger-Tang + + * include/unwind-ia64.h: Include via "unwind.h" to ensure + the file is picked up from same directory. + +2002-01-16 David Mosberger-Tang + + * include/unwind.h: Define UNW_ESTOPUNWIND. This error code may + be returned by acquire_unwind_info() to force termination of + unwinding. An application may want to do this when encountering a + call frame for dynamically generated code, for example. + + * unwind.h: Pass opaque argument pointer to acquire_unwind_info() + and release_unwind_info() like we do for access_mem() etc. + +2002-01-14 David Mosberger-Tang + + * Version 0.0 released. + +2002-01-11 David Mosberger-Tang + + * ChangeLog created. diff --git a/NOTES b/NOTES index e69de29b..a2a2f63d 100644 --- a/NOTES +++ b/NOTES @@ -0,0 +1,74 @@ +The central data structure of the unwind API is the unwind cursor. +This structure tracks the frame registers and the preserved registers. +The distinction between frame registers and preserved registers is +important: the former represent the *current* value of a register (as +it existed at the current IP); the latter represent the *saved* value +of a register (i.e., the value that existed on entry to the current +procedure). The unwind API defines a handful of well-known frame +"registers": + + - ip: the instruction pointer (pc) + - rp: the return pointer (rp, aka "return address" or "return link") + - sp: the stack pointer (memory stack pointer, in the case of ia64) + - fp: the frame pointer + - first_ip: the starting address of the current "procedure" + - handler: a pointer to an architecture & language-specific + "personality" routine + - lsda: a pointer to an architecture & language-specific + data-area + +The API defines no well-known preserved registers. Each architecture +can define additional registers as needed. Of course, a portable +application may only rely on well-known registers. The names for +preserved registers are defined in the architecture-specific header +file . For example, to get the IA-64-specific register +names, an application would do: + + #include + +The API is designed to handle two primary cases: unwinding within the +current (local) process and unwinding of another ("remote") process +(e.g., through ptrace()). In the local case, the initial machine +state is captured by an unwind context (currently the same as +ucontext_t). In the remote case, the initial machine state is +captured by an unwind accessor structure, which provides callback +routines for reading/writing memory and registers and for obtaining +unwind information. + +Once a cursor has been initialized, you can step through the call +chain with the unw_step() routine. The frame registers and the +preserved state can then be accessed with unw_get_reg() or modified +with unw_set_reg(). For floating-point registers, there are separate +unw_get_fpreg() and unw_set_fpreg() routines (on some arches, e.g., +Alpha, these could be just aliases for unw_{g,s}et_reg()). The +unw_resume() routine can be used to resume execution at an arbitrary +point in the call-chain (as identified by an unwind cursor). This is +intended for exception handling and, at least for now, the intention +is to support this routine only for the local case. Kevin, if you +feel gdb could benefit from such a routine, I'd be interested to hear +about it. + +Note that it is perfectly legal to make copies of the unwind cursor. +This makes it possible, e.g., to obtain an unwind context, modify the +state in an earlier call frame, and then resume execution at the point +at which the unwind context was captured. + +Here is a quick example of how to use the unwind API to do a simple +stack trace: + + unw_cursor_t cursor; + unw_word_t ip, sp; + ucontext_t uc; + + getcontext(&uc); + unw_init_local(&cursor, &uc); + do + { + unw_get_reg(&cursor, UNW_REG_IP, &ip); + unw_get_reg(&cursor, UNW_REG_SP, &sp); + printf ("ip=%016lx sp=%016lx\n", ip, sp); + } + while (unw_step (&cursor) > 0); + +Note that this particular example should work on pretty much any +architecture, as it doesn't rely on any arch-specific registers. diff --git a/README b/README index e69de29b..fc1a5984 100644 --- a/README +++ b/README @@ -0,0 +1,46 @@ +This is version 0.0 of the unwind library. At the moment, only the +IA-64 Linux (IPF Linux) platform is supported and even that support +has received only light testing. Consequently, this release is +intended primarily to expose the unwind API to more developers and to +collect feedback on what does and does not work. Having said that, +backtracing through gcc-generated code might work reasonably well. + +There is virtually no documentation at the moment. A brief outline of +the unwind API is in file NOTES. To get a feel for how things are +intended to work, you may also want to take a look at include/unwind.h +and include/unwind-ia64.h. Finally, the test program in +src/tests/bt.c shows two ways of how to do a simple backtrace: one +uses libunwind directly, the other uses a libunwind-based +implementation of the backtrace() function. The test program in +src/tests/exc.c shows the basics of how to do exception handling with +this library. + +The following steps should be used to compile and install this library: + + $ cd src + $ make dep + $ make + $ make install prefix=PREFIX + +where PREFIX is the installation prefix. By default, a prefix of /usr +is used, such that libunwind.a is installed in /usr/lib and unwind.h +is installed in /usr/include. For testing, you may want to use a +prefix of /usr/local instead. + +Please direct all questions regarding this library to: + + libunwind@linux.hpl.hp.com + +For spam protection, you'll have to subscribe to this list before +posting a question. You can do this by sending a mail to +libunwind-request@linux.hpl.hp.com with a body of: + + subscribe libunwind + +Note: the host that is running this list is behind a firewall, so +you'll not be able to use the Web interface to manage your +subscription. Send a mail containing "help" to +libunwind-request@linux.hpl.hp.com for information on how to manage +your subscription via email. + + --david diff --git a/include/config.h b/include/config.h index e69de29b..726a584f 100644 --- a/include/config.h +++ b/include/config.h @@ -0,0 +1 @@ +#undef SIGCONTEXT_HAS_AR25_AND_AR26 diff --git a/include/libunwind-config.h b/include/libunwind-config.h index e69de29b..703ff6d3 100644 --- a/include/libunwind-config.h +++ b/include/libunwind-config.h @@ -0,0 +1,2 @@ +#define UNW_TARGET_IA64 +#undef UNW_LOCAL_ONLY diff --git a/include/libunwind-ia64.h b/include/libunwind-ia64.h index e69de29b..ba357c99 100644 --- a/include/libunwind-ia64.h +++ b/include/libunwind-ia64.h @@ -0,0 +1,99 @@ +/* libunwind - a platform-independent unwind library + Copyright (C) 2001-2002 Hewlett-Packard Co + Contributed by David Mosberger-Tang + +This file is part of libunwind. + +libunwind 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. + +libunwind 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 GNU CC; see the file COPYING. If not, write to +the Free Software Foundation, 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. + +As a special exception, if you link this library with other files to +produce an executable, this library does not by itself cause the +resulting executable to be covered by the GNU General Public License. +This exception does not however invalidate any other reasons why the +executable file might be covered by the GNU General Public +License. */ + +#include + +typedef enum + { + /* Note: general registers are excepted to start with index 0. + This convention facilitates architecture-independent + implementation of the C++ exception handling ABI. See + _Unwind_SetGR() and _Unwind_GetGR() for details. */ + UNW_IA64_GR = 0, /* general registers (r0..r127) */ + UNW_IA64_GP = UNW_IA64_GR + 1, + UNW_IA64_SP = UNW_IA64_GR + 12, + UNW_IA64_TP = UNW_IA64_GR + 13, + + UNW_IA64_NAT = UNW_IA64_GR + 128, /* NaT registers (nat0..nat127) */ + + UNW_IA64_FR = UNW_IA64_NAT + 128, /* fp registers (f0..f127) */ + + UNW_IA64_AR = UNW_IA64_FR + 128, /* application registers (ar0..r127) */ + UNW_IA64_AR_RSC = UNW_IA64_AR + 16, + UNW_IA64_AR_BSP = UNW_IA64_AR + 17, + UNW_IA64_AR_BSPSTORE = UNW_IA64_AR + 18, + UNW_IA64_AR_RNAT = UNW_IA64_AR + 19, + UNW_IA64_AR_25 = UNW_IA64_AR + 25, /* reserved (scratch) */ + UNW_IA64_AR_26 = UNW_IA64_AR + 26, /* reserved (scratch) */ + UNW_IA64_AR_CCV = UNW_IA64_AR + 32, + UNW_IA64_AR_UNAT = UNW_IA64_AR + 36, + UNW_IA64_AR_FPSR = UNW_IA64_AR + 40, + UNW_IA64_AR_PFS = UNW_IA64_AR + 64, + UNW_IA64_AR_LC = UNW_IA64_AR + 65, + UNW_IA64_AR_EC = UNW_IA64_AR + 66, + + UNW_IA64_BR = UNW_IA64_AR + 128, /* branch registers (b0..p7) */ + UNW_IA64_PR = UNW_IA64_BR + 8, /* predicate registers (p0..p63) */ + UNW_IA64_CFM, + + /* frame info (read-only): */ + UNW_IA64_CURRENT_BSP, /* read-only */ + + UNW_TDEP_LAST_REG = UNW_IA64_CURRENT_BSP + } +ia64_regnum_t; + +/* Info needed for a single IA-64 unwind. A pointer to this structure + is expected in the acquire/release callbacks of the unwind + accessors. */ +typedef struct unw_ia64_table + { + const char *name; /* table name (or NULL if none) */ + unw_word_t gp; /* global pointer for this load-module */ + unw_word_t segbase; /* base for offsets in the unwind table */ + unw_word_t length; /* number of entries in unwind table array */ + + /* Local copy of the unwind descriptor table: */ + const struct ia64_unwind_table_entry *array; + + /* Local copy of the unwind descriptor information. This is + initialized such that adding the unwind entry's info_offset + yields the address at which the corresponding descriptors can + be found. */ + const unsigned char *unwind_info_base; + } +unw_ia64_table_t; + +/* On IA-64, we can directly use ucontext_t as the unwind context. */ +typedef ucontext_t unw_tdep_context_t; + +/* XXX this is not ideal: an application should not be prevented from + using the "getcontext" name just because it's using libunwind. We + can't just use __getcontext() either, because that isn't exported + by glibc... */ +#define unw_tdep_getcontext(uc) getcontext(uc) diff --git a/include/libunwind.h b/include/libunwind.h index e69de29b..27c5d2b4 100644 --- a/include/libunwind.h +++ b/include/libunwind.h @@ -0,0 +1,189 @@ +/* libunwind - a platform-independent unwind library + Copyright (C) 2001-2002 Hewlett-Packard Co + Contributed by David Mosberger-Tang + +This file is part of libunwind. + +libunwind 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. + +libunwind 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. + +As a special exception, if you link this library with other files to +produce an executable, this library does not by itself cause the +resulting executable to be covered by the GNU General Public License. +This exception does not however invalidate any other reasons why the +executable file might be covered by the GNU General Public +License. */ + +#ifndef LIBUNWIND_H +#define LIBUNWIND_H + +#include "libunwind-config.h" + +#ifdef UNW_LOCAL_ONLY +# define UNW_PREFIX _Ul_ +#else /* !UNW_LOCAL_ONLY */ +# define UNW_PREFIX _U_ +#endif /* !UNW_LOCAL_ONLY */ + +#define UNW_PASTE2(x,y) x##y +#define UNW_PASTE(x,y) UNW_PASTE2(x,y) +#define UNW_OBJ(fn) UNW_PASTE(UNW_PREFIX, fn) + +typedef unsigned long unw_word_t; + +#if defined(UNW_TARGET_IA64) +# include "libunwind-ia64.h" +#else +# error Sorry, target architecture is not yet supported. +#endif + +/* This needs to be big enough to accommodate the unwind state of any + architecture, while leaving some slack for future expansion. + Changing this value will require recompiling all users of this + library. */ +#define UNW_STATE_LEN 127 + +/* Error codes. The unwind routines return the *negated* values of + these error codes on error and a non-negative value on success. */ +typedef enum + { + UNW_ESUCCESS = 0, /* no error */ + UNW_EUNSPEC, /* unspecified (general) error */ + UNW_ENOMEM, /* out of memory */ + UNW_EBADREG, /* bad register number */ + UNW_EREADONLYREG, /* attempt to write read-only register */ + UNW_ESTOPUNWIND, /* stop unwinding */ + UNW_EINVALIDIP, /* invalid IP */ + UNW_EBADFRAME /* bad frame */ + } +unw_error_t; + +/* The following enum defines the indices for a couple of + (pseudo-)registers which have the same meaning across all + platforms. (RO) means read-only. (RW) means read-write. General + registers (aka "integer registers") are expected to start with + index 0. The number of such registers is architecture-dependent. + The remaining indices can be used as an architecture sees fit. The + last valid register index is given by UNW_REG_LAST. */ +typedef enum + { + UNW_REG_IP = -1, /* (rw) instruction pointer (pc) */ + UNW_REG_SP = -2, /* (ro) stack pointer */ + UNW_REG_PROC_START = -3, /* (ro) starting addr. of procedure */ + UNW_REG_HANDLER = -4, /* (ro) addr. of "personality routine" */ + UNW_REG_LSDA = -5, /* (ro) addr. of language-specific data area */ + UNW_REG_LAST = UNW_TDEP_LAST_REG + } +unw_frame_regnum_t; + +typedef int unw_regnum_t; + +/* The unwind cursor starts at the youngest (most deeply nested) frame + and is used to track the frame state as the unwinder steps from + frame to frame. It is safe to make (shallow) copies of variables + of this type. */ +typedef struct unw_cursor + { + unw_word_t opaque[UNW_STATE_LEN]; + } +unw_cursor_t; + +/* This type encapsulates the entire (preserved) machine-state. */ +typedef unw_tdep_context_t unw_context_t; + +/* unw_getcontext() fills the unw_context_t pointed to by UC with the + machine state as it exists at the call-site. For implementation + reasons, this needs to be a target-dependent macro. It's easiest + to think of unw_getcontext() as being identical to getcontext(). */ +#define unw_getcontext(uc) unw_tdep_getcontext(uc) + +/* We will assume that "long double" is sufficiently large and aligned + to hold the contents of a floating-point register. Note that the + fp register format is not usually the same format as a "long + double". Instead, the content of unw_fpreg_t should be manipulated + only through the "raw.bits" member. */ +typedef union + { + struct { unsigned long bits[1]; } raw; + long double dummy; /* dummy to force 16-byte alignment */ + } +unw_fpreg_t; + +/* These are backend callback routines that provide access to the + state of a "remote" process. This can be used, for example, to + unwind another process through the ptrace() interface. */ +typedef struct unw_accessors + { + /* Lock for unwind info for address IP. The architecture specific + UNWIND_INFO is updated as necessary. */ + int (*acquire_unwind_info) (unw_word_t ip, void *unwind_info, void *arg); + int (*release_unwind_info) (void *unwind_info, void *arg); + + /* Access aligned word at address ADDR. */ + int (*access_mem) (unw_word_t addr, unw_word_t *val, int write, void *arg); + + /* Access register number REG at address ADDR. */ + int (*access_reg) (unw_regnum_t reg, unw_word_t *val, int write, + void *arg); + + /* Access register number REG at address ADDR. */ + int (*access_fpreg) (unw_regnum_t reg, unw_fpreg_t *val, int write, + void *arg); + + int (*resume) (unw_cursor_t *c, void *arg); + + void *arg; /* application-specific data */ + } +unw_accessors_t; + +/* These routines work both for local and remote unwinding. */ + +extern int UNW_OBJ(init_local) (unw_cursor_t *c, ucontext_t *u); +extern int UNW_OBJ(init_remote) (unw_cursor_t *c, unw_accessors_t *a); +extern int UNW_OBJ(step) (unw_cursor_t *c); +extern int UNW_OBJ(resume) (unw_cursor_t *c); +extern int UNW_OBJ(get_reg) (unw_cursor_t *c, int regnum, unw_word_t *valp); +extern int UNW_OBJ(set_reg) (unw_cursor_t *c, int regnum, unw_word_t val); +extern int UNW_OBJ(get_fpreg) (unw_cursor_t *c, int regnum, unw_fpreg_t *val); +extern int UNW_OBJ(set_fpreg) (unw_cursor_t *c, int regnum, unw_fpreg_t val); +extern int UNW_OBJ(is_signal_frame) (unw_cursor_t *c); + +/* Initialize cursor C such that unwinding starts at the point + represented by the context U. Returns zero on success, negative + value on failure. */ +#define unw_init_local(c,u) UNW_OBJ(init_local)(c, u) + +/* Initialize cursor C such that it accesses the unwind target through + accessors A. */ +#define unw_init_remote(c,a) UNW_OBJ(init_remote)(c, a) + +/* Move cursor up by one step (up meaning toward earlier, less deeply + nested frames). Returns positive number if there are more frames + to unwind, 0 if last frame has been reached, negative number in + case of an error. */ +#define unw_step(c) UNW_OBJ(step)(c) + +/* Resume execution at the point identified by the cursor. */ +#define unw_resume(c) UNW_OBJ(resume)(c) + +/* Register accessor routines. Return zero on success, negative value + on failure. */ +#define unw_get_reg(c,r,v) UNW_OBJ(get_reg)(c,r,v) +#define unw_set_reg(c,r,v) UNW_OBJ(set_reg)(c,r,v) + +/* Floating-point accessor routines. Return zero on success, negative + value on failure. */ +#define unw_get_fpreg(c,r,v) UNW_OBJ(get_fpreg)(c,r,v) +#define unw_set_fpreg(c,r,v) UNW_OBJ(set_fpreg)(c,r,v) + +/* Returns non-zero value if the cursor points to a signal frame. */ +#define unw_is_signal_frame(c) UNW_OBJ(is_signal_frame)(c) + +#endif /* LIBUNWIND_H */ diff --git a/src/Makefile b/src/Makefile index e69de29b..58167282 100644 --- a/src/Makefile +++ b/src/Makefile @@ -0,0 +1,342 @@ +prefix = /usr +libdir = $(prefix)/lib +includedir = $(prefix)/include + +# XXX fix for cross-compilation: +ARCH = $(shell uname -m) +CPPFLAGS = -I$(ARCH) -I../include -D_GNU_SOURCE -DIA64_UNW_ACCESSORS +CFLAGS = -Wall -g -O +SUBDIRS = tests + +ia64_OBJS = \ + __ia64_install_context.o \ + init.o parser.o regs.o script.o tables-glibc.o \ + unw_get_reg.o unw_resume.o unw_set_reg.o \ + unw_init_local.o unw_init_remote.o unw_step.o + +OBJS = backtrace.o $(patsubst %,$(ARCH)/%,$($(ARCH)_OBJS)) +LIBUNWIND = libunwind.a + +all: $(LIBUNWIND) all-recursive + +clean: clean-recursive + rm -f $(LIBUNWIND) *.o $(ARCH)/*.o + +distclean: clean distclean-recursive + rm -f *.bak *~ $(ARCH)/*~ ../include/*~ + +install: + install -m 644 libunwind.a $(libdir) + install -m 644 ../include/libunwind-config.h $(includedir) + install -m 644 ../include/libunwind.h $(includedir) + install -m 644 ../include/libunwind-$(ARCH).h $(includedir) + +%-recursive: + @for subdir in $(SUBDIRS); do \ + make -C $$subdir $*; \ + done + +libunwind.a: libunwind.a($(OBJS)) + +$(ARCH)/%.o: $(ARCH)/%.c + $(COMPILE.c) $(OUTPUT_OPTION) $< + +$(ARCH)/%.o: $(ARCH)/%.S + $(COMPILE.S) $(OUTPUT_OPTION) $< + +dep: + makedepend $(CPPFLAGS) *.c $(ARCH)/*.c + +.PHONY: clean distclean SUBDIRS + +# DO NOT DELETE + +backtrace.o: ../include/libunwind.h ../include/libunwind-config.h +backtrace.o: ../include/libunwind-ia64.h /usr/include/ucontext.h +backtrace.o: /usr/include/features.h /usr/include/sys/cdefs.h +backtrace.o: /usr/include/gnu/stubs.h /usr/include/sys/ucontext.h +backtrace.o: /usr/include/signal.h /usr/include/bits/sigset.h +backtrace.o: /usr/include/bits/types.h +backtrace.o: /usr/lib/gcc-lib/ia64-redhat-linux/2.96/include/stddef.h +backtrace.o: /usr/include/bits/pthreadtypes.h /usr/include/bits/sched.h +backtrace.o: /usr/include/bits/signum.h /usr/include/time.h +backtrace.o: /usr/include/bits/siginfo.h /usr/include/bits/sigaction.h +backtrace.o: /usr/include/bits/sigcontext.h /usr/include/asm/fpu.h +backtrace.o: /usr/include/asm/types.h /usr/include/bits/sigstack.h +backtrace.o: /usr/include/bits/sigthread.h +ia64/init.o: ia64/unwind_i.h /usr/include/memory.h /usr/include/features.h +ia64/init.o: /usr/include/sys/cdefs.h /usr/include/gnu/stubs.h +ia64/init.o: /usr/include/string.h +ia64/init.o: /usr/lib/gcc-lib/ia64-redhat-linux/2.96/include/stddef.h +ia64/init.o: /usr/include/xlocale.h /usr/include/stdint.h +ia64/init.o: /usr/include/bits/wchar.h /usr/include/bits/wordsize.h +ia64/init.o: ../include/libunwind.h ../include/libunwind-config.h +ia64/init.o: ../include/libunwind-ia64.h /usr/include/ucontext.h +ia64/init.o: /usr/include/sys/ucontext.h /usr/include/signal.h +ia64/init.o: /usr/include/bits/sigset.h /usr/include/bits/types.h +ia64/init.o: /usr/include/bits/pthreadtypes.h /usr/include/bits/sched.h +ia64/init.o: /usr/include/bits/signum.h /usr/include/time.h +ia64/init.o: /usr/include/bits/siginfo.h /usr/include/bits/sigaction.h +ia64/init.o: /usr/include/bits/sigcontext.h /usr/include/asm/fpu.h +ia64/init.o: /usr/include/asm/types.h /usr/include/bits/sigstack.h +ia64/init.o: /usr/include/bits/sigthread.h /usr/include/stdio.h +ia64/init.o: /usr/include/libio.h /usr/include/_G_config.h +ia64/init.o: /usr/include/wchar.h /usr/include/gconv.h +ia64/init.o: /usr/lib/gcc-lib/ia64-redhat-linux/2.96/include/stdarg.h +ia64/init.o: /usr/include/bits/stdio_lim.h ia64/script.h +ia64/parser.o: /usr/include/assert.h /usr/include/features.h +ia64/parser.o: /usr/include/sys/cdefs.h /usr/include/gnu/stubs.h +ia64/parser.o: /usr/include/stdio.h +ia64/parser.o: /usr/lib/gcc-lib/ia64-redhat-linux/2.96/include/stddef.h +ia64/parser.o: /usr/include/bits/types.h /usr/include/bits/pthreadtypes.h +ia64/parser.o: /usr/include/bits/sched.h /usr/include/libio.h +ia64/parser.o: /usr/include/_G_config.h /usr/include/wchar.h +ia64/parser.o: /usr/include/bits/wchar.h /usr/include/gconv.h +ia64/parser.o: /usr/lib/gcc-lib/ia64-redhat-linux/2.96/include/stdarg.h +ia64/parser.o: /usr/include/bits/stdio_lim.h /usr/include/stdlib.h +ia64/parser.o: /usr/include/bits/waitflags.h /usr/include/bits/waitstatus.h +ia64/parser.o: /usr/include/endian.h /usr/include/bits/endian.h +ia64/parser.o: /usr/include/xlocale.h /usr/include/sys/types.h +ia64/parser.o: /usr/include/time.h /usr/include/sys/select.h +ia64/parser.o: /usr/include/bits/select.h /usr/include/bits/sigset.h +ia64/parser.o: /usr/include/bits/time.h /usr/include/sys/sysmacros.h +ia64/parser.o: /usr/include/alloca.h /usr/include/string.h ia64/unwind_i.h +ia64/parser.o: /usr/include/memory.h /usr/include/stdint.h +ia64/parser.o: /usr/include/bits/wordsize.h ../include/libunwind.h +ia64/parser.o: ../include/libunwind-config.h ../include/libunwind-ia64.h +ia64/parser.o: /usr/include/ucontext.h /usr/include/sys/ucontext.h +ia64/parser.o: /usr/include/signal.h /usr/include/bits/signum.h +ia64/parser.o: /usr/include/bits/siginfo.h /usr/include/bits/sigaction.h +ia64/parser.o: /usr/include/bits/sigcontext.h /usr/include/asm/fpu.h +ia64/parser.o: /usr/include/asm/types.h /usr/include/bits/sigstack.h +ia64/parser.o: /usr/include/bits/sigthread.h ia64/script.h +ia64/parser.o: ia64/unwind_decoder.c +ia64/regs.o: ../include/config.h /usr/include/assert.h +ia64/regs.o: /usr/include/features.h /usr/include/sys/cdefs.h +ia64/regs.o: /usr/include/gnu/stubs.h ia64/unwind_i.h /usr/include/memory.h +ia64/regs.o: /usr/include/string.h +ia64/regs.o: /usr/lib/gcc-lib/ia64-redhat-linux/2.96/include/stddef.h +ia64/regs.o: /usr/include/xlocale.h /usr/include/stdint.h +ia64/regs.o: /usr/include/bits/wchar.h /usr/include/bits/wordsize.h +ia64/regs.o: ../include/libunwind.h ../include/libunwind-config.h +ia64/regs.o: ../include/libunwind-ia64.h /usr/include/ucontext.h +ia64/regs.o: /usr/include/sys/ucontext.h /usr/include/signal.h +ia64/regs.o: /usr/include/bits/sigset.h /usr/include/bits/types.h +ia64/regs.o: /usr/include/bits/pthreadtypes.h /usr/include/bits/sched.h +ia64/regs.o: /usr/include/bits/signum.h /usr/include/time.h +ia64/regs.o: /usr/include/bits/siginfo.h /usr/include/bits/sigaction.h +ia64/regs.o: /usr/include/bits/sigcontext.h /usr/include/asm/fpu.h +ia64/regs.o: /usr/include/asm/types.h /usr/include/bits/sigstack.h +ia64/regs.o: /usr/include/bits/sigthread.h /usr/include/stdio.h +ia64/regs.o: /usr/include/libio.h /usr/include/_G_config.h +ia64/regs.o: /usr/include/wchar.h /usr/include/gconv.h +ia64/regs.o: /usr/lib/gcc-lib/ia64-redhat-linux/2.96/include/stdarg.h +ia64/regs.o: /usr/include/bits/stdio_lim.h ia64/script.h ia64/rse.h +ia64/script.o: ia64/rse.h ia64/unwind_i.h /usr/include/memory.h +ia64/script.o: /usr/include/features.h /usr/include/sys/cdefs.h +ia64/script.o: /usr/include/gnu/stubs.h /usr/include/string.h +ia64/script.o: /usr/lib/gcc-lib/ia64-redhat-linux/2.96/include/stddef.h +ia64/script.o: /usr/include/xlocale.h /usr/include/stdint.h +ia64/script.o: /usr/include/bits/wchar.h /usr/include/bits/wordsize.h +ia64/script.o: ../include/libunwind.h ../include/libunwind-config.h +ia64/script.o: ../include/libunwind-ia64.h /usr/include/ucontext.h +ia64/script.o: /usr/include/sys/ucontext.h /usr/include/signal.h +ia64/script.o: /usr/include/bits/sigset.h /usr/include/bits/types.h +ia64/script.o: /usr/include/bits/pthreadtypes.h /usr/include/bits/sched.h +ia64/script.o: /usr/include/bits/signum.h /usr/include/time.h +ia64/script.o: /usr/include/bits/siginfo.h /usr/include/bits/sigaction.h +ia64/script.o: /usr/include/bits/sigcontext.h /usr/include/asm/fpu.h +ia64/script.o: /usr/include/asm/types.h /usr/include/bits/sigstack.h +ia64/script.o: /usr/include/bits/sigthread.h /usr/include/stdio.h +ia64/script.o: /usr/include/libio.h /usr/include/_G_config.h +ia64/script.o: /usr/include/wchar.h /usr/include/gconv.h +ia64/script.o: /usr/lib/gcc-lib/ia64-redhat-linux/2.96/include/stdarg.h +ia64/script.o: /usr/include/bits/stdio_lim.h ia64/script.h +ia64/tables-glibc.o: /usr/include/link.h /usr/include/features.h +ia64/tables-glibc.o: /usr/include/sys/cdefs.h /usr/include/gnu/stubs.h +ia64/tables-glibc.o: /usr/include/elf.h /usr/include/stdint.h +ia64/tables-glibc.o: /usr/lib/gcc-lib/ia64-redhat-linux/2.96/include/stddef.h +ia64/tables-glibc.o: /usr/include/bits/wchar.h /usr/include/bits/wordsize.h +ia64/tables-glibc.o: /usr/include/dlfcn.h /usr/include/bits/dlfcn.h +ia64/tables-glibc.o: /usr/include/sys/types.h /usr/include/bits/types.h +ia64/tables-glibc.o: /usr/include/bits/pthreadtypes.h +ia64/tables-glibc.o: /usr/include/bits/sched.h /usr/include/time.h +ia64/tables-glibc.o: /usr/include/endian.h /usr/include/bits/endian.h +ia64/tables-glibc.o: /usr/include/sys/select.h /usr/include/bits/select.h +ia64/tables-glibc.o: /usr/include/bits/sigset.h /usr/include/bits/time.h +ia64/tables-glibc.o: /usr/include/sys/sysmacros.h +ia64/tables-glibc.o: /usr/include/bits/elfclass.h /usr/include/stdlib.h +ia64/tables-glibc.o: /usr/include/bits/waitflags.h +ia64/tables-glibc.o: /usr/include/bits/waitstatus.h /usr/include/xlocale.h +ia64/tables-glibc.o: /usr/include/alloca.h ia64/unwind_i.h +ia64/tables-glibc.o: /usr/include/memory.h /usr/include/string.h +ia64/tables-glibc.o: ../include/libunwind.h ../include/libunwind-config.h +ia64/tables-glibc.o: ../include/libunwind-ia64.h /usr/include/ucontext.h +ia64/tables-glibc.o: /usr/include/sys/ucontext.h /usr/include/signal.h +ia64/tables-glibc.o: /usr/include/bits/signum.h /usr/include/bits/siginfo.h +ia64/tables-glibc.o: /usr/include/bits/sigaction.h +ia64/tables-glibc.o: /usr/include/bits/sigcontext.h /usr/include/asm/fpu.h +ia64/tables-glibc.o: /usr/include/asm/types.h /usr/include/bits/sigstack.h +ia64/tables-glibc.o: /usr/include/bits/sigthread.h /usr/include/stdio.h +ia64/tables-glibc.o: /usr/include/libio.h /usr/include/_G_config.h +ia64/tables-glibc.o: /usr/include/wchar.h /usr/include/gconv.h +ia64/tables-glibc.o: /usr/lib/gcc-lib/ia64-redhat-linux/2.96/include/stdarg.h +ia64/tables-glibc.o: /usr/include/bits/stdio_lim.h ia64/script.h +ia64/tables-glibc.o: /usr/include/unistd.h /usr/include/bits/posix_opt.h +ia64/tables-glibc.o: /usr/include/bits/environments.h +ia64/tables-glibc.o: /usr/include/bits/confname.h /usr/include/getopt.h +ia64/tables-glibc.o: /usr/include/sys/syscall.h /usr/include/asm/unistd.h +ia64/tables-glibc.o: /usr/include/asm/break.h /usr/include/bits/syscall.h +ia64/unw_get_reg.o: ia64/unwind_i.h /usr/include/memory.h +ia64/unw_get_reg.o: /usr/include/features.h /usr/include/sys/cdefs.h +ia64/unw_get_reg.o: /usr/include/gnu/stubs.h /usr/include/string.h +ia64/unw_get_reg.o: /usr/lib/gcc-lib/ia64-redhat-linux/2.96/include/stddef.h +ia64/unw_get_reg.o: /usr/include/xlocale.h /usr/include/stdint.h +ia64/unw_get_reg.o: /usr/include/bits/wchar.h /usr/include/bits/wordsize.h +ia64/unw_get_reg.o: ../include/libunwind.h ../include/libunwind-config.h +ia64/unw_get_reg.o: ../include/libunwind-ia64.h /usr/include/ucontext.h +ia64/unw_get_reg.o: /usr/include/sys/ucontext.h /usr/include/signal.h +ia64/unw_get_reg.o: /usr/include/bits/sigset.h /usr/include/bits/types.h +ia64/unw_get_reg.o: /usr/include/bits/pthreadtypes.h +ia64/unw_get_reg.o: /usr/include/bits/sched.h /usr/include/bits/signum.h +ia64/unw_get_reg.o: /usr/include/time.h /usr/include/bits/siginfo.h +ia64/unw_get_reg.o: /usr/include/bits/sigaction.h +ia64/unw_get_reg.o: /usr/include/bits/sigcontext.h /usr/include/asm/fpu.h +ia64/unw_get_reg.o: /usr/include/asm/types.h /usr/include/bits/sigstack.h +ia64/unw_get_reg.o: /usr/include/bits/sigthread.h /usr/include/stdio.h +ia64/unw_get_reg.o: /usr/include/libio.h /usr/include/_G_config.h +ia64/unw_get_reg.o: /usr/include/wchar.h /usr/include/gconv.h +ia64/unw_get_reg.o: /usr/lib/gcc-lib/ia64-redhat-linux/2.96/include/stdarg.h +ia64/unw_get_reg.o: /usr/include/bits/stdio_lim.h ia64/script.h +ia64/unw_init_local.o: /usr/include/string.h /usr/include/features.h +ia64/unw_init_local.o: /usr/include/sys/cdefs.h /usr/include/gnu/stubs.h +ia64/unw_init_local.o: /usr/lib/gcc-lib/ia64-redhat-linux/2.96/include/stddef.h +ia64/unw_init_local.o: /usr/include/xlocale.h ia64/rse.h ia64/unwind_i.h +ia64/unw_init_local.o: /usr/include/memory.h /usr/include/stdint.h +ia64/unw_init_local.o: /usr/include/bits/wchar.h /usr/include/bits/wordsize.h +ia64/unw_init_local.o: ../include/libunwind.h ../include/libunwind-config.h +ia64/unw_init_local.o: ../include/libunwind-ia64.h /usr/include/ucontext.h +ia64/unw_init_local.o: /usr/include/sys/ucontext.h /usr/include/signal.h +ia64/unw_init_local.o: /usr/include/bits/sigset.h /usr/include/bits/types.h +ia64/unw_init_local.o: /usr/include/bits/pthreadtypes.h +ia64/unw_init_local.o: /usr/include/bits/sched.h /usr/include/bits/signum.h +ia64/unw_init_local.o: /usr/include/time.h /usr/include/bits/siginfo.h +ia64/unw_init_local.o: /usr/include/bits/sigaction.h +ia64/unw_init_local.o: /usr/include/bits/sigcontext.h /usr/include/asm/fpu.h +ia64/unw_init_local.o: /usr/include/asm/types.h /usr/include/bits/sigstack.h +ia64/unw_init_local.o: /usr/include/bits/sigthread.h /usr/include/stdio.h +ia64/unw_init_local.o: /usr/include/libio.h /usr/include/_G_config.h +ia64/unw_init_local.o: /usr/include/wchar.h /usr/include/gconv.h +ia64/unw_init_local.o: /usr/lib/gcc-lib/ia64-redhat-linux/2.96/include/stdarg.h +ia64/unw_init_local.o: /usr/include/bits/stdio_lim.h ia64/script.h +ia64/unw_init_remote.o: ia64/rse.h ia64/unwind_i.h /usr/include/memory.h +ia64/unw_init_remote.o: /usr/include/features.h /usr/include/sys/cdefs.h +ia64/unw_init_remote.o: /usr/include/gnu/stubs.h /usr/include/string.h +ia64/unw_init_remote.o: /usr/lib/gcc-lib/ia64-redhat-linux/2.96/include/stddef.h +ia64/unw_init_remote.o: /usr/include/xlocale.h /usr/include/stdint.h +ia64/unw_init_remote.o: /usr/include/bits/wchar.h +ia64/unw_init_remote.o: /usr/include/bits/wordsize.h ../include/libunwind.h +ia64/unw_init_remote.o: ../include/libunwind-config.h +ia64/unw_init_remote.o: ../include/libunwind-ia64.h /usr/include/ucontext.h +ia64/unw_init_remote.o: /usr/include/sys/ucontext.h /usr/include/signal.h +ia64/unw_init_remote.o: /usr/include/bits/sigset.h /usr/include/bits/types.h +ia64/unw_init_remote.o: /usr/include/bits/pthreadtypes.h +ia64/unw_init_remote.o: /usr/include/bits/sched.h /usr/include/bits/signum.h +ia64/unw_init_remote.o: /usr/include/time.h /usr/include/bits/siginfo.h +ia64/unw_init_remote.o: /usr/include/bits/sigaction.h +ia64/unw_init_remote.o: /usr/include/bits/sigcontext.h /usr/include/asm/fpu.h +ia64/unw_init_remote.o: /usr/include/asm/types.h /usr/include/bits/sigstack.h +ia64/unw_init_remote.o: /usr/include/bits/sigthread.h /usr/include/stdio.h +ia64/unw_init_remote.o: /usr/include/libio.h /usr/include/_G_config.h +ia64/unw_init_remote.o: /usr/include/wchar.h /usr/include/gconv.h +ia64/unw_init_remote.o: /usr/lib/gcc-lib/ia64-redhat-linux/2.96/include/stdarg.h +ia64/unw_init_remote.o: /usr/include/bits/stdio_lim.h ia64/script.h +ia64/unw_is_signal_frame.o: ia64/unwind_i.h /usr/include/memory.h +ia64/unw_is_signal_frame.o: /usr/include/features.h /usr/include/sys/cdefs.h +ia64/unw_is_signal_frame.o: /usr/include/gnu/stubs.h /usr/include/string.h +ia64/unw_is_signal_frame.o: /usr/lib/gcc-lib/ia64-redhat-linux/2.96/include/stddef.h +ia64/unw_is_signal_frame.o: /usr/include/xlocale.h /usr/include/stdint.h +ia64/unw_is_signal_frame.o: /usr/include/bits/wchar.h +ia64/unw_is_signal_frame.o: /usr/include/bits/wordsize.h +ia64/unw_is_signal_frame.o: ../include/libunwind.h +ia64/unw_is_signal_frame.o: ../include/libunwind-config.h +ia64/unw_is_signal_frame.o: ../include/libunwind-ia64.h +ia64/unw_is_signal_frame.o: /usr/include/ucontext.h +ia64/unw_is_signal_frame.o: /usr/include/sys/ucontext.h /usr/include/signal.h +ia64/unw_is_signal_frame.o: /usr/include/bits/sigset.h +ia64/unw_is_signal_frame.o: /usr/include/bits/types.h +ia64/unw_is_signal_frame.o: /usr/include/bits/pthreadtypes.h +ia64/unw_is_signal_frame.o: /usr/include/bits/sched.h +ia64/unw_is_signal_frame.o: /usr/include/bits/signum.h /usr/include/time.h +ia64/unw_is_signal_frame.o: /usr/include/bits/siginfo.h +ia64/unw_is_signal_frame.o: /usr/include/bits/sigaction.h +ia64/unw_is_signal_frame.o: /usr/include/bits/sigcontext.h +ia64/unw_is_signal_frame.o: /usr/include/asm/fpu.h /usr/include/asm/types.h +ia64/unw_is_signal_frame.o: /usr/include/bits/sigstack.h +ia64/unw_is_signal_frame.o: /usr/include/bits/sigthread.h +ia64/unw_is_signal_frame.o: /usr/include/stdio.h /usr/include/libio.h +ia64/unw_is_signal_frame.o: /usr/include/_G_config.h /usr/include/wchar.h +ia64/unw_is_signal_frame.o: /usr/include/gconv.h +ia64/unw_is_signal_frame.o: /usr/lib/gcc-lib/ia64-redhat-linux/2.96/include/stdarg.h +ia64/unw_is_signal_frame.o: /usr/include/bits/stdio_lim.h ia64/script.h +ia64/unw_resume.o: ia64/unwind_i.h /usr/include/memory.h +ia64/unw_resume.o: /usr/include/features.h /usr/include/sys/cdefs.h +ia64/unw_resume.o: /usr/include/gnu/stubs.h /usr/include/string.h +ia64/unw_resume.o: /usr/lib/gcc-lib/ia64-redhat-linux/2.96/include/stddef.h +ia64/unw_resume.o: /usr/include/xlocale.h /usr/include/stdint.h +ia64/unw_resume.o: /usr/include/bits/wchar.h /usr/include/bits/wordsize.h +ia64/unw_resume.o: ../include/libunwind.h ../include/libunwind-config.h +ia64/unw_resume.o: ../include/libunwind-ia64.h /usr/include/ucontext.h +ia64/unw_resume.o: /usr/include/sys/ucontext.h /usr/include/signal.h +ia64/unw_resume.o: /usr/include/bits/sigset.h /usr/include/bits/types.h +ia64/unw_resume.o: /usr/include/bits/pthreadtypes.h /usr/include/bits/sched.h +ia64/unw_resume.o: /usr/include/bits/signum.h /usr/include/time.h +ia64/unw_resume.o: /usr/include/bits/siginfo.h /usr/include/bits/sigaction.h +ia64/unw_resume.o: /usr/include/bits/sigcontext.h /usr/include/asm/fpu.h +ia64/unw_resume.o: /usr/include/asm/types.h /usr/include/bits/sigstack.h +ia64/unw_resume.o: /usr/include/bits/sigthread.h /usr/include/stdio.h +ia64/unw_resume.o: /usr/include/libio.h /usr/include/_G_config.h +ia64/unw_resume.o: /usr/include/wchar.h /usr/include/gconv.h +ia64/unw_resume.o: /usr/lib/gcc-lib/ia64-redhat-linux/2.96/include/stdarg.h +ia64/unw_resume.o: /usr/include/bits/stdio_lim.h ia64/script.h +ia64/unw_set_reg.o: ia64/unwind_i.h /usr/include/memory.h +ia64/unw_set_reg.o: /usr/include/features.h /usr/include/sys/cdefs.h +ia64/unw_set_reg.o: /usr/include/gnu/stubs.h /usr/include/string.h +ia64/unw_set_reg.o: /usr/lib/gcc-lib/ia64-redhat-linux/2.96/include/stddef.h +ia64/unw_set_reg.o: /usr/include/xlocale.h /usr/include/stdint.h +ia64/unw_set_reg.o: /usr/include/bits/wchar.h /usr/include/bits/wordsize.h +ia64/unw_set_reg.o: ../include/libunwind.h ../include/libunwind-config.h +ia64/unw_set_reg.o: ../include/libunwind-ia64.h /usr/include/ucontext.h +ia64/unw_set_reg.o: /usr/include/sys/ucontext.h /usr/include/signal.h +ia64/unw_set_reg.o: /usr/include/bits/sigset.h /usr/include/bits/types.h +ia64/unw_set_reg.o: /usr/include/bits/pthreadtypes.h +ia64/unw_set_reg.o: /usr/include/bits/sched.h /usr/include/bits/signum.h +ia64/unw_set_reg.o: /usr/include/time.h /usr/include/bits/siginfo.h +ia64/unw_set_reg.o: /usr/include/bits/sigaction.h +ia64/unw_set_reg.o: /usr/include/bits/sigcontext.h /usr/include/asm/fpu.h +ia64/unw_set_reg.o: /usr/include/asm/types.h /usr/include/bits/sigstack.h +ia64/unw_set_reg.o: /usr/include/bits/sigthread.h /usr/include/stdio.h +ia64/unw_set_reg.o: /usr/include/libio.h /usr/include/_G_config.h +ia64/unw_set_reg.o: /usr/include/wchar.h /usr/include/gconv.h +ia64/unw_set_reg.o: /usr/lib/gcc-lib/ia64-redhat-linux/2.96/include/stdarg.h +ia64/unw_set_reg.o: /usr/include/bits/stdio_lim.h ia64/script.h +ia64/unw_step.o: /usr/include/signal.h /usr/include/features.h +ia64/unw_step.o: /usr/include/sys/cdefs.h /usr/include/gnu/stubs.h +ia64/unw_step.o: /usr/include/bits/sigset.h /usr/include/bits/types.h +ia64/unw_step.o: /usr/lib/gcc-lib/ia64-redhat-linux/2.96/include/stddef.h +ia64/unw_step.o: /usr/include/bits/pthreadtypes.h /usr/include/bits/sched.h +ia64/unw_step.o: /usr/include/bits/signum.h /usr/include/time.h +ia64/unw_step.o: /usr/include/bits/siginfo.h /usr/include/bits/sigaction.h +ia64/unw_step.o: /usr/include/bits/sigcontext.h /usr/include/asm/fpu.h +ia64/unw_step.o: /usr/include/asm/types.h /usr/include/bits/sigstack.h +ia64/unw_step.o: /usr/include/ucontext.h /usr/include/sys/ucontext.h +ia64/unw_step.o: /usr/include/bits/sigthread.h ia64/rse.h ia64/unwind_i.h +ia64/unw_step.o: /usr/include/memory.h /usr/include/string.h +ia64/unw_step.o: /usr/include/xlocale.h /usr/include/stdint.h +ia64/unw_step.o: /usr/include/bits/wchar.h /usr/include/bits/wordsize.h +ia64/unw_step.o: ../include/libunwind.h ../include/libunwind-config.h +ia64/unw_step.o: ../include/libunwind-ia64.h /usr/include/stdio.h +ia64/unw_step.o: /usr/include/libio.h /usr/include/_G_config.h +ia64/unw_step.o: /usr/include/wchar.h /usr/include/gconv.h +ia64/unw_step.o: /usr/lib/gcc-lib/ia64-redhat-linux/2.96/include/stdarg.h +ia64/unw_step.o: /usr/include/bits/stdio_lim.h ia64/script.h diff --git a/src/backtrace.c b/src/backtrace.c index e69de29b..e3d0a9a3 100644 --- a/src/backtrace.c +++ b/src/backtrace.c @@ -0,0 +1,50 @@ +/* libunwind - a platform-independent unwind library + Copyright (C) 2001-2002 Hewlett-Packard Co + Contributed by David Mosberger-Tang + +This file is part of libunwind. + +libunwind 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. + +libunwind 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. + +As a special exception, if you link this library with other files to +produce an executable, this library does not by itself cause the +resulting executable to be covered by the GNU General Public License. +This exception does not however invalidate any other reasons why the +executable file might be covered by the GNU General Public +License. */ + +#include + +/* See glibc manual for a description of this function. */ + +int +backtrace (void **buffer, int size) +{ + unw_cursor_t cursor; + unw_context_t uc; + unw_word_t ip; + int n = 0; + + unw_getcontext (&uc); + if (unw_init_local (&cursor, &uc) < 0) + return 0; + + while (unw_step (&cursor) > 0) + { + if (n >= size) + return n; + + if (unw_get_reg (&cursor, UNW_REG_IP, &ip) < 0) + return n; + buffer[n++] = (void *) ip; + } + return n; +} diff --git a/src/ia64/NOTES b/src/ia64/NOTES index e69de29b..fa2e2a80 100644 --- a/src/ia64/NOTES +++ b/src/ia64/NOTES @@ -0,0 +1,62 @@ +- the frame state consists of the following: + + - ip current instruction pointer + - sp current stack pointer value + - bsp current backing store pointer + - cfm current frame mask + + these are derived from the next younger (more deeply nested) frame + as follows: + + - ip == saved return-link (may be b0 or an alternate branch-reg) + - sp == if younger frame has a fixed-sized frame, sp + size-of-frame, + else saved sp + - cfm == saved ar.pfs + - bsp == if ar.bsp has been saved, saved ar.bsp, otherwise, + ar.bsp \ominus saved ar.pfs.pfm.sol + +The unwind cursor should represent the machine state as it existed at +the address contained in register ip. This state consists of the +*current* frame state and the save locations in the next younger +frame. + +An unwind script current takes the old save locations and updates them +for the next older frame. With the new setup, we need to update the +frame state first, without updating the other save locations. For this +to work, we need the following info: + + - save location of return-link + - save location of ar.pfs + - save location of bsp (if it has been saved) + - size of stack frame (fixed case) or save location of sp + + +setup: + + func: ... + ... + ... + br.call foo <-- call site + ... <-- ip + ... + +initial state: + + The unwind cursor represents the (preserved) machine state + as it existed at "ip". + + Evaluating the unwind descriptors for "ip" yields the following + info: + + - frame size at call site (or previous sp) + - what registers where saved where by func before + the call site was reached + + + Problem: there is some procedure info that needs to be obtained + for the new "ip" which is contained in the unwind + descriptors. Specifically, the following is needed: + + - procedure's start address + - personality address + - pointer to language-specific data area diff --git a/src/ia64/TODO b/src/ia64/TODO index e69de29b..c74a09b6 100644 --- a/src/ia64/TODO +++ b/src/ia64/TODO @@ -0,0 +1,3 @@ +- c->prev_script is redundant now that we have c->script +- finish & test unwinding across signal frame (pay attention that signal + frame gets updated) diff --git a/src/ia64/__ia64_install_context.S b/src/ia64/__ia64_install_context.S index e69de29b..3fff0d4f 100644 --- a/src/ia64/__ia64_install_context.S +++ b/src/ia64/__ia64_install_context.S @@ -0,0 +1,146 @@ +/* libunwind - a platform-independent unwind library + Copyright (C) 2001-2002 Hewlett-Packard Co + Contributed by David Mosberger-Tang + +This file is part of libunwind. + +libunwind 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. + +libunwind 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. + +As a special exception, if you link this library with other files to +produce an executable, this library does not by itself cause the +resulting executable to be covered by the GNU General Public License. +This exception does not however invalidate any other reasons why the +executable file might be covered by the GNU General Public +License. */ + +#include "ucontext_i.h" + +/* __ia64_install_context (const ucontext_t *ucp, + long r15, long r16, long r17, long r18) + + Restores the machine context in UCP and thereby resumes execution + in that context. + + This implementation in intended to be used for *synchronous* context + switches only. Therefore, it does not have to restore anything + other than the PRESERVED state. */ + + .global __ia64_install_context + .proc __ia64_install_context +__ia64_install_context: + alloc r16 = ar.pfs, 5, 0, 0, 0 + invala + add r2 = SC_NAT, r32 + ;; + ld8 rNAT = [r2], (SC_BSP-SC_NAT) + add r3 = SC_RNAT, r32 // r3 <- &sc_ar_rnat + add rPOS = SC_GR, r32 // rPOS <- &sc_gr[0] + ;; + ld8 rBSP = [r2], (SC_UNAT-SC_BSP) + ld8 rRNAT = [r3], (SC_FPSR-SC_RNAT) + extr.u rPOS = rPOS, 3, 6 // get NaT bit number for r0 + ;; + ld8 rUNAT = [r2], (SC_PFS-SC_UNAT) + ld8 rFPSR = [r3], (SC_LC-SC_FPSR) + /* + * Rotate NaT bits by rPOS positions to the left: + */ + sub rCPOS = 64, rPOS + ;; + ld8 rPFS = [r2], (SC_PR-SC_PFS) + ld8 rLC = [r3], (SC_BR+0*8-SC_LC) + shl rTMP = rNAT, rPOS + ;; + ld8 rPR = [r2], (SC_BR+1*8-SC_PR) + ld8 rB0 = [r3], 16 + shr.u rNAT = rNAT, rCPOS + ;; + ld8 rB1 = [r2], 16 + ld8 rB2 = [r3], 16 + or rNAT = rNAT, rTMP + ;; + mov.m ar.unat = rNAT + mov.m rRSC = ar.rsc + ;; + ld8 rB3 = [r2], 16 + ld8 rB4 = [r3], (SC_GR+1*8-(SC_BR+4*8)) + mov r15 = in1 + ;; + ld8 rB5 = [r2], (SC_GR+4*8-(SC_BR+5*8)) + ld8.fill r1 = [r3], (5*8 - 1*8) + mov r16 = in2 + ;; + ld8.fill r4 = [r2], 16 + ld8.fill r5 = [r3], 16 + mov b0 = rB0 + ;; + ld8.fill r6 = [r2], 48 + ld8.fill r7 = [r3], (SC_FR+2*16-(SC_GR+7*8)) + mov r17 = in3 + ;; + ld8.fill sp = [r2], (SC_FR+3*16-(SC_GR+12*8)) + mov.m ar.fpsr = rFPSR + mov.i ar.pfs = rPFS + ;; + ldf.fill f3 = [r2], 16 + ldf.fill f2 = [r3], 48 + mov b1 = rB1 + ;; + ldf.fill f4 = [r2], (16*16-4*16) + ldf.fill f5 = [r3], (17*16-5*16) + mov b2 = rB2 + ;; + ldf.fill f16 = [r2], 32 + ldf.fill f17 = [r3], 32 + mov b3 = rB3 + ;; + ldf.fill f18 = [r2], 32 + ldf.fill f19 = [r3], 32 + mov b4 = rB4 + ;; + ldf.fill f20 = [r2], 32 + ldf.fill f21 = [r3], 32 + mov b5 = rB5 + ;; + ldf.fill f22 = [r2], 32 + ldf.fill f23 = [r3], 32 + mov r8 = 0 + ;; + ldf.fill f24 = [r2], 32 + ldf.fill f25 = [r3], 32 + mov r9 = 0 + ;; + ldf.fill f26 = [r2], 32 + ldf.fill f27 = [r3], 32 + dep rTMP = 0, rRSC, 16, 14 // clear ar.rsc.loadrs + ;; + ldf.fill f28 = [r2], 32 + ldf.fill f29 = [r3], 32 + and rTMP = ~0x3, rTMP // clear ar.rsc.mode + ;; + ldf.fill f30 = [r2], 32 + ldf.fill f31 = [r3], 32 + mov pr = rPR, -1 + ;; + mov.m ar.rsc = rTMP // put RSE into enforced lazy mode + ;; + loadrs // drop dirty partition + mov r18 = in4 + ;; + mov.m ar.bspstore = rBSP + mov.m ar.unat = rUNAT + mov.i ar.lc = rLC + ;; + mov.m ar.rnat = rRNAT + mov.m ar.rsc = rRSC + br.ret.sptk rp + + .endp __ia64_install_context diff --git a/src/ia64/init.c b/src/ia64/init.c index e69de29b..e4c6298e 100644 --- a/src/ia64/init.c +++ b/src/ia64/init.c @@ -0,0 +1,250 @@ +/* libunwind - a platform-independent unwind library + Copyright (C) 2001-2002 Hewlett-Packard Co + Contributed by David Mosberger-Tang + +This file is part of libunwind. + +libunwind 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. + +libunwind 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. + +As a special exception, if you link this library with other files to +produce an executable, this library does not by itself cause the +resulting executable to be covered by the GNU General Public License. +This exception does not however invalidate any other reasons why the +executable file might be covered by the GNU General Public +License. */ + +#include "unwind_i.h" + +struct ia64_global_unwind_state unw = + { + first_time: 1, +// tables: &unw.kernel_table, +// lock: SPIN_LOCK_UNLOCKED, + save_order: { + IA64_REG_RP, IA64_REG_PFS, IA64_REG_PSP, IA64_REG_PR, + IA64_REG_UNAT, IA64_REG_LC, IA64_REG_FPSR, IA64_REG_PRI_UNAT_GR + }, + preg_index: { + struct_offset(struct ia64_cursor, pri_unat_loc)/8, /* PRI_UNAT_GR */ + struct_offset(struct ia64_cursor, pri_unat_loc)/8, /* PRI_UNAT_MEM */ + struct_offset(struct ia64_cursor, bsp_loc)/8, + struct_offset(struct ia64_cursor, bspstore_loc)/8, + struct_offset(struct ia64_cursor, pfs_loc)/8, + struct_offset(struct ia64_cursor, rnat_loc)/8, + struct_offset(struct ia64_cursor, psp)/8, + struct_offset(struct ia64_cursor, rp_loc)/8, + struct_offset(struct ia64_cursor, r4_loc)/8, + struct_offset(struct ia64_cursor, r5_loc)/8, + struct_offset(struct ia64_cursor, r6_loc)/8, + struct_offset(struct ia64_cursor, r7_loc)/8, + struct_offset(struct ia64_cursor, nat4_loc)/8, + struct_offset(struct ia64_cursor, nat5_loc)/8, + struct_offset(struct ia64_cursor, nat6_loc)/8, + struct_offset(struct ia64_cursor, nat7_loc)/8, + struct_offset(struct ia64_cursor, unat_loc)/8, + struct_offset(struct ia64_cursor, pr_loc)/8, + struct_offset(struct ia64_cursor, lc_loc)/8, + struct_offset(struct ia64_cursor, fpsr_loc)/8, + struct_offset(struct ia64_cursor, b1_loc)/8, + struct_offset(struct ia64_cursor, b2_loc)/8, + struct_offset(struct ia64_cursor, b3_loc)/8, + struct_offset(struct ia64_cursor, b4_loc)/8, + struct_offset(struct ia64_cursor, b5_loc)/8, + struct_offset(struct ia64_cursor, f2_loc)/8, + struct_offset(struct ia64_cursor, f3_loc)/8, + struct_offset(struct ia64_cursor, f4_loc)/8, + struct_offset(struct ia64_cursor, f5_loc)/8, + struct_offset(struct ia64_cursor, fr_loc[16 - 16])/8, + struct_offset(struct ia64_cursor, fr_loc[17 - 16])/8, + struct_offset(struct ia64_cursor, fr_loc[18 - 16])/8, + struct_offset(struct ia64_cursor, fr_loc[19 - 16])/8, + struct_offset(struct ia64_cursor, fr_loc[20 - 16])/8, + struct_offset(struct ia64_cursor, fr_loc[21 - 16])/8, + struct_offset(struct ia64_cursor, fr_loc[22 - 16])/8, + struct_offset(struct ia64_cursor, fr_loc[23 - 16])/8, + struct_offset(struct ia64_cursor, fr_loc[24 - 16])/8, + struct_offset(struct ia64_cursor, fr_loc[25 - 16])/8, + struct_offset(struct ia64_cursor, fr_loc[26 - 16])/8, + struct_offset(struct ia64_cursor, fr_loc[27 - 16])/8, + struct_offset(struct ia64_cursor, fr_loc[28 - 16])/8, + struct_offset(struct ia64_cursor, fr_loc[29 - 16])/8, + struct_offset(struct ia64_cursor, fr_loc[30 - 16])/8, + struct_offset(struct ia64_cursor, fr_loc[31 - 16])/8, + }, + hash : { [0 ... IA64_UNW_HASH_SIZE - 1] = -1 }, +#if IA64_UNW_DEBUG + debug_level: 0, + preg_name: { + "pri_unat_gr", "pri_unat_mem", "bsp", "bspstore", "ar.pfs", "ar.rnat", + "psp", "rp", + "r4", "r5", "r6", "r7", + "ar.unat", "pr", "ar.lc", "ar.fpsr", + "b1", "b2", "b3", "b4", "b5", + "f2", "f3", "f4", "f5", + "f16", "f17", "f18", "f19", "f20", "f21", "f22", "f23", + "f24", "f25", "f26", "f27", "f28", "f29", "f30", "f31" + } +#endif +}; + +#ifdef IA64_UNW_DEBUG + +static const char *regname[] = { + "", + "", + "", + "", + "", + "", + "", + "r0", "gp", "r2", "r3", "r4", "r5", "r6", "r7", + "r8", "r9", "r10", "r11", "sp", "tp", "r14", "r15", + "r16", "r17", "r18", "r19", "r20", "r21", "r22", "r23", + "r24", "r25", "r26", "r27", "r28", "r29", "r30", "r31", + "r32", "r33", "r34", "r35", "r36", "r37", "r38", "r39", + "r40", "r41", "r42", "r43", "r44", "r45", "r46", "r47", + "r48", "r49", "r50", "r51", "r52", "r53", "r54", "r55", + "r56", "r57", "r58", "r59", "r60", "r61", "r62", "r63", + "r64", "r65", "r66", "r67", "r68", "r69", "r70", "r71", + "r72", "r73", "r74", "r75", "r76", "r77", "r78", "r79", + "r80", "r81", "r82", "r83", "r84", "r85", "r86", "r87", + "r88", "r89", "r90", "r91", "r92", "r93", "r94", "r95", + "r96", "r97", "r98", "r99", "r100", "r101", "r102", "r103", + "r104", "r105", "r106", "r107", "r108", "r109", "r110", "r111", + "r112", "r113", "r114", "r115", "r116", "r117", "r118", "r119", + "r120", "r121", "r122", "r123", "r124", "r125", "r126", "r127", + "nat0", "nat1", "nat2", "nat3", "nat4", "nat5", "nat6", "nat7", + "nat8", "nat9", "nat10", "nat11", "nat12", "nat13", "nat14", "nat15", + "nat16", "nat17", "nat18", "nat19", "nat20", "nat21", "nat22", "nat23", + "nat24", "nat25", "nat26", "nat27", "nat28", "nat29", "nat30", "nat31", + "nat32", "nat33", "nat34", "nat35", "nat36", "nat37", "nat38", "nat39", + "nat40", "nat41", "nat42", "nat43", "nat44", "nat45", "nat46", "nat47", + "nat48", "nat49", "nat50", "nat51", "nat52", "nat53", "nat54", "nat55", + "nat56", "nat57", "nat58", "nat59", "nat60", "nat61", "nat62", "nat63", + "nat64", "nat65", "nat66", "nat67", "nat68", "nat69", "nat70", "nat71", + "nat72", "nat73", "nat74", "nat75", "nat76", "nat77", "nat78", "nat79", + "nat80", "nat81", "nat82", "nat83", "nat84", "nat85", "nat86", "nat87", + "nat88", "nat89", "nat90", "nat91", "nat92", "nat93", "nat94", "nat95", + "nat96", "nat97", "nat98", "nat99", "nat100", "nat101", "nat102", "nat103", + "nat104","nat105","nat106", "nat107", "nat108", "nat109", "nat110", "nat111", + "nat112","nat113","nat114", "nat115", "nat116", "nat117", "nat118", "nat119", + "nat120","nat121","nat122", "nat123", "nat124", "nat125", "nat126", "nat127", + "f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7", + "f8", "f9", "f10", "f11", "f12", "f13", "f14", "f15", + "f16", "f17", "f18", "f19", "f20", "f21", "f22", "f23", + "f24", "f25", "f26", "f27", "f28", "f29", "f30", "f31", + "f32", "f33", "f34", "f35", "f36", "f37", "f38", "f39", + "f40", "f41", "f42", "f43", "f44", "f45", "f46", "f47", + "f48", "f49", "f50", "f51", "f52", "f53", "f54", "f55", + "f56", "f57", "f58", "f59", "f60", "f61", "f62", "f63", + "f64", "f65", "f66", "f67", "f68", "f69", "f70", "f71", + "f72", "f73", "f74", "f75", "f76", "f77", "f78", "f79", + "f80", "f81", "f82", "f83", "f84", "f85", "f86", "f87", + "f88", "f89", "f90", "f91", "f92", "f93", "f94", "f95", + "f96", "f97", "f98", "f99", "f100", "f101", "f102", "f103", + "f104", "f105", "f106", "f107", "f108", "f109", "f110", "f111", + "f112", "f113", "f114", "f115", "f116", "f117", "f118", "f119", + "f120", "f121", "f122", "f123", "f124", "f125", "f126", "f127", + "ar0", "ar1", "ar2", "ar3", "ar4", "ar5", "ar6", "ar7", + "ar8", "ar9", "ar10", "ar11", "ar12", "ar13", "ar14", "ar15", + "rsc", "bsp", "bspstore", "rnat", "ar20", "ar21", "ar22", "ar23", + "ar24", "ar25", "ar26", "ar27", "ar28", "ar29", "ar30", "ar31", + "ccv", "ar33", "ar34", "ar35", "unat", "ar37", "ar38", "ar39", + "fpsr", "ar41", "ar42", "ar43", "ar44", "ar45", "ar46", "ar47", + "ar48", "ar49", "ar50", "ar51", "ar52", "ar53", "ar54", "ar55", + "ar56", "ar57", "ar58", "ar59", "ar60", "ar61", "ar62", "ar63", + "pfs", "lc", "ec", "ar67", "ar68", "ar69", "ar70", "ar71", + "ar72", "ar73", "ar74", "ar75", "ar76", "ar77", "ar78", "ar79", + "ar80", "ar81", "ar82", "ar83", "ar84", "ar85", "ar86", "ar87", + "ar88", "ar89", "ar90", "ar91", "ar92", "ar93", "ar94", "ar95", + "ar96", "ar97", "ar98", "ar99", "ar100", "ar101", "ar102", "ar103", + "ar104", "ar105", "ar106", "ar107", "ar108", "ar109", "ar110", "ar111", + "ar112", "ar113", "ar114", "ar115", "ar116", "ar117", "ar118", "ar119", + "ar120", "ar121", "ar122", "ar123", "ar124", "ar125", "ar126", "ar127", + "rp", "b1", "b2", "b3", "b4", "b5", "b6", "b7", + "pr", + "cfm", + "current-bsp" +}; + +const char * +_U_ia64_regname (unw_regnum_t reg) +{ + if ((unsigned) (reg + 7) < sizeof (regname) / sizeof (regname[0])) + return regname[reg + 7]; + else + return "???"; +} + +#endif /* IA64_UNW_DEBUG */ + +void +ia64_init (void) +{ + extern void unw_hash_index_t_is_too_narrow (void); + extern void unw_cursor_t_is_too_small (void); + uint8_t f1_bytes[16] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, + 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + uint8_t nat_val_bytes[16] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0xfe, + 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + uint8_t int_val_bytes[16] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0xfe, + 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + uint8_t *lep, *bep; + long i; + + unw.f0.raw.bits[0] = 0; + unw.f0.raw.bits[1] = 0; + + lep = (uint8_t *) &unw.f1_le + 16; + bep = (uint8_t *) &unw.f1_be; + for (i = 0; i < 16; ++i) + { + *--lep = f1_bytes[i]; + *bep++ = f1_bytes[i]; + } + + lep = (uint8_t *) &unw.nat_val_le + 16; + bep = (uint8_t *) &unw.nat_val_be; + for (i = 0; i < 16; ++i) + { + *--lep = nat_val_bytes[i]; + *bep++ = nat_val_bytes[i]; + } + + lep = (uint8_t *) &unw.int_val_le + 16; + bep = (uint8_t *) &unw.int_val_be; + for (i = 0; i < 16; ++i) + { + *--lep = int_val_bytes[i]; + *bep++ = int_val_bytes[i]; + } + + if (sizeof (struct ia64_cursor) > sizeof (unw_cursor_t)) + unw_cursor_t_is_too_small(); + + if (8*sizeof(unw_hash_index_t) < IA64_LOG_UNW_HASH_SIZE) + unw_hash_index_t_is_too_narrow(); + + for (i = 0; i < IA64_UNW_CACHE_SIZE; ++i) { + if (i > 0) + unw.cache[i].lru_chain = (i - 1); + unw.cache[i].coll_chain = -1; +// unw.cache[i].lock = RW_LOCK_UNLOCKED; + } + unw.lru_head = IA64_UNW_CACHE_SIZE - 1; + unw.lru_tail = 0; +} diff --git a/src/ia64/parser.c b/src/ia64/parser.c index e69de29b..a943ca7f 100644 --- a/src/ia64/parser.c +++ b/src/ia64/parser.c @@ -0,0 +1,875 @@ +/* libunwind - a platform-independent unwind library + Copyright (C) 2001-2002 Hewlett-Packard Co + Contributed by David Mosberger-Tang + +This file is part of libunwind. + +libunwind 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. + +libunwind 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. + +As a special exception, if you link this library with other files to +produce an executable, this library does not by itself cause the +resulting executable to be covered by the GNU General Public License. +This exception does not however invalidate any other reasons why the +executable file might be covered by the GNU General Public +License. */ + +#include +#include +#include +#include + +#include "unwind_i.h" + +typedef unsigned long unw_word; + +#define alloc_reg_state() (malloc (sizeof(struct ia64_state_record))) +#define free_reg_state(usr) (free (usr)) + +/* Unwind decoder routines */ + +static inline void +push (struct ia64_state_record *sr) +{ + struct ia64_reg_state *rs; + + rs = alloc_reg_state (); + if (!rs) + { + fprintf (stderr, "unwind: cannot stack reg state!\n"); + return; + } + memcpy (rs, &sr->curr, sizeof (*rs)); + rs->next = sr->stack; + sr->stack = rs; +} + +static void +pop (struct ia64_state_record *sr) +{ + struct ia64_reg_state *rs; + + if (!sr->stack) + { + fprintf (stderr, "unwind: stack underflow!\n"); + return; + } + rs = sr->stack; + sr->stack = rs->next; + free_reg_state (rs); +} + +static enum ia64_pregnum __attribute__ ((const)) +decode_abreg (unsigned char abreg, int memory) +{ + switch (abreg) + { + case 0x04 ... 0x07: + return IA64_REG_R4 + (abreg - 0x04); + case 0x22 ... 0x25: + return IA64_REG_F2 + (abreg - 0x22); + case 0x30 ... 0x3f: + return IA64_REG_F16 + (abreg - 0x30); + case 0x41 ... 0x45: + return IA64_REG_B1 + (abreg - 0x41); + case 0x60: + return IA64_REG_PR; + case 0x61: + return IA64_REG_PSP; + case 0x62: + return memory ? IA64_REG_PRI_UNAT_MEM : IA64_REG_PRI_UNAT_GR; + case 0x63: + return IA64_REG_RP; + case 0x64: + return IA64_REG_BSP; + case 0x65: + return IA64_REG_BSPSTORE; + case 0x66: + return IA64_REG_RNAT; + case 0x67: + return IA64_REG_UNAT; + case 0x68: + return IA64_REG_FPSR; + case 0x69: + return IA64_REG_PFS; + case 0x6a: + return IA64_REG_LC; + default: + break; + } + dprintf ("unwind: bad abreg=0x%x\n", abreg); + return IA64_REG_LC; +} + +static void +set_reg (struct ia64_reg_info *reg, enum ia64_where where, int when, + unsigned long val) +{ + reg->val = val; + reg->where = where; + if (reg->when == IA64_WHEN_NEVER) + reg->when = when; +} + +static void +alloc_spill_area (unsigned long *offp, unsigned long regsize, + struct ia64_reg_info *lo, struct ia64_reg_info *hi) +{ + struct ia64_reg_info *reg; + + for (reg = hi; reg >= lo; --reg) + { + if (reg->where == IA64_WHERE_SPILL_HOME) + { + reg->where = IA64_WHERE_PSPREL; + reg->val = 0x10 - *offp; + *offp += regsize; + } + } +} + +static inline void +spill_next_when (struct ia64_reg_info **regp, struct ia64_reg_info *lim, + unw_word t) +{ + struct ia64_reg_info *reg; + + for (reg = *regp; reg <= lim; ++reg) + { + if (reg->where == IA64_WHERE_SPILL_HOME) + { + reg->when = t; + *regp = reg + 1; + return; + } + } + dprintf ("unwind: excess spill!\n"); +} + +static inline void +finish_prologue (struct ia64_state_record *sr) +{ + struct ia64_reg_info *reg; + unsigned long off; + int i; + + /* First, resolve implicit register save locations (see Section + "11.4.2.3 Rules for Using Unwind Descriptors", rule 3). */ + for (i = 0; i < (int) sizeof (unw.save_order) / sizeof (unw.save_order[0]); + ++i) + { + reg = sr->curr.reg + unw.save_order[i]; + if (reg->where == IA64_WHERE_GR_SAVE) + { + reg->where = IA64_WHERE_GR; + reg->val = sr->gr_save_loc++; + } + } + + /* Next, compute when the fp, general, and branch registers get + saved. This must come before alloc_spill_area() because we need + to know which registers are spilled to their home locations. */ + + if (sr->imask) + { + unsigned char kind, mask = 0, *cp = sr->imask; + unsigned long t; + static const unsigned char limit[3] = + { + IA64_REG_F31, IA64_REG_R7, IA64_REG_B5 + }; + struct ia64_reg_info *(regs[3]); + + regs[0] = sr->curr.reg + IA64_REG_F2; + regs[1] = sr->curr.reg + IA64_REG_R4; + regs[2] = sr->curr.reg + IA64_REG_B1; + + for (t = 0; t < sr->region_len; ++t) + { + if ((t & 3) == 0) + mask = *cp++; + kind = (mask >> 2 * (3 - (t & 3))) & 3; + if (kind > 0) + spill_next_when (®s[kind - 1], sr->curr.reg + limit[kind - 1], + sr->region_start + t); + } + } + + /* Next, lay out the memory stack spill area. */ + + if (sr->any_spills) + { + off = sr->spill_offset; + alloc_spill_area (&off, 16, sr->curr.reg + IA64_REG_F2, + sr->curr.reg + IA64_REG_F31); + alloc_spill_area (&off, 8, sr->curr.reg + IA64_REG_B1, + sr->curr.reg + IA64_REG_B5); + alloc_spill_area (&off, 8, sr->curr.reg + IA64_REG_R4, + sr->curr.reg + IA64_REG_R7); + } +} + +/* Region header descriptors. */ + +static void +desc_prologue (int body, unw_word rlen, unsigned char mask, + unsigned char grsave, struct ia64_state_record *sr) +{ + int i; + + if (!(sr->in_body || sr->first_region)) + finish_prologue (sr); + sr->first_region = 0; + + /* check if we're done: */ + if (body && sr->when_target < sr->region_start + sr->region_len) + { + sr->done = 1; + return; + } + + for (i = 0; i < sr->epilogue_count; ++i) + pop (sr); + sr->epilogue_count = 0; + sr->epilogue_start = IA64_WHEN_NEVER; + + if (!body) + push (sr); + + sr->region_start += sr->region_len; + sr->region_len = rlen; + sr->in_body = body; + + if (!body) + { + for (i = 0; i < 4; ++i) + { + if (mask & 0x8) + set_reg (sr->curr.reg + unw.save_order[i], IA64_WHERE_GR, + sr->region_start + sr->region_len - 1, grsave++); + mask <<= 1; + } + sr->gr_save_loc = grsave; + sr->any_spills = 0; + sr->imask = 0; + sr->spill_offset = 0x10; /* default to psp+16 */ + } +} + +/* Prologue descriptors. */ + +static inline void +desc_abi (unsigned char abi, unsigned char context, + struct ia64_state_record *sr) +{ + if (abi == 0 && context == 's') + sr->flags |= IA64_FLAG_SIGTRAMP; + else + dprintf ("unwind: ignoring unwabi(abi=0x%x,context=0x%x)\n", abi, context); +} + +static inline void +desc_br_gr (unsigned char brmask, unsigned char gr, + struct ia64_state_record *sr) +{ + int i; + + for (i = 0; i < 5; ++i) + { + if (brmask & 1) + set_reg (sr->curr.reg + IA64_REG_B1 + i, IA64_WHERE_GR, + sr->region_start + sr->region_len - 1, gr++); + brmask >>= 1; + } +} + +static inline void +desc_br_mem (unsigned char brmask, struct ia64_state_record *sr) +{ + int i; + + for (i = 0; i < 5; ++i) + { + if (brmask & 1) + { + set_reg (sr->curr.reg + IA64_REG_B1 + i, IA64_WHERE_SPILL_HOME, + sr->region_start + sr->region_len - 1, 0); + sr->any_spills = 1; + } + brmask >>= 1; + } +} + +static inline void +desc_frgr_mem (unsigned char grmask, unw_word frmask, + struct ia64_state_record *sr) +{ + int i; + + for (i = 0; i < 4; ++i) + { + if ((grmask & 1) != 0) + { + set_reg (sr->curr.reg + IA64_REG_R4 + i, IA64_WHERE_SPILL_HOME, + sr->region_start + sr->region_len - 1, 0); + sr->any_spills = 1; + } + grmask >>= 1; + } + for (i = 0; i < 20; ++i) + { + if ((frmask & 1) != 0) + { + set_reg (sr->curr.reg + IA64_REG_F2 + i, IA64_WHERE_SPILL_HOME, + sr->region_start + sr->region_len - 1, 0); + sr->any_spills = 1; + } + frmask >>= 1; + } +} + +static inline void +desc_fr_mem (unsigned char frmask, struct ia64_state_record *sr) +{ + int i; + + for (i = 0; i < 4; ++i) + { + if ((frmask & 1) != 0) + { + set_reg (sr->curr.reg + IA64_REG_F2 + i, IA64_WHERE_SPILL_HOME, + sr->region_start + sr->region_len - 1, 0); + sr->any_spills = 1; + } + frmask >>= 1; + } +} + +static inline void +desc_gr_gr (unsigned char grmask, unsigned char gr, + struct ia64_state_record *sr) +{ + int i; + + for (i = 0; i < 4; ++i) + { + if ((grmask & 1) != 0) + set_reg (sr->curr.reg + IA64_REG_R4 + i, IA64_WHERE_GR, + sr->region_start + sr->region_len - 1, gr++); + grmask >>= 1; + } +} + +static inline void +desc_gr_mem (unsigned char grmask, struct ia64_state_record *sr) +{ + int i; + + for (i = 0; i < 4; ++i) + { + if ((grmask & 1) != 0) + { + set_reg (sr->curr.reg + IA64_REG_R4 + i, IA64_WHERE_SPILL_HOME, + sr->region_start + sr->region_len - 1, 0); + sr->any_spills = 1; + } + grmask >>= 1; + } +} + +static inline void +desc_mem_stack_f (unw_word t, unw_word size, struct ia64_state_record *sr) +{ + set_reg (sr->curr.reg + IA64_REG_PSP, IA64_WHERE_NONE, + sr->region_start + MIN ((int) t, sr->region_len - 1), 16 * size); +} + +static inline void +desc_mem_stack_v (unw_word t, struct ia64_state_record *sr) +{ + sr->curr.reg[IA64_REG_PSP].when = + sr->region_start + MIN ((int) t, sr->region_len - 1); +} + +static inline void +desc_reg_gr (unsigned char reg, unsigned char dst, + struct ia64_state_record *sr) +{ + set_reg (sr->curr.reg + reg, IA64_WHERE_GR, + sr->region_start + sr->region_len - 1, dst); +} + +static inline void +desc_reg_psprel (unsigned char reg, unw_word pspoff, + struct ia64_state_record *sr) +{ + set_reg (sr->curr.reg + reg, IA64_WHERE_PSPREL, + sr->region_start + sr->region_len - 1, 0x10 - 4 * pspoff); +} + +static inline void +desc_reg_sprel (unsigned char reg, unw_word spoff, + struct ia64_state_record *sr) +{ + set_reg (sr->curr.reg + reg, IA64_WHERE_SPREL, + sr->region_start + sr->region_len - 1, 4 * spoff); +} + +static inline void +desc_rp_br (unsigned char dst, struct ia64_state_record *sr) +{ + sr->return_link_reg = dst; +} + +static inline void +desc_reg_when (unsigned char regnum, unw_word t, struct ia64_state_record *sr) +{ + struct ia64_reg_info *reg = sr->curr.reg + regnum; + + if (reg->where == IA64_WHERE_NONE) + reg->where = IA64_WHERE_GR_SAVE; + reg->when = sr->region_start + MIN ((int) t, sr->region_len - 1); +} + +static inline void +desc_spill_base (unw_word pspoff, struct ia64_state_record *sr) +{ + sr->spill_offset = 0x10 - 4 * pspoff; +} + +static inline unsigned char * +desc_spill_mask (unsigned char *imaskp, struct ia64_state_record *sr) +{ + sr->imask = imaskp; + return imaskp + (2 * sr->region_len + 7) / 8; +} + +/* Body descriptors. */ + +static inline void +desc_epilogue (unw_word t, unw_word ecount, struct ia64_state_record *sr) +{ + sr->epilogue_start = sr->region_start + sr->region_len - 1 - t; + sr->epilogue_count = ecount + 1; +} + +static inline void +desc_copy_state (unw_word label, struct ia64_state_record *sr) +{ + struct ia64_reg_state *rs; + + for (rs = sr->reg_state_list; rs; rs = rs->next) + { + if (rs->label == label) + { + memcpy (&sr->curr, rs, sizeof (sr->curr)); + return; + } + } + fprintf (stderr, "unwind: failed to find state labelled 0x%lx\n", label); +} + +static inline void +desc_label_state (unw_word label, struct ia64_state_record *sr) +{ + struct ia64_reg_state *rs; + + rs = alloc_reg_state (); + if (!rs) + { + fprintf (stderr, "unwind: cannot stack!\n"); + return; + } + memcpy (rs, &sr->curr, sizeof (*rs)); + rs->label = label; + rs->next = sr->reg_state_list; + sr->reg_state_list = rs; +} + +/** General descriptors. */ + +static inline int +desc_is_active (unsigned char qp, unw_word t, struct ia64_state_record *sr) +{ + if (sr->when_target <= sr->region_start + MIN ((int) t, sr->region_len - 1)) + return 0; + if (qp > 0) + { + if ((sr->pr_val & (1UL << qp)) == 0) + return 0; + sr->pr_mask |= (1UL << qp); + } + return 1; +} + +static inline void +desc_restore_p (unsigned char qp, unw_word t, unsigned char abreg, + struct ia64_state_record *sr) +{ + struct ia64_reg_info *r; + + if (!desc_is_active (qp, t, sr)) + return; + + r = sr->curr.reg + decode_abreg (abreg, 0); + r->where = IA64_WHERE_NONE; + r->when = IA64_WHEN_NEVER; + r->val = 0; +} + +static inline void +desc_spill_reg_p (unsigned char qp, unw_word t, unsigned char abreg, + unsigned char x, unsigned char ytreg, + struct ia64_state_record *sr) +{ + enum ia64_where where = IA64_WHERE_GR; + struct ia64_reg_info *r; + + if (!desc_is_active (qp, t, sr)) + return; + + if (x) + where = IA64_WHERE_BR; + else if (ytreg & 0x80) + where = IA64_WHERE_FR; + + r = sr->curr.reg + decode_abreg (abreg, 0); + r->where = where; + r->when = sr->region_start + MIN ((int) t, sr->region_len - 1); + r->val = (ytreg & 0x7f); +} + +static inline void +desc_spill_psprel_p (unsigned char qp, unw_word t, unsigned char abreg, + unw_word pspoff, struct ia64_state_record *sr) +{ + struct ia64_reg_info *r; + + if (!desc_is_active (qp, t, sr)) + return; + + r = sr->curr.reg + decode_abreg (abreg, 1); + r->where = IA64_WHERE_PSPREL; + r->when = sr->region_start + MIN ((int) t, sr->region_len - 1); + r->val = 0x10 - 4 * pspoff; +} + +static inline void +desc_spill_sprel_p (unsigned char qp, unw_word t, unsigned char abreg, + unw_word spoff, struct ia64_state_record *sr) +{ + struct ia64_reg_info *r; + + if (!desc_is_active (qp, t, sr)) + return; + + r = sr->curr.reg + decode_abreg (abreg, 1); + r->where = IA64_WHERE_SPREL; + r->when = sr->region_start + MIN ((int) t, sr->region_len - 1); + r->val = 4 * spoff; +} + +#define UNW_DEC_BAD_CODE(code) fprintf (stderr, "unwind: unknown code 0x%02x\n", code); + +/* Register names. */ +#define UNW_REG_BSP IA64_REG_BSP +#define UNW_REG_BSPSTORE IA64_REG_BSPSTORE +#define UNW_REG_FPSR IA64_REG_FPSR +#define UNW_REG_LC IA64_REG_LC +#define UNW_REG_PFS IA64_REG_PFS +#define UNW_REG_PR IA64_REG_PR +#define UNW_REG_RNAT IA64_REG_RNAT +#define UNW_REG_PSP IA64_REG_PSP +#define UNW_REG_RP IA64_REG_RP +#define UNW_REG_UNAT IA64_REG_UNAT + +/* Region headers. */ +#define UNW_DEC_PROLOGUE_GR(fmt,r,m,gr,arg) desc_prologue(0,r,m,gr,arg) +#define UNW_DEC_PROLOGUE(fmt,b,r,arg) desc_prologue(b,r,0,32,arg) + +/* Prologue descriptors. */ +#define UNW_DEC_ABI(fmt,a,c,arg) desc_abi(a,c,arg) +#define UNW_DEC_BR_GR(fmt,b,g,arg) desc_br_gr(b,g,arg) +#define UNW_DEC_BR_MEM(fmt,b,arg) desc_br_mem(b,arg) +#define UNW_DEC_FRGR_MEM(fmt,g,f,arg) desc_frgr_mem(g,f,arg) +#define UNW_DEC_FR_MEM(fmt,f,arg) desc_fr_mem(f,arg) +#define UNW_DEC_GR_GR(fmt,m,g,arg) desc_gr_gr(m,g,arg) +#define UNW_DEC_GR_MEM(fmt,m,arg) desc_gr_mem(m,arg) +#define UNW_DEC_MEM_STACK_F(fmt,t,s,arg) desc_mem_stack_f(t,s,arg) +#define UNW_DEC_MEM_STACK_V(fmt,t,arg) desc_mem_stack_v(t,arg) +#define UNW_DEC_REG_GR(fmt,r,d,arg) desc_reg_gr(r,d,arg) +#define UNW_DEC_REG_PSPREL(fmt,r,o,arg) desc_reg_psprel(r,o,arg) +#define UNW_DEC_REG_SPREL(fmt,r,o,arg) desc_reg_sprel(r,o,arg) +#define UNW_DEC_REG_WHEN(fmt,r,t,arg) desc_reg_when(r,t,arg) +#define UNW_DEC_PRIUNAT_WHEN_GR(fmt,t,arg) \ + desc_reg_when(IA64_REG_PRI_UNAT_GR,t,arg) +#define UNW_DEC_PRIUNAT_WHEN_MEM(fmt,t,arg) \ + desc_reg_when(IA64_REG_PRI_UNAT_MEM,t,arg) +#define UNW_DEC_PRIUNAT_GR(fmt,r,arg) \ + desc_reg_gr(IA64_REG_PRI_UNAT_GR,r,arg) +#define UNW_DEC_PRIUNAT_PSPREL(fmt,o,arg) \ + desc_reg_psprel(IA64_REG_PRI_UNAT_MEM,o,arg) +#define UNW_DEC_PRIUNAT_SPREL(fmt,o,arg) \ + desc_reg_sprel(IA64_REG_PRI_UNAT_MEM,o,arg) +#define UNW_DEC_RP_BR(fmt,d,arg) desc_rp_br(d,arg) +#define UNW_DEC_SPILL_BASE(fmt,o,arg) desc_spill_base(o,arg) +#define UNW_DEC_SPILL_MASK(fmt,m,arg) (m = desc_spill_mask(m,arg)) + +/* Body descriptors. */ +#define UNW_DEC_EPILOGUE(fmt,t,c,arg) desc_epilogue(t,c,arg) +#define UNW_DEC_COPY_STATE(fmt,l,arg) desc_copy_state(l,arg) +#define UNW_DEC_LABEL_STATE(fmt,l,arg) desc_label_state(l,arg) + +/* General unwind descriptors. */ +#define UNW_DEC_SPILL_REG_P(f,p,t,a,x,y,arg) desc_spill_reg_p(p,t,a,x,y,arg) +#define UNW_DEC_SPILL_REG(f,t,a,x,y,arg) desc_spill_reg_p(0,t,a,x,y,arg) +#define UNW_DEC_SPILL_PSPREL_P(f,p,t,a,o,arg) \ + desc_spill_psprel_p(p,t,a,o,arg) +#define UNW_DEC_SPILL_PSPREL(f,t,a,o,arg) \ + desc_spill_psprel_p(0,t,a,o,arg) +#define UNW_DEC_SPILL_SPREL_P(f,p,t,a,o,arg) desc_spill_sprel_p(p,t,a,o,arg) +#define UNW_DEC_SPILL_SPREL(f,t,a,o,arg) desc_spill_sprel_p(0,t,a,o,arg) +#define UNW_DEC_RESTORE_P(f,p,t,a,arg) desc_restore_p(p,t,a,arg) +#define UNW_DEC_RESTORE(f,t,a,arg) desc_restore_p(0,t,a,arg) + +#include "unwind_decoder.c" + +static inline const struct ia64_unwind_table_entry * +lookup (struct ia64_unwind_table *table, unw_word_t rel_ip) +{ + const struct ia64_unwind_table_entry *e = 0; + unsigned long lo, hi, mid; + + /* do a binary search for right entry: */ + for (lo = 0, hi = table->info.length; lo < hi;) + { + mid = (lo + hi) / 2; + e = &table->info.array[mid]; + if (rel_ip < e->start_offset) + hi = mid; + else if (rel_ip >= e->end_offset) + lo = mid + 1; + else + break; + } + return e; +} + +static int +get_proc_info (struct ia64_cursor *c) +{ + const struct ia64_unwind_table_entry *e = 0; + struct ia64_unwind_table *table; + unw_word_t segbase, len; + uint8_t *dp, *desc_end; + unw_ia64_table_t info; + unw_word_t ip = c->ip; + uint64_t hdr; + int ret; + + /* search the kernels and the modules' unwind tables for IP: */ + + for (table = unw.tables; table; table = table->next) + if (ip >= table->start && ip < table->end) + break; + + if (!table) + { + ret = ia64_acquire_unwind_info (c, ip, &info); + if (ret < 0) + return ret; + + segbase = info.segbase; + len = info.length; + + /* XXX avoid malloc: */ + table = malloc (sizeof (struct ia64_unwind_table)); + if (!table) + { + dprintf ("%s: out of memory\n", __FUNCTION__); + return -1; + } + memset (table, 0, sizeof (*table)); + table->info = info; + table->start = segbase + table->info.array[0].start_offset; + table->end = segbase + table->info.array[len - 1].end_offset; + + /* XXX LOCK { */ + table->next = unw.tables; + unw.tables = table; + /* XXX LOCK } */ + } + + assert (ip >= table->start && ip < table->end); + + e = lookup (table, ip - table->info.segbase); + + hdr = *(uint64_t *) (table->info.unwind_info_base + e->info_offset); + dp = (uint8_t *) (table->info.unwind_info_base + e->info_offset + 8); + desc_end = dp + 8 * IA64_UNW_LENGTH (hdr); + + c->pi.flags = 0; + c->pi.gp = table->info.gp; + c->pi.proc_start = table->info.segbase + e->start_offset; + c->pi.pers_addr = (uint64_t *) desc_end; + c->pi.desc = dp; + + /* XXX Perhaps check UNW_VER / UNW_FLAG_OSMASK ? */ + if (IA64_UNW_FLAG_EHANDLER (hdr) | IA64_UNW_FLAG_UHANDLER (hdr)) + c->pi.flags |= IA64_FLAG_HAS_HANDLER; + + return 0; +} + +int +ia64_create_state_record (struct ia64_cursor *c, struct ia64_state_record *sr) +{ + unw_word_t ip = c->ip, predicates = c->pr; + struct ia64_reg_info *r; + uint8_t *dp, *desc_end; + int ret; + STAT(unsigned long start;) + STAT(++unw.stat.parse.calls; start = ia64_get_itc ()); + + /* build state record */ + memset (sr, 0, sizeof (*sr)); + for (r = sr->curr.reg; r < sr->curr.reg + IA64_NUM_PREGS; ++r) + r->when = IA64_WHEN_NEVER; + sr->pr_val = predicates; + + ret = get_proc_info (c); + if (ret < 0) + return ret; + + if (!c->pi.desc) + { + /* No info, return default unwinder (leaf proc, no mem stack, no + saved regs). */ + dprintf ("unwind: no unwind info for ip=0x%lx\n", ip); + sr->curr.reg[IA64_REG_RP].where = IA64_WHERE_BR; + sr->curr.reg[IA64_REG_RP].when = -1; + sr->curr.reg[IA64_REG_RP].val = 0; + STAT(unw.stat.parse.time += ia64_get_itc () - start); + return 0; + } + + sr->when_target = (3 * ((ip & ~0xfUL) - c->pi.proc_start) + / 16 + (ip & 0xfUL)); + + dp = c->pi.desc; + desc_end = (uint8_t *) c->pi.pers_addr; + while (!sr->done && dp < desc_end) + dp = unw_decode (dp, sr->in_body, sr); + + c->pi.flags |= sr->flags; + + if (sr->when_target > sr->epilogue_start) + { + /* sp has been restored and all values on the memory stack below + psp also have been restored. */ + sr->curr.reg[IA64_REG_PSP].val = 0; + sr->curr.reg[IA64_REG_PSP].where = IA64_WHERE_NONE; + sr->curr.reg[IA64_REG_PSP].when = IA64_WHEN_NEVER; + for (r = sr->curr.reg; r < sr->curr.reg + IA64_NUM_PREGS; ++r) + if ((r->where == IA64_WHERE_PSPREL && r->val <= 0x10) + || r->where == IA64_WHERE_SPREL) + { + r->val = 0; + r->where = IA64_WHERE_NONE; + r->when = IA64_WHEN_NEVER; + } + } + + /* If RP did't get saved, generate entry for the return link + register. */ + if (sr->curr.reg[IA64_REG_RP].when >= sr->when_target) + { + sr->curr.reg[IA64_REG_RP].where = IA64_WHERE_BR; + sr->curr.reg[IA64_REG_RP].when = -1; + sr->curr.reg[IA64_REG_RP].val = sr->return_link_reg; + } + +#if IA64_UNW_DEBUG + if (unw.debug_level > 0) + { + printf ("unwind: state record for func 0x%lx, t=%u:\n", + c->pi.proc_start, sr->when_target); + for (r = sr->curr.reg; r < sr->curr.reg + IA64_NUM_PREGS; ++r) + { + if (r->where != IA64_WHERE_NONE || r->when != IA64_WHEN_NEVER) + { + printf (" %s <- ", unw.preg_name[r - sr->curr.reg]); + switch (r->where) + { + case IA64_WHERE_GR: + printf ("r%lu", r->val); + break; + case IA64_WHERE_FR: + printf ("f%lu", r->val); + break; + case IA64_WHERE_BR: + printf ("b%lu", r->val); + break; + case IA64_WHERE_SPREL: + printf ("[sp+0x%lx]", r->val); + break; + case IA64_WHERE_PSPREL: + printf ("[psp+0x%lx]", r->val); + break; + case IA64_WHERE_NONE: + printf ("%s+0x%lx", unw.preg_name[r - sr->curr.reg], r->val); + break; + default: + printf ("BADWHERE(%d)", r->where); + break; + } + printf ("\t\t%d\n", r->when); + } + } + } +#endif + return 0; +} + +int +ia64_free_state_record (struct ia64_state_record *sr) +{ + struct ia64_reg_state *rs, *next; + + /* free labelled register states & stack: */ + + STAT(parse_start = ia64_get_itc ()); + for (rs = sr->reg_state_list; rs; rs = next) + { + next = rs->next; + free_reg_state (rs); + } + while (sr->stack) + pop (sr); + + STAT(unw.stat.script.parse_time += ia64_get_itc () - parse_start); + return 0; +} + +int +ia64_get_proc_info (struct ia64_cursor *c) +{ +#ifdef IA64_UNW_SCRIPT_CACHE + struct ia64_script *script = ia64_script_lookup (c); + + if (script) + { + c->pi = script->pi; + return 0; + } +#endif + + return get_proc_info (c); +} diff --git a/src/ia64/regs.c b/src/ia64/regs.c index e69de29b..da049736 100644 --- a/src/ia64/regs.c +++ b/src/ia64/regs.c @@ -0,0 +1,590 @@ +/* libunwind - a platform-independent unwind library + Copyright (C) 2001-2002 Hewlett-Packard Co + Contributed by David Mosberger-Tang + +This file is part of libunwind. + +libunwind 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. + +libunwind 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. + +As a special exception, if you link this library with other files to +produce an executable, this library does not by itself cause the +resulting executable to be covered by the GNU General Public License. +This exception does not however invalidate any other reasons why the +executable file might be covered by the GNU General Public +License. */ + +#include "config.h" + +#include +#include "unwind_i.h" +#include "rse.h" + +/* The first three 64-bit words in a signal frame contain the signal + number, siginfo pointer, and sigcontext pointer passed to the + signal handler. We use this to locate the sigcontext pointer. */ +#define SIGFRAME_ARG2_OFF 0x10 + +unw_word_t +ia64_get_sigcontext_addr (struct ia64_cursor *c) +{ + unw_word_t addr; + + if (!(c->pi.flags & IA64_FLAG_SIGTRAMP)) + return 0; + + if (ia64_get (c, c->sp + 0x10 + SIGFRAME_ARG2_OFF, &addr) < 0) + return 0; + + return addr; +} + +/* Apply rotation to a general register. The number REG must be in + the range of 0-127. */ + +static inline int +rotate_gr (struct ia64_cursor *c, int reg) +{ + unsigned int rrb_gr, sor; + unw_word_t cfm; + int preg, ret; + + ret = ia64_get (c, c->cfm_loc, &cfm); + if (ret < 0) + return ret; + + sor = 8 * ((cfm >> 14) & 0xf); + rrb_gr = (cfm >> 18) & 0x7f; + + if ((unsigned) (reg - 32) > sor) + preg = reg; /* register not part of the rotating partition */ + else + { + preg = reg + rrb_gr; /* apply rotation */ + if (preg > 32 + sor) + preg -= sor; /* wrap around */ + } + debug (100, "%s: sor=%u rrb.gr=%u, r%d -> r%d\n", __FUNCTION__, sor, rrb_gr, + reg, preg); + return preg; +} + +/* Apply rotation to a floating-point register. The number REG must + be in the range of 0-127. */ + +static inline int +rotate_fr (struct ia64_cursor *c, int reg) +{ + unsigned int rrb_fr; + unw_word_t cfm; + int preg, ret; + + ret = ia64_get (c, c->cfm_loc, &cfm); + if (ret < 0) + return ret; + + rrb_fr = (cfm >> 25) & 0x7f; + if (reg < 32) + preg = reg; /* register not part of the rotating partition */ + else + { + preg = reg + rrb_fr; /* apply rotation */ + if (preg > 127) + preg -= 96; /* wrap around */ + } + debug (100, "%s: rrb.fr=%u, f%d -> f%d\n", __FUNCTION__, rrb_fr, reg, preg); + return preg; +} + +/* Apply logical-to-physical rotation. */ + +static inline unw_word_t +pr_ltop (struct ia64_cursor *c, unw_word_t pr) +{ + unw_word_t rrb_pr, mask, rot, cfm; + int ret; + + ret = ia64_get (c, c->cfm_loc, &cfm); + if (ret < 0) + return ret; + + rrb_pr = (cfm >> 32) & 0x3f; + rot = pr >> 16; + mask = ((unw_word_t) 1 << rrb_pr) - 1; + rot = ((pr & mask) << (48 - rrb_pr)) | ((pr >> rrb_pr) & mask); + return (pr & 0xffff) | (rot << 16); +} + +/* Apply physical-to-logical rotation. */ + +static inline unw_word_t +pr_ptol (struct ia64_cursor *c, unw_word_t pr) +{ + unw_word_t rrb_pr, mask, rot, cfm; + int ret; + + ret = ia64_get (c, c->cfm_loc, &cfm); + if (ret < 0) + return ret; + + rrb_pr = 48 - ((cfm >> 32) & 0x3f); + rot = pr >> 16; + mask = ((unw_word_t) 1 << rrb_pr) - 1; + rot = ((pr & mask) << (48 - rrb_pr)) | ((pr >> rrb_pr) & mask); + return (pr & 0xffff) | (rot << 16); +} + +static inline int +update_nat (struct ia64_cursor *c, unw_word_t nat_loc, unw_word_t mask, + unw_word_t *valp, int write) +{ + unw_word_t nat_word; + int ret; + + ret = ia64_get (c, nat_loc, &nat_word); + if (ret < 0) + return ret; + + if (write) + { + if (*valp) + nat_word |= mask; + else + nat_word &= ~mask; + ret = ia64_put (c, nat_loc, nat_word); + } + else + *valp = (nat_word & mask) != 0; + return ret; +} + +static int +access_nat (struct ia64_cursor *c, unw_word_t loc, unw_word_t reg_loc, + unw_word_t *valp, int write) +{ + unw_word_t nat_loc = -8, mask = 0, sc_addr; + unw_fpreg_t tmp; + int ret; + + if (IA64_IS_FP_LOC (reg_loc)) + { + /* NaT bit is saved as a NaTVal. This happens when a general + register is saved to a floating-point register. */ + if (write) + { + if (*valp) + { + if (c->pi.flags & IA64_FLAG_BIG_ENDIAN) + ret = ia64_putfp (c, reg_loc, unw.nat_val_be); + else + ret = ia64_putfp (c, reg_loc, unw.nat_val_le); + } + else + { + unw_fpreg_t tmp; + + ret = ia64_getfp (c, reg_loc, &tmp); + if (ret < 0) + return ret; + + /* Reset the exponent to 0x1003e so that the significand + will be interpreted as an integer value. */ + if (c->pi.flags & IA64_FLAG_BIG_ENDIAN) + tmp.raw.bits[0] = unw.int_val_be.raw.bits[0]; + else + tmp.raw.bits[1] = unw.int_val_le.raw.bits[1]; + + ret = ia64_putfp (c, reg_loc, tmp); + } + } + else + { + ret = ia64_getfp (c, reg_loc, &tmp); + if (ret < 0) + return ret; + + if (c->pi.flags & IA64_FLAG_BIG_ENDIAN) + *valp = (memcmp (&tmp, &unw.nat_val_be, sizeof (tmp)) == 0); + else + *valp = (memcmp (&tmp, &unw.nat_val_le, sizeof (tmp)) == 0); + } + return ret; + } + + if (IA64_IS_MEMSTK_NAT (loc)) + { + nat_loc = IA64_GET_LOC (loc) << 3; + mask = (unw_word_t) 1 << ia64_rse_slot_num ((unsigned long *) reg_loc); + } + else + { + loc = IA64_GET_LOC (loc); + assert (loc >= 0 && loc < 128); + if (!loc) + { + /* NaT bit is not saved. This happens if a general register + is saved to a branch register. Since the NaT bit gets + lost, we need to drop it here, too. Note that if the NaT + bit had been set when the save occurred, it would have + caused a NaT consumption fault. */ + if (write) + { + if (*valp) + return -UNW_EBADREG; /* can't set NaT bit */ + } + else + *valp = 0; + return 0; + } + + if (loc >= 4 && loc <= 7) + { + /* NaT bit is saved in a NaT register. This happens when a + general register is saved to another general + register. */ + if (write) + ret = ia64_put (c, UNW_IA64_NAT + loc, *valp); + else + ret = ia64_get (c, UNW_IA64_NAT + loc, valp); + return ret; + } + else if (loc >= 32) + { + /* NaT bit is saved in a stacked register. */ + nat_loc = (unw_word_t) ia64_rse_rnat_addr ((unsigned long *) + reg_loc); + if (nat_loc > c->rbs_top) + nat_loc = c->top_rnat_loc; + mask = (unw_word_t) 1 << ia64_rse_slot_num ((unsigned long *) + reg_loc); + } + else + { + /* NaT bit is saved in a scratch register. */ + if (!(c->pi.flags & IA64_FLAG_SIGTRAMP)) + return -UNW_EBADREG; + + sc_addr = ia64_get_sigcontext_addr (c); + if (!sc_addr) + return -UNW_EBADREG; + + nat_loc = sc_addr + struct_offset (struct sigcontext, sc_nat); + mask = (unw_word_t) 1 << loc; + } + } + return update_nat (c, nat_loc, mask, valp, write); +} + +int +ia64_access_reg (struct ia64_cursor *c, unw_regnum_t reg, unw_word_t *valp, + int write) +{ + unw_word_t loc = -8, reg_loc, sc_off = 0, nat, nat_loc, cfm, mask, pr; + int ret, readonly = 0; + + switch (reg) + { + /* frame registers: */ + + case UNW_IA64_CURRENT_BSP: + if (write) + return -UNW_EREADONLYREG; + *valp = c->bsp; + return 0; + + case UNW_REG_SP: + case UNW_REG_PROC_START: + case UNW_REG_HANDLER: + case UNW_REG_LSDA: + if (write) + return -UNW_EREADONLYREG; + switch (reg) + { + case UNW_REG_SP: *valp = c->sp; break; + case UNW_REG_PROC_START:*valp = c->pi.proc_start; break; + case UNW_REG_LSDA: + *valp = (unw_word_t) (c->pi.pers_addr + 1); + break; + case UNW_REG_HANDLER: + if (c->pi.flags & IA64_FLAG_HAS_HANDLER) + /* *c->pers_addr is the linkage-table offset of the word + that stores the address of the personality routine's + function descriptor. */ + *valp = *(unw_word_t *) (*c->pi.pers_addr + c->pi.gp); + else + *valp = 0; + break; + } + return 0; + + case UNW_REG_IP: + loc = c->rp_loc; + break; + + /* preserved registers: */ + + case UNW_IA64_GR + 4: loc = c->r4_loc; break; + case UNW_IA64_GR + 5: loc = c->r5_loc; break; + case UNW_IA64_GR + 6: loc = c->r6_loc; break; + case UNW_IA64_GR + 7: loc = c->r7_loc; break; + case UNW_IA64_AR_BSP: loc = c->bsp_loc; break; + case UNW_IA64_AR_BSPSTORE: loc = c->bspstore_loc; break; + case UNW_IA64_AR_PFS: loc = c->pfs_loc; break; + case UNW_IA64_AR_RNAT: loc = c->rnat_loc; break; + case UNW_IA64_AR_UNAT: loc = c->unat_loc; break; + case UNW_IA64_AR_LC: loc = c->lc_loc; break; + case UNW_IA64_AR_FPSR: loc = c->fpsr_loc; break; + case UNW_IA64_BR + 1: loc = c->b1_loc; break; + case UNW_IA64_BR + 2: loc = c->b2_loc; break; + case UNW_IA64_BR + 3: loc = c->b3_loc; break; + case UNW_IA64_BR + 4: loc = c->b4_loc; break; + case UNW_IA64_BR + 5: loc = c->b5_loc; break; + case UNW_IA64_CFM: loc = c->cfm_loc; break; + + case UNW_IA64_PR: + if (write) + { + pr = pr_ltop (c, *valp); + return ia64_put (c, c->pr_loc, pr); + } + else + { + ret = ia64_get (c, c->pr_loc, &pr); + if (ret < 0) + return ret; + *valp = pr_ptol (c, pr); + } + return 0; + + case UNW_IA64_GR + 32 ... UNW_IA64_GR + 127: /* stacked reg */ + reg = rotate_gr (c, reg - UNW_IA64_GR) + UNW_IA64_GR; + loc = (unw_word_t) ia64_rse_skip_regs ((unsigned long *) c->bsp, + reg - (UNW_IA64_GR + 32)); + break; + + case UNW_IA64_NAT + 32 ... UNW_IA64_NAT + 127: /* stacked reg */ + reg = rotate_gr (c, reg - UNW_IA64_NAT) + UNW_IA64_NAT; + loc = (unw_word_t) ia64_rse_skip_regs ((unsigned long *) c->bsp, + reg - (UNW_IA64_NAT + 32)); + nat_loc = (unw_word_t) ia64_rse_rnat_addr ((unsigned long *) loc); + if (nat_loc > c->rbs_top) + nat_loc = c->top_rnat_loc; + mask = (unw_word_t) 1 << ia64_rse_slot_num ((unsigned long *) loc); + return update_nat (c, nat_loc, mask, valp, write); + + case UNW_IA64_AR_EC: + ret = ia64_get (c, c->cfm_loc, &cfm); + if (ret < 0) + return ret; + if (write) + ret = ia64_put (c, c->cfm_loc, ((cfm & ~((unw_word_t) 0x3f << 52)) + | (*valp & 0x3f) << 52)); + else + *valp = (cfm >> 52) & 0x3f; + return ret; + + + /* scratch & special registers: */ + + case UNW_IA64_GR + 0: + if (write) + return -UNW_EREADONLYREG; + *valp = 0; + return 0; + + case UNW_IA64_GR + 1: /* global pointer */ + if (write) + return -UNW_EREADONLYREG; + *valp = c->pi.gp; + return 0; + + case UNW_IA64_GR + 15 ... UNW_IA64_GR + 18: + if (!(c->pi.flags & IA64_FLAG_SIGTRAMP)) + { + if (write) + c->eh_args[reg - (UNW_IA64_GR + 15)] = *valp; + else + *valp = c->eh_args[reg - (UNW_IA64_GR + 15)] = *valp; + return 0; + } + else + sc_off = struct_offset (struct sigcontext, sc_gr[reg]); + break; + + case UNW_IA64_GR + 2 ... UNW_IA64_GR + 3: + case UNW_IA64_GR + 8 ... UNW_IA64_GR + 14: + case UNW_IA64_GR + 19 ... UNW_IA64_GR + 31: + sc_off = struct_offset (struct sigcontext, sc_gr[reg]); + break; + + case UNW_IA64_NAT + 0: + case UNW_IA64_NAT + 1: /* global pointer */ + if (write) + return -UNW_EREADONLYREG; + *valp = 0; + return 0; + + case UNW_IA64_NAT + 2 ... UNW_IA64_NAT + 3: + case UNW_IA64_NAT + 8 ... UNW_IA64_NAT + 31: + mask = (unw_word_t) 1 << (reg - UNW_IA64_NAT); + loc = ia64_get_sigcontext_addr (c); + if (!loc) + return -UNW_EBADREG; + + loc += struct_offset (struct sigcontext, sc_nat); + + ret = ia64_get (c, loc, &nat); + if (ret < 0) + return ret; + + if (write) + { + if (*valp) + nat |= mask; + else + nat &= ~mask; + ret = ia64_put (c, loc, nat); + } + else + *valp = (nat & mask) != 0; + return ret; + + case UNW_IA64_NAT + 4 ... UNW_IA64_NAT + 7: + loc = (&c->nat4_loc)[reg - (UNW_IA64_NAT + 4)]; + reg_loc = (&c->r4_loc)[reg - (UNW_IA64_NAT + 4)]; + return access_nat (c, loc, reg_loc, valp, write); + + case UNW_IA64_AR_RSC: + sc_off = struct_offset (struct sigcontext, sc_ar_rsc); + break; + +#ifdef SIGCONTEXT_HAS_AR25_AND_AR26 + case UNW_IA64_AR_25: + sc_off = struct_offset (struct sigcontext, sc_ar25); + break; + + case UNW_IA64_AR_26: + sc_off = struct_offset (struct sigcontext, sc_ar26); + break; +#endif + + case UNW_IA64_AR_CCV: + sc_off = struct_offset (struct sigcontext, sc_ar_ccv); + break; + + default: + dprintf ("%s: bad register number %d\n", __FUNCTION__, reg); + return -UNW_EBADREG; + } + + if (sc_off) + { + loc = ia64_get_sigcontext_addr (c); + if (!loc) + return -UNW_EBADREG; + + loc += sc_off; + } + + if (write) + { + if (readonly) + return -UNW_EREADONLYREG; + return ia64_put (c, loc, *valp); + } + else + return ia64_get (c, loc, valp); +} + +int +ia64_access_fpreg (struct ia64_cursor *c, int reg, unw_fpreg_t *valp, + int write) +{ + unw_word_t loc = -8, flags, tmp_loc; + int ret, i; + + switch (reg) + { + case UNW_IA64_FR + 0: + if (write) + return -UNW_EREADONLYREG; + *valp = unw.f0; + return 0; + + case UNW_IA64_FR + 1: + if (write) + return -UNW_EREADONLYREG; + + if (c->pi.flags & IA64_FLAG_BIG_ENDIAN) + *valp = unw.f1_be; + else + *valp = unw.f1_le; + return 0; + + case UNW_IA64_FR + 2: loc = c->f2_loc; break; + case UNW_IA64_FR + 3: loc = c->f3_loc; break; + case UNW_IA64_FR + 4: loc = c->f4_loc; break; + case UNW_IA64_FR + 5: loc = c->f5_loc; break; + case UNW_IA64_FR + 16 ... UNW_IA64_FR + 31: + loc = c->fr_loc[reg - (UNW_IA64_FR + 16)]; + break; + + case UNW_IA64_FR + 6 ... UNW_IA64_FR + 15: + loc = ia64_get_sigcontext_addr (c); + if (!loc) + return -UNW_EBADREG; + loc += struct_offset (struct sigcontext, sc_fr[reg - UNW_IA64_FR]); + break; + + case UNW_IA64_FR + 32 ... UNW_IA64_FR + 127: + reg = rotate_fr (c, reg - UNW_IA64_FR) + UNW_IA64_FR; + loc = ia64_get_sigcontext_addr (c); + if (!loc) + return -UNW_EBADREG; + + ret = ia64_get (c, loc + struct_offset (struct sigcontext, sc_flags), + &flags); + if (ret < 0) + return ret; + + if (!(flags & IA64_SC_FLAG_FPH_VALID)) + { + if (write) + { + /* initialize fph partition: */ + tmp_loc = loc + struct_offset (struct sigcontext, sc_fr[32]); + for (i = 32; i < 128; ++i, tmp_loc += 16) + { + ret = ia64_putfp (c, tmp_loc, unw.f0); + if (ret < 0) + return ret; + } + /* mark fph partition as being valid: */ + ret = ia64_put (c, loc + struct_offset (struct sigcontext, + sc_flags), + flags | IA64_SC_FLAG_FPH_VALID); + if (ret < 0) + return ret; + } + else + { + *valp = unw.f0; + return 0; + } + } + loc += struct_offset (struct sigcontext, sc_fr[reg - UNW_IA64_FR]); + break; + } + + if (write) + return ia64_putfp (c, loc, *valp); + else + return ia64_getfp (c, loc, valp); +} diff --git a/src/ia64/rse.h b/src/ia64/rse.h index e69de29b..b65b2842 100644 --- a/src/ia64/rse.h +++ b/src/ia64/rse.h @@ -0,0 +1,66 @@ +#ifndef _ASM_IA64_RSE_H +#define _ASM_IA64_RSE_H + +/* + * Copyright (C) 1998, 1999 Hewlett-Packard Co + * Copyright (C) 1998, 1999 David Mosberger-Tang + * + * Register stack engine related helper functions. This file may be + * used in applications, so be careful about the name-space and give + * some consideration to non-GNU C compilers (though __inline__ is + * fine). + */ + +static __inline__ unsigned long +ia64_rse_slot_num (unsigned long *addr) +{ + return (((unsigned long) addr) >> 3) & 0x3f; +} + +/* + * Return TRUE if ADDR is the address of an RNAT slot. + */ +static __inline__ unsigned long +ia64_rse_is_rnat_slot (unsigned long *addr) +{ + return ia64_rse_slot_num(addr) == 0x3f; +} + +/* + * Returns the address of the RNAT slot that covers the slot at + * address SLOT_ADDR. + */ +static __inline__ unsigned long * +ia64_rse_rnat_addr (unsigned long *slot_addr) +{ + return (unsigned long *) ((unsigned long) slot_addr | (0x3f << 3)); +} + +/* + * Calcuate the number of registers in the dirty partition starting at + * BSPSTORE with a size of DIRTY bytes. This isn't simply DIRTY + * divided by eight because the 64th slot is used to store ar.rnat. + */ +static __inline__ unsigned long +ia64_rse_num_regs (unsigned long *bspstore, unsigned long *bsp) +{ + unsigned long slots = (bsp - bspstore); + + return slots - (ia64_rse_slot_num(bspstore) + slots)/0x40; +} + +/* + * The inverse of the above: given bspstore and the number of + * registers, calculate ar.bsp. + */ +static __inline__ unsigned long * +ia64_rse_skip_regs (unsigned long *addr, long num_regs) +{ + long delta = ia64_rse_slot_num(addr) + num_regs; + + if (num_regs < 0) + delta -= 0x3e; + return addr + num_regs + delta/0x3f; +} + +#endif /* _ASM_IA64_RSE_H */ diff --git a/src/ia64/script.c b/src/ia64/script.c index e69de29b..b3b861ca 100644 --- a/src/ia64/script.c +++ b/src/ia64/script.c @@ -0,0 +1,469 @@ +/* libunwind - a platform-independent unwind library + Copyright (C) 2001-2002 Hewlett-Packard Co + Contributed by David Mosberger-Tang + +This file is part of libunwind. + +libunwind 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. + +libunwind 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. + +As a special exception, if you link this library with other files to +produce an executable, this library does not by itself cause the +resulting executable to be covered by the GNU General Public License. +This exception does not however invalidate any other reasons why the +executable file might be covered by the GNU General Public +License. */ + +#include "rse.h" +#include "unwind_i.h" + +static inline unw_hash_index_t +hash (unw_word_t ip) +{ +# define magic 0x9e3779b97f4a7c16 /* based on (sqrt(5)/2-1)*2^64 */ + + return (ip >> 4) * magic >> (64 - IA64_LOG_UNW_HASH_SIZE); +} + +static inline long +cache_match (struct ia64_script *script, unw_word_t ip, unw_word_t pr) +{ + /* XXX lock script cache */ + if (ip == script->ip && ((pr ^ script->pr_val) & script->pr_mask) == 0) + /* keep the lock... */ + return 1; + /* XXX unlock script cache */ + return 0; +} + +inline struct ia64_script * +ia64_script_lookup (struct ia64_cursor *c) +{ + struct ia64_script *script = unw.cache + c->hint; + unsigned short index; + unw_word_t ip, pr; + + STAT(++unw.stat.cache.lookups); + + ip = c->ip; + pr = c->pr; + + if (cache_match (script, ip, pr)) + { + STAT(++unw.stat.cache.hinted_hits); + return script; + } + + index = unw.hash[hash (ip)]; + if (index >= IA64_UNW_CACHE_SIZE) + return 0; + + script = unw.cache + index; + while (1) + { + if (cache_match (script, ip, pr)) + { + /* update hint; no locking needed: single-word writes are atomic */ + STAT(++unw.stat.cache.normal_hits); + c->hint = unw.cache[c->prev_script].hint = script - unw.cache; + return script; + } + if (script->coll_chain >= IA64_UNW_HASH_SIZE) + return 0; + script = unw.cache + script->coll_chain; + STAT(++unw.stat.cache.collision_chain_traversals); + } +} + +/* On returning, the lock for the SCRIPT is still being held. */ + +static inline struct ia64_script * +script_new (unw_word_t ip) +{ + struct ia64_script *script, *prev, *tmp; + unw_hash_index_t index; + unsigned short head; + + STAT(++unw.stat.script.news); + + /* XXX lock hash table */ + { + head = unw.lru_head; + script = unw.cache + head; + unw.lru_head = script->lru_chain; + } + /* XXX unlock hash table */ + + /* XXX We'll deadlock here if we interrupt a thread that is holding + the script->lock. */ + /* XXX lock script */ + + /* XXX lock unwind data lock */ + { + /* re-insert script at the tail of the LRU chain: */ + unw.cache[unw.lru_tail].lru_chain = head; + unw.lru_tail = head; + + /* remove the old script from the hash table (if it's there): */ + if (script->ip) + { + index = hash (script->ip); + tmp = unw.cache + unw.hash[index]; + prev = 0; + while (1) + { + if (tmp == script) + { + if (prev) + prev->coll_chain = tmp->coll_chain; + else + unw.hash[index] = tmp->coll_chain; + break; + } + else + prev = tmp; + if (tmp->coll_chain >= IA64_UNW_CACHE_SIZE) + /* old script wasn't in the hash-table */ + break; + tmp = unw.cache + tmp->coll_chain; + } + } + + /* enter new script in the hash table */ + index = hash (ip); + script->coll_chain = unw.hash[index]; + unw.hash[index] = script - unw.cache; + + script->ip = ip; /* set new IP while we're holding the locks */ + + STAT(if (script->coll_chain < IA64_UNW_CACHE_SIZE) + ++unw.stat.script.collisions); + } + /* XXX unlock unwind data lock */ + + script->hint = 0; + script->count = 0; + return script; +} + +static void +script_finalize (struct ia64_script *script, struct ia64_state_record *sr) +{ + script->pr_mask = sr->pr_mask; + script->pr_val = sr->pr_val; +} + +static inline void +script_emit (struct ia64_script *script, struct ia64_script_insn insn) +{ + if (script->count >= IA64_MAX_SCRIPT_LEN) + { + dprintf ("%s: script exceeds maximum size of %u instructions!\n", + __FUNCTION__, IA64_MAX_SCRIPT_LEN); + return; + } + script->insn[script->count++] = insn; +} + +static inline void +emit_nat_info (struct ia64_state_record *sr, int i, struct ia64_script *script) +{ + struct ia64_reg_info *r = sr->curr.reg + i; + struct ia64_script_insn insn; + enum ia64_script_insn_opcode opc = IA64_INSN_SET; + unsigned long val = 0; + + switch (r->where) + { + case IA64_WHERE_GR: + val = IA64_LOC (r->val, 0); + break; + + case IA64_WHERE_FR: + val = 0; /* value doesn't matter... */ + break; + + case IA64_WHERE_BR: + val = IA64_LOC (0, 0); /* no NaT bit */ + break; + + case IA64_WHERE_PSPREL: + case IA64_WHERE_SPREL: + opc = IA64_INSN_SETNAT_MEMSTK; + break; + + default: + dprintf ("%s: don't know how to emit nat info for where = %u\n", + __FUNCTION__, r->where); + return; + } + insn.opc = opc; + insn.dst = unw.preg_index[i]; + insn.val = val; + script_emit (script, insn); +} + +static void +compile_reg (struct ia64_state_record *sr, int i, struct ia64_script *script) +{ + struct ia64_reg_info *r = sr->curr.reg + i; + enum ia64_script_insn_opcode opc; + unsigned long val, rval; + struct ia64_script_insn insn; + long is_preserved_gr; + + if (r->where == IA64_WHERE_NONE || r->when >= sr->when_target) + return; + + opc = IA64_INSN_MOVE; + val = rval = r->val; + is_preserved_gr = (i >= IA64_REG_R4 && i <= IA64_REG_R7); + + switch (r->where) + { + case IA64_WHERE_GR: + if (rval >= 32) + { + /* register got spilled to a stacked register */ + opc = IA64_INSN_MOVE_STACKED; + val = rval - 32; + } + else if (rval >= 4 && rval <= 7) + /* register got spilled to a preserved register */ + val = unw.preg_index[IA64_REG_R4 + (rval - 4)]; + else + { + /* register got spilled to a scratch register */ + opc = IA64_INSN_MOVE_SIGCONTEXT; + val = struct_offset (struct sigcontext, sc_gr[rval]); + } + break; + + case IA64_WHERE_FR: + if (rval <= 5) + val = unw.preg_index[IA64_REG_F2 + (rval - 1)]; + else if (rval >= 16 && rval <= 31) + val = unw.preg_index[IA64_REG_F16 + (rval - 16)]; + else + { + opc = IA64_INSN_MOVE_SIGCONTEXT; + val = struct_offset (struct sigcontext, sc_fr[rval]); + } + break; + + case IA64_WHERE_BR: + if (rval >= 1 && rval <= 5) + val = unw.preg_index[IA64_REG_B1 + (rval - 1)]; + else + { + opc = IA64_INSN_MOVE_SIGCONTEXT; + val = struct_offset (struct sigcontext, sc_br[rval]); + } + break; + + case IA64_WHERE_SPREL: + opc = IA64_INSN_ADD_SP; + if (i >= IA64_REG_F2 && i <= IA64_REG_F31) + val |= IA64_LOC_TYPE_FP; + break; + + case IA64_WHERE_PSPREL: + opc = IA64_INSN_ADD_PSP; + if (i >= IA64_REG_F2 && i <= IA64_REG_F31) + val |= IA64_LOC_TYPE_FP; + break; + + default: + dprintf ("%s: register %u has unexpected `where' value of %u\n", + __FUNCTION__, i, r->where); + break; + } + insn.opc = opc; + insn.dst = unw.preg_index[i]; + insn.val = val; + script_emit (script, insn); + if (is_preserved_gr) + emit_nat_info (sr, i, script); + + if (i == IA64_REG_PSP) + { + /* info->psp must contain the _value_ of the previous sp, not + it's save location. We get this by dereferencing the value + we just stored in info->psp: */ + insn.opc = IA64_INSN_LOAD; + insn.dst = insn.val = unw.preg_index[IA64_REG_PSP]; + script_emit (script, insn); + } +} + +/* Build an unwind script that unwinds from state OLD_STATE to the + entrypoint of the function that called OLD_STATE. */ + +static inline int +build_script (struct ia64_cursor *c, struct ia64_script **scriptp) +{ + struct ia64_script *script; + struct ia64_state_record sr; + struct ia64_script_insn insn; + int i, ret; + STAT(unsigned long start, parse_start;) + STAT(++unw.stat.script.builds; start = ia64_get_itc ()); + STAT(unw.stat.script.parse_time += ia64_get_itc () - parse_start); + + script = script_new (c->ip); + if (!script) + { + dprintf ("%s: failed to create unwind script\n", __FUNCTION__); + STAT(unw.stat.script.build_time += ia64_get_itc() - start); + return -UNW_EUNSPEC; + } + unw.cache[c->prev_script].hint = script - unw.cache; + + ret = ia64_create_state_record (c, &sr); + if (ret < 0) + return ret; + + /* First, set psp if we're dealing with a fixed-size frame; + subsequent instructions may depend on this value. */ + if (sr.when_target > sr.curr.reg[IA64_REG_PSP].when + && (sr.curr.reg[IA64_REG_PSP].where == IA64_WHERE_NONE) + && sr.curr.reg[IA64_REG_PSP].val != 0) + { + /* new psp is psp plus frame size */ + insn.opc = IA64_INSN_ADD; + insn.dst = struct_offset (struct ia64_cursor, psp) / 8; + insn.val = sr.curr.reg[IA64_REG_PSP].val; /* frame size */ + script_emit (script, insn); + } + + /* determine where the primary UNaT is: */ + if (sr.when_target < sr.curr.reg[IA64_REG_PRI_UNAT_GR].when) + i = IA64_REG_PRI_UNAT_MEM; + else if (sr.when_target < sr.curr.reg[IA64_REG_PRI_UNAT_MEM].when) + i = IA64_REG_PRI_UNAT_GR; + else if (sr.curr.reg[IA64_REG_PRI_UNAT_MEM].when > + sr.curr.reg[IA64_REG_PRI_UNAT_GR].when) + i = IA64_REG_PRI_UNAT_MEM; + else + i = IA64_REG_PRI_UNAT_GR; + + compile_reg (&sr, i, script); + + for (i = IA64_REG_BSP; i < IA64_NUM_PREGS; ++i) + compile_reg (&sr, i, script); + + script_finalize (script, &sr); + script->pi = c->pi; + + ia64_free_state_record (&sr); + + STAT(unw.stat.script.build_time += ia64_get_itc () - start); + *scriptp = script; + return 0; +} + +/* Apply the unwinding actions represented by OPS and update SR to + reflect the state that existed upon entry to the function that this + unwinder represents. */ + +static inline int +run_script (struct ia64_script *script, struct ia64_cursor *c) +{ + struct ia64_script_insn *ip, *limit, next_insn; + unw_word_t val, unat_addr, *s = (unw_word_t *) c; + unsigned long opc, dst; + int ret; + STAT(unsigned long start;) + + STAT(++unw.stat.script.runs; start = ia64_get_itc ()); + c->pi = script->pi; + ip = script->insn; + limit = script->insn + script->count; + next_insn = *ip; + + while (ip++ < limit) + { + opc = next_insn.opc; + dst = next_insn.dst; + val = next_insn.val; + next_insn = *ip; + + switch (opc) + { + case IA64_INSN_SET: + s[dst] = val; + break; + + case IA64_INSN_ADD: + s[dst] += val; + break; + + case IA64_INSN_ADD_PSP: + s[dst] = c->psp + val; + break; + + case IA64_INSN_ADD_SP: + s[dst] = c->sp + val; + break; + + case IA64_INSN_MOVE: + s[dst] = s[val]; + break; + + case IA64_INSN_MOVE_SIGCONTEXT: + s[dst] = ia64_get_sigcontext_addr (c); + break; + + case IA64_INSN_MOVE_STACKED: + s[dst] = (unsigned long) ia64_rse_skip_regs ((unsigned long *) + c->bsp, val); + break; + + case IA64_INSN_SETNAT_MEMSTK: + ret = ia64_get (c, c->pri_unat_loc, &unat_addr); + if (ret < 0) + return ret; + s[dst] = IA64_LOC (unat_addr >> 3, IA64_LOC_TYPE_MEMSTK_NAT); + break; + + case IA64_INSN_LOAD: + ret = ia64_get (c, s[val], &s[dst]); + if (ret < 0) + return ret; + break; + } + } + STAT(unw.stat.script.run_time += ia64_get_itc () - start); + return 0; +} + +int +ia64_find_save_locs (struct ia64_cursor *c) +{ + struct ia64_script *script = ia64_script_lookup (c); + int ret; + + if (!script) + { + ret = build_script (c, &script); + if (ret < 0) + { + if (ret != UNW_ESTOPUNWIND) + dprintf ("%s: failed to locate/build unwind script for ip %lx\n", + __FUNCTION__, c->ip); + return ret; + } + } + c->hint = script->hint; + c->prev_script = script - unw.cache; + + run_script (script, c); + return 0; +} diff --git a/src/ia64/script.h b/src/ia64/script.h index e69de29b..6659df1e 100644 --- a/src/ia64/script.h +++ b/src/ia64/script.h @@ -0,0 +1,73 @@ +/* libunwind - a platform-independent unwind library + Copyright (C) 2001-2002 Hewlett-Packard Co + Contributed by David Mosberger-Tang + +This file is part of libunwind. + +libunwind 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. + +libunwind 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. + +As a special exception, if you link this library with other files to +produce an executable, this library does not by itself cause the +resulting executable to be covered by the GNU General Public License. +This exception does not however invalidate any other reasons why the +executable file might be covered by the GNU General Public +License. */ + +#define IA64_LOG_UNW_CACHE_SIZE 7 +#define IA64_UNW_CACHE_SIZE (1 << IA64_LOG_UNW_CACHE_SIZE) + +#define IA64_LOG_UNW_HASH_SIZE (IA64_LOG_UNW_CACHE_SIZE + 1) +#define IA64_UNW_HASH_SIZE (1 << IA64_LOG_UNW_HASH_SIZE) + +typedef unsigned char unw_hash_index_t; + +enum ia64_script_insn_opcode + { + IA64_INSN_SET, /* s[dst] = val */ + IA64_INSN_ADD, /* s[dst] += val */ + IA64_INSN_ADD_PSP, /* s[dst] = (s.psp + val) */ + IA64_INSN_ADD_SP, /* s[dst] = (s.sp + val) */ + IA64_INSN_MOVE, /* s[dst] = s[val] */ + IA64_INSN_MOVE_STACKED, /* s[dst] = ia64_rse_skip(*s.bsp_loc, val) */ + IA64_INSN_MOVE_SIGCONTEXT, /* s[dst] = sigcontext + val */ + IA64_INSN_SETNAT_MEMSTK, /* s[dst].nat.type = s.pri_unat_loc | MEMSTK */ + IA64_INSN_LOAD /* s[dst] = *s[val] */ + }; + +struct ia64_script_insn + { + unsigned int opc; + unsigned int dst; + unw_word_t val; + }; + +/* Preserved general static registers (r4-r7) give rise to two script + instructions; everything else yields at most one instruction; at + the end of the script, the psp gets popped, accounting for one more + instruction. */ +#define IA64_MAX_SCRIPT_LEN (IA64_NUM_PREGS + 5) + +struct ia64_script + { + unw_word_t ip; /* ip this script is for */ + unw_word_t pr_mask; /* mask of predicates script depends on */ + unw_word_t pr_val; /* predicate values this script is for */ + struct ia64_proc_info pi; /* info about underlying procedure */ + unsigned short lru_chain; /* used for least-recently-used chain */ + unsigned short coll_chain; /* used for hash collisions */ + unsigned short hint; /* hint for next script to try (or -1) */ + unsigned short count; /* number of instructions in script */ + struct ia64_script_insn insn[IA64_MAX_SCRIPT_LEN]; + }; + +#define ia64_script_lookup UNW_OBJ(ia64_script_lookup) + +extern struct ia64_script *ia64_script_lookup (struct ia64_cursor *c); diff --git a/src/ia64/tables-glibc.c b/src/ia64/tables-glibc.c index e69de29b..ebb54294 100644 --- a/src/ia64/tables-glibc.c +++ b/src/ia64/tables-glibc.c @@ -0,0 +1,197 @@ +/* libunwind - a platform-independent unwind library + Copyright (C) 2001-2002 Hewlett-Packard Co + Contributed by David Mosberger-Tang + +This file is part of libunwind. + +libunwind 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. + +libunwind 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. + +As a special exception, if you link this library with other files to +produce an executable, this library does not by itself cause the +resulting executable to be covered by the GNU General Public License. +This exception does not however invalidate any other reasons why the +executable file might be covered by the GNU General Public +License. */ + +/* This file is bsed on gcc/config/ia64/fde-glibc.c, which is copyright + by: + + Copyright (C) 2000, 2001 Free Software Foundation, Inc. + Contributed by Richard Henderson . */ + +#include +#include +#include + +#include "unwind_i.h" + +#if __GLIBC__ < 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ < 2) \ + || (__GLIBC__ == 2 && __GLIBC_MINOR__ == 2 && !defined(DT_CONFIG)) +# error You need GLIBC 2.2.4 or later on IA-64 Linux +#endif + +#if 0 + +extern unsigned long getunwind (void *buf, size_t len); + +#else + +/* XXX fix me */ + +#include +#include + +# ifndef __NR_getunwind +# define __NR_getunwind 1215 +# endif + +static unsigned long +getunwind (void *buf, size_t len) +{ + return syscall (SYS_getunwind, buf, len); +} + +#endif + +static int +get_kernel_table (void *ptr) +{ + struct ia64_unwind_table_entry *ktab, *etab; + unw_ia64_table_t *info = ptr; + size_t size; + + debug (100, "unwind: checking kernel unwind table"); + + size = getunwind (NULL, 0); + ktab = malloc (size); + if (!ktab) + { + dprintf (__FILE__".%s: failed to allocated %Zu bytes", + __FUNCTION__, size); + return -1; + } + getunwind (ktab, size); + + /* Determine length of kernel's unwind table. */ + for (etab = ktab; etab->start_offset; ++etab); + + if (info->segbase < ktab->start_offset || info->segbase >= ktab->end_offset) + { + free (ktab); + return -1; + } + + info->name = ""; + info->gp = 0; + info->segbase = 0; + info->length = etab - ktab; + info->array = ktab; + info->unwind_info_base = (const u_int8_t *) ktab; + + debug (100, "unwind: found table `%s': segbase=%lx, length=%lu, gp=%lx\n", + info->name, info->segbase, info->length, info->gp); + + return 0; +} + +static int +callback (struct dl_phdr_info *info, size_t size, void *ptr) +{ + unw_ia64_table_t *data = ptr; + const Elf64_Phdr *phdr, *p_unwind, *p_dynamic; + long n, match; + Elf64_Addr load_base, segbase; + + /* Make sure struct dl_phdr_info is at least as big as we need. */ + if (size < offsetof (struct dl_phdr_info, dlpi_phnum) + + sizeof (info->dlpi_phnum)) + return -1; + + debug (100, "unwind: checking `%s'\n", info->dlpi_name); + + match = 0; + phdr = info->dlpi_phdr; + load_base = info->dlpi_addr; + p_unwind = NULL; + p_dynamic = NULL; + segbase = ~(Elf64_Addr) 0; + + /* See if PC falls into one of the loaded segments. Find the unwind + segment at the same time. */ + for (n = info->dlpi_phnum; --n >= 0; phdr++) + { + if (phdr->p_type == PT_LOAD) + { + Elf64_Addr vaddr = phdr->p_vaddr + load_base; + if (data->segbase >= vaddr && data->segbase < vaddr + phdr->p_memsz) + match = 1; + if (vaddr < segbase) + segbase = vaddr; + } + else if (phdr->p_type == PT_IA_64_UNWIND) + p_unwind = phdr; + else if (phdr->p_type == PT_DYNAMIC) + p_dynamic = phdr; + } + if (!match || !p_unwind) + return 0; + + if (p_dynamic) + { + /* For dynamicly linked executables and shared libraries, + DT_PLTGOT is the gp value for that object. */ + Elf64_Dyn *dyn = (Elf64_Dyn *)(p_dynamic->p_vaddr + load_base); + for (; dyn->d_tag != DT_NULL ; dyn++) + if (dyn->d_tag == DT_PLTGOT) + { + /* On IA-64, _DYNAMIC is writable and GLIBC has relocated it. */ + data->gp = dyn->d_un.d_ptr; + break; + } + } + else + { + /* Otherwise this is a static executable with no _DYNAMIC. + The gp is constant program-wide. */ + register unsigned long gp __asm__("gp"); + data->gp = gp; + } + data->name = info->dlpi_name; + data->array + = (const struct ia64_unwind_table_entry *) (p_unwind->p_vaddr + load_base); + data->length = p_unwind->p_memsz / sizeof (struct ia64_unwind_table_entry); + data->segbase = segbase; + data->unwind_info_base = (const u_int8_t *) segbase; + + debug (100, "unwind: found table `%s': segbase=%lx, length=%lu, gp=%lx, " + "array=%p\n", data->name, data->segbase, data->length, data->gp, + data->array); + + return 1; +} + +int +ia64_glibc_acquire_unwind_info (unw_word_t ip, void *info, void *arg) +{ + ((unw_ia64_table_t *) info)->segbase = ip; /* this is cheap... */ + + if (dl_iterate_phdr (callback, info) >= 0) + return 0; + + return get_kernel_table (info); +} + +int +ia64_glibc_release_unwind_info (void *info, void *arg) +{ + /* nothing to do */ + return 0; +} diff --git a/src/ia64/ucontext_i.h b/src/ia64/ucontext_i.h index e69de29b..8efaf913 100644 --- a/src/ia64/ucontext_i.h +++ b/src/ia64/ucontext_i.h @@ -0,0 +1,59 @@ +/* Copyright (C) 2001 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Contributed by David Mosberger-Tang . + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +/* Constants shared between setcontext() and getcontext(). Don't + install this header file. */ + +#define SIG_BLOCK 0 +#define SIG_UNBLOCK 1 +#define SIG_SETMASK 2 + +#define SC_NAT 0x008 +#define SC_BSP 0x048 +#define SC_RNAT 0x050 +#define SC_UNAT 0x060 +#define SC_FPSR 0x068 +#define SC_PFS 0x070 +#define SC_LC 0x078 +#define SC_PR 0x080 +#define SC_BR 0x088 +#define SC_GR 0x0c8 +#define SC_FR 0x1d0 +#define SC_MASK 0x9d0 + + +#define rTMP r14 +#define rPOS r14 +#define rCPOS r17 +#define rNAT r18 + +#define rB5 r18 +#define rB4 r19 +#define rB3 r20 +#define rB2 r21 +#define rB1 r22 +#define rB0 r23 +#define rRSC r24 +#define rBSP r25 +#define rRNAT r26 +#define rUNAT r27 +#define rFPSR r28 +#define rPFS r29 +#define rLC r30 +#define rPR r31 diff --git a/src/ia64/unw_get_reg.c b/src/ia64/unw_get_reg.c index e69de29b..4f6f9742 100644 --- a/src/ia64/unw_get_reg.c +++ b/src/ia64/unw_get_reg.c @@ -0,0 +1,32 @@ +/* libunwind - a platform-independent unwind library + Copyright (C) 2001-2002 Hewlett-Packard Co + Contributed by David Mosberger-Tang + +This file is part of libunwind. + +libunwind 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. + +libunwind 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. + +As a special exception, if you link this library with other files to +produce an executable, this library does not by itself cause the +resulting executable to be covered by the GNU General Public License. +This exception does not however invalidate any other reasons why the +executable file might be covered by the GNU General Public +License. */ + +#include "unwind_i.h" + +int +unw_get_reg (unw_cursor_t *cursor, int regnum, unw_word_t *valp) +{ + struct ia64_cursor *c = (struct ia64_cursor *) cursor; + + return ia64_access_reg (c, regnum, valp, 0); +} diff --git a/src/ia64/unw_init_local.c b/src/ia64/unw_init_local.c index e69de29b..5dcc285e 100644 --- a/src/ia64/unw_init_local.c +++ b/src/ia64/unw_init_local.c @@ -0,0 +1,279 @@ +/* libunwind - a platform-independent unwind library + Copyright (C) 2001-2002 Hewlett-Packard Co + Contributed by David Mosberger-Tang + +This file is part of libunwind. + +libunwind 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. + +libunwind 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. + +As a special exception, if you link this library with other files to +produce an executable, this library does not by itself cause the +resulting executable to be covered by the GNU General Public License. +This exception does not however invalidate any other reasons why the +executable file might be covered by the GNU General Public +License. */ + +#include + +#include "rse.h" +#include "unwind_i.h" + +#ifdef IA64_UNW_ACCESSORS + +static int +access_mem (unw_word_t addr, unw_word_t *val, int write, void *arg) +{ + if (write) + { + debug (100, "%s: mem[%lx] <- %lx\n", __FUNCTION__, addr, *val); + *(unw_word_t *) addr = *val; + } + else + { + *val = *(unw_word_t *) addr; + debug (100, "%s: mem[%lx] -> %lx\n", __FUNCTION__, addr, *val); + } + return 0; +} + +static int +access_reg (unw_regnum_t reg, unw_word_t *val, int write, void *arg) +{ + ucontext_t *uc = arg; + unw_word_t *addr, mask; + + switch (reg) + { + case UNW_IA64_SP: addr = &uc->uc_mcontext.sc_gr[12]; break; + case UNW_IA64_AR_PFS: addr = &uc->uc_mcontext.sc_ar_pfs; break; + case UNW_IA64_AR_RNAT: addr = &uc->uc_mcontext.sc_ar_rnat; break; + case UNW_IA64_AR_UNAT: addr = &uc->uc_mcontext.sc_ar_unat; break; + case UNW_IA64_AR_LC: addr = &uc->uc_mcontext.sc_ar_lc; break; + case UNW_IA64_AR_FPSR: addr = &uc->uc_mcontext.sc_ar_fpsr; break; + case UNW_IA64_PR: addr = &uc->uc_mcontext.sc_pr; break; + + case UNW_IA64_AR_BSP: + /* bsp and bspstore are equal after a flushrs: */ + addr = &uc->uc_mcontext.sc_ar_bsp; + break; + + case UNW_IA64_GR + 4 ... UNW_IA64_GR + 7: + addr = &uc->uc_mcontext.sc_gr[reg - UNW_IA64_GR]; + break; + + case UNW_IA64_NAT + 4 ... UNW_IA64_NAT + 7: + mask = ((unw_word_t) 1) << (reg - UNW_IA64_NAT); + if (write) + { + if (*val) + uc->uc_mcontext.sc_nat |= mask; + else + uc->uc_mcontext.sc_nat &= ~mask; + } + else + *val = (uc->uc_mcontext.sc_nat & mask) != 0; + + if (write) + debug (100, "%s: %s <- %lx\n", __FUNCTION__, _U_ia64_regname(reg), + *val); + else + debug (100, "%s: %s -> %lx\n", __FUNCTION__, _U_ia64_regname(reg), + *val); + return 0; + + case UNW_IA64_BR + 0 ... UNW_IA64_BR + 5: + addr = &uc->uc_mcontext.sc_br[reg - UNW_IA64_BR]; + break; + + default: + debug (1, "%s: bad register number %u\n", __FUNCTION__, reg); + return -1; /* attempt to access a non-preserved register */ + } + + if (write) + { + *(unw_word_t *) addr = *val; + debug (100, "%s: %s <- %lx\n", __FUNCTION__, _U_ia64_regname(reg), *val); + } + else + { + *val = *(unw_word_t *) addr; + debug (100, "%s: %s -> %lx\n", __FUNCTION__, _U_ia64_regname(reg), *val); + } + return 0; +} + +static int +access_fpreg (unw_regnum_t reg, unw_fpreg_t *val, int write, void *arg) +{ + ucontext_t *uc = arg; + unw_fpreg_t *addr; + + switch (reg) + { + case UNW_IA64_FR+ 2 ... UNW_IA64_FR+ 5: + case UNW_IA64_FR+16 ... UNW_IA64_FR+31: + addr = (unw_fpreg_t *) &uc->uc_mcontext.sc_fr[reg - UNW_IA64_FR]; break; + + default: + debug (1, "%s: bad register number %u\n", __FUNCTION__, reg); + return -1; /* attempt to access a non-preserved register */ + } + + if (write) + { + debug (100, "%s: %s <- %016lx.%016lx\n", __FUNCTION__, + _U_ia64_regname(reg), val->raw.bits[1], val->raw.bits[0]); + *(unw_fpreg_t *) addr = *val; + } + else + { + *val = *(unw_fpreg_t *) addr; + debug (100, "%s: %s -> %016lx.%016lx\n", __FUNCTION__, + _U_ia64_regname(reg), val->raw.bits[1], val->raw.bits[0]); + } + return 0; +} + +static int +resume (unw_cursor_t *cursor, void *arg) +{ + struct ia64_cursor *c = (struct ia64_cursor *) cursor; + unsigned long *bsp, sol; + unw_fpreg_t fpval; + ucontext_t *uc = arg; + unw_word_t val; + int i, ret; +# define SET_NAT(n, r) \ + do \ + { \ + ret = ia64_get (c, c->r, &val); \ + if (ret < 0) \ + return ret; \ + if (val) \ + uc->uc_mcontext.sc_nat |= (unw_word_t) 1 << n; \ + } \ + while (0) +# define SET_REG(f, r) \ + do \ + { \ + ret = ia64_get (c, c->r, &val); \ + if (ret < 0) \ + return ret; \ + uc->uc_mcontext.f = val; \ + } \ + while (0) +# define SET_FPREG(f, r) \ + do \ + { \ + ret = ia64_getfp (c, c->r, &fpval); \ + if (ret < 0) \ + return ret; \ + uc->uc_mcontext.f.u.bits[0] = fpval.raw.bits[0]; \ + uc->uc_mcontext.f.u.bits[1] = fpval.raw.bits[1]; \ + } \ + while (0) + + SET_REG (sc_ar_pfs, pfs_loc); + SET_REG (sc_br[0], rp_loc); + SET_REG (sc_pr, pr_loc); + SET_REG (sc_ar_rnat, rnat_loc); + SET_REG (sc_ar_lc, lc_loc); + SET_REG (sc_ar_fpsr, fpsr_loc); + + SET_REG (sc_gr[4], r4_loc); SET_REG(sc_gr[5], r5_loc); + SET_REG (sc_gr[6], r6_loc); SET_REG(sc_gr[7], r7_loc); + uc->uc_mcontext.sc_nat = 0; + SET_NAT (4, nat4_loc); SET_NAT(5, nat5_loc); + SET_NAT (6, nat6_loc); SET_NAT(7, nat7_loc); + + SET_REG (sc_br[1], b1_loc); + SET_REG (sc_br[2], b2_loc); + SET_REG (sc_br[3], b3_loc); + SET_REG (sc_br[4], b4_loc); + SET_REG (sc_br[5], b5_loc); + SET_FPREG (sc_fr[2], f2_loc); + SET_FPREG (sc_fr[3], f3_loc); + SET_FPREG (sc_fr[4], f4_loc); + SET_FPREG (sc_fr[5], f5_loc); + for (i = 16; i < 32; ++i) + SET_FPREG (sc_fr[i], fr_loc[i - 16]); + + if ((c->pi.flags & IA64_FLAG_SIGTRAMP) != 0) + fprintf (stderr, "%s: fix me!!\n", __FUNCTION__); + + /* Account for the fact that __ia64_install_context() returns via + br.ret, which will decrement bsp by size-of-locals. */ + bsp = (unsigned long *) c->bsp; + sol = (uc->uc_mcontext.sc_ar_pfs >> 7) & 0x7f; + bsp = ia64_rse_skip_regs (bsp, sol); + + uc->uc_mcontext.sc_ar_bsp = (unsigned long) bsp; + uc->uc_mcontext.sc_flags = 0; + uc->uc_mcontext.sc_gr[1] = c->pi.gp; + uc->uc_mcontext.sc_gr[12] = c->psp; + + __ia64_install_context (uc, c->eh_args[0], c->eh_args[1], c->eh_args[2], + c->eh_args[3]); +} + +#endif /* IA64_UNW_ACCESSORS */ + +int +unw_init_local (unw_cursor_t *cursor, ucontext_t *uc) +{ + STAT(unsigned long start, flags; ++unw.stat.api.inits; + start = ia64_get_itc ();) + int ret; + + if (unw.first_time) + { + unw.first_time = 0; + ia64_init (); + } + +#ifdef IA64_UNW_ACCESSORS + { + unw_accessors_t a; + + a.arg = uc; + a.acquire_unwind_info = ia64_glibc_acquire_unwind_info; + a.release_unwind_info = ia64_glibc_release_unwind_info; + a.access_mem = access_mem; + a.access_reg = access_reg; + a.access_fpreg = access_fpreg; + a.resume = resume; + ret = ia64_init_remote (cursor, &a); + } +#else + { + struct ia64_cursor *c = (struct ia64_cursor *) cursor; + unw_word_t bsp, sol; + + c->uc = uc; + + /* What we do here is initialize the unwind cursor so unwinding + starts at parent of the function that created the ucontext_t. */ + + c->sp = c->psp = uc->uc_mcontext.sc_gr[12]; + c->cfm_loc = &uc->uc_mcontext.sc_ar_pfs; + bsp = uc->uc_mcontext.sc_ar_bsp; + sol = (*c->cfm_loc >> 7) & 0x7f; + c->bsp = (unsigned long) ia64_rse_skip_regs((unsigned long *) bsp, -sol); + c->ip = uc->uc_mcontext.sc_br[0]; + c->pr = uc->uc_mcontext.sc_pr; + + ret = ia64_find_save_locs (c); + } +#endif + STAT(unw.stat.api.init_time += ia64_get_itc() - start;) + return ret; +} diff --git a/src/ia64/unw_init_remote.c b/src/ia64/unw_init_remote.c index e69de29b..caa96c7c 100644 --- a/src/ia64/unw_init_remote.c +++ b/src/ia64/unw_init_remote.c @@ -0,0 +1,98 @@ +/* libunwind - a platform-independent unwind library + Copyright (C) 2001-2002 Hewlett-Packard Co + Contributed by David Mosberger-Tang + +This file is part of libunwind. + +libunwind 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. + +libunwind 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. + +As a special exception, if you link this library with other files to +produce an executable, this library does not by itself cause the +resulting executable to be covered by the GNU General Public License. +This exception does not however invalidate any other reasons why the +executable file might be covered by the GNU General Public +License. */ + +#include "rse.h" +#include "unwind_i.h" + +int +ia64_init_remote (unw_cursor_t *cursor, unw_accessors_t *a) +{ + struct ia64_cursor *c = (struct ia64_cursor *) cursor; + int i; + + if (unw.first_time) + { + unw.first_time = 0; + ia64_init (); + } + + c->acc = *a; + + c->cfm_loc = IA64_REG_LOC (UNW_IA64_AR_PFS); + c->top_rnat_loc = IA64_REG_LOC (UNW_IA64_AR_RNAT); + c->bsp_loc = IA64_REG_LOC (UNW_IA64_AR_BSP); + c->bspstore_loc = IA64_REG_LOC (UNW_IA64_AR_BSP); + c->pfs_loc = IA64_REG_LOC (UNW_IA64_AR_PFS); + c->rnat_loc = IA64_REG_LOC (UNW_IA64_AR_RNAT); + c->rp_loc = IA64_REG_LOC (UNW_IA64_BR + 0); + c->pri_unat_loc = 0; /* no primary UNaT location */ + c->unat_loc = IA64_REG_LOC (UNW_IA64_AR_UNAT); + c->pr_loc = IA64_REG_LOC (UNW_IA64_PR); + c->lc_loc = IA64_REG_LOC (UNW_IA64_AR_LC); + c->fpsr_loc = IA64_REG_LOC (UNW_IA64_AR_FPSR); + + c->r4_loc = IA64_REG_LOC (UNW_IA64_GR + 4); + c->r5_loc = IA64_REG_LOC (UNW_IA64_GR + 5); + c->r6_loc = IA64_REG_LOC (UNW_IA64_GR + 6); + c->r7_loc = IA64_REG_LOC (UNW_IA64_GR + 7); + + c->nat4_loc = IA64_REG_LOC (UNW_IA64_NAT + 4); + c->nat5_loc = IA64_REG_LOC (UNW_IA64_NAT + 5); + c->nat6_loc = IA64_REG_LOC (UNW_IA64_NAT + 6); + c->nat7_loc = IA64_REG_LOC (UNW_IA64_NAT + 7); + + c->b1_loc = IA64_REG_LOC (UNW_IA64_BR + 1); + c->b2_loc = IA64_REG_LOC (UNW_IA64_BR + 2); + c->b3_loc = IA64_REG_LOC (UNW_IA64_BR + 3); + c->b4_loc = IA64_REG_LOC (UNW_IA64_BR + 4); + c->b5_loc = IA64_REG_LOC (UNW_IA64_BR + 5); + + c->f2_loc = IA64_FPREG_LOC (UNW_IA64_FR + 2); + c->f3_loc = IA64_FPREG_LOC (UNW_IA64_FR + 3); + c->f4_loc = IA64_FPREG_LOC (UNW_IA64_FR + 4); + c->f5_loc = IA64_FPREG_LOC (UNW_IA64_FR + 5); + for (i = 16; i <= 31; ++i) + c->fr_loc[i - 16] = IA64_FPREG_LOC (UNW_IA64_FR + i); + + if (ia64_get (c, IA64_REG_LOC (UNW_IA64_SP), &c->psp) < 0) + return -1; + + if (ia64_get (c, c->bsp_loc, &c->bsp) < 0) + return -1; + + c->rbs_top = c->bsp; + c->pi.flags = 0; + c->eh_args[0] = 0; + c->eh_args[1] = 0; + c->eh_args[2] = 0; + c->eh_args[3] = 0; + +#ifdef IA64_UNW_SCRIPT_CACHE + c->hint = 0; + c->prev_script = 0; +#endif + + return ia64_get_frame_state (c); +} + +alias (ia64_init_remote, UNW_OBJ(init_remote)) diff --git a/src/ia64/unw_is_signal_frame.c b/src/ia64/unw_is_signal_frame.c index e69de29b..d11cab49 100644 --- a/src/ia64/unw_is_signal_frame.c +++ b/src/ia64/unw_is_signal_frame.c @@ -0,0 +1,32 @@ +/* libunwind - a platform-independent unwind library + Copyright (C) 2001-2002 Hewlett-Packard Co + Contributed by David Mosberger-Tang + +This file is part of libunwind. + +libunwind 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. + +libunwind 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. + +As a special exception, if you link this library with other files to +produce an executable, this library does not by itself cause the +resulting executable to be covered by the GNU General Public License. +This exception does not however invalidate any other reasons why the +executable file might be covered by the GNU General Public +License. */ + +#include "unwind_i.h" + +int +unw_is_signal_frame (unw_cursor_t *cursor) +{ + struct ia64_cursor *c = (struct ia64_cursor *) cursor; + + return (c->flags & IA64_FLAG_SIGTRAMP) != 0; +} diff --git a/src/ia64/unw_resume.c b/src/ia64/unw_resume.c index e69de29b..dcb20522 100644 --- a/src/ia64/unw_resume.c +++ b/src/ia64/unw_resume.c @@ -0,0 +1,36 @@ +/* libunwind - a platform-independent unwind library + Copyright (C) 2001-2002 Hewlett-Packard Co + Contributed by David Mosberger-Tang + +This file is part of libunwind. + +libunwind 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. + +libunwind 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. + +As a special exception, if you link this library with other files to +produce an executable, this library does not by itself cause the +resulting executable to be covered by the GNU General Public License. +This exception does not however invalidate any other reasons why the +executable file might be covered by the GNU General Public +License. */ + +#include "unwind_i.h" + +int +unw_resume (unw_cursor_t *cursor) +{ + struct ia64_cursor *c = (struct ia64_cursor *) cursor; + +#ifdef IA64_UNW_ACCESSORS + return (*c->acc.resume) (cursor, c->acc.arg); +#else +# error fix me. +#endif +} diff --git a/src/ia64/unw_set_reg.c b/src/ia64/unw_set_reg.c index e69de29b..272a30aa 100644 --- a/src/ia64/unw_set_reg.c +++ b/src/ia64/unw_set_reg.c @@ -0,0 +1,32 @@ +/* libunwind - a platform-independent unwind library + Copyright (C) 2001-2002 Hewlett-Packard Co + Contributed by David Mosberger-Tang + +This file is part of libunwind. + +libunwind 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. + +libunwind 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. + +As a special exception, if you link this library with other files to +produce an executable, this library does not by itself cause the +resulting executable to be covered by the GNU General Public License. +This exception does not however invalidate any other reasons why the +executable file might be covered by the GNU General Public +License. */ + +#include "unwind_i.h" + +int +unw_set_reg (unw_cursor_t *cursor, int regnum, unw_word_t valp) +{ + struct ia64_cursor *c = (struct ia64_cursor *) cursor; + + return ia64_access_reg (c, regnum, &valp, 1); +} diff --git a/src/ia64/unw_step.c b/src/ia64/unw_step.c index e69de29b..039a8477 100644 --- a/src/ia64/unw_step.c +++ b/src/ia64/unw_step.c @@ -0,0 +1,134 @@ +/* libunwind - a platform-independent unwind library + Copyright (C) 2001-2002 Hewlett-Packard Co + Contributed by David Mosberger-Tang + +This file is part of libunwind. + +libunwind 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. + +libunwind 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. + +As a special exception, if you link this library with other files to +produce an executable, this library does not by itself cause the +resulting executable to be covered by the GNU General Public License. +This exception does not however invalidate any other reasons why the +executable file might be covered by the GNU General Public +License. */ + +#include + +#include "rse.h" +#include "unwind_i.h" + +int +ia64_get_frame_state (struct ia64_cursor *c) +{ + unw_word_t prev_ip, prev_sp, prev_bsp, ip, pr, num_regs, cfm; + int ret; + + prev_ip = c->ip; + prev_sp = c->sp; + prev_bsp = c->bsp; + + /* restore the ip */ + ret = ia64_get (c, c->rp_loc, &ip); + if (ret < 0) + return ret; + c->ip = ip; + + if ((ip & 0xf) != 0) + { + /* don't let obviously bad addresses pollute the cache */ + debug (1, "%s: rejecting bad ip=0x%lx\n", __FUNCTION__, c->ip); + c->rp_loc = 0; + return -UNW_EINVALIDIP; + } + + /* restore the cfm: */ + c->cfm_loc = c->pfs_loc; + + /* restore the bsp: */ + pr = c->pr; + num_regs = 0; + if ((c->pi.flags & IA64_FLAG_SIGTRAMP)) + { + unw_word_t sigcontext_addr, sigcontext_flags; + + ret = ia64_get (c, c->sp + 0x10, &sigcontext_addr); + if (ret < 0) + return ret; + + ret = ia64_get (c, (sigcontext_addr + + struct_offset (struct sigcontext, sc_flags)), + &sigcontext_flags); + if (ret < 0) + return ret; + + if ((sigcontext_flags & IA64_SC_FLAG_IN_SYSCALL_BIT) == 0) + { + unw_word_t cfm; + + ret = ia64_get (c, c->cfm_loc, &cfm); + if (ret < 0) + return ret; + + num_regs = cfm & 0x7f; /* size of frame */ + } + c->pfs_loc = (c->sp + 0x10 + struct_offset (struct sigcontext, + sc_ar_pfs)); + } + else + { + ret = ia64_get (c, c->cfm_loc, &cfm); + if (ret < 0) + return ret; + num_regs = (cfm >> 7) & 0x7f; /* size of locals */ + } + c->bsp = (unsigned long) ia64_rse_skip_regs ((unsigned long *) c->bsp, + -num_regs); + + /* restore the sp: */ + c->sp = c->psp; + + if (c->ip == prev_ip && c->sp == prev_sp && c->bsp == prev_bsp) + { + dprintf ("%s: ip, sp, bsp remain unchanged; stopping here (ip=0x%lx)\n", + __FUNCTION__, ip); + STAT(unw.stat.api.unwind_time += ia64_get_itc () - start); + return -UNW_EBADFRAME; + } + + /* as we unwind, the saved ar.unat becomes the primary unat: */ + c->pri_unat_loc = c->unat_loc; + + /* restore the predicates: */ + ret = ia64_get (c, c->pr_loc, &c->pr); + if (ret < 0) + return ret; + + return ia64_get_proc_info (c); +} + + +int +unw_step (unw_cursor_t *cursor) +{ + struct ia64_cursor *c = (struct ia64_cursor *) cursor; + int ret; + + ret = ia64_find_save_locs (c); + if (ret < 0) + return ret; + + ret = ia64_get_frame_state (c); + if (ret < 0) + return ret; + + return (c->ip == 0) ? 0 : 1; +} diff --git a/src/ia64/unwind_decoder.c b/src/ia64/unwind_decoder.c index e69de29b..f704eed2 100644 --- a/src/ia64/unwind_decoder.c +++ b/src/ia64/unwind_decoder.c @@ -0,0 +1,482 @@ +/* libunwind - a platform-independent unwind library + Copyright (C) 2001-2002 Hewlett-Packard Co + Contributed by David Mosberger-Tang + +This file is part of libunwind. + +libunwind 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. + +libunwind 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. + +As a special exception, if you link this library with other files to +produce an executable, this library does not by itself cause the +resulting executable to be covered by the GNU General Public License. +This exception does not however invalidate any other reasons why the +executable file might be covered by the GNU General Public +License. */ + +/* + * Copyright (C) 2000 Hewlett-Packard Co + * Copyright (C) 2000 David Mosberger-Tang + * + * Generic IA-64 unwind info decoder. + * + * This file is used both by the Linux kernel and objdump. Please keep + * the two copies of this file in sync. + * + * You need to customize the decoder by defining the following + * macros/constants before including this file: + * + * Types: + * unw_word Unsigned integer type with at least 64 bits + * + * Register names: + * UNW_REG_BSP + * UNW_REG_BSPSTORE + * UNW_REG_FPSR + * UNW_REG_LC + * UNW_REG_PFS + * UNW_REG_PR + * UNW_REG_RNAT + * UNW_REG_PSP + * UNW_REG_RP + * UNW_REG_UNAT + * + * Decoder action macros: + * UNW_DEC_BAD_CODE(code) + * UNW_DEC_ABI(fmt,abi,context,arg) + * UNW_DEC_BR_GR(fmt,brmask,gr,arg) + * UNW_DEC_BR_MEM(fmt,brmask,arg) + * UNW_DEC_COPY_STATE(fmt,label,arg) + * UNW_DEC_EPILOGUE(fmt,t,ecount,arg) + * UNW_DEC_FRGR_MEM(fmt,grmask,frmask,arg) + * UNW_DEC_FR_MEM(fmt,frmask,arg) + * UNW_DEC_GR_GR(fmt,grmask,gr,arg) + * UNW_DEC_GR_MEM(fmt,grmask,arg) + * UNW_DEC_LABEL_STATE(fmt,label,arg) + * UNW_DEC_MEM_STACK_F(fmt,t,size,arg) + * UNW_DEC_MEM_STACK_V(fmt,t,arg) + * UNW_DEC_PRIUNAT_GR(fmt,r,arg) + * UNW_DEC_PRIUNAT_WHEN_GR(fmt,t,arg) + * UNW_DEC_PRIUNAT_WHEN_MEM(fmt,t,arg) + * UNW_DEC_PRIUNAT_WHEN_PSPREL(fmt,pspoff,arg) + * UNW_DEC_PRIUNAT_WHEN_SPREL(fmt,spoff,arg) + * UNW_DEC_PROLOGUE(fmt,body,rlen,arg) + * UNW_DEC_PROLOGUE_GR(fmt,rlen,mask,grsave,arg) + * UNW_DEC_REG_PSPREL(fmt,reg,pspoff,arg) + * UNW_DEC_REG_REG(fmt,src,dst,arg) + * UNW_DEC_REG_SPREL(fmt,reg,spoff,arg) + * UNW_DEC_REG_WHEN(fmt,reg,t,arg) + * UNW_DEC_RESTORE(fmt,t,abreg,arg) + * UNW_DEC_RESTORE_P(fmt,qp,t,abreg,arg) + * UNW_DEC_SPILL_BASE(fmt,pspoff,arg) + * UNW_DEC_SPILL_MASK(fmt,imaskp,arg) + * UNW_DEC_SPILL_PSPREL(fmt,t,abreg,pspoff,arg) + * UNW_DEC_SPILL_PSPREL_P(fmt,qp,t,abreg,pspoff,arg) + * UNW_DEC_SPILL_REG(fmt,t,abreg,x,ytreg,arg) + * UNW_DEC_SPILL_REG_P(fmt,qp,t,abreg,x,ytreg,arg) + * UNW_DEC_SPILL_SPREL(fmt,t,abreg,spoff,arg) + * UNW_DEC_SPILL_SPREL_P(fmt,qp,t,abreg,pspoff,arg) + */ + +static unw_word +unw_decode_uleb128 (unsigned char **dpp) +{ + unsigned shift = 0; + unw_word byte, result = 0; + unsigned char *bp = *dpp; + + while (1) + { + byte = *bp++; + result |= (byte & 0x7f) << shift; + if ((byte & 0x80) == 0) + break; + shift += 7; + } + *dpp = bp; + return result; +} + +static unsigned char * +unw_decode_x1 (unsigned char *dp, unsigned char code, void *arg) +{ + unsigned char byte1, abreg; + unw_word t, off; + + byte1 = *dp++; + t = unw_decode_uleb128 (&dp); + off = unw_decode_uleb128 (&dp); + abreg = (byte1 & 0x7f); + if (byte1 & 0x80) + UNW_DEC_SPILL_SPREL(X1, t, abreg, off, arg); + else + UNW_DEC_SPILL_PSPREL(X1, t, abreg, off, arg); + return dp; +} + +static unsigned char * +unw_decode_x2 (unsigned char *dp, unsigned char code, void *arg) +{ + unsigned char byte1, byte2, abreg, x, ytreg; + unw_word t; + + byte1 = *dp++; byte2 = *dp++; + t = unw_decode_uleb128 (&dp); + abreg = (byte1 & 0x7f); + ytreg = byte2; + x = (byte1 >> 7) & 1; + if ((byte1 & 0x80) == 0 && ytreg == 0) + UNW_DEC_RESTORE(X2, t, abreg, arg); + else + UNW_DEC_SPILL_REG(X2, t, abreg, x, ytreg, arg); + return dp; +} + +static unsigned char * +unw_decode_x3 (unsigned char *dp, unsigned char code, void *arg) +{ + unsigned char byte1, byte2, abreg, qp; + unw_word t, off; + + byte1 = *dp++; byte2 = *dp++; + t = unw_decode_uleb128 (&dp); + off = unw_decode_uleb128 (&dp); + + qp = (byte1 & 0x3f); + abreg = (byte2 & 0x7f); + + if (byte1 & 0x80) + UNW_DEC_SPILL_SPREL_P(X3, qp, t, abreg, off, arg); + else + UNW_DEC_SPILL_PSPREL_P(X3, qp, t, abreg, off, arg); + return dp; +} + +static unsigned char * +unw_decode_x4 (unsigned char *dp, unsigned char code, void *arg) +{ + unsigned char byte1, byte2, byte3, qp, abreg, x, ytreg; + unw_word t; + + byte1 = *dp++; byte2 = *dp++; byte3 = *dp++; + t = unw_decode_uleb128 (&dp); + + qp = (byte1 & 0x3f); + abreg = (byte2 & 0x7f); + x = (byte2 >> 7) & 1; + ytreg = byte3; + + if ((byte2 & 0x80) == 0 && byte3 == 0) + UNW_DEC_RESTORE_P(X4, qp, t, abreg, arg); + else + UNW_DEC_SPILL_REG_P(X4, qp, t, abreg, x, ytreg, arg); + return dp; +} + +static unsigned char * +unw_decode_r1 (unsigned char *dp, unsigned char code, void *arg) +{ + int body = (code & 0x20) != 0; + unw_word rlen; + + rlen = (code & 0x1f); + UNW_DEC_PROLOGUE(R1, body, rlen, arg); + return dp; +} + +static unsigned char * +unw_decode_r2 (unsigned char *dp, unsigned char code, void *arg) +{ + unsigned char byte1, mask, grsave; + unw_word rlen; + + byte1 = *dp++; + + mask = ((code & 0x7) << 1) | ((byte1 >> 7) & 1); + grsave = (byte1 & 0x7f); + rlen = unw_decode_uleb128 (&dp); + UNW_DEC_PROLOGUE_GR(R2, rlen, mask, grsave, arg); + return dp; +} + +static unsigned char * +unw_decode_r3 (unsigned char *dp, unsigned char code, void *arg) +{ + unw_word rlen; + + rlen = unw_decode_uleb128 (&dp); + UNW_DEC_PROLOGUE(R3, ((code & 0x3) == 1), rlen, arg); + return dp; +} + +static unsigned char * +unw_decode_p1 (unsigned char *dp, unsigned char code, void *arg) +{ + unsigned char brmask = (code & 0x1f); + + UNW_DEC_BR_MEM(P1, brmask, arg); + return dp; +} + +static unsigned char * +unw_decode_p2_p5 (unsigned char *dp, unsigned char code, void *arg) +{ + if ((code & 0x10) == 0) + { + unsigned char byte1 = *dp++; + + UNW_DEC_BR_GR(P2, ((code & 0xf) << 1) | ((byte1 >> 7) & 1), + (byte1 & 0x7f), arg); + } + else if ((code & 0x08) == 0) + { + unsigned char byte1 = *dp++, r, dst; + + r = ((code & 0x7) << 1) | ((byte1 >> 7) & 1); + dst = (byte1 & 0x7f); + switch (r) + { + case 0: UNW_DEC_REG_GR(P3, UNW_REG_PSP, dst, arg); break; + case 1: UNW_DEC_REG_GR(P3, UNW_REG_RP, dst, arg); break; + case 2: UNW_DEC_REG_GR(P3, UNW_REG_PFS, dst, arg); break; + case 3: UNW_DEC_REG_GR(P3, UNW_REG_PR, dst, arg); break; + case 4: UNW_DEC_REG_GR(P3, UNW_REG_UNAT, dst, arg); break; + case 5: UNW_DEC_REG_GR(P3, UNW_REG_LC, dst, arg); break; + case 6: UNW_DEC_RP_BR(P3, dst, arg); break; + case 7: UNW_DEC_REG_GR(P3, UNW_REG_RNAT, dst, arg); break; + case 8: UNW_DEC_REG_GR(P3, UNW_REG_BSP, dst, arg); break; + case 9: UNW_DEC_REG_GR(P3, UNW_REG_BSPSTORE, dst, arg); break; + case 10: UNW_DEC_REG_GR(P3, UNW_REG_FPSR, dst, arg); break; + case 11: UNW_DEC_PRIUNAT_GR(P3, dst, arg); break; + default: UNW_DEC_BAD_CODE(r); break; + } + } + else if ((code & 0x7) == 0) + UNW_DEC_SPILL_MASK(P4, dp, arg); + else if ((code & 0x7) == 1) + { + unw_word grmask, frmask, byte1, byte2, byte3; + + byte1 = *dp++; byte2 = *dp++; byte3 = *dp++; + grmask = ((byte1 >> 4) & 0xf); + frmask = ((byte1 & 0xf) << 16) | (byte2 << 8) | byte3; + UNW_DEC_FRGR_MEM(P5, grmask, frmask, arg); + } + else + UNW_DEC_BAD_CODE(code); + return dp; +} + +static unsigned char * +unw_decode_p6 (unsigned char *dp, unsigned char code, void *arg) +{ + int gregs = (code & 0x10) != 0; + unsigned char mask = (code & 0x0f); + + if (gregs) + UNW_DEC_GR_MEM(P6, mask, arg); + else + UNW_DEC_FR_MEM(P6, mask, arg); + return dp; +} + +static unsigned char * +unw_decode_p7_p10 (unsigned char *dp, unsigned char code, void *arg) +{ + unsigned char r, byte1, byte2; + unw_word t, size; + + if ((code & 0x10) == 0) + { + r = (code & 0xf); + t = unw_decode_uleb128 (&dp); + switch (r) + { + case 0: + size = unw_decode_uleb128 (&dp); + UNW_DEC_MEM_STACK_F(P7, t, size, arg); + break; + + case 1: UNW_DEC_MEM_STACK_V(P7, t, arg); break; + case 2: UNW_DEC_SPILL_BASE(P7, t, arg); break; + case 3: UNW_DEC_REG_SPREL(P7, UNW_REG_PSP, t, arg); break; + case 4: UNW_DEC_REG_WHEN(P7, UNW_REG_RP, t, arg); break; + case 5: UNW_DEC_REG_PSPREL(P7, UNW_REG_RP, t, arg); break; + case 6: UNW_DEC_REG_WHEN(P7, UNW_REG_PFS, t, arg); break; + case 7: UNW_DEC_REG_PSPREL(P7, UNW_REG_PFS, t, arg); break; + case 8: UNW_DEC_REG_WHEN(P7, UNW_REG_PR, t, arg); break; + case 9: UNW_DEC_REG_PSPREL(P7, UNW_REG_PR, t, arg); break; + case 10: UNW_DEC_REG_WHEN(P7, UNW_REG_LC, t, arg); break; + case 11: UNW_DEC_REG_PSPREL(P7, UNW_REG_LC, t, arg); break; + case 12: UNW_DEC_REG_WHEN(P7, UNW_REG_UNAT, t, arg); break; + case 13: UNW_DEC_REG_PSPREL(P7, UNW_REG_UNAT, t, arg); break; + case 14: UNW_DEC_REG_WHEN(P7, UNW_REG_FPSR, t, arg); break; + case 15: UNW_DEC_REG_PSPREL(P7, UNW_REG_FPSR, t, arg); break; + default: UNW_DEC_BAD_CODE(r); break; + } + } + else + { + switch (code & 0xf) + { + case 0x0: /* p8 */ + { + r = *dp++; + t = unw_decode_uleb128 (&dp); + switch (r) + { + case 1: UNW_DEC_REG_SPREL(P8, UNW_REG_RP, t, arg); break; + case 2: UNW_DEC_REG_SPREL(P8, UNW_REG_PFS, t, arg); break; + case 3: UNW_DEC_REG_SPREL(P8, UNW_REG_PR, t, arg); break; + case 4: UNW_DEC_REG_SPREL(P8, UNW_REG_LC, t, arg); break; + case 5: UNW_DEC_REG_SPREL(P8, UNW_REG_UNAT, t, arg); break; + case 6: UNW_DEC_REG_SPREL(P8, UNW_REG_FPSR, t, arg); break; + case 7: UNW_DEC_REG_WHEN(P8, UNW_REG_BSP, t, arg); break; + case 8: UNW_DEC_REG_PSPREL(P8, UNW_REG_BSP, t, arg); break; + case 9: UNW_DEC_REG_SPREL(P8, UNW_REG_BSP, t, arg); break; + case 10: UNW_DEC_REG_WHEN(P8, UNW_REG_BSPSTORE, t, arg); break; + case 11: UNW_DEC_REG_PSPREL(P8, UNW_REG_BSPSTORE, t, arg); break; + case 12: UNW_DEC_REG_SPREL(P8, UNW_REG_BSPSTORE, t, arg); break; + case 13: UNW_DEC_REG_WHEN(P8, UNW_REG_RNAT, t, arg); break; + case 14: UNW_DEC_REG_PSPREL(P8, UNW_REG_RNAT, t, arg); break; + case 15: UNW_DEC_REG_SPREL(P8, UNW_REG_RNAT, t, arg); break; + case 16: UNW_DEC_PRIUNAT_WHEN_GR(P8, t, arg); break; + case 17: UNW_DEC_PRIUNAT_PSPREL(P8, t, arg); break; + case 18: UNW_DEC_PRIUNAT_SPREL(P8, t, arg); break; + case 19: UNW_DEC_PRIUNAT_WHEN_MEM(P8, t, arg); break; + default: UNW_DEC_BAD_CODE(r); break; + } + } + break; + + case 0x1: + byte1 = *dp++; byte2 = *dp++; + UNW_DEC_GR_GR(P9, (byte1 & 0xf), (byte2 & 0x7f), arg); + break; + + case 0xf: /* p10 */ + byte1 = *dp++; byte2 = *dp++; + UNW_DEC_ABI(P10, byte1, byte2, arg); + break; + + case 0x9: + return unw_decode_x1 (dp, code, arg); + + case 0xa: + return unw_decode_x2 (dp, code, arg); + + case 0xb: + return unw_decode_x3 (dp, code, arg); + + case 0xc: + return unw_decode_x4 (dp, code, arg); + + default: + UNW_DEC_BAD_CODE(code); + break; + } + } + return dp; +} + +static unsigned char * +unw_decode_b1 (unsigned char *dp, unsigned char code, void *arg) +{ + unw_word label = (code & 0x1f); + + if ((code & 0x20) != 0) + UNW_DEC_COPY_STATE(B1, label, arg); + else + UNW_DEC_LABEL_STATE(B1, label, arg); + return dp; +} + +static unsigned char * +unw_decode_b2 (unsigned char *dp, unsigned char code, void *arg) +{ + unw_word t; + + t = unw_decode_uleb128 (&dp); + UNW_DEC_EPILOGUE(B2, t, (code & 0x1f), arg); + return dp; +} + +static unsigned char * +unw_decode_b3_x4 (unsigned char *dp, unsigned char code, void *arg) +{ + unw_word t, ecount, label; + + if ((code & 0x10) == 0) + { + t = unw_decode_uleb128 (&dp); + ecount = unw_decode_uleb128 (&dp); + UNW_DEC_EPILOGUE(B3, t, ecount, arg); + } + else if ((code & 0x07) == 0) + { + label = unw_decode_uleb128 (&dp); + if ((code & 0x08) != 0) + UNW_DEC_COPY_STATE(B4, label, arg); + else + UNW_DEC_LABEL_STATE(B4, label, arg); + } + else + switch (code & 0x7) + { + case 1: return unw_decode_x1 (dp, code, arg); + case 2: return unw_decode_x2 (dp, code, arg); + case 3: return unw_decode_x3 (dp, code, arg); + case 4: return unw_decode_x4 (dp, code, arg); + default: UNW_DEC_BAD_CODE(code); break; + } + return dp; +} + +typedef unsigned char *(*unw_decoder) (unsigned char *, unsigned char, void *); + +static unw_decoder unw_decode_table[2][8] = +{ + /* prologue table: */ + { + unw_decode_r1, /* 0 */ + unw_decode_r1, + unw_decode_r2, + unw_decode_r3, + unw_decode_p1, /* 4 */ + unw_decode_p2_p5, + unw_decode_p6, + unw_decode_p7_p10 + }, + { + unw_decode_r1, /* 0 */ + unw_decode_r1, + unw_decode_r2, + unw_decode_r3, + unw_decode_b1, /* 4 */ + unw_decode_b1, + unw_decode_b2, + unw_decode_b3_x4 + } +}; + +/* + * Decode one descriptor and return address of next descriptor. + */ +static inline unsigned char * +unw_decode (unsigned char *dp, int inside_body, void *arg) +{ + unw_decoder decoder; + unsigned char code; + + code = *dp++; + decoder = unw_decode_table[inside_body][code >> 5]; + dp = (*decoder) (dp, code, arg); + return dp; +} diff --git a/src/ia64/unwind_i.h b/src/ia64/unwind_i.h index e69de29b..b681bf64 100644 --- a/src/ia64/unwind_i.h +++ b/src/ia64/unwind_i.h @@ -0,0 +1,485 @@ +/* libunwind - a platform-independent unwind library + Copyright (C) 2001-2002 Hewlett-Packard Co + Contributed by David Mosberger-Tang + +This file is part of libunwind. + +libunwind 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. + +libunwind 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. + +As a special exception, if you link this library with other files to +produce an executable, this library does not by itself cause the +resulting executable to be covered by the GNU General Public License. +This exception does not however invalidate any other reasons why the +executable file might be covered by the GNU General Public +License. */ + +#define IA64_UNW_SCRIPT_CACHE + +#include +#include +#include + +#define struct_offset(str,fld) ((char *)&((str *)NULL)->fld - (char *) 0) + +#define IA64_UNW_VER(x) ((x) >> 48) +#define IA64_UNW_FLAG_MASK 0x0000ffff00000000 +#define IA64_UNW_FLAG_OSMASK 0x0000f00000000000 +#define IA64_UNW_FLAG_EHANDLER(x) ((x) & 0x0000000100000000L) +#define IA64_UNW_FLAG_UHANDLER(x) ((x) & 0x0000000200000000L) +#define IA64_UNW_LENGTH(x) ((x) & 0x00000000ffffffffL) + +#define MIN(a,b) ((a) < (b) ? (a) : (b)) + +#define IA64_UNW_DEBUG 1 +#define IA64_UNW_STATS 0 + +#if IA64_UNW_DEBUG +# include +# define debug(level,format...) \ + do { if (unw.debug_level > level) printf (format); } while (0) +# define dprintf(format...) \ + printf (format) +# define inline __attribute__ ((unused)) + extern const char *_U_ia64_regname (unw_regnum_t reg); +#else +# define debug(level,format...) +# define dprintf(format...) +#endif + +#if IA64_UNW_STATS +# define STAT(x...) x +#else +# define STAT(x...) +#endif + +enum ia64_pregnum + { + /* primary unat: */ + IA64_REG_PRI_UNAT_GR, + IA64_REG_PRI_UNAT_MEM, + + /* register stack */ + IA64_REG_BSP, /* register stack pointer */ + IA64_REG_BSPSTORE, + IA64_REG_PFS, /* previous function state */ + IA64_REG_RNAT, + /* memory stack */ + IA64_REG_PSP, /* previous memory stack pointer */ + /* return pointer: */ + IA64_REG_RP, + + /* preserved registers: */ + IA64_REG_R4, IA64_REG_R5, IA64_REG_R6, IA64_REG_R7, + IA64_REG_NAT4, IA64_REG_NAT5, IA64_REG_NAT6, IA64_REG_NAT7, + IA64_REG_UNAT, IA64_REG_PR, IA64_REG_LC, IA64_REG_FPSR, + IA64_REG_B1, IA64_REG_B2, IA64_REG_B3, IA64_REG_B4, IA64_REG_B5, + IA64_REG_F2, IA64_REG_F3, IA64_REG_F4, IA64_REG_F5, + IA64_REG_F16, IA64_REG_F17, IA64_REG_F18, IA64_REG_F19, + IA64_REG_F20, IA64_REG_F21, IA64_REG_F22, IA64_REG_F23, + IA64_REG_F24, IA64_REG_F25, IA64_REG_F26, IA64_REG_F27, + IA64_REG_F28, IA64_REG_F29, IA64_REG_F30, IA64_REG_F31, + IA64_NUM_PREGS + }; + +#define IA64_FLAG_SIGTRAMP (1 << 0) /* signal trampoline? */ +#define IA64_FLAG_BIG_ENDIAN (1 << 1) /* big-endian? */ +#define IA64_FLAG_HAS_HANDLER (1 << 2) /* has personality routine? */ + +struct ia64_proc_info + { + unsigned int flags; /* see IA64_FLAG_* above */ + unw_word_t gp; /* global pointer value */ + unw_word_t proc_start; /* start address of procedure */ + uint64_t *pers_addr; /* address of personality routine pointer */ + uint8_t *desc; /* encoded unwind descriptors (or NULL) */ + }; + +struct ia64_cursor + { +#ifdef IA64_UNW_ACCESSORS + struct unw_accessors acc; +#else + ucontext_t *uc; /* pointer to struct of preserved registers */ +#endif + + /* current frame info: */ + unw_word_t bsp; /* backing store pointer value */ + unw_word_t sp; /* stack pointer value */ + unw_word_t psp; /* previous sp value */ + unw_word_t ip; /* instruction pointer value */ + struct ia64_proc_info pi; /* info about current procedure */ + unw_word_t pr; /* current predicate values */ + unw_word_t cfm_loc; /* cfm save location (or NULL) */ + + unw_word_t rbs_top; /* address of end of register backing store */ + unw_word_t top_rnat_loc; /* location of final (topmost) RNaT word */ + + /* preserved state: */ + unw_word_t bsp_loc; /* previous bsp save location */ + unw_word_t bspstore_loc; + unw_word_t pfs_loc; + unw_word_t rnat_loc; + unw_word_t rp_loc; + unw_word_t pri_unat_loc; + unw_word_t unat_loc; + unw_word_t pr_loc; + unw_word_t lc_loc; + unw_word_t fpsr_loc; + unw_word_t r4_loc, r5_loc, r6_loc, r7_loc; + unw_word_t nat4_loc, nat5_loc, nat6_loc, nat7_loc; + unw_word_t b1_loc, b2_loc, b3_loc, b4_loc, b5_loc; + unw_word_t f2_loc, f3_loc, f4_loc, f5_loc, fr_loc[16]; + + unw_word_t eh_args[4]; /* exception handler arguments */ + +#ifdef IA64_UNW_SCRIPT_CACHE + short hint; + short prev_script; +#endif +}; + +#ifdef IA64_UNW_SCRIPT_CACHE +# include "script.h" +#endif + +/* Bits 0 to 2 of an location are used to encode its type: + + bit 0: set if location uses floating-point format. + bit 1: set if location is a NaT bit on memory stack + bit 2: set if location is a register. */ + +#define IA64_LOC_TYPE_FP (1 << 0) +#define IA64_LOC_TYPE_MEMSTK_NAT (1 << 1) +#define IA64_LOC_TYPE_REG (1 << 2) + +#define IA64_LOC(l,t) (((l) << 3) | (t)) + +#define IA64_GET_LOC(l) ((l) >> 3) +#define IA64_MASK_LOC_TYPE(l) ((l) & ~0x7) +#define IA64_IS_FP_LOC(loc) (((loc) & IA64_LOC_TYPE_FP) != 0) +#define IA64_IS_MEMSTK_NAT(loc) (((loc) & IA64_LOC_TYPE_MEMSTK_NAT) != 0) +#define IA64_IS_REG_LOC(r) (((r) & IA64_LOC_TYPE_REG) != 0) + +#define IA64_REG_LOC(r) IA64_LOC((r), IA64_LOC_TYPE_REG) +#define IA64_FPREG_LOC(r) IA64_LOC((r), (IA64_LOC_TYPE_REG \ + | IA64_LOC_TYPE_FP)) + +#if 0 +#define IA64_FP_LOC(l) ((l) | 0x1) +#define IA64_NAT_TYPE_LOC(t,l) ((l) | ((t) << 2)) +#define IA64_GET_REG_LOC(r) ((r) >> 3) +#define IA64_GET_NAT_LOC(loc) ((loc) >> 3) +#endif + +#ifdef IA64_UNW_ACCESSORS +# define ia64_acquire_unwind_info(c,ip,i) \ + (*(c)->acc.acquire_unwind_info)((ip), (i), (c)->acc.arg) +# define ia64_release_unwind_info(c,ip,i) \ + (*(c)->acc.release_unwind_info)((i), (c)->acc.arg) + +static inline int +ia64_getfp (struct ia64_cursor *c, unw_word_t loc, unw_fpreg_t *val) +{ + int ret; + + if (IA64_IS_REG_LOC (loc)) + return (*c->acc.access_fpreg) (IA64_GET_LOC (loc), val, 0, c->acc.arg); + + loc = IA64_MASK_LOC_TYPE(loc); + ret = (*c->acc.access_mem) (loc + 0, &val->raw.bits[0], 0, c->acc.arg); + if (ret < 0) + return ret; + + return (*c->acc.access_mem) (loc + 8, &val->raw.bits[1], 0, c->acc.arg); +} + +static inline int +ia64_putfp (struct ia64_cursor *c, unw_word_t loc, unw_fpreg_t val) +{ + int ret; + + if (IA64_IS_REG_LOC (loc)) + return (*c->acc.access_fpreg) (IA64_GET_LOC (loc), &val, 1, c->acc.arg); + + loc = IA64_MASK_LOC_TYPE(loc); + ret = (*c->acc.access_mem) (loc + 0, &val.raw.bits[0], 1, c->acc.arg); + if (ret < 0) + return ret; + + return (*c->acc.access_mem) (loc + 8, &val.raw.bits[1], 1, c->acc.arg); +} + +/* Get the 64 data bits from location LOC. If bit 0 is cleared, LOC + is a memory address, otherwise it is a register number. If the + register is a floating-point register, the 64 bits are read from + the significand bits. */ + +static inline int +ia64_get (struct ia64_cursor *c, unw_word_t loc, unw_word_t *val) +{ + if (IA64_IS_FP_LOC (loc)) + { + unw_fpreg_t tmp; + int ret; + + ret = ia64_getfp (c, loc, &tmp); + if (ret < 0) + return ret; + + if (c->pi.flags & IA64_FLAG_BIG_ENDIAN) + *val = tmp.raw.bits[1]; + else + *val = tmp.raw.bits[0]; + return 0; + } + + if (IA64_IS_REG_LOC (loc)) + return (*c->acc.access_reg)(IA64_GET_LOC (loc), val, 0, c->acc.arg); + else + return (*c->acc.access_mem)(loc, val, 0, c->acc.arg); +} + +static inline int +ia64_put (struct ia64_cursor *c, unw_word_t loc, unw_word_t val) +{ + if (IA64_IS_FP_LOC (loc)) + { + unw_fpreg_t tmp; + + memset (&tmp, 0, sizeof (tmp)); + if (c->pi.flags & IA64_FLAG_BIG_ENDIAN) + tmp.raw.bits[1] = val; + else + tmp.raw.bits[0] = val; + return ia64_putfp (c, loc, tmp); + } + + if (loc & 1) + return (*c->acc.access_reg)(loc >> 1, &val, 1, c->acc.arg); + else + return (*c->acc.access_mem)(loc, &val, 1, c->acc.arg); +} + +#else + extern int ia64_acquire_unwind_info (unw_word_t, void *); + extern int ia64_release_unwind_info (void *); +# define ia64_acquire_unwind_info(c,ip,i) \ + ia64_acquire_unwind_info((ip), (i)) +# define ia64_release_unwind_info(c,ip,i) \ + ia64_release_unwind_info((i)) +# define ia64_get(c,l,v) (*(v) = *(unw_word_t *) (l), 0) +#endif + +struct ia64_unwind_block + { + unw_word_t header; + unw_word_t desc[0]; /* unwind descriptors */ + + /* Personality routine and language-specific data follow behind + descriptors. */ + }; + +struct ia64_unwind_table_entry + { + unw_word_t start_offset; + unw_word_t end_offset; + unw_word_t info_offset; + }; + +struct ia64_unwind_table + { + struct ia64_unwind_table *next; /* must be first member! */ + unw_word_t start; /* start offset covered by table */ + unw_word_t end; /* end offset covered table */ + unw_ia64_table_t info; + }; + +enum ia64_where + { + IA64_WHERE_NONE, /* register isn't saved at all */ + IA64_WHERE_GR, /* register is saved in a general register */ + IA64_WHERE_FR, /* register is saved in a floating-point register */ + IA64_WHERE_BR, /* register is saved in a branch register */ + IA64_WHERE_SPREL, /* register is saved on memstack (sp-relative) */ + IA64_WHERE_PSPREL, /* register is saved on memstack (psp-relative) */ + + /* At the end of each prologue these locations get resolved to + IA64_WHERE_PSPREL and IA64_WHERE_GR, respectively: */ + + IA64_WHERE_SPILL_HOME, /* register is saved in its spill home */ + IA64_WHERE_GR_SAVE /* register is saved in next general register */ +}; + +#define IA64_WHEN_NEVER 0x7fffffff + +struct ia64_reg_info + { + unw_word_t val; /* save location: register number or offset */ + enum ia64_where where; /* where the register gets saved */ + int when; /* when the register gets saved */ + }; + +struct ia64_state_record + { + unsigned int first_region : 1; /* is this the first region? */ + unsigned int done : 1; /* are we done scanning descriptors? */ + unsigned int any_spills : 1; /* got any register spills? */ + unsigned int in_body : 1; /* are we inside prologue or body? */ + unsigned int flags; /* see IA64_FLAG_* */ + + uint8_t *imask; /* imask of of spill_mask record or NULL */ + unw_word_t pr_val; /* predicate values */ + unw_word_t pr_mask; /* predicate mask */ + + long spill_offset; /* psp-relative offset for spill base */ + int region_start; + int region_len; + int epilogue_start; + int epilogue_count; + int when_target; + + uint8_t gr_save_loc; /* next save register */ + uint8_t return_link_reg; /* branch register used as return pointer */ + + struct ia64_reg_state + { + struct ia64_reg_state *next; + unsigned long label; /* label of this state record */ + struct ia64_reg_info reg[IA64_NUM_PREGS]; + } + curr, *stack, *reg_state_list; +}; + +struct ia64_global_unwind_state + { + int first_time; + + /* List of unwind tables (one per load-module). */ + struct ia64_unwind_table *tables; + + /* Table of registers that prologues can save (and order in which + they're saved). */ + const unsigned char save_order[8]; + + /* Maps a preserved register index (preg_index) to corresponding + ucontext_t offset. */ + unsigned short uc_off[sizeof(unw_cursor_t) / 8]; + + /* Index into unw_cursor_t for preserved register i */ + unsigned short preg_index[IA64_NUM_PREGS]; + + unw_fpreg_t f0, f1_le, f1_be, nat_val_le; + unw_fpreg_t nat_val_be, int_val_le, int_val_be; + +#ifdef IA64_UNW_SCRIPT_CACHE + unsigned short lru_head; /* index of lead-recently used script */ + unsigned short lru_tail; /* index of most-recently used script */ + + /* hash table that maps instruction pointer to script index: */ + unsigned short hash[IA64_UNW_HASH_SIZE]; + + /* script cache: */ + struct ia64_script cache[IA64_UNW_CACHE_SIZE]; +#endif + +# if IA64_UNW_DEBUG + long debug_level; + const char *preg_name[IA64_NUM_PREGS]; +# endif +# if IA64_UNW_STATS + struct + { + struct + { + int lookups; + int hinted_hits; + int normal_hits; + int collision_chain_traversals; + } + cache; + struct + { + unsigned long build_time; + unsigned long run_time; + unsigned long parse_time; + int builds; + int news; + int collisions; + int runs; + } + script; + struct + { + unsigned long init_time; + unsigned long unwind_time; + int inits; + int unwinds; + } + api; + } + stat; +# endif /* IA64_UNW_STATS */ + }; + +/* Convenience macros: */ +#define unw UNW_OBJ(ia64_data) +#define ia64_get_proc_info UNW_OBJ(ia64_get_proc_info) +#define ia64_create_state_record UNW_OBJ(ia64_create_state_record) +#define ia64_free_state_record UNW_OBJ(ia64_free_state_record) +#define ia64_get_frame_state UNW_OBJ(ia64_get_frame_state) +#define ia64_find_save_locs UNW_OBJ(ia64_find_save_locs) +#define ia64_init UNW_OBJ(ia64_init) +#define ia64_init_remote UNW_OBJ(ia64_init_remote) +#define ia64_glibc_acquire_unwind_info UNW_OBJ(ia64_glibc_acquire_unwind_info) +#define ia64_glibc_release_unwind_info UNW_OBJ(ia64_glibc_release_unwind_info) +#define ia64_access_reg UNW_OBJ(ia64_access_reg) +#define ia64_access_fpreg UNW_OBJ(ia64_access_fpreg) +#define ia64_get_sigcontext_addr UNW_OBJ(ia64_get_sigcontext_addr) + +extern struct ia64_global_unwind_state unw; + +extern int ia64_get_proc_info (struct ia64_cursor *c); +extern int ia64_create_state_record (struct ia64_cursor *c, + struct ia64_state_record *sr); +extern int ia64_free_state_record (struct ia64_state_record *sr); +extern int ia64_get_frame_state (struct ia64_cursor *c); +extern int ia64_find_save_locs (struct ia64_cursor *c); +extern void ia64_init (void); +extern int ia64_init_remote (unw_cursor_t *c, unw_accessors_t *a); +extern int ia64_glibc_acquire_unwind_info (unw_word_t ip, void *info, + void *arg); +extern int ia64_glibc_release_unwind_info (void *info, void *arg); +extern int ia64_access_reg (struct ia64_cursor *c, unw_regnum_t reg, + unw_word_t *valp, int write); +extern int ia64_access_fpreg (struct ia64_cursor *c, unw_regnum_t reg, + unw_fpreg_t *valp, int write); +extern unw_word_t ia64_get_sigcontext_addr (struct ia64_cursor *c); + +extern void __ia64_install_context (const ucontext_t *ucp, long r15, long r16, + long r17, long r18) + __attribute__ ((noreturn)); + +/* XXX temporary (from glibc): */ +#define weak_alias(name, aliasname) \ + extern __typeof (name) aliasname __attribute__ ((weak, alias (#name))); +#define alias(name, aliasname) \ + extern __typeof (name) aliasname __attribute__ ((alias (#name))); + +/* XXX should be in glibc: */ +#ifndef IA64_SC_FLAG_ONSTACK +# define IA64_SC_FLAG_ONSTACK_BIT 0 /* running on signal stack? */ +# define IA64_SC_FLAG_IN_SYSCALL_BIT 1 /* did signal interrupt a syscall? */ +# define IA64_SC_FLAG_FPH_VALID_BIT 2 /* is state in f[32]-f[127] valid? */ + +# define IA64_SC_FLAG_ONSTACK (1 << IA64_SC_FLAG_ONSTACK_BIT) +# define IA64_SC_FLAG_IN_SYSCALL (1 << IA64_SC_FLAG_IN_SYSCALL_BIT) +# define IA64_SC_FLAG_FPH_VALID (1 << IA64_SC_FLAG_FPH_VALID_BIT) +#endif diff --git a/src/tests/Makefile b/src/tests/Makefile index e69de29b..282248ac 100644 --- a/src/tests/Makefile +++ b/src/tests/Makefile @@ -0,0 +1,19 @@ +CPPFLAGS = -I../../include +CFLAGS = -g -O -Wall + +PROGS = bt exc + +all: $(PROGS) + +clean: + rm -f *.o $(PROGS) + +distclean: clean + rm -f *~ + +.PHONY: clean distclean + +verify: verify.o ../libunwind.a +exc: exc.o ../libunwind.a +sig: sig.o ../libunwind.a +bt: bt.o ../libunwind.a diff --git a/src/tests/bt.c b/src/tests/bt.c index e69de29b..decb1e95 100644 --- a/src/tests/bt.c +++ b/src/tests/bt.c @@ -0,0 +1,74 @@ +/* libunwind - a platform-independent unwind library + Copyright (C) 2001-2002 Hewlett-Packard Co + Contributed by David Mosberger-Tang + +This file is part of libunwind. + +libunwind 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. + +libunwind 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. */ + +#include +#include +#include +#include + +#define panic(args...) \ + { fprintf (stderr, args); exit (-1); } + +static void +do_backtrace (void) +{ + unw_cursor_t cursor; + unw_word_t ip, sp; + unw_context_t uc; + + unw_getcontext (&uc); + if (unw_init_local (&cursor, &uc) < 0) + panic ("unw_init_local failed!\n"); + + do + { + unw_get_reg (&cursor, UNW_REG_IP, &ip); + unw_get_reg (&cursor, UNW_REG_SP, &sp); + printf ("ip=%016lx sp=%016lx\n", ip, sp); + + { + unw_word_t proc_start, handler, lsda, bsp; + + unw_get_reg (&cursor, UNW_REG_PROC_START, &proc_start); + unw_get_reg (&cursor, UNW_REG_HANDLER, &handler); + unw_get_reg (&cursor, UNW_REG_LSDA, &lsda); + unw_get_reg (&cursor, UNW_IA64_CURRENT_BSP, &bsp); + printf ("\tproc_start=%016lx handler=%lx lsda=%lx bsp=%lx\n", + proc_start, handler, lsda, bsp); + } + } + while (unw_step (&cursor) > 0); +} + +static void +foo (void) +{ + void *buffer[20]; + int i, n; + + do_backtrace (); + + n = backtrace (buffer, 20); + for (i = 0; i < n; ++i) + printf ("[%d] ip=%p\n", i, buffer[i]); +} + +int +main (int argc, char **argv) +{ + foo (); + return 0; +} diff --git a/src/tests/exc.c b/src/tests/exc.c index e69de29b..49100027 100644 --- a/src/tests/exc.c +++ b/src/tests/exc.c @@ -0,0 +1,89 @@ +/* libunwind - a platform-independent unwind library + Copyright (C) 2001-2002 Hewlett-Packard Co + Contributed by David Mosberger-Tang + +This file is part of libunwind. + +libunwind 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. + +libunwind 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. */ + +/* This illustrates the basics of using the unwind interface for + exception handling. */ + +#include +#include +#include + +#define panic(args...) \ + { fprintf (stderr, args); exit (-1); } + +int true = 1; + +static void +raise_exception (void *addr) +{ + unw_cursor_t cursor; + unw_word_t ip; + unw_context_t uc; + + unw_getcontext (&uc); + if (unw_init_local (&cursor, &uc) < 0) + panic ("unw_init_local() failed!\n"); + + /* unwind to frame b(): */ + if (unw_step (&cursor) < 0) + panic ("unw_step() failed!\n"); + + /* unwind to frame a(): */ + if (unw_step (&cursor) < 0) + panic ("unw_step() failed!\n"); + + unw_get_reg (&cursor, UNW_REG_IP, &ip); + printf ("ip = %lx\n", ip); + + if (unw_set_reg (&cursor, UNW_REG_IP, (unw_word_t) addr) < 0) + panic ("unw_set_reg() failed!\n"); + + unw_resume (&cursor); /* transfer control to exception handler */ +} + +static void +b (void *addr) +{ + printf ("b() calling raise_exception()\n"); + raise_exception (addr); +} + +static int +a (void) +{ + register long sp asm ("r12"); + printf("a: sp=%lx bsp=%p\n", sp, __builtin_ia64_bsp ()); + b (&&handler); + printf ("unexpected return from func()!\n"); + + if (true) + return -1; + + handler: + printf ("exception handler: here we go (sp=%lx, bsp=%p)...\n", + sp, __builtin_ia64_bsp ()); + return 0; +} + +int +main (int argc, char **argv) +{ + if (a () == 0) + printf ("test succeeded!\n"); + else + printf ("bummer: test failed; try again?\n"); + return 0; +} diff --git a/src/tests/sig.c b/src/tests/sig.c index e69de29b..94308afa 100644 --- a/src/tests/sig.c +++ b/src/tests/sig.c @@ -0,0 +1,82 @@ +/* libunwind - a platform-independent unwind library + Copyright (C) 2001-2002 Hewlett-Packard Co + Contributed by David Mosberger-Tang + +This file is part of libunwind. + +libunwind 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. + +libunwind 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. */ + +/* This shows how to use the unwind interface to modify any ancestor + frame while still returning to the parent frame. */ + +#include +#include + +#include + +#define panic(args...) \ + { fprintf (stderr, args); exit (-1); } + +static void +sighandler (int signal) +{ + unw_cursor_t cursor, cursor2; + unw_word_t rp; + unw_context_t uc; + + printf ("caught signal %d\n", signal); + + unw_getcontext(&uc); + + if (unw_init (&cursor, &uc) < 0) + panic ("unw_init() failed!\n"); + + /* get cursor for caller of sighandler: */ + if (unw_step (&cursor) < 0) + panic ("unw_step() failed!\n"); + + cursor2 = cursor; + while (!unw_is_signal_frame (&cursor2)) + if (unw_step (&cursor2) < 0) + panic ("failed to find signal frame!\n"); + + if (unw_get_reg (&cursor2, UNW_REG_RP, &rp) < 0) + panic ("failed to get IP!\n"); + + /* skip faulting instruction (doesn't handle MLX template) */ + ++rp; + if (rp & 0x3 == 0x3) + rp += 13; + + if (unw_set_reg (&cursor2, UNW_REG_RP, rp) < 0) + panic ("failed to set IP!\n"); + + unw_resume (&cursor); /* update context & return to caller of sighandler() */ + + panic ("unexpected return from unw_resume()!\n"); +} + +static void +doit (char *p) +{ + int ch; + + ch = *p; /* trigger SIGSEGV */ + + printf ("doit: finishing execution!\n"); +} + +int +main (int argc, char **argv) +{ + signal (SIGSEGV, sighandler); + doit (0); +} diff --git a/src/tests/verify.c b/src/tests/verify.c index e69de29b..db143f5d 100644 --- a/src/tests/verify.c +++ b/src/tests/verify.c @@ -0,0 +1,137 @@ +/* libunwind - a platform-independent unwind library + Copyright (C) 2001-2002 Hewlett-Packard Co + Contributed by David Mosberger-Tang + +This file is part of libunwind. + +libunwind 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. + +libunwind 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. */ + +#include +#include +#include + +#define panic(args...) \ + { fprintf (stderr, args); exit (-1); } + +static void +init_state (unw_context_t *ucp) +{ + int i; + + ucp->uc_mcontext.sc_flags = 0; + + ucp->uc_mcontext.sc_ar_ccv = random (); + ucp->uc_mcontext.sc_ar_lc = random (); + ucp->uc_mcontext.sc_pr = random (); + +#if 0 + ucp->uc_mcontext.sc_ip = xxx; + ucp->uc_mcontext.sc_cfm = xxx; + ucp->uc_mcontext.sc_um = xxx; + ucp->uc_mcontext.sc_ar_rsc = xxx; + ucp->uc_mcontext.sc_ar_bsp = xxx; + ucp->uc_mcontext.sc_ar_rnat = xxx; + ucp->uc_mcontext.sc_ar_unat = xxx; + ucp->uc_mcontext.sc_ar_fpsr = xxx; + ucp->uc_mcontext.sc_ar_pfs = xxx; +#endif + + /* initialize static registers without trashing gp (r1), sp (r12), + or tp (r13). */ + for (i = 2; i < 32; ++i) + { + if (i != 12 && i != 13) + { + ucp->uc_mcontext.sc_gr[i] = random (); + ucp->uc_mcontext.sc_nat |= (random () & 1) << i; + } + } + +#if 0 + /* initialize stacked registers: */ + for (i = 32; i < 128; ++i) + { + xxx; + } +#endif + + for (i = 0; i < 8; ++i) + ucp->uc_mcontext.sc_br[i] = random (); + + for (i = 0; i < 128; ++i) + { + ucp->uc_mcontext.sc_fr[i].u.bits[0] = random (); + ucp->uc_mcontext.sc_fr[i].u.bits[0] = random (); + } +#if 0 + ucp->uc_mcontext.sc_rbs_base = xxx; + ucp->uc_mcontext.sc_loadrs = xxx; + ucp->uc_mcontext.sc_ar25 = xxx; + ucp->uc_mcontext.sc_ar26 = xxx; +#endif +} + +static void +check_state (ucontext_t *orig_state, unw_cursor_t *c) +{ + unw_word_t val; + + unw_get_reg (c, UNW_REG_IP, &val); + printf ("IP: orig=%016lx now=%016lx\n", orig_state->uc_mcontext.sc_ip, val); +} + +static void +setup_context (ucontext_t *unwind_ucp) +{ + asm volatile ("mov ar.fpsr = %0" :: "r"(0x9804c8a70033f)); + + init_state (unwind_ucp); + setcontext (unwind_ucp); +} + +static void +check (ucontext_t *unwind_ucp, ucontext_t *setup_ucp, + void (*doit) (ucontext_t *)) +{ + swapcontext (unwind_ucp, setup_ucp); + (*doit) (unwind_ucp); +} + +static void +test1 (ucontext_t *orig_state) +{ + unw_cursor_t cursor; + ucontext_t uc; + + getcontext (&uc); + if (unw_init_local (&cursor, &uc) < 0) + panic ("unw_init_local failed\n"); + + if (unw_step (&cursor) < 0) + panic ("unw_step failed\n"); + + check_state (orig_state, &cursor); +} + +int +main (int argc, char **argv) +{ + ucontext_t unwind_uc, setup_uc; + unsigned char stack_mem[256*1024]; + + setup_uc.uc_stack.ss_sp = stack_mem; + setup_uc.uc_stack.ss_flags = 0; + setup_uc.uc_stack.ss_size = sizeof (stack_mem); + makecontext (&setup_uc, (void (*) (void)) setup_context, + 2, &setup_uc, &unwind_uc); + check (&unwind_uc, &setup_uc, test1); + return 0; +}