diff options
Diffstat (limited to 'gdb')
62 files changed, 27570 insertions, 1382 deletions
diff --git a/gdb/.Sanitize b/gdb/.Sanitize index e641619..15753f5 100644 --- a/gdb/.Sanitize +++ b/gdb/.Sanitize @@ -126,6 +126,7 @@ a29k-tdep.c a68v-nat.c abug-rom.c acconfig.h +acinclude.m4 aclocal.m4 alpha-nat.c alpha-tdep.c @@ -244,12 +245,15 @@ gould-tdep.c gould-xdep.c h8300-tdep.c h8500-tdep.c +hp-psymtab-read.c +hp-symtab-read.c hp300ux-nat.c hppa-tdep.c hppab-nat.c hppah-nat.c hppam3-nat.c hpread.c +hpread.h hpux-thread.c i386-stub.c i386-tdep.c @@ -269,6 +273,7 @@ inflow.c infptrace.c infrun.c inftarg.c +infttrace.c irix4-nat.c irix5-nat.c isi-xdep.c @@ -435,6 +440,7 @@ top.c top.h tracepoint.c tracepoint.h +tui typeprint.c typeprint.h ultra3-nat.c diff --git a/gdb/ChangeLog b/gdb/ChangeLog index db86c1d..3e03426 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,2821 @@ +Thu Dec 31 16:54:30 1998 David Taylor <taylor@texas.cygnus.com> + + The following changes were made by + Elena Zannoni <ezannoni@kwikemart.cygnus.com> + Edith Epstein <eepstein@sophia.cygnus.com> + David Taylor <taylor@texas.cygnus.com> + + * config/pa/tm-hppa.h (INSTRUCTION_NULLIFIED): change to read + nullify instruction bit from IPSW only when we are not in a system + call. + (STRCAT_REGISTER, pa_do_strcat_registers_info): additional + parameter -- precision. + + * Makefile.in (BUILD_TUI): to build the tui, only when configured + with --enable-tui. + (YLWRAP): use ylwrap to avoid problems on systems w/o bison. + (gdb$(EXEEXT)): make it dependent on BUILD_TUI. + (all-tui): remove dependency from phony target. + (c-exp.tab.c): use ylwrap instead of bison. + (jv-exp.tab.c): ditto. + (f-exp.tab.c): ditto. + (m2-exp.tab.c): ditto. + + * configure.in (ENABLE_CFLAGS): define and export BUILD_TUI. + + * configure : regenerated. + + * c-typeprint.c (c_type_print_base): get to the method name by + skipping over all the namespaces, classes and '::'. + + * infcmd.c (run_command): only call SOLIB_RESTART if it's + defined. + (detach_command): ditto. + + * infptrace.c (call_ptrace): add some debugging code. + + * infrun.c (follow_inferior_fork): only define on HP. + (wait_for_inferior): only call SOLIB_IN_DYNAMIC_LINKER if we have + shared libraries; restore test of IN_SOLIB_DYNSYM_RESOLVE_CODE + removed by HP. + + Also, altered calls to c_val_print and val_print, to + reflect additional parameter to c_val_print and val_print. + Affected functions : scm_val_print, scm_value_print + + Also, altered calls to c_val_print, to + reflect additional parameter to c_val_print. + Affected functions : m2_val_print + + Altered calls to val_print, to reflect the + additional parameter for val_print. + Affected files : val_print_array_elements + + In addition there are new macro definitions : + target_pid_to_exec_file, target_acknowledge_forked_child, + target_create_catch_fork_hook, target_create_catch_vfork_hook, + target_has_forked, target_has_vforked. These are generalized + macros for dealing with fork/vfork/exec. + + In addition, there are new functions : normal_pid_to_exec_file, + normal_acknowledge_forked_child, normal_target_has_forked, + normal_target_has_vforked. + + Note : initialized the new fields to NULL -> use the definitions + supplied by inftarg.c. This may not be the correct thing + in all cases. Fix later. + + The INHERIT macro has additional entries for + to_require_attach and to_require_detach. + + The de_fault macro has additional entries for + to_require_attach and to_require_detach. + + The setup_target_debug function is altered to make + sure that the to_require_attach and to_require_detach + fields are set for the current_target. + + There are 4 new functions : find_default_require_attach, + find_default_require_detach, debug_to_require_attach, + debug_to_require_detach. + + removed some target_ops vector fields, HP folks changed + their minds about them : to_create_catch_fork_hook, + to_create_catch_vfork_hook + + * Makefile.in (ALLDEPFILES): add somread.c, hp-psymtab-read.c, + hp-symtab-read.c. + (SFILES): remove the above files + (COMMON_OBS): remove somread.o + + * Makefile.in (SFILES): Add the tui files to this, so they get + included in etags tables. + + * Makefile.in (all-tui): Renamed from tui-all, for consistency + with everything else. + + * Makefile.in (gdb$(EXEEXT)): Add tui-all to the list of + dependencies, and add tui/libtui.a to the link list. + (tui-all): New rule, which does a recursive make in the tui + subdir. + + * Makefile.in (tui/libtui.a): When recursing, pass down + ${FLAGS_TO_PASS}. And don't echo the make command. This is + closer to what the other recursions do. + + * Makefile.in: (HFILES_NO_SRCDIR) add hpread.h + (COMMON_OBS): add hp-psymtab-read.o, hp-symtab-read.o + (SFILES): add hp-psymtab-read.c, hp-symtab-read.c + add rules for the new files. + Remove hpread.c, hpread.o + + * Makefile.in: Allow the TUI code to be conditionally enabled. + (TUI_TARGET, TUI_LIBRARY): New variables, whose values are set by + the configuration script. They're set to the empty string when + the TUI isn't enabled. + (gdb$(GDBEXT)): Use those, instead of referring to tui-all and + tui/libtui.a directly. + + * Makefile.in: Avoid spurious relinking. + (gdb$(EXEEXT)): Depend on the actual tui library, not on a + fictitious target. Since the fictitious target never existed, make + would always relink. + (tui/libtui.a): Renamed from all-tui. Always recurse to make sure + the library is up to date. + (TUI_TARGET): Variable removed; there's no need for it any more. + + * Makefile.in: look for tui include files in the tui source dir. + + * Use automake's `aclocal' program to generate aclocal.m4, to allow + us to use automake macros in configure.in with impunity. + + * acconfig.h: Add an entry for the `TUI' symbol. + + * acinclude.m4: New file, containing the code from the old + aclocal.m4. Incorporate (by reference) ../bfd/acinclude.m4, not + ../bfd/aclocal.m4, since we only want bfd's local macros. + + * aclocal.m4: Now automagically generated. Just run aclocal! + + * annotate.c: HP merge, 4/15/98 snapshot + New function. annotate_catchpoint + + * annotate.h: HP merge, 4/15/98 snapshot + New function declaration. annotate_catchpoint + + * annotate.h: taking the new includes (symtab.h and gdbtypes.h). + not taking the ansic C build fix. + + * blockframe.c (blockvector_for_pc_sect): check that the end of + the block is >= to the pc, not just >. + (bpstat_do_actions): if we just set cmd to NULL, don't then try to + set it to cmd->next as we'll SEGV. + + * breakpoint.c (create_temp_exception_breakpoint): #if it out -- + nothing calls it. + (bpstat_stop_status): don't call SOLIB_HAVE_LOAD_EVENT if it's not + defined; don't call SOLIB_HAVE_UNLOAD_EVENT if it's not defined. + (bpstat_get_triggered_catchpoints): if we don't have shared + library support, then don't call SOLIB_LOADED_LIBRARY_PATHNAME nor + SOLIB_UNLOADED_LIBRARY_PATHNAME. + (watch_command_1): don't require a run before a watch command + unless we're on HP [it's an HP OS bug, not a generic limitation] + (catch_load_command_1): don't define if no shared libraries. + (catch_command_1): don't claim to support fork catchpoints unless + CHILD_INSERT_FORK_CATCHPOINT is defined, don't claim to support + vfork catchpoints unless CHILD_INSERT_VFORK_CATCHPOINT is defined, + don't clain to support shared library load catchpoints if shared + libraries aren't supported, and don't claim to support exec + catchpoints unless CHILD_INSERT_EXEC_CATCHPOINT is defined + + (bpstat_do_actions): simplify significantly. It's + now almost as simple as before the merge and it no longer has the + HP bug that breakpoint commands are executed repeatedly. + + (break_at_finish_command_1): rewrite and make sure + selected_frame points to a frame before using it. Fix string + termination error. + (break_at_finish_at_depth_command_1): ditto. + + (can_use_hw_watchpoints): new static variable. + (read_memory_nobpt): test for breakpoint type bp_none. + (insert_breakpoints): test for breakpoint type bp_catch_exec; + insure have a current frame before getting the frame address. + (remove_breakpoints): check for breakpoints of types bp_none, + bp_catch_fork, bp_catch_vfork, and bp_catch_exec. + (bpstat_stop_status): fix updates of b->hit_count. + (bpstat_have_active_hw_watchpoints): new function. + (create_exec_event_watchpoint): new function. + (watch_command_1): use can_use_hw_watchpoints. + (catch_fork_command_1): change name of function to call from + target_create_catch_(v)fork_hook to create_(v)fork_even_catchpoint. + (delete_breakpoint): test for already deleted breakpoints; add + support for bp_catch_fork, bp_catch_vfork, and bp_catch_exec + breakpoints. + (_initialize_breakpoint): add can-use-hw-watchpoints to list of + user settable debugger variables. + + (clear_command): When there is no argument + to the clear command, delete all breakpoints that are hit at + default line. This will include a breakpoint whose line number + does not correspond to the default line, but has been set at + the default address. + + (delete_breakpoint): don't call bpstat_clear_actions, instead + clear things explicitly; if clearing breakpoint_at, then also + clear any associated actions so that bpstat_do_actions won't try + to execute them. + (_initialize_breakpoint): fix function name for bx command. + + (tbreak_command): remove static from declaration. + (maintenance_info_breakpoints): ditto. + + (reattach_breakpoints): new funct definition, used with with + hardware watchpoints + (breakpoint_1): change format and add entries to bptypes[] + (maintenance_info_breakpoints): function is no longer static + + (_initialize_breakpoint): removed a comment. + (exception_catchpoints_are_fragile, + exception_support_initialized): define. + (breakpoint_here_p): fixed syntax error in conditional + (disable_watchpoints_before_interactive_call_start): fixed call to + check_duplicates. Need a section parameter. + (enable_watchpoints_after_interactive_call_stop): fixed call to + check_duplicates. Need a section parameter. + (breakpoint_re_set_one): fixed call to check_duplicates. Need a + section parameter. + (delete_command): fixed syntax error in conditional + (breakpoint_re_set): fixed some typos. + + (args_for_catchpoint_enable): New type for handling exceptions. + (current_exception_event): New variable for handling exceptions. + (insert_breakpoints): check for additional breakpoint types -- + bp_catch_throw, bp_catch_catch, call_disabled. Also, do some + additional work to handle an exception catchpoint. + (remove_breakpoint): there are additional breakpoint types to + check for : bp_catch_throw, bp_catch_catch, call_disabled. Also do + some additional work to remove the exception catchpoints + (breakpoint_init_inferior): new input parameter. If there are + exception catchpoints delete them. + (breakpoint_here_p): there are additional breakpoint enable + settings to check for : shlib_disabled, call_disabled + (breakpoint_thread_match): there are additional breakpoint enable + settings to check for : call_disabled + (ep_is_catchpoint): there are additional breakpoint types to check + for : bp_catch_throw, bp_catch_catch + (ep_is_exception_catchpoint): new function + (bpstat_find_step_resume_breakpoint): new function + (bpstat_do_actions): introduce a local copy of the bpstat + structure. + (print_it_normal): there are additional breakpoint types to check + for : bp_catch_throw, bp_catch_catch Changeing the control + structure a bit (adding else ifs) Add code to print out info about + exceptions. + (bpstat_stop_status): there are additional breakpoint enable + settings to check for : call_disabled. there are additional + breakpoint types to chack for : bp_catch_catch and bp_catch_throw. + Check to see if stopped due to an exception. Minor fixes to the + catch_errors calls. Make sure to count all encountered + breakpoints. There was something funky going on previously with + the counting. + (bpstat_what): Add cases for new breakpoint types : + bp_catch_catch, bp_catch_throw. + (bpstat_get_triggered_catchpoints): Check for new breakpoint types + : bp_catch_catch, bp _catch_throw. + (breakpoint_1): account for new breakpoint types. + (describe_other_breakpoints): account for new breakpoint enable + setting (call_disabled) + (check_duplicates): account for new breakpoint enable setting + (call_disabled) + (disable_breakpoints_in_shlibs): new function + (disable_watchpoints_before_interactive_call_start): new function + (mention): account for new breakpoint types. + (break_command_1): some additional checking for a valid PC. + (watch_command_1): some dditional checking to prevent a watch + before a run command. + (ep_parse_optional_filename): simplified for loop. + (create_exception_catchpoint): new function + (cover_target_enable_exception_callback): new function + (handle_gnu_4_16_catch_command): this used to be thcatch_command_1 + function.e + (create_temp_exception_breakpoint): new function + (catch_command_1): differs from gdb 4.16 and gdb 4.17. Is now + calling catch_exception_command_1 using the EX_EVENT_CATCH and + EX_EVENT_THROW values as parameters. + (clear_command): additional comments + (delete_breakpoint): handle exceptions. Check for additional + breakpoint enable settings : shlib_disabled, call_disabled. + (delete_command): HP folks are claiming that we should not delete + shlib_event breakpoints + (breakpoint_re_set_one): moved call to check_duplicates. Add new + breakpoint types to switch statement. + (breakpoint_re_set_thread): new function + (enable_command): account for new breakpoint types. + + (insertion_state_t): new enumerated type. + (remove_breakpoint): new param in funct prototype. + (insert_breakpoints): check for bp_catch_fork and bp_catch_vfork. + (remove_breakpoints): changed call to remove_breakpoint. + (detach_breakpoints): new function. + (remove_breakpoint): New parameter, is. Also changed the + way b->inserted is set. + (ep_is_catchpoint): New function. + (ep_is_shlib_catchpoint): New function. + (print_it_normal): check for bp_catch_load, bp_catch_unload, + bp_catch_fork, bp_catch_vfork, bp_catch_exec. Also new code + to print out catchpoints properly. + (bpstat_stop_status): check for bp_catch_fork, bp_catch_vfork, + and bp_catch_exec. Also, some code to check for catching a + shared library load/unload. + (bpstat_what): Added catch_shlib_event to class enumeration. + Defined new macro, shlr. Expanded the bpstat_what_main_action + table. Add cases for bp_catch_load, bp_catch_unload, + bp_catch_fork, bp_catch_vfork, and bp_catch_exec. + (bpstat_get_triggered_catchpoints): New function. + (breakpoint_1): changes to bptypes definition. Also check for + bp_catch_load, bp_catch_unload, bp_catch_fork, bp_catch_vfork, + bp_catch_exec. Similar changes to the switch statement. + (set_raw_breakpoint): initialize new breakpoint structure fields. + dll_pathname, triggered_dll_pathname, forked_inferior_pid, + exec_pathname. + (create_solib_load_unload_event_breakpoint): New function. + (create_solib_load_event_breakpoint): New function. + (create_solib_unload_event_breakpoint): New function. + (create_fork_vfork_event_catchpoint): New function. + (create_fork_event_catchpoint): New function. + (create_vfork_event_catchpoint): New function. + (mention): new cases for bp_catch_load, bp_catch_unload, + bp_catch_fork, bp_catch_vfork, bp_catch_exec. + (ep_skip_leading_whitespace): New function. + (ep_find_event_name_end): New function. + (ep_parse_optional_if_clause): New function. + (ep_parse_optional_filename): New function. + (catch_fork_kind): New enumerated type. + (catch_fork_command_1): New function. + (catch_exec_command_1) : New function. + (catch_load_command_1) : New function. + (catch_unload_command_1) : New function. + (catch_throw_command_1) : New function. + (catch_command_1) : now calls catch_throw_command_1. + (tcatch_command) : New function. + (delete_breakpoint) : changed call to remove_breakpoint. + Also free the new fields in the breakpoint structure. + (breakpoint_re_set_one) : handle bp_catch_load, bp_catch_unload, + bp_catch_fork, bp_catch_vfork, bp_catch_exec. + (disable_command): handle bp_catch_load, bp_catch_unload, + bp_catch_fork, bp_catch_vfork, bp_catch_exec. + (enable_command): handle bp_catch_load, bp_catch_unload, + bp_catch_fork, bp_catch_vfork, bp_catch_exec. + (_initialize_breakpoint): Alter add_com call for catchpoints, + add add_com call for watchpoints. + + * breakpoint.h (enum bptype): new entries bp_catch_catch, + bp_catch_throw, and bp_none, bp_catch_load, bp_catch_unload, + bp_catch_fork, bp_catch_vfork,bp_catch_exec. Add declarations for + new functions bpstat_have_active_hw_watchpoints and + create_exec_event_catchpoint. + (tbreak_command): add prototype. + (update_breakpoints_after_exec): add prototype; update comments. + (reattach_breakpoints): new funct prototype declaration. + (enable): new enumerated value call_disabled. + (bpstat_find_step_resume_breakpoint): new funct decl. + (inf_context): new enumerated type. + (breakpoint_re_set_thread): new funct decl. + (breakpoint_init_inferior): new parameter. + (disable_watchpoints_before_interactive_call_start): new funct decl. + (enable_watchpoints_after_interactive_call_stop): new funct decl. + (disable_breakpoints_in_shlibs): new funct decl. + (struct breakpoint) : new fields, dll_pathname,triggered_dll_pathname, + forked_inferior_pid,exec_pathname BPSTAT_WHAT_CHECK_SHLIBS_RESUME_FROM_HOOK. + (bpstat_get_triggered_catchpoints): New function. + (detach_breakpoints): New function. + (create_solib_load_event_breakpoint): New function. + (create_solib_unload_event_breakpoint) New function. + (create_fork_event_catchpoint): New function. + (create_vfork_event_catchpoint): New function. + (ep_is_catchpoint): New function. + (ep_is_shlib_catchpoint) New function. + (enum bpstat_what_main_action) : new entry. + + * buildsym.c (finish_block): get rid of processing_hp_compilation; + handle LOC_INDIRECT case. Set the BLOCK_GCC_COMPILED to the right + compiler. + (push_context): add symbols for parameters to the context_stack. + (merge_symbol_lists): new function. Merges two symbol lists. + (struct context_stack): add new field param. + + (processing_hp_compilation): new external var. + + * c-exp.y: use external flag hp_som_som_object_present to decide + whether code was compiled by HP's compilers. Add two new C++ + tokens for true and false. + (yylex): check for template name is done differently for the + HP/aCC compiler case; change some of the template processing code + for handling HP aCC templates. + + * c-lang.c (c_create_fundamental_type): added case to handle + template args. Handle FT_BOOLEAN type. Set no sign flag for + FT_CHAR. + (cplus_builtin_types): new structure for c++ builtin types. + (cplus_language_defn): use cplus_builtin_types instead of + c_builtin_types. + + * c-lang.h(cp_print_value_fields): update prototype; fixed + prototype decl for c_val_print funct -- it needed an + embedded_offset param; fixed prototype of cp_print_value_fields. + include value.h. + (C_LANG_H): define. + + * c-typeprint.c (c_type_print_base): don't print 'privete' label + for a class if all members are private, similarly don't print + 'public' for a struct. Add support for sized enums (HP/aCC). get + rid of the 'static' keyword printed by the demangler for member + function, when printing the type of a class. 'static' will be + added by this function. If the demangled name is null, and the + method is not stubbed, get the signature by looking at the + information stored in the symbol structure. Remove printing of + 'const' and 'volatile' keywords for methods. This is now taken + care as part of the demangled member names. + (cp_type_print_method_args): new function. To print a C++ method + arguments and name to the output stream. + + (c_type_print_cv_qualifier): new function. Print out "const" and + "volatile" attributes. + (c_type_print_varspec_prefix): print const or volatile qualifiers. + (c_type_print_args): print 'void' for c++. + (c_type_print_varspec_suffix): print 'void' for a no argument + function. + (c_type_print_base): print const or volatile qualifiers. Do not + print 'unnamed union' if HP aCC compiler used. Distinguish + between struct and class based on the DECLARED_TYPE. Handle + HP/aCC compiler case for not printing vtable. Add Template + support. + + (cp_type_print_derivation_info): print out 'protected' when + appropriate. This applies only to HP's compilers, not gcc. + + (c_val_print): added parameter embedded_offset. Add + embedded_offset to valaddr in function calls; fix calls to + val_print and cp_print_value_fields. process TYPE_CODE_METHOD as + well. moved call to check_typedef out of conditional. added + embedded offset param to val_print call. + + (c_value_print): add new parameter to call to val_print. + handle pointer to class case. Attempt to + determine the real type of the object to be printed. + ensure that const char *, const unsigned char * + come out without the type but the volatile variants + and the signed variants don't. + + * ch-lang.h: + Added a parameter to the chill_val_print funct decl + + * coff-solib.c (coff_solib_add): add parameters to call + to symbol_file_add. + + * coff-solib.h: + (SOLIB_REMOVE_INFERIOR_HOOK): new macro. defined to 0. + functionality not implemented for coff. + + * coff-solib.h: + (SOLIB_CREATE_CATCH_LOAD_HOOK): new macro, generate error message + for coff. + (SOLIB_CREATE_CATCH_UNLOAD_HOOK): ditto. + (SOLIB_HAVE_LOAD_EVENT): ditto. + (SOLIB_LOADED_LIBRARY_PATHNAME): ditto. + (SOLIB_HAVE_UNLOAD_EVENT): ditto. + (SOLIB_UNLOADED_LIBRARY_PATHNAME): ditto. + (SOLIB_IN_DYNAMIC_LINKER): ditto. + (SOLIB_RESTART): ditto. + + * command.c (find_cmd): new function. (lookup_cmd_1): call it, + change parsing if tui_version or xdb_commands is set. + (_initialize_command): install new alias if xdb_commands is set. + + * complaints.h: add ifdef...endif pair at beginning and end of file. + + * config.in, configure: Regenerated. + + * config/pa/hppabsd.mh (NATDEPFILES): added new files + hp-psymtab-read.o and hp-symtab-read.o. + * config/pa/hppahpux.mh (NATDEPFILES): ditto. + + * config/pa/hppahpux.mh (TERMCAP): use -lHcurses + * config/pa/hppaosf.mh (NATDEPFILES): ditto. + + * config/pa/hpux1020.mh (TERMCAP): use -lHcurses + (MH_CFLAGS): new flag, -D__HP_CURSES, this define + is used by HP's linker to find the correct curses library + + * config/pa/hpux1020.mh: new file. + + * config/pa/hpux1020.mt: new file. + + * config/pa/hpux1100.mh (TERMCAP): Link against -lcurses, not + -lHcurses. The latter does not contain mvwaddstr, wscrl, or + wstbwlmkfzz. + + * config/pa/hpux1100.mh (TERMCAP): use -lHcurses + (MH_CFLAGS): new flag, -D__HP_CURSES, this define + is used by HP's linker to find the correct curses library + + * config/pa/hpux1100.mh (TERMCAP): when hosting on hpux 11.00, use + -lHcurses rather than -lcurses. + + * config/pa/hpux1100.mh: new file. + + * config/pa/hpux1100.mt: new file. + + * config/pa/nm-hppah.h (CHILD_HAS_SYSCALL_EVENT): new macro + (CHILD_THREAD_ALIVE): new macro + (STOPPED_BY_WATCHPOINT): add a condition to the macro, + ! stepped_after_stopped_by_watchpoint + (TARGET_ENABLE_HW_WATCHPOINTS): new macro + (hppa_enable_hw_watchpoints): new funct decl + (TARGET_DISABLE_HW_WATCHPOINTS): new macro + ( hppa_disable_hw_watchpoints): new funct decl + these are for HP's implementation of fast + watchpoints (via page protection). + (target_pid_to_str): new macro, calls hppa_pid_to_str + (target_tid_to_str): new macro, calls hppa_tid_to_str + + * config/pa/nm-hppah.h (CHILD_POST_WAIT): delete; + (CHILD_CREATE_CATCH_FORK_HOOK): replace with + CHILD_INSERT_FORK_CATCHPOINT and CHILD_REMOVE_FORK_CATCHPOINT. + (CHILD_CREATE_CATCH_VFORK_HOOK): replace with + CHILD_INSERT_VFORK_CATCHPOINT and CHILD_REMOVE_VFORK_CATCHPOINT. + (CHILD_CAN_FOLLOW_VFORK_PRIOR_TO_EXEC, + CHILD_INSERT_EXEC_CATCHPOINT, CHILD_REMOVE_EXEC_CATCHPOINT, + CHILD_HAS_EXECD, CHILD_REPORTED_EXEC_EVENTS_PER_EXEC_CALL, + CHILD_POST_ATTACH, TARGET_HAS_HARDWARE_WATCHPOINTS, + TARGET_CAN_USE_HARDWARE_WATCHPOINT, + TARGET_REGION_SIZE_OK_FOR_HW_WATCHPOINT, + TARGET_RANGE_PROFITABLE_FOR_HW_WATCHPOINT, STOPPED_BY_WATCHPOINT, + HAVE_NONSTEPPABLE_WATCHPOINT, target_insert_watchpoint, + target_remote_watchpoint): new macros.. + + * config/pa/nm-hppah.h (CHILD_XFER_MEMORY): reinsert accidentally + deleted define. + + * config/pa/nm-hppah.h: + (PREPARE_TO_PROCEED): defined macro to use + hppa_prepare_to_proceed + (hppa_pid_to_str): extern decl + (hppa_tid_to_str): extern decl + (target_pid_or_tid_to_str): new macro definition + (hppa_pid_or_tid_to_str): extern decl + (ENSURE_VFORKING_PARENT_REMAINS_STOPPED): new macro - for + handling events caused by a call to vfork. + (hppa_ensure_vforking_parent_remains_stopped): extern decl + (RESUME_EXECD_VFORKING_CHILD_TO_GET_PARENT_VFORK): new macro - + for handling events caused by a call to vfork. + (hppa_resume_execd_vforking_child_to_get_parent_vfork): extern decl + + * config/pa/nm-hppah.h: fix PREPARE_TO_PROCEED macro. + + * config/pa/nm-hppah.h: fix for gcc compile on HPUX + change PT_RDUAREA to PT_RUAREA + + * config/pa/nm-hppah.h: introduced an HPUXHPPA define. + A bit of a hack so that I can ifdef some code that + only works for the HP wildebeest debugger. + + * config/pa/nm-hppah.h: lots of new CHILD_ defines; + process_event_kind enum. + + * config/pa/tm-hppa.h (BREAKPOINT32): new define. + (CALL_DUMMY_HAS_COMPLETED): new define. + + * config/pa/tm-hppa.h (STACK_ALIGN): new macro + (NO_EXTRA_ALIGNMENT_NEEDED): new macro + (PC_REQUIRES_RUN_BEFORE_USE): new macro. + (REGISTER_NAMES): formatting in file changed. + (CR27_REGNUM): base register for thread local storage. + (USE_STRUCT_CONVENTION): new macro used to decide whether + a function returning a value of type type will + put it on the stack or into registers (based on the + PA risc calling conventions). + (EXTRACT_RETURN_VALUE): fixed calculation for extracting return value + (VALUE_RETURNED_FROM_STACK): new macro. + (TARGET_READ_PC): declared the function used in the definition. + (SKIP_TRAMPOLINE_CODE): declared the function used in the definition. + (TARGET_WRITE_PC): declared the function used in the definition. + (TARGET_READ_FP): declared the function used in the definition. + + * config/pa/tm-hppa.h (STRCAT_REGISTER): define macro for + future use. + (pa_do_strcat_registers_info): moved function decl from + defs.h to this HPUX specific .h file. + + * config/pa/tm-hppa.h (USE_STRUCT_CONVENTION): TYPE_LENGTH returns + bytes, not bits; fix off by 8 error. + + * config/pa/tm-hppa.h: + new comment for obj_unwind_info definition + New typedef, obj_private_struct + + * config/pa/tm-hppa.h: delete most target_ macros -- use default + versions instead; remove extraneous comma from proc_wait macro. + + * config/pa/tm-hppa.h: get rid of macro HP_COMPILED_TARGET. + + * config/pa/tm-hppa.h: removed redefinitions of + USE_STRUCT_CONVENTION and STACK_ALIGN macros. + + * config/pa/tm-hppa.h: some new definitions + New macros : ARG0_REGNUM, ARG1_REGNUM, ARG2_REGNUM, ARG3_REGNUM. + target_pid_to_exec_file, target_acknowledge_forked_child, + target_create_catch_fork_hook, target_create_catch_vfork_hook, + target_has_forked, target_has_vforked, require_attach, + require_detach, proc_wait + New funct decls : hppa_pid_to_exec_file, + hppa_acknowledge_forked_child, hppa_create_catch_fork_hook, + hppa_create_catch_vfork_hook, hppa_target_has_forked, + hppa_target_has_vforked, hppa_require_attach, + hppa_require_detach, process_wait + (unwind_table_entry): added comments to describe struct fields. + + * config/pa/tm-hppah.h (somsolib.h): include it. + + * config/pa/tm-hppah.h: + (CHILD_ENABLE_EXCEPTION_CALLBACK): new define + (CHILD_GET_CURRENT_EXCEPTION_EVENT): new define + + * config/pa/tm-hppah.h: Added a #define HPPUX_SNAP2 + + * configure, config.in: Regenerated. + + * configure.host (hppa-*-hpux10.20, hppa-*-hpux11.0*): New configs. + + * configure.in (AC_CHECK_HEADERS): Add check for term.h. + + * configure.in: Add an --enable-tui argument. + + * configure.in: Construct tui/Makefile from tui/Makefile.in. + + * configure.in: Use AM_PROG_CC_STDC. If we have the GUI, then we + need this to process libgui.h. + + * configure: Regenerated. + + * convex-tdep.c (decout): change FILE to GDB_FILE. + + * corefile.c (): add new include "objfiles.h". + (core_file_command): attempt to determine the name of the symbol + file from the core file. (read_memory_string): new function. + + * corefile.c: + (core_file_command): temporary hack to make non-hpux + work. For, non-hpux, t->to_core_file_to_sym_file + does not have a reasonable value. No target_ops vector + on the stack gives it a non-zero value. fix later. + + * corefile.c: add include of symfile.h (needed by objfiles.h). + + * corelow.c: + (core_file_to_sym_file): added new local variable, failing command, + and do some explicit type castings. + (core_ops): add three new fields : to_has_syscall_event, + to_enable_exception_callback, to_get_current_exception_event. + Necessary since we still have oldstyle initialization in + this file + + * corelow.c: HP merge, 4/15/98 snapshot + There are new target_ops vector fields that pertain + only to HPUX. Added the to_require_attach and + to_require_detach fields to core_ops. These new + fields are ifdef'ed for HPUX_SNAP1. + + * corelow.c: More target_ops vector changes for HPUX + (core_ops): new fields. ifdefed for HPUX_SNAP2. New fields are + to_post_wait, to_post_startup_inferior + to_acknowledge_created_inferior, to_clone_and_follow_inferior, + to_post_follow_inferior_by_clone, to_create_catch_fork_hook, + to_create_catch_vfork_hook, to_has_forked, to_has_vforked, + to_post_follow_vfork, to_pid_to_exec_file + + * corelow.c: include unistd.h + (core_file_to_sym_file): new function + (core_file_thread_alive): new function + (core_ops) : added new target ops vector fields. see below. And + yes we definitiely need to initialize them here, as long as + we're using static initialization. + + * corelow.c: remove HPUX_SNAP1 and HPUX_SNAP2 ifdefs + + * cxux-nat.c: + (add_shared_symbol_files): additonal params for calls to + symbol_file_add + + * defs.h + (gdb_file_isatty): new function decl. + + * defs.h (GDB_FILE): if TUI is defined, define a structure rather + than making this an alias for FILE. + (gdb_stdout, gdb_stderr): if TUI is defined, then define these + as pointers to variables of type GDB_FILE rather than making them + be aliases for stdout and stderr. + + * defs.h (TUIDO): add definition conditionalized on definition + (or lack thereof) of TUI. + + * defs.h (command_class): add two additional values. + (precision_type): new enum. + + * defs.h (gdb_fclose): add declaration. + + * defs.h (store_address): change prototype to match function. + + * defs.h (tui_version, xdb_commands, dbx_commands): add decl's. + + * defs.h: + (gdb_file_deallocate): new function declaration + + * defs.h: + (pa_do_strcat_registers_info): new function declaration. + tuiRegs.c currently calls this function directly. This + is not great. Should instead use a macro that is defined + in tm-hppa.h to be mapped to pa_do_strcat_registers_info. + Will change soon. Checking this in so that HP will have + something functional. + + * defs.h: + (streamtype): new enumerated type to distinguish between + output to a FILE and output to a buffer. + (tui_stream): new struct type, named GDB_FILE. Contains, + streamtype, FILE, buffer, and bufferlength fields. + (gdb_stdout): of type GDB_FILE, will pass this around gdb + rather than stdout. + (gdb_stderr): of type GDB_FILE, will pass this around gdb + rather than stderr. + (fputs_unfiltered_hook): change stream parameter from FILE to + GDB_FILE + (flush_hook): change stream parameter from FILE to GDB_FILE + (gdb_fclose): Fix declaration for gdb_fclose; parameter is now of + type GDB_FILE ** + (gdb_file_adjust_strbuf): new function declaration. function lives in + utils.c. + (gdb_file_init_astring): new function declaration. function lives + in utils.c + (gdb_file_get_strbuf): new function declaration. function lives + in utils.c + + * defs.h: Remove pa_do_strcat_registers_info function + declaration. + + * defs.h: additional include files included when TUI is defined. + + * defs.h: funct decl source_full_path_of. + + * demangle.c: add HP_DEMANGLING_STYLE_STRING. + + * demangle.c: added new demangling style, EDG_DEMANGLING_STYLE_STRING, + to the demanglers structure. This is for support of + Kuck & Assoc.'s changes for demangling. + + * eval.c (evaluate_subexp_standard): C++ member function changes. + + * eval.c (evaluate_subexp_standard): verify TYPE_TARGET_TYPE is + non NULL before dereferencing it. + + * eval.c (evaluate_subexp_standard): with HP/aCC compiler it is not possible + to perform inferior calls via function pointers. + Resolve calls to overloaded functions using find_overload_match. + We cannot handle HP/aCC pointers to member functions. + Deal with HP/aCC pointers to members in various kind of expressions. + + * f-lang.c (f_printchar): change FILE to GDB_FILE. + (f_printstr): ditto. + (emit_char): ditto. + + * f-lang.c (f_printstr): change stdout to gdb_stdout. + + * f-typeprint.c (f_print_type): change FILE to GDB_FILE. + (f_type_print_varspec_prefix): ditto. + (f_type_print_args): ditto. + (f_type_print_varspec_suffix): ditto. + (print_equivalent_f77_float_type): ditto. + (f_type_print_base): ditto. + + * findvar.c (): HP snapshot 3 changes. (extract_address): coerce + return value from extract_unsigned_integer to CORE_ADDR. + (store_address): change val from CORE_ADDR to LONGEST; changes to + support machines where CORE_ADDR and LONGEST are different sizes. + (get_saved_register): coerce arg to store_address to LONGEST. + (read_relative_register_raw_bytes): cast last arg to + store_address to LONGEST. (read_register): cast return from + extract_address to a CORE_ADDR. (write_register_pid): change val + from LONGEST to CORE_ADDR. (read_pc_pid): save and restore + inferior_pid if necessary. (write_pc_pid): ditto. + (read_var_value): cast arg to store_address. + + * findvar.c (read_relative_register_raw_bytes_for_frame): new + function. + (read_relative_register_raw_bytes): call it. + + * findvar.c (symbol_read_needs_frame): handle LOC_THREAD_LOCAL_STATIC and + LOC_INDIRECT. + + * fork-child.c (fork_inferior): chenge fifth parameter to be a + function returning void. + + * fork-child.c (fork_inferior): delete unused variable f. + + * fork-child.c: + (STARTUP_WITH_SHELL): new macro -- interim fix for a bug + (breakup_args): new function -- breaks up an argument string into + an argument suitable for passing into execvp(). + (fork_inferior): handling problems with starting up gdb with a shell. + -- again, this appears to be an interim fix. + + * fork-child.c: + (fork_inferior): Added a comment + (clone_and_follow_inferior): new function. + (startup_inferior): minor formatting changes. + + * fork-child.c: + (fork_inferior): HP change is problematic. The -f option has + different meanings for different shells. It is particularly + inappropriate for bourne shells. + + * fork-child.c: + (fork_inferior): added new parameter, pre_trace_fun. + pre_trace_fun is a function pointer. For some targets, + like HPUX, this function gets called to prepare for forking + a child. + + * fork-child.c: + (fork_inferior): fixed call to init_trace_fun + + * fork-child.c: + Moved definition of STARTUP_WITH_SHELL to inferior.h + Added a DEBUGGING macro. Currently set to 0. May remove + later. + breakup_args: add DEBUGGING ifdefs. more sophisticated + parsing to break up args. + (fork_inferior): rename kshell variable to shell. new local + variable, tryname. Make use of STARTUP_WITH_SHELL macro. + More error processing if starting up with a shell. + (startup_inferior): distinguish between starting up with a shell + and not doing so. + + * gdbthread.h: + Declarations for load_infrun_state and save_infrun_state take + an additional parameter. + + * gdbthread.h: Note that sometime between gdb 4.16 and 4.17, + thread.h was renamed gdbthread.h + (load_infrun_state): additional parameters + (store_infrun_state): additional parameters + + * gdbthread.h: include breakpoint.h + + * gnu-nat.c: + (init_gnu_ops): adding new target_ops vector fields and + removing a few. see list below + + * gnu-nat.c: + (init_gnu_ops): initializing new target ops vector fields + to_require_attach, to_require_detach, to_post_wait, + to_post_startup_inferior, to_acknowledge_created_inferior, + to_clone_and_follow_inferior, to_create_catch_fork_hook, + to_create_catch_vfork_hook, to_has_forked, to_has_vforked, + to_post_follow_vfork, to_pid_to_exec_file + (gnu_create_inferior): add param to fork_inferior call + + * hp-psymtab-read.c (QUICK_LOOK_UP): redefine to be 0. + (hpread_build_psymtabs): deal with enums. + (hpread_start_psymtab): include section offset. + (hpread_end_psymtab): take care of offset. + + * hp-psymtab-read.c (TRUE): define. + (FALSE): define. + (file_exists): new function. Checks for existance of file. + (hpread_pxdb_needed): rewrite. + (hpread_quick_traverse): use correct demangling style. + Handle F77 case. + (hpread_get_header): rewrite. + (hpread_get_textlow): add support for DOC_FUNCTION. + (hpread_build_psymtabs): make sure we do the right thing + for pxdb and F77. + + * hp-psymtab-read.c (hpread_pxdb_check): change parenthesis positions. + + * hp-psymtab-read.c (hpread_quick_traverse): compare CORE_ADDR + variable end_addr to 0 instaed of NULL to get rif of gcc warning. + + * hp-psymtab-read.c: + (hpread_get_textlow) : added param to function + Defined convennience macros and some datatypes and variables for + processing the quick lookup-tables. Looks like the code existed + before, but has been munged. + (hpread_pxdb_needed): Major rearrangements of code. Additional local + variables. Also, more extensive checking for various scenarios : + debug info for optimized code vs. unoptimized code, pxdb has been + run vs. pxdb has not been run. + (VALID_FILE): new macro + (VALID_MODULE): new macro + (VALID_PROC): new macro + (VALID_CLASS): new macro + (FILE_START): new macro + (MODULE_START): new macro + (PROC_START): new macro + (FILE_END): new macro + (MODULE_END): new macro + (PROC_END): new macro + (FILE_ISYM): new macro + (MODULE_ISYM): new macro + (PROC_ISYM): new macro + (VALID_CURR_FILE): new macro + (VALID_CURR_MODULE): new macro + (VALID_CURR_PROC): new macro + (VALID_CURR_CLASS): new macro + (CURR_FILE_START): new macro + (CURR_MODULE_START): new macro + (CURR_PROC_END): new macro + (CURR_FILE_ISYM): new macro + (CURR_MODULE_ISYM): new macro + (CURR_PROC_ISYM): new macro + (TELL_OBJFILE): new macro + (pst_syms_struct) : new typedef to keep track of the start/end symbol + table (LNTT) indices of psymtabs created so far. + (pst_syms_count) : new variable + (pst_syms_size): new variable + (told_objfile): new variable + (init_pst_syms): new function. sets up psymtab symbol index stuff. + (clear_pst_syms): new function. clean up psymtab symbol index stuff. + (record_pst_syms): new function. add info about newest psymtab to symbol + index table. + (find_next_pst_start): new function. Find a suitable symbol table index. + (find_next_file_isym): new function + (find_next_proc_isym): new function + (find_next_module_isym): new function + (scan_procs): new function. Scan and record partial symbols for all + functions starting from specified index and in a specified code range. + (hpread_quick_traverse: major rearrangement of code. The function + now uses all the nifty macros. There are some new local variables. + Check for EDG_DEMANGLING style. ifdef out some code for handling F77. + Previously, the function looped over all the modules in the table. + Now, the function loops over all the files, modules, and procedures. + With HP aCC and CTTI, it is possible for a compiled object to have a + file and no module. + (hpread_build_psymtabs): Added a section of code ifdefed by + QUICK_LOOK_UP. It check to see whether or not there are any globals + in the executable. Fix number of params to hpread_start_psymtab call. + Some changes to the way DNTT_TYPE_MODULE is handled. + (hpread_get_textlow): change in signature, minor code changes. The + function finds the low address associated with a specified symbol. + In looking for the address for the symbol avoid going of the end of + the LNTT file. + + * hp-psymtab-read.c: change TRUE to 1 and FALSE to 0. Do some + reformatting. + + * hp-psymtab-read.c: include demangle.h + (trans_lang): new function to let gdb know the correct language. + (hpread_quick_traverse): Use ARM style demangling. + Demangle procedures names. + Use gdb language names instead of hp language names. + Add symbol to list using its demangled name. + + * hp-psymtab-read.c: new file. + (hpread_call_pxdb): new function. Call PXDB to process our file. + (hpread_pxdb_check): new function. Return TRUE if the file needs + pre-processing by PXDB and we have thus called PXDB to do this + processing and the file needs to be re-loaded. + (hpread_quick_traverse): new function. Traverse the quick look-up + tables, building a set of psymtabs. + (hpread_get_header): new function. Get appropriate header from obj + file, based on pxdb type + (hpread_symfile_init): no change from hpread.c + (hpread_build_psymtabs): if there are quick lookup tables, read those, + then scan the global section LNTT. Otherwise, just scan the whole LNTT. + Changed: Add a global function entry to the global partial symbol list. + Handle end of symbols, for QLT case. + In case of TAGDEF, if it is a class or a template, add the name to the + var_namespace, so that it is known as a type by gdb. + In case of CONSTANT, and it is global, add it to the globals. + (hpread_symfile_finish): no change from hpread.c + (hpread_get_lntt): make it not static + (hpread_get_gntt): no change from hpread.c + (hpread_get_slt): make it not static + (hpread_get_textlow): no change from hpread.c + (hpread_start_psymtab): no change from hpread.c + (hpread_end_psymtab): no change from hpread.c + + * hp-symtab-read.c (hpread_get_scope_start): renamed. It was + hpread_get_depth. + (hpread_type_translate): distinguish between signed and unsigned char + types. + (hpread_psymtab_to_symtab): set flag for hp compilation. + (hpread_read_function_type): append symbols for parameters to local + list as well as to the global list. Get the parameters types from the + local list instead of the global list. + (hpread_read_struct_type): add new field num_fn_fields to next_fn_field + structure. Rewrite handling of templates + (hpread_type_lookup): change handling of dntt_type_modifier. + (hpread_process_one_debug_symbol): call hpread_get_scope_start instea + of hpread_get_depth. Handle enum as well. + (hpread_get_scope_depth): new function. Get nesting depth for a + DNTT entry. + + * hp-symtab-read.c (hpread_psymtab_to_symtab): set + processing_gcc_compilation to 0. + + * hp-symtab-read.c (hpread_psymtab_to_symtab_1): change stdout to + gdb_stdout; change fflush to gdb_flush. + (hpread_psymtab_to_symtab): change fflush to gdb_flush. + + * hp-symtab-read.c (hpread_read_enum_type): declare variable. + (hpread_read_struct_type): eliminate references + to 'args' member of fn_field. + + * hp-symtab-read.c (hpread_read_struct_type): a static member + is now indicated by the bitsize field, not the bitpos. + Initialize physname to empty. + (fix_static_member_physnames): use new macros to deal with + physnames. + + * hp-symtab-read.c (hpread_read_struct_type): change references + to bitpos member of struct field to use the FIELD_BITPOS macro. + + * hp-symtab-read.c (hpread_read_struct_type): comment out reference to + obsolete field fn_field.args. + Add struct complaint definitions for complaints. + (hpread_read_struct_type): change call to complain. + (hpread_read_array_type): change call to complain. + (hpread_type_lookup): change call to complain. + (hpread_process_one_debug_symbol): change calls to complain. + (hpread_type_translate): change calls to complain. + + * hp-symtab-read.c (hpread_read_struct_type): make sure bitvector + has been allocated before calling has_vtable. + + * hp-symtab-read.c (hpread_read_struct_type): revert change, + just check for vtable without checking for bitvectors too. + + * hp-symtab-read.c: + (hpread_expand_symtab): change name of local variable from + at_end_of_module to at_module_boundary. + Also, if demangling style is already EDG, do not reset it + to the HP demangling style. + Change at_end_of_module param to hpread_process_one_debug_symbol + call to at_module_boundary. + No longer break out of loop when reach end of module. With CTTI, + the compiler can generate function symbols which are not in + any module. Typically they show up after the end of one + module and before the start of the next module. + (hpread_read_struct_type): check that the debug info for + a TEMPLATE_ARG is correct. + (hpread_process_one_debug_symbol): change name of at_end_of_module_p + param to at_module_boundary_p. + Also set *at_module_boundary_p = -1 if missing a module end and set + it to 1 when finished expanding the debug info. + Handle TLS variable. + + * hp-symtab-read.c: include defs.h, symtab.h, gdbtypes.h, complaints.h. + (fixup_class): new static variable. + (fixup_method): new static variable. + (hpread_get_location): rewrite. + (hpread_has_name): add cases for DNTT_TYPE_DOC_FUNCTION and + DNTT_TYPE_DOC_MEMFUNC + (hpread_expand_symtab): use HP demangling style. + Set hp_som_som_object_present to 1. + (hpread_type_translate): error out if not immediate. Issue warning + if there is an unhandled type code. + (error_in_hpread_type_translate_complaint): remove this structure. + (hpread_read_enum_type): don't assume size of enum is always 4 bytes. + (hpread_read_function_type): add new parameter to indicate a new block. + Do not add the parameters to the symbol list. + If the type was read in earlier, do not modify the type structure. + If we are creating a new block, set the local symbol list to be the + param list. + Need to mark this type as preprocessed. + (hpread_read_doc_function_type): new function. Read and internalize + a native DOC function debug symbol. + (hpread_read_struct_type): a method can be of type doc_function and + doc_memfunc too. + Handle case in which a method is read before its class. Deal with + incomplete method types. + Handle cases in which HP/aCC compiler creates operator names w/o + the 'operator' keyword. Rewrite the loop over the fileds. + (fix_static_member_physnames): new function. Adjust the physnames for + each static member. + (fixup_class_method_type): new function. Fix-up the type structure for a + class. + (hpread_read_array_type): cahnge complaint to warning. + (hpread_type_lookup): add case for DNTT_TYPE_DOC_FUNCTION. + For structures/classes set static member to point to strings with full + names. + Change calls to hpread_read_function_type to pass extra parameter. + (hpread_record_lines): handle case for SLT_NORMAL_OFFSET. + (class_of): new function. Given a function "f" which is a member of a class, + find the classname that it is a member of. + (hpread_process_one_debug_symbol): deal with possible alias field from the + som record for the Function or Entry type. + Do the demangling ourselves if the gdb demangler failed. + Add support for DOC functions. + For function types, add parameters to local list. + (hpread_get_scope_depth): make this function a no-op. + (hpread_adjust_bitoffsets): new function. Adjust the bitoffsets for all + fields of an anonymous union. + (hpread_get_next_skip_over_anon_unions): new function. Skip over anonymous + unions. + + * hp-symtab-read.c: include demangle.h + (hpread_expand_symtab): Ensure we are using ARM-style demangling. + (hpread_process_one_debug_symbol): Set the mangled and demangled + names for functions. + Record the class name to generate the demangled names of member + functions. + + * hp-symtab-read.c: new file. + (hpread_get_depth): no change from hpread.c + (hpread_get_line): no change from hpread.c + (hpread_get_location): no change from hpread.c + (hpread_has_name): make it not static. Return 1 for DNTT_TYPE_BLOCKDATA + and DNTT_TYPE_MEMFUNC. Return 0 for CLASS_SCOPE, REFERENCE,PTRMEM, + PTRMEMFUNC, CLASS, GENFIELD, VFUNC, MEMACCESS, INHERITANCE, + FRIEND_CLASS, FRIEND_FUNC, MODIFIER, OBJECT_ID, TEMPLATE, TEMPLATE_ARG, + FUNC_TEMPLATE, LINK. + (hpread_psymtab_to_symtab_1): no changes from hpread.c + (hpread_psymtab_to_symtab): make it a static function + (hpread_expand_symtab): modified + (hpread_type_translate): if not typep.dntti.immediate do not abort, + but complain and return. Same for default action. Handle more HP_TYPEs. + (hpread_lookup_type): initially allocate a correct-size type-vector. + (hpread_alloc_type): reset type_addr only if a type was allocated. + (hpread_read_enum_type): if this has already a type associated, return. + (hpread_read_function_type): do different things depending on whether + function is a MEMFUNC, a TEMPLATE, a FUNCTION som record. + Do not use the LOC_REGPARM_ADDR symbol class. + (hpread_read_struct_type): handle classes and templates too. Major + rewrite. + (hpread_get_nth_template_arg): new function. + (hpread_read_templ_arg_type): new function. + (hpread_read_set_type): no change from hpread.c + (hpread_read_array_type): modified + (hpread_read_subrange_type): add handling of more DNTT entries. + added support for templates, classes, references, virtual functions. + (hpread_type_lookup): handle DNNT_TYPE_MODULE. + (hpread_record_lines): no changes from hpread.c + (hpread_process_one_debug_symbol): Handle WITH, COMMON, + CLASS_SCOPE. Expand TAGDEF case to handle classes and templates. + + * hppa-tdep.c: + (pa_do_strcat_registers_info): Has a new parameter, precision, + which is passed into the call to pa_strcat_fp_reg to indicate + whether to display the floating point registers using + single or double preceision. + (pa_strcat_registers): Introduce local variable, precision, and + pass it into call to pa_strcat_fp_reg. + (pa_strcat_fp_reg): Modified function. New parameter, precision, + used by function to decide whether to use single or double + precision. Also added the code to put a double precision value + into a buffer. + + * hppa-tdep.c: (): add'l includes <machine/save_state.h>, + <unistd.h>, declare pa_register_look_aside, define is_pa_2. + (rp_saved): check for where to read the return pointer from. + (pa_do_registers_info): handle is_pa_2. (pa_register_look_aside): + new function. (pa_print_registers): handle is_pa_2. + (in_solib_call_trampoline): handle a compiler/linker error. + (skip_trampoline_code): changes to some masks used in examining + instructions. (inst_saves_fr): test for FSTWS instruction. + (skip_prologue): renamed to skip_prologue_hard_way. + (after_prologue): new function. (skip_prologue): new function. + + * hppa-tdep.c (after_prologue): if f is NULL, don't dereference + it. + + * hppa-tdep.c (after_prologue): if no debug info, return zero + telling caller that we need to find the end of the prologue via + the hard way (instruction examination). + + * hppa-tdep.c (find_unwind_entry): avoid dereferencing a null + pointer. + + * hppa-tdep.c (hppa_pid_to_exec_file): deleted -- no longer used. + + * hppa-tdep.c (hppa_prepare_to_proceeed): add prototype. + (read_unwind_info): purecov comments, bug fixes. + (find_unwind_entry): purecov comments, bug fixes. + (find_stub_with_shl_get): purecov comments. + (frame_chain): additional parens. + (hppa_push_arguments): changes to commented out version of routine. + (hppa_fix_call_dummy): purecov comments, fix location of end.o. + (in_solib_call_trampoline): purecov comments. + (in_solib_return_trampoline): purecov comments. + (setup_d_pid_in_inferior): fix location of end.o. + (initialize_hp_cxx_exception_support): fix location of end.o. + (child_enable_exception_callback): purecov comments. + + * hppa-tdep.c: + (pa_do_strcat_registers_info): new routine. called by + tui/tuiRegs.c:_tuiRegisterFormat to place a register name + and value into a string buffer. Interface may change in + future. Checking this in so that we have something + functional for HP. + (pa_strcat_registers): new routine, called by + pa_do_strcat_registers_info. Does same thing as + pa_print_registers except it takes a stream parameter. + This routine should disappear in future. Checking in + so that we have something functional to give HP + (pa_strcat_fp_reg): new routine, called by + pa_do_strcat_registers_info and pa_strvat_registers + to place a floating point register name and value into + a buffer. This interface may change in future. + Checking in so that we have something functional to give HP. + + * hppa-tdep.c: (pa_print_fp_reg): change prototype to match def'n. + (pa_register_look_aside): fix comment immediately before function. + + * hppa-tdep.c: changes to better support stack unwinding, + reading and writing registers for HPUX. The HP folks had + an advantage ... access to a runtime architecture spec ;-}. + New includes : ptrace.h + (internalize_unwinds): initialize new fields in table. + (read_unwind_info): entries in the table are now more complex + structures. References of the form ...->table[index].stub_type + are now ...->table[index].stub_unwind.stub_type. + (find_proc_framesize) : added a check for pc == 0. + (rp_saved): entries in the table are now more complex + structures. References of the form ...->table[index].stub_type + are now ...->table[index].stub_unwind.stub_type. + (frameless_function_invocation): stub_type becomes + stub_unwind.stub_type + (saved_pc_after_call): stub_type becomes stub_unwind.stub_type + (hppa_frame_saved_pc): stub_type becomes stub_unwind.stub_type + (frame_chain_valid): stub_type becomes stub_unwind.stub_type + (hppa_call_dummy): stub_type becomes stub_unwind.stub_type + (pa_print_fp_reg): additional params to call val_print + (in_solib_call_trampoline): stub_type becomes stub_unwind.stub_type + (in_solib_return_trampoline): stub_type becomes stub_unwind.stub_typ + (skip_trampoline_code): additional code to handle external + dyncalls. Also stub_type becomes stub_unwind.stub_type + (hppa_pid_to_exec_file): new funct. FOr HPUX 10.0 and beyond there + is an explicit ptrace request for getting the pathname associated + with a process id (pid). + + * hppa-tdep.c: fix for gcc compile on HPUX + (hppa_pid_to_exec_file): remove unwanted param from + call to call_ptrace. Note, hppa_pid_to_exec_file goes + away in subsequent hp snapshots. + + * hppa-tdep.c: include bfd.h. + include dl.h + (args_for_find_stub): new structure. + (find_unwind_entry): deal with null input pc value. + (rp_saved): ditto. + For the import stub, return -24 always. + (hppa_frame_saved_pc): save old pc value, to detect we are in a loop. + (init_extra_frame_info): use TARGET_READ_FP. + (frame_chain): include thread support. + If the caller's pc is zero, we loose and return, just like stack bottom. + Disable warning about being unable to find unwind info. + (hppa_push_arguments): rewrite. + (hppa_value_returned_from_stack): new function. Handles returning a value + larger that 64 bits, stored on the stack. + (find_stub_with_shl_get): new function. To look up symbols in shlibs. + (cover_find_stub_with_shl_get): new function. Cover routine for + find_stub_with_shl_get to pass to catch_errors. + (hppa_fix_call_dummy): comment out old stub mechanism. Rewrite using dyncall. + (target_read_fp): new function. + (pa_do_registers_info): floating point registers start at FP4. + (pa_print_registers): use FP4_REGNUM instead of 72. + (skip_trampoline_code): do machine instruction matching for PA2.0. + (setup_d_pid_in_inferior): new function. Exception handling support. + (initialize_hp_cxx_exception_support): ditto. + (child_enable_exception_callback): ditto. + (child_get_current_exception_event): ditto. + + * hppah-nat.c (child_post_wait, child_post_follow_vfork, + child_post_follow_inferior_by_clone): new functions. + + * hppah-nat.c (child_xfer_memory): make sure the call to ptrace really + fails before we give up. + (hppa_pid_to_str): new function. Format a process id. + (hppa_tid_to_str): new function. Format a thread id. + + * hppah-nat.c (child_xfer_memory): use xmalloc, not alloca. + (child_post_wait): delete. + (child_post_follow_vfork): delete decl of child_ops; delete + large chunks of function -- let it be handled by the normal + mechanism that notices and handles exec events, in resume(). + + * hppah-nat.c (require_notification_of_exec_events): new function; + just notify of exec events, not all events, and just the specified + pid, don't include it's children (10.20 version). + (child_acknowledge_created_inferior): call new function + require_notification_of_exec_events instead of + require_notification_of_events. + + * hppah-nat.c [!GDB_NATIVE_HPUX_11]: Move HPUX 10.x-specific + support code here from infptrace.c. + + * hppah-nat.c: removed #define ptrace call_ptrace + replaced all calls to ptrace with calls to call_ptrace + (parent_attach_all): removed call to ptrace + + * hpread.c (hpread_psymtab_to_symtab_1): change fflush to + gdb_flush; change stdout to gdb_stdout. + (hpread_psymtab_to_symtab): change fflush to gdb_flush. + + * hpread.h: new file. Includes all includes, struct defs, defines + from hpread.c. + + * infcmd.c + (attach_command): new local variable, exec_file, added code to + determine exec_file from pid if exec_file is not already known, + call new target operation, target_post_attach -- a no-op unless + on HPUXHPPA + (detach_command): after detaching, do a SOLIB_RESTART + + * infcmd.c (objfiles.h): fix typo on include line. + + * infcmd.c (run_command): only call SOLIB_RESTART if it's + defined. + (detach_command): ditto. + + * infcmd.c: + (run_stack_dummy): add calls to + disable_watchpoints_before_interactive_call_start and + enable_watchpoints_after_interactive_call_stops + (finish_command): alter code handling the evaluation and printing + of the target function's return value. + (attach_command): when given a pid, but no exec file, try to determine + the exec file from the process. If the process does not record a + full path name, try to qualify the filename against the source path. + (_initialize_infcmd): add some verbiage about how to use the attach command + + * infcmd.c: + Include objfiles.h + (run_command): If program has already been started, and decide + to restart it, the kill the target, fluch the caches, + call init_wait_for_inferior. Also purge old solib objfiles. + + * infcmd.c: cahnged calls to val_print, using a new macro, + SOLIB_RESTART + (run_command): calls SOLIB_RESTART + (do_registers_info): changed calls to val_print + + * infcmd.c: made the symfile.h include preceed the + objfiles.h include. The other ordering caused a + compile problem (incompletely defined types). + + * inferior.h (REQUIRE_DETACH): Fix default definition. + * inftarg.c (child_post_attach): Fix declaration, make static. + (proc_wait): Make globally visible. + (child_insert_fork_catchpoint, etc): Fix return type. + + * inferior.h (STARTUP_WITH_SHELL): new define. + (START_INFERIOR_TRAPS_EXPECTED): new define + + * inferior.h (fork_inferior): change fifth parameter to be a function + returning void. + + * inferior.h (proc_wait): Declare. + + * inferior.h: + (REQUIRE_ATTACH): new macro + (REQUIRE_DETACH): new macro + (detach): definition is now an extern + (clone_and_follow_inferior): new definition, it's also an extern + + * inferior.h: + (require_attach): default definition for require_attach funct + (require_detach): default definition for require_detach funct + (pre_fork_inferior): new funct decl for function defined in + infptrace.c + (fork_inferior): new parameter in funct decl. + + * inferior.h: + New variable decls : inferior_ignoring_startup_exec_events, + inferior_ignoring_leading_exec_events -- these variables + are used when processing an exec call. + (CALL_DUMMY_HAS_COMPLETED): new default macro -- for targets + where PC in call dummy implies that call dummy has + completed. Note, that on HPUX this inference does not hold. + + * infptrace.c + (require_notification_of_events): new function + (child_acknowledge_created_inferior): previously named + hppa_acknowledge_forked_child. Also calling + require_notification_of_events and clearing some semaphore + variables + (child_post_startup_inferior): new function + (child_create_catch_fork_hook): previously named + hppa_create_catch_fork_hook + (child_create_catch_vfork_hook): previously named + hppa_create_catch_vfork_hook + (child_has_forked) : previously named hppa_target_has_forked + (child_has_vforked): previously named hppa_target_has_vforked + (process_wait): changed to call target_post_wait + (attach): add call to require_notification_of_events + (child_pid_to_exec_file): new function + (hppa_require_attach): new local variable, pt_status + (hppa_get_process_events): new function + + * infptrace.c (call_ptrace): Simplify control flow. + (proc_wait): Move here from inftarg.c, add target_post_wait call. + + * infptrace.c (call_ptrace): add some debugging code. + + * infptrace.c (child_pid_to_exec_file): declare variable. + + * infptrace.c (kill_inferior): clean up call to proc_wait. + + * infptrace.c: + (call_ptrace): when the ptrace request is PT_SETTRC, + call ptrace and then call parent_attach_all. + + * infptrace.c: + (child_has_syscall_event): new function. only applicable + (for now) on HPUX 10.30 and beyond via the ttrace call. + In infptrace.c there is a default operation. + With ttrace, it is possible to tell the kernel to + notify the debugger that the target program is about to make + or return from a syscall. + (child_thread_alive): new function. a default function. + ptrace doesn't support kernel threads. + (hppa_enable_page_protection_events): defualt function + (hppa_disable_page_protection_events): default function + + * infptrace.c: + (child_pid_to_exec_file): fix number of params to cal_ptrace call. + + * infptrace.c: + (hppa_pid_or_tid_to_str): new function + (hppa_switched_threads): new function + (hppa_ensure_vforking_parent_remains_stopped): new function + (hppa_resume_execd_vforking_child_to_get_parent_vfork): new function + + * infptrace.c: Most of the changes found in infptrace.c should + be moved to hppah-nat.c + (PT_VERSION): a new define + (startup_semaphore_t): a new struct type. it is used to + coordinate the parent and child processes after a fork and + before an exec on HPUX. + (call_ptrace): changes to determine whether the ptrace + request is for starting the tracing of the target process. + (parent_attach_all): new funct. used on HPUX for coordinating + the parent and child processes after a fork and before and exec. + (hppa_acknowledge_forked_child) : new funct. prabably belongs + in hppah-nat.c + (hppa_enable_catch_fork) : new funct. probably belongs in + hppah-nat.c + (hppa_disable_catch_fork): new funct. probably belongs in + hppah-nat.c + (hppa_create_catch_fork_hook): new funct. probably belongs in + hppah-nat.c + (hppa_enable_catch_vfork): new funct. probably belongs in + hppah-nat.c + (hppa_disable_catch_vfork): new funct. probably belongs in + hppah-nat.c + (hppa_create_catch_vfork_hook): new funct. probably belongs to + hppah-nat.c + (hppa_target_has_forked): new funct. probably belongs in + hppah-nat.c + (hppa_target_has_vforked): new funct. probably belongs in + hppah-nat.c + (process_wait): new funct. also ifdefed for proc_wait. + (kill_inferior): call proc_wait rather than wait. this is + pretty hacky. + (pre_fork_inferior): new function. used only by HPUX. + probably should be defined elsewhere. + + * infrun.c (follow_inferior_fork): only define on HP. + (wait_for_inferior): only call SOLIB_IN_DYNAMIC_LINKER if we have + shared libraries; restore test of IN_SOLIB_DYNSYM_RESOLVE_CODE + removed by HP. + + * infrun.c (normal_stop): Add a call to the TUIDO + macro just before the annotate_stopped label. This + updates the windows after each program stop. + + * infrun.c (normal_stop): verify stop_command is non-zero before + dereferencing it (it won't be set if dbx_commands is set). + + * infrun.c (resume): Add #ifdef HPPAHPUX around HPUX-specific + code. + + * infrun.c (resume): add missing semicolon. + + * infrun.c (wait_for_inferior): fix syntax error. + + * infrun.c: + (follow_fork): eclosed calls to target_require_detach and + target_require_attach with HPUX_SNAP1 ifdef + + * infrun.c: + (follow_fork_mode_kind_names) : removed "both" option + (follow_fork): added parameters. additional code for handling + following of parent, following of child + (resume): added code for deciding how to resume in presence of + fork. Additional params to follow_fork call. + + * infrun.c: + (follow_exec): ifdef for HPUXHPPA for the moment, the + code in here assumes the existance of the child_ops + target vector. This is incorrect for Solaris. + + * infrun.c: + (resume): fixed ifdefs, HPPAHPUX -> HPUXHPPA + + * infrun.c: + (wait_for_inferior): fixed a matching parenths problem -- + matching curly brace inside ifdefed code which is not being + compiled. Change local validFlag to be an 'int' rather than + a 'bool' and fixed the corresponding assignment statements. + + * infrun.c: + Two new global variables: inferior_ignoring_startup_exec_events and + inferior_ignoring_leading_exec_events. + New static variables: parent_of_vfork_trap_expected_and_ignorable, + step_resume_breakpoint, through_sigtramp_breakpoint, pending_follow, + follow_vfork_when_exec + (follow_inferior_fork): does what follow_fork did! + (follow_fork): is now a wrapper function for follow_inferior_fork + (follow_vfork): is now a wrapper function for follow_inferior_fork + (follow_exec): new function, handles an exec event. + (resume): remove 3 local variables: child_pid, has_forked, has_vforked. + move and expand code that tries to follow a fork (i.e. also check + for vfork and exec + (init_wait_for_inferior): initialize the new structure, pending_follow + (delete_breakpoint_current_contents): when deleting all the breakpoints also + set the breakpoint struct pointer to NULL. + (wait_for_inferior): a number of changes. + The step_resume_breakpoint and through_sigtramp_breakpoint local + variables are now visible in entire module. + Changed name of variable from child_inferior_pid to saved_inferior_pid. + Added several cases to the event processing switch statement: + TARGET_WAITKIND_FORKED, TARGET_WAITKIND_VFORKED, TARGET_WAITKIND_EXECD. + Also, for TARGET_WAITKIND_STOPPED, check to see if expecting a trap + from the parent of a vfork (ignorable) otherwise break as usual. + When determining the value of 'random_signal' (0 or 1), no longer check for + catchpoints. + When determining how to handle unexpected signals, must now take into + account fork, vfork, and exec. + Change call to PC_IN_CALL_DUMMY to a call to CALL_DUMMY_HAS_COMPLETED + At stop_stepping label, check to see if stopped for fork or + vfork event. + + * infrun.c: New code is related to threads and fork/vfork/exec. + New static variable : thread_step_needed + Deleted static variable : parent_of_vfork_trap_expected_and_ignorable + Altered the pending_follow and fork_event structs + (follow_inferior_fork): Before detaching from child and removing + all breakpoints form it -- but only if forking or following + vforks as soon as they happen. Also reset the solib inferior hook. + The same kind of logic applies to hitting step_resume_breakpoints + (calling breakpoint_re_set_thread) and to resetting and inserting + breakpoints. + (follow_exec): forward decl + (follow_vfork): check to see if gdb followed the child. If + the child exec'd before gdb saw the parent's vfork event + then call follow_exec. + (follow_exec): if the exec occured after a vfork, then follow + the vfork as well. Do it before following the exec. + Make sure to update breakpoints after and exec + (resume): new local variable, should_resume. + Change parameters in calls to follow_fork, follow_vfork, and + follow_exec. Some changes to the way various pending_follow.kind + situations are handled (there's TARGET_WAITKIND_FORKED, + TARGET_WAITKIND_VFORKED, ARGET_WAITKIND_EXECD. Some additional + conditions to check before deciding to resume the target (i.e. + should_resume=1, stepping?, thread_step_needed?i, regular + resume?) + (proceed): When proceeded at location that does not have a breakpoint + set thread_step_needed=0 to indicate that it is not necessary to + single step thread over breakpoint. SOme additional checks to see + if it is necessary to step thread over breakpoint. + (start_remote): remove call to clear_proceed_status. + (init_wait_for_inferior): initialize new fields in fork_event + structure and add a call to clear_proceed_status. + (wait_for_inferior): new local variable : new_thread_event. + Initialize thread_step_needed = 0. + Minor massaging of conditions for adding a new thread to the thread list. + No longer resuming execution after adding a new thread. Let user play with thread first. + Some changes in the way TARGET_WAITKIND_FORKED, ARGET_WAITKIND_VFORKED, + TARGET_WAITKIND_EXECD are handled -- this is all HPUX related. + Simplified TARGET_WAITKIND_STOPPED -- HP previously had some + more complicated code in here. + Moved the code to resume threads to after the large case statement that processes the events. + Additional processing for stop_signal=TARGET_SIGNAL_TRAP. + Cleanup code at process_event_stop_test label. + Set thread_step_needed when processing a BPSTAT_WHAT_SINGLE. + Minor massaging of fork/vfork/exec part of stop_stepping code. + (normal_stop): minor changes. calling show_and_print_stack_frame. + (xdb_handle_command): new function + (_initialize_infrun): handle xdb_commands. also handle dbx commands + + * infrun.c: changes to support following forks, and handling + catchpoints. + (follow_fork_mode_kind_names): new array + (follow_fork): new function. implements the follow parent, + or child functionality. + (resume): additions to check whether the target process + just forked and decide which process to follow. + (wait_for_inferior): additional variables (child_inferior_pid, + stepping_through_solib_after_catch, + - stepping_through_solib_catchpoints. + - Altered CURRENTLY_STEPPING macro to check for stepping through + a shared library after hitting a catchpoint. + - Add parameters to save_infrun_state call + - Check for fork and vfork when deciding if event is a random + signal + - When considering stops due to breakpoints, check for + BPSTAT_WHAT_CHECK_SHLIBS_RESUME_FROM_HOOK + - Check for stop due to an explicit catchpoint + - When checking for single stepping event, also check for + stepping to get out of dynamic linker's hook after catching + a shlib event + (is_internal_shlib_eventpoint): new funct. check to see if + event type is bp_shlib_event. + (stopped_for_internal_shlib_event): new funct. check for shlib + events + (stopped_for_shlib_catchpoint): new funct. check for catchpoints. + (normal_stop): additions to check for shlib event + (set_follow_fork_mode_command): new funct. handles the new follow + fork command. + (_initialize_infrun): additions for follow-fork-mode command. + + * infrun.c: ifdefing references to + switched_from_inferior_pid for HPUXHPPA. They don't seem + useful for Solaris (i.e. non-HPUX) + + * infrun.c: included tuiData.h and tuiDataWin.h, ifdefed for TUI. + Included top.h. New static variables : switched_from_inferior_pid, + number_of_threads_in_syscalls. + (follow_inferior_fork): If there is a step_resume breakpoint + explicitly reset the thread number. + (resume): For TARGET_WAITKIND_VFORKED, removed a check for getting + the vfork event to soon. + (init_wait_for_inferior): added parameter to call to + breakpoint_init_inferior. Initialize number_of_threads_in_syscalls. + (wait_for_inferior): New local variables : prev_sal, + enable_hw_watchpoints_after_wait, stepping_through_sigtramp, + stepped_after_stopped_by_watchpoint. Enable watchpoints after a wait. + Added cases for TARGET_WAITKIND_SYSCALL_ENTRY and + TARGET_WAITKIND_SYSCALL_RETURN. + Do additional processing if stop due to breakpoint, but breakpoint is + only valid for a thread other than the one currently running. Additional + parameters to save_infrun_state and load_infrun_state. Some additional + processing for BPSTAT_WHAT_STEP_RESUME. Some additional processing to + handle stepping over a function. + (normal_stop): added notification of switching threads. ifdefing some + TUI changes and leaving out non-essential TUI changes. + (restore_selected_frame): ifdefing some TUI changes + (restore_inferior_status): ifdefing some TUI changes + + * infrun.c: remove HPUX_SNAP1 and HPUX_SNAP2 ifdefs + + * infrun.c: removed the TUI ifdefs and TUI code. Also removed + include for top.h. HP introduced this. I'm taking it out. + + * inftarg.c (child_detach_from_process): declare. + (child_attach_to_process): declare. + (child_stop): make static to match declaration. + + * inftarg.c (ptrace_him): change prototype back to return int. + + * inftarg.c (ptrace_me): Remove debug output, pass NULL to + fork_inferior if not HPUX. + + * inftarg.c: + (child_require_attach): new funct prototype and definition + (child_require_detach): new funct prototype and definition + (proc_wait): funct prototype and definition are enclosed by + proc_wait ifndef + (child_attach_to_process): new function, does most of the + work that child_attach used to do and some additional + work to determine whether gdb is already attached to the + target how to react. + (child_attach): altered. It's now a wrapper for + child_attach_to_process. + (child_require_attach): new function, called if should attach + even when gdb is already attached to target. + (child_detach_from_process): new function, does most of the + work that child_detach used to do and some additional work + to determine whether gdb is currently attached to the target. + (child_detach): altered. It's now a wrapper for + child_detach_from_process. + (child_require_detach): new function, called if should try to + detach even when gdb is not attached to target. + (ptrace_him): calls a new function, target_acknowledge_forked_child. + Currently, target_acknowledge_forked_child, is only defined to + do something for HPUX. + (child_create_inferior): changed call to fork_inferior. + (child_ops): added to_require_attach and to_require_detach fields + to the child_ops target ops vector. + + * inftarg.c: + Some hacks for ttrace work + (child_wait): Additional local variables, additional code in + while loop to check for : process exited, process forked, + process vforked, process execd + (child_thread_alive): John B. seems to think that the kill + call is inapproapriate for HPUX. + (child_attach_to_process): using strtol rather than atoi. + no longer check for case where there is no known exec file. + (child_post_attach): new function, a default, a no-op + (child_insert_fork_catchpoint): new function, a default, a no-op + (child_remove_fork_catchpoint): new function, a default, a no-op + (child_create_catch_fork_hook): deleted + (child_create_catch_vfork_hook): deleted + (child_insert_vfork_catchpoint): new function, a default, a no-op + (child_remove_vfork_catchpoint): new function, a default, a no-op + (child_can_follow_vfork_prior_to_exec ):new function, a default, + a no-op + (child_insert_exec_catchpoint): new function, a default, a no-op + (child_remove_exec_catchpoint): new function, a default, a no-op + (child_has_execd): new function, a default, returns 0 + (child_reported_exec_events_per_exec_call): new function, a + default, returns 1 + (child_has_exited): new function, a default. + (child_core_file_to_sym_file): new function, a default, returns NULL. + (child_ops): initialize new target_ops vector fields to the + child* functions. + * infptrace.c: + (call_ptrace): for HPUX, handle additional requests : PT_CONTIN1, + PT_STEP1. + (require_notification_of_events): add several signals to the + set of events requiring notification : PTRACE_SIGNAL, + PTRACE_EXEC, PTRACE_FORK, PTRACE_VFORK + (child_acknowledge_created_inferior): This function is only + defined if CHILD_ACKNOWLEDGE_CREATED_INFERIOR is defined. + (child_post_startup_inferior): function is only defined if + CHILD_POST_STARTUP_INFERIOR is defiend. Also, now call + require_notification_of_events. + (child_create_catch_fork_hook): deleted + (child_create_catch_vfork_hook): deleted + (child_insert_fork_catchpoint): new function + (child_remove_fork_catchpoint): new function + (child_insert_vfork_catchpoint): new function + (child_remove_vfork_catchpoint): new function + (child_has_forked): now enclosed by a CHILD_HAS_FORKED ifdef + (child_has_vforked): now enclosed by CHILD_HAS_VFORKED ifdef + (child_can_follow_vfork_prior_to_exec): new function + (child_insert_exec_catchpoint): new function + (attach): removed call to require_notification_of_events + (child_post_attach): new function, call to + require_notification_of_events moved here. + (child_pid_to_exec_file): new enclosed by CHILD_PID_TO_EXEC_FILE ifdef + introduced the concept of a saved_inferior_pid + (hppa_require_attach): Add some code to decide if gdb is already + attached to process. Can not figure this out via a ptrace call. + (hppa_insert_hw_watchpoint): new function + (hppa_remove_hw_watchpoint): new function + + * inftarg.c: + (child_attach_to_process): change position in file + (child_detach_from_process): change position in file + + * inftarg.c: + (child_attach_to_process): changed parameter to child_wait call + + * inftarg.c: + (child_post_wait): new function declaration and definition + (ptrace_him): + - change return value to a void. + - change target_acknowledge_forked_child call to + target_acknowledge_created_inferior + - call target_post_startup_inferior rather than returning pid. + (child_attach_to_process): change param name, fail_if_already_attached + -> after_fork. + Invert a couple of if-then-else statments. + Use REQUIRE_ATTACH macro + (child_attach): change params in child_attach_to_process call + (child_require_attach): change params in child_attach_to_process call + (child_detach_to_process): change param name, + fail_if_already_attached -> after_fork. + Invert a couple of if-then-else statments. + Use REQUIRE_DETACH macro + (child_detach): change params in child_detach_from_process call + (child_require_detach): change params in child_detach_from_process + call + (child_post_startup_inferior): new function + (child_acknowledge_created_inferior): new function + (child_clone_and_follow_inferior): new function + (child_post_follow_inferior_by_clone): new function + (child_create_catch_fork_hook): new function + (child_create_catch_vfork_hook): new function + (child_has_forked): new function + (child_has_vforked): new function + (child_post_follow_vfork): new function + (child_stop): no longer a static function + (child_pid_to_exec_file): new function + + * inftarg.c: + (child_wait): child_pid becomes related pid. return pid + rather than inferior_pid. Changes are in code handling fork + and vfork + + * inftarg.c: + Include gdb_stat.h and sys/unistd.h + (child_wait): new local variables. Check for live threads. + Check for syscall events + (child_thread_alive): no longer a static funct. + (ptrace_him): remove some code inserted in snap3 + (child_create_inferior): added a bunch of code to handle a + bad interaction between start-up-with-shell and the HP + catch-fork/catch-exec logic. I am ifdefing this for + HPUXHPPA for now. + (child_has_syscall_event): new default target_ops function + (child_enable_exception_callback): new default target_ops function + (child_get_current_exception_event): new default target_ops function + (child_ops): 3 new fields + + * inftarg.c: remove HPUX_SNAP1 and HPUX_SNAP2 ifdefs + + * inftarg.c: reverted previous change. + + * infttrace.c (hppa_remove_hw_watchpoint): fix check for write + access hardware watchpoint. + + * infttrace.c (proc_wait): Rename from proc_wait. + + * infttrace.c (require_notification_of_exec_events): new function; + just notify of exec events, not all events, and just the specified + pid, don't include it's children. + (child_acknowledge_created_inferior): call new function + require_notification_of_exec_events instead of + require_notification_of_events. + (child_post_startup_inferior): call require_notification_of_events + + * infttrace.c: changed all references to boolean to int. + Changed all references to TRUE and FALSE to 1 and 0. + + * irix5-nat.c: + (symbol_add_stub): add params to call to symbol_file_add. + + * jv-lang.c: + (get_dynamics_objfile): add 2 more parameters to call to + allocate_objfile. + + * m3-nat.c: + (m3_create_inferior): add param to fork_inferior call + (m3_pid_to_exec_file): new function + (m3_ops): initializing new target ops vector fields. see list below. + + * m3-nat.c: + (m3_ops): adding new target_ops vector fields and + removing a few. see list below + + * main.c: + (fputs_unfiltered): changes to prevent cursor form jumping around + in the TUI. Altered where tuiTermUnsetup and tuiTermSetup are + called + + * main.c (fputs_unfiltered): Changed function so that it + checks to see if output is to a string buffer or to a + FILE stream and does the correct action (i.e. strcat or + fputs). Fixed params for fputs call. + + * main.c (fputs_unfiltered): Don't try to call the TUI's + CommandCharCount functions when the TUI isn't enabled. + + * main.c (fputs_unfiltered): change FILE to GDB_FILE. + + * main.c (main): If the user asks for the TUI, then disable + GDB/Tk. + + * main.c (main): If the user gives the --version or --help flags, + disable the TUI. + + * main.c (tui_version, xdb_commands, dbx_commands): new variables. + (main): new command line arguments --tui, --xdb, --dbx; add call + to tyiCleanUp via tuiDo to main loop. + (fputs_unfiltered): tui related changes. + + * main.c: + Define 2 new global variables, gdb_stdout and gdb_stderr of type GDB_FILE. + (main): Allocate space for and initialize gdb_stdout and gdb_stdin. + + * objfiles.c (find_pc_sect_section): make end condition be less + than s->endaddr, not less than or equal to s->endaddr. + + * objfiles.c: + (allocate_objfile): 2 new parameters : user_loaded and is_solib. + When appropriate, record in the object file that it is user loaded. + The run command can use this information to purge object file + entries associated with the old inferior and keep user loaded + object files loaded via the add-symbol-file command. + (objfile_purge_solibs): new function. deletes all objectfile entries + that are not explicitly loaded by the user. + + * objfiles.c: + (objfile_relocate): check for LOC_INDIRECT + (find_pc_sect_section): change condition from + pc < s->endaddr to pc <= s->endaddr + + * objfiles.h: + New variables : user_loaded and is_solib + (OBJF_SHARED): new macro. used to distinguish objfile for + shared library from "vanilla" objfile. + (allocate_objfile): add new parameters to function decl. + (objfile_purge_solibs): new function decl. + + * objfiles.h: Add some typedefs : ImportEntry, ExportEntry. + Add some new variables : import_list, import_list_size, + export_list, export_list_size + + * osfsolib.c: + (symbol_add_stub): added params to call to symbol_file_add + + * pa/hpux1020.mh (NATDEPFILES): Add corelow.o, symbol table and + solib files. + + * pa/hpux1100.mh (NAT_FILE): Use nm-hppah11.h. + (NATDEPFILES): Add symbol table and solib files. + + * pa/nm-hppah11.h: New file, HPUX11-only definitions. + + * pa/tm-hppa.h (proc_wait): Remove decl and macro. + + * parse.c (write_dollar_variable): handle cases in which variables + besides the debugger ones start with $ and $$. + (parse_nested_classes_for_hpacc): new function. Parse a string that + is possibly a namespace / nested class specification. + (find_template_name_end): new function. + + * procfs.c: + (procfs_init_inferior): return value is now a void. + + * procfs.c: + (procfs_ops): initializing new target ops vector fields. see list below. + + * procfs.c: + (procfs_ops): adding new target_ops vector fields and + removing a few. see list below + + * procfs.c: added new fields to procfs_ops. + Necessary since we still have oldstyle initialization in + this file + + * pyr-tdep.c (pyr_do_registers_info): change stdout to gdb_stdout. + (frame_locals_address): change stderr to gdb_stderr. + (frame_args_addr): ditto. + + * pyr-xdep.c (fetch_inferior_registers): change stderr to + gdb_stderr. + + * remote-udi.c: + (init_udi_ops): adding new target_ops vector fields and + removing a few. see list below + + * remote-udi.c: + (init_udi_ops): initializing new target ops vector fields. see list below. + + * remote-udi.c: + (udi_load): fixed params in call to symbol_file_add + + * remote-vx.c: + (vx_add_symbols): fixed params in call to symbol_file_add + + * remote-vx.c: + (init_vx_ops): adding new target_ops vector fields and + removing a few. see list below + (init_vx_run_ops): ditto + + * remote-vx.c: + (init_vx_ops): initializing new target ops vector fields. see list below. + (init_vx_run_ops):initializing new target ops vector fields. see list below. + + * serial.c (serial_close): call gdb_fclose, not fclose on a + GDB_FILE. + + * serial.c (serial_logchar): change chtype to ch_type. sigh. + + * solib.c (look_for_base): the parameter to file must be + of type FILE *. So cast exec_bfd -> iostream in the call + to fileno as a FILE *, not a GDB_FILE *. This will work because + exec_bfd -> iostream is declared and given a value in bdf and + bfd will continue to use FILE rather than GDB_FILE. + + * solib.c: + (solib_add): remove references to exec_ops. + + * solib.c: + (solib_add): update exec_ops.to_sections + + * solib.c: + (symbol_add_stub): added params to call to symbol_file_add + + * solib.h: + (SOLIB_REMOVE_INFERIOR_HOOK): new macro. defined to 0. + functionality not implemented for this target. + + * solib.h: Added macro definitions. These macros generate + error messages for solaris?? + (SOLIB_CREATE_CATCH_LOAD_HOOK) + (SOLIB_CREATE_CATCH_UNLOAD_HOOK) + (SOLIB_HAVE_LOAD_EVENT) + (SOLIB_LOADED_LIBRARY_PATHNAME) + (SOLIB_HAVE_UNLOAD_EVENT) + (SOLIB_UNLOADED_LIBRARY_PATHNAME) + (SOLIB_IN_DYNAMIC_LINKER) + (SOLIB_RESTART) + + * somread.c (is_in_import_list): ditto. + + * somread.c (som_symfile_read): added some comments + + * somread.c (som_symfile_read): read in import and export lists. + (som_symtab_read): change test for dynamic executable. + (is_in_import_list): new function. Check if a given symbol name + is in the import list. + (init_import_symbols): new function. Read in and initialize the + som import list. + (init_export_symbols): new function. Read in and initialize the + som export list. + + * somread.c: + (som_symfile_read): fix missing comment delimiters + + * somsolib.c (DLD_FLAGS_MAPPRIVATE): new macro. + Define bit of __dld_flags in HP-UX a.out files. + (DLD_FLAGS_HOOKVALID): ditto. + (DLD_FLAGS_LISTVALID): ditto. + (DLD_FLAGS_BOR_ENABLE): ditto. + (som_solib_total_st_size): cumulative size in bytes of the + symbol tables of all shared objects on the so_list_head list. + (som_solib_st_size_threshhold_exceeded): threshold for adding symbols + for shlibs. + (som_solib_sizeof_symbol_table): new function. Computes size of + symbol table for a shlib. + (som_solib_load_symbols): new function. Load symbols from shlib. + (som_solib_add): detect if __dld_list is not valid. + Record main program's symbol table size. + Load symbols if called from command line. + Keep threshold into account when loading shlib symbols. + (som_solib_create_inferior_hook): use dld_flags macros. + (som_sharedlibrary_info_command): let user know if symbols were + not loaded. + (som_solib_restart): discard all the shlibs descriptors. + (_initialize_som_solib): chenge help message for auto-solib-add + command. + Set threshold for symbol table to 50 megabytes. + + * somsolib.c (_initialize_som_solib): added call to som_solib_restart. + (som_solib_restart): new function + (som_solib_in_dynamic_linker): new function + (som_solib_desire_dynamic_linker_symbols): new function + (som_solib_unloaded_library_pathname): new function + (som_solib_loaded_library_pathname): new function + (som_solib_library_pathname): new function + (som_solib_have_unload_event): new function + (som_solib_have_load_event): new function + (som_solib_create_catch_unload_hook): new function + (som_solib_create_catch_load_hook): new function + (som_solib_create_inferior_hook): rewritten + dld_cache: new struct + addr_and_unwind_t: new struct + (find_unwind_entry) added prototype + + * somsolib.c (som_solib_create_inferior_hook): introduce new local + msymbol2 and change some msymbol's to msymbol2's -- was clobbering + msymbol, passing a NULL to lookup_minimal_symbol_solib_trampoline, + and ultimately core dumping with a SEGV. + + * somsolib.c: + Include assert.h + (som_solib_mapped_entry): additional comments for text_addr, + text_link_addr, text_end, and tsd_start_addr fields. Commenting + out 2 tsd fields, __data_start and __data_end. + (som_solib_add_solib_objfile): add params to calls to symbol_file_add. + Add some code for distinguishing between a shared library and other + objfiles. This appears to be a prelude to thread local storage. + (som_solib_load_symbols): changes to printf statement + enclosed by SOLIB_DEBUG ifdef. + (som_solib_add): change comment to correctly specify path + to end.o -- /opt/langtools/lib/end.o. changes to printf statement + enclosed by SOLIB_DEBUG ifdef. + Removed several SOLIB_DEBUG ifdefs and the associated printfs. + Add code to find the start address for the object file's thread + local storage + (som_solib_create_inferior_hook): Fix warning messages use correct + path to end.o -- /opt/langtools/lib/end.o. Change control flow. + No longer user early returns from function is cases of error. + (reset_inferior_pid): new function + (som_solib_remove_inferior_hook): new function + (so_lib_thread_start_addr): new function. used for tsd. + + * somsolib.c: Removed references to ASSERT macro. + + * somsolib.c: add debugging macro. + (struct som_solib_mapped_entry): add new field tsd_start_addr. + (struct so_list): added new field solib_addr. + (som_solib_add_solib_objfile): new function. + (som_solib_load_symbols): rewritten. + (som_solib_add): make sure we don't load the symbols in if the + threshold was exceeded. + (som_solib_get_solib_by_pc): new function. Return the address of + handle of the shared library. + (som_solib_restart): disable breakpoints at restart. + (_initialize_som_solib): set threshold to 100 megabytes. + + * somsolib.c: add include of fcntl.h so that O_RDONLY is defined. + + * somsolib.h (DISABLE_UNSETTABLE_BREAK): new macro. + (PC_SOLIB): new macro. + + * somsolib.h: + (SOLIB_CREATE_CATCH_LOAD_HOOK): define + (SOLIB_CREATE_CATCH_UNLOAD_HOOK): define + (SOLIB_HAVE_LOAD_EVENT): define + (SOLIB_LOADED_LIBRARY_PATHNAME): define + (SOLIB_HAVE_UNLOAD_EVENT): define + (SOLIB_UNLOADED_LIBRARY_PATHNAME): define + (SOLIB_IN_DYNAMIC_LINKER): define + (SOLIB_RESTART): define + + * somsolib.h: + (SOLIB_REMOVE_INFERIOR_HOOK): new macro. defined to use + som_solib_remove_inferior_hook. + + * somsolib.h: + (som_solib_create_catch_load_hook) + (som_solib_create_catch_unload_hook) + (som_solib_have_load_event) + (som_solib_loaded_library_pathname) + (som_solib_have_unload_event) + (som_solib_unloaded_library_pathname) + (som_solib_in_dynamic_linker) + Fix prototypes to use type names, not parameter names. + + * source.c (find_source_lines): make non static. + (open_source_file): ditto. + (source_full_path_of): new function. + (print_source_lines): rename to print_source_lines_base and make + static; formatting. + (print_source_lines): new function. + (forward_search_command): tui changes. + (reverse_search_command): tui changes. + (_initialize_source): add xdb and dbx compatibility commands. + + * source.c (list_command): handle case of odd number of source + lines to display. + + * source.c: + (source_full_path_of): new function. file was overlooked + in merge ;-/. + + * stack.c (func_command): make high bound be <, not <=. + + * stack.c (_initialize_stack): for the backtrace command, delete + the help line about usage, since this has to be a valid help + message for the 'where' command too. + + * stack.c (current_frame_command): add a check for the + existance of a stack. If there is no stack produce an + error message and exit. + + * stack.c (down_silently_base, up_silently_base, + args_plus_locals_info, print_frame_info_base, + print_stack_frame_base, print_stack_frame_base_stub): declare. + (print_frame_local_vars): add'l parameter. + (print_stack_frame_stub): new version created, old version renamed + to show_and_print_stack_frame_base_stub. + (print_stack_frame_base_stub, print_only_stack_frame_stub, + show_and_print_stack_frame, print_only_stack_frame, + stack_publish_stopped_with_no_frame, print_frame_info, + show_stack_frame, backtrace_full_command, args_plus_locals_info, + select_and_print_frame, select_and_maybe_print_frame, + current_frame_command, func_command): new functions. + (backtrace_command): new function, old renamed to + backtrace_command_1. + (print_block_frame_locals, print_frame_local_vars): additional + parameter, number of tabs. + (up_silently_command): new function, old renamed to + up_silently_command_base. + (down_silently_command): new function, old renamed to + down_silently_base. + (_initialize_stack): register new commands based on values of + xdb_commands and dbx_commands variables. + + * stack.c (func_command): make high bound be <, not <=. + + * stack.c (parse_frame_specification): fix prototype to match + function definition. + (show_and_print_stack_frame_stub): fix name. + (select_and_print_frame): change uncaught tuiDO call. + + * stack.c (up_silent_base): rename from up_silently_command_base. + + * symfile.c (symbol_file_command): only call SOLIB_RESTART if it's + defined. + + * symfile.c (add_psymbol_with_dem_name_to_list): new function. + Adds a symbol with a long value to a psymtab. Differs from + add_psymbol_to_list in taking both a mangled and a demangled name. + + * symfile.c (compare_psymbols): call strcmp directly, instead of + using macro. + + * symfile.c (symbol_file_add): reindent portions. + (symbol_file_command): add call to tuiDo. + + * symfile.c (symbol_file_command): only call SOLIB_RESTART if it's + defined. + + * symfile.c (symfile_bfd_open): add code to call PXDB on hpux, if + the file has not already been processed by pxdb. + Added define USE_PXDB. + + * symfile.c (symfile_bfd_open): change parenthesis positioning + around call to hpread_pxdb_check. + + * symfile.c (symfile_bfd_open): make not static. + (RESET_HP_UX_GLOBALS): new macro. Resets globals when new symbol + file loaded. + (USE_PXDB): not needed. Removed. + (symbol_file_add): add HP specific code to deal with pxdb. + (symbol_file_command): reset HP specific globals if new symbol file + loaded. + (symfile_bfd_open): comment out checking for pxdb. + (reread_symbols): reset HP specific globals. + + * symfile.c (symfile_bfd_open): uncomment hpus specific code. + + * symfile.c: + (symbol_file_add): add user_loaded and is_solib parameters. + fixed number of parameters in call to allocate_objfile + (symbol_file_command): added call to SOLIB_RESTART macro. + fixed number of parameters in calls to symbol_file_add. + (add_symbol_file_command): fixed number of parameters in calls to + symbol_file_add. + + * symfile.c: added prototype for hpread_pxdb_check. + + * symfile.c: changed HPUX_SNAP1 ifdef to HPUXHPPA. enclosed calls to + RESET_HP_UX_GLOBALS with an HPUXHPPA ifdef + + * symfile.h (symfile_bfd_open): add protptype. + + * symfile.h: add prototype for add_psymbol_with_dem_name_to_list. + + * symfile.h: clarify purpose of auto_solib_add. + + * symmisc.c (maintenance_print_symbols): call gdb_fclose, not + fclose on a GDB_FILE* during cleanup. + (maintenance_print_psymbols): call gdb_fclose, not fclose on a + GDB_FILE* during cleanup. + (maintenance_print_msymbols): call gdb_fclose, not fclose on a + GDB_FILE* during cleanup. + + * symmisc.c (maintenance_print_symbols): gdb_fclose now takes a + GDB_FILE ** parameter. Fix the local GDB_FILE variables and the + call to make_cleanup. + (maintenance_print_psymbols): ditto + (maintenance_print_msymbols): ditto + + * symmisc.c (print_objfile_statistics): close quotes in + output strings. + + * symmisc.c: + (print_symbol): Add LOC_INDIRECT to switch statement + (print_partial_symbols): Add LOC_INDIRECT to switch statement + + * symtab.c (find_pc_sect_psymtab): high bounds should be <, not <=. + (find_pc_sect_symtab): ditto. + + * symtab.c (hp_som_som_object_present): new flag to indicate HP + compiled code. + (find_pc_sect_psymtab): change tests to make sure we are checking + the texthigh adress as well. + (lookup_transparent_type): new function. Look up a type name + in the struct_namespace. The type returned must not be opaque. + (find_pc_sect_symtab): make sure we check the address 'pc' itself, + too. + (find_addr_symbol): prepare to handle LOC_INDIRECT address class, but + leave it commented out. + (find_pc_sect_line): return correct information if pc is in import + or export stub (trampoline). + (decode_line_1): skip two chars, if they are '$$'. Like for HP's + $$dyncall. Handle cases in which varaible and function names can start + with $. + (overload_list_add_symbol): if cannot demangle name, use it as is. + Free string after use. + (make_symbol_overload_list): initialize oload_name to NULL and + oload_name_len to 0. If demangle not successful, use name as it is. + Free string after use. + + * symtab.c (lookup_symbol): Changed call to find_pc_sect_symtab, + to the original find_pc_symtab, in HP added fragment. + + * symtab.c (lookup_symbol): change HPUX_SNAP1 ifdef to a HPUXHPPA ifdef + + * symtab.c (lookup_symbol): ifdef the searching of symbol in the + minimal symbol tables, for hpux we move this check at the end + of the function. + Before we error out if symbol is not found in the symtab, look + in the statics. + Before erroring out if static symbol not found look in the globals. + + * symtab.c (lookup_symbol): return symbol as soon as found. + (decode_line_1): check whether we have a conditional break. Temporarily + remove it from the line, to not confure perenthesis checking. + Handle namespaces. + (overload_list_add_symbol): new function. Overload + resolution support. + (make_symbol_overload_list): ditto. + + * symtab.c: + (find_template_name_end): new prototype decl. + (lookup_symbol): when a global or static symbol shows up in the + psymtab table, but not the symtab table, tell the user that + the symbol may be an inlined function or a template function and + provide some guidance to the user about how to more fully + specify the symbol. + (lookup_transparent_type): when a global or static symbol shows up + in the psymtab table, but not the symtab table, tell the user that + the symbol may be an inlined function or a template function and + provide some guidance to the user about how to more fully + specify the symbol. + (decode_line_1): handle template function specification when decoding a + line. May need to be ifdefed for HP's aCC? + (_initialize_symtab): handle dbx commands. + + * symtab.h (address_class): add new address calss for + LOC_THREAD_LOCAL_STATIC and LOC_INDIRECT. + (lookup_transparent_type): add prototype. + (exception_event_kind): new enum for exception catchpoints. + (exception_event_record): new structure for exception catchpoints. + (CURRENT_EXCEPTION_KIND): new macro. + (CURRENT_EXCEPTION_CATCH_SAL): new macro. + (CURRENT_EXCEPTION_CATCH_LINE): new macro. + (CURRENT_EXCEPTION_CATCH_FILE): new macro. + (CURRENT_EXCEPTION_CATCH_PC): new macro. + (CURRENT_EXCEPTION_THROW_SAL): new macro. + (CURRENT_EXCEPTION_THROW_LINE): new macro. + (CURRENT_EXCEPTION_THROW_FILE) new macro.: + (CURRENT_EXCEPTION_THROW_PC): new macro. + + * symtab.h(make_symbol_overload_list): add prototype. + + * symtab.h: + (symbol_file_add): add new params to function decl. + + * target.c (cleanup_target): changed casting of default functions for + to_has_forked, to_has_vforked, to_pid_to_exec_file to get rid of + warnings. + + * target.c (cleanup_target): changed the default functions for + to_pid_to_exec_file and to_core_file_to_sym_file + + * target.c (cleanup_target): fixed PARAMS for to_has_syscall_event + + * target.c (cleanup_target): syntax error, mismatched paranthesis. + + * target.c (debug_to_open): change stderr to gdb_stderr. + (debug_to_close): ditto. + (debug_to_attach): ditto. + (debug_to_post_attach): ditto. + (debug_to_require_attach): ditto. + (debug_to_detach): ditto. + (debug_to_require_detach): ditto. + (debug_to_resume): ditto. + (debug_to_wait): ditto. + (debug_to_post_wait): ditto. + (debug_to_fetch_registers): ditto. + (debug_to_store_registers): ditto. + (debug_to_prepare_to_store): ditto. + (debug_to_xfer_memory): ditto. + (debug_to_files_info): ditto. + (debug_to_insert_breakpoint): ditto. + (debug_to_remove_breakpoint): ditto. + (debug_to_terminal_init): ditto. + (debug_to_terminal_inferior): ditto. + (debug_to_terminal_ours_for_output): ditto. + (debug_to_terminal_ours): ditto. + (debug_to_terminal_info): ditto. + (debug_to_kill): ditto. + (debug_to_load): ditto. + (debug_to_lookup_symbol): ditto. + (debug_to_create_inferior): ditto. + (debug_to_post_startup_inferior): ditto. + (debug_to_acknowledge_created_inferior): ditto. + (debug_to_clone_and_follow_inferior): ditto. + (debug_to_post_follow_inferior_by_clone): ditto. + (debug_to_insert_fork_catchpoint): ditto. + (debug_to_remove_fork_catchpoint): ditto. + (debug_to_insert_vfork_catchpoint): ditto. + (debug_to_remove_vfork_catchpoint): ditto. + (debug_to_has_forked): ditto. + (debug_to_has_vforked): ditto. + (debug_to_can_follow_vfork_prior_to_exec): ditto. + (debug_to_post_follow_vfork): ditto. + (debug_to_insert_exec_catchpoint): ditto. + (debug_to_remove_exec_catchpoint): ditto. + (debug_to_has_execd): ditto. + (debug_to_reported_exec_events_per_exec_call): ditto. + (debug_to_has_syscall_event): ditto. + (debug_to_has_exited): ditto. + (debug_to_mourn_inferior): ditto. + (debug_to_can_run): ditto. + (debug_to_notice_signals): ditto. + (debug_to_thread_alive): ditto. + (debug_to_stop): ditto. + (debug_to_enable_exception_callback): ditto. + (debug_to_get_current_exception_event): ditto. + (debug_to_pid_to_exec_file): ditto. + (debug_to_core_file_to_sym_file): ditto. + + * target.c: + (default_clone_and_follow_inferior): new funct prototype declaration + and function definition + (dummy_target) : More target_ops vector changes for HPUX + new fields. ifdefed for HPUX_SNAP2. New fields are + to_post_wait, to_post_startup_inferior + to_acknowledge_created_inferior, to_clone_and_follow_inferior, + to_post_follow_inferior_by_clone, to_create_catch_fork_hook, + to_create_catch_vfork_hook, to_has_forked, to_has_vforked, + to_post_follow_vfork, to_pid_to_exec_file + (de_fault): add new HPUX specific target_ops operations to + the de_fault macro + (INHERIT): add new HPUX specific target_ops operations to the + INHERIT macro + (find_default_clone_and_follow_inferior): new funct definition + (debug_to_post_wait): new funct + (debug_to_post_startup_inferior): new funct + (debug_to_acknowledge_created_inferior): new funct + (debug_to_clone_and_follow_inferior): new funct + (debug_to_post_follow_inferior_by_clone): new funct + (debug_to_create_catch_fork_hook): new funct + (debug_to_create_catch_vfork_hook): new funct + (debug_to_has_forked): new funct + (debug_to_has_vforked): new funct + (debug_to_post_follow_vfork): new funct + (setup_target_debug): initialize new target_ops vector fields. + + * target.c: + (cleanup_target): fixed the return type on a few of the + default function values. + + * target.c: + (dummy_target): Add 3 new fields + (nosupport_runtime): new function, used in cleanup_target + (cleanup_target): changes in the de_fault macro, both to + accomodate the new target_ops vector fields and to use + more accurate default functions. + (update_current_target): Add new target_ops vector fields to the + INHERIT macro + (generic_mourn_inferior): the call to breakpoint_init_inferior now takes a + parameter + (normal_pid_to_str): Adding a \0 to the end of buf. + (debug_to_has_syscall_event): new func + (debug_to_enable_exception_callback): new func + (debug_to_get_current_exception_event): new func + (setup_target_debug): initialize the 3 new target_ops vector fields + + * target.c: + (struct signals): fix message associated with SIGRETRACT. + + * target.c: + (dummy_target): fix syntax error + (cleanup_target): changed the default values for the new + target_ops vector fields. HP folks inappropriately set + most of them to noprocess(). They should be a mixture + of ignore() and return_zero(). + + * target.c: + (dummy_target): Add new target_ops vector fields and their initializations + (cleanup_target): Added new new target_ops vector fields to the de_fault + macro definition. + (update_current_target): Added new new target_ops vector fields to the INHERIT + macro definition + (return_one): new function, used by the de_fault macro + (debug_to_post_attach): new function + (debug_to_wait): added new cases : TARGET_WAITKIND_FORKED, TARGET_WAITKIND_VFORKED, + TARGET_WAITKIND_EXECD + (debug_to_insert_fork_catchpoint): new function + (debug_to_remove_fork_catchpoint): new function + (debug_to_insert_vfork_catchpoint): new function + (debug_to_remove_vfork_catchpoint): new function + (debug_to_can_follow_vfork_prior_to_exec): new function + (debug_to_insert_exec_catchpoint): new function + (debug_to_remove_exec_catchpoint): new function + (debug_to_core_file_to_sym_file): new function + (setup_target_debug): give new fields in current_target target_ops vector values. + + * target.c: HP merge, 4/15/98 snapshot + There are new target_ops fields that pertain only + to HPUX. All the changes relate to this. Fist, + new fields are added to the dummy_target target_ops + vector : to_require_attach, to_require_detach. + + * target.c: remove HPUX_SNAP1 and HPUX_SNAP2 ifdefs + + * thread.c (info_threads_command): call print_only_stack_frame + instead of print_stack_frame. + (_initialize_thread): make t an alias for thread only if + xdb_commands is not set. + + * thread.c (thread_command): if no arguments, don't generate an + error, instead tell the user which thread is current. + (info_threads_commands): don't lose the users position within the + current thread -- remember it and then restore it. + + * thread.c: + (struct thread_info): add stepping_through_sigtramp field + (add_thread): initialize stepping_through_sigtramp field + (load_infrun_state): add stepping_through_sigtramp param and + make sure it gets assigned a value. + (save_infrun_state): add stepping_through_sigtramp param and + make sure that the value gets saved. + (info_threads_command): ifdefing some local variables and + code for HPUXHPPA. HP folks want print the tid rather than pid? + Also, looks like the HP folks solved the same thread switching + problem that 4.17 solves. Taking 4.17. + (restore_current_thread): print out the current frame after + switching threads. + (thread_apply_all_command): ifdefing a print statement for + HPUXHPPA. The HP folks want to print out a tid rather than pid? + (thread_apply_command): ifdefing a print statement for + HPUXHPPA. The HP folks want to print out a tid rather than pid? + (thread_command): decided not to take HP change. + + * thread.c: Fixing gdb core dump problem causing many testsuite + failures. + (add_thread): remove call to bpstat_clear, initialize + tp->stepping_through_solib_catchpoints = NULL; + + * thread.c: changes for catchpoints, shared libaries, + (thread_info) : additional fields in the thread_info struct + for stepping_through_solib_after_catch and + stepping_through_solib_catchpoints. + (add_thread): initialize the new thread_info fields. + (load_infrun_state) : additional parameters for handling + catchpoints and shared libraries. + (save_infrun_state): additional parameters for handling + catchpoints and shared libraries. + + * top.c (command_loop): initialize space_at_cmd_start to 0. + + * top.c (set_prompt): new function. + + * top.c (togglelist, stoplist): new command lists. + (command_loop): tui changes -- paranoia to make sure + insert mode is off when not editing. + (quit_force): clean up tui on exit. + (init_main): make definition of info status command dependent upon + dbx mode not being set. + + * top.c: + (fputs_unfiltered_hook): changed stream parameter from FILE + to GDB_FILE + (flush_hook): changed stream parameter from FILE to GDB_FILE + + * top.h: declare it. + + * typeprint.c (whatis_exp): decide real runtime type. For the vtable + case. + + + * utils.c: + (query): changes to prevent cursor from jumping around in the + TUI. Call tuiBufferGetc explicitly, rather than passing it + into tuiDo. The tuiDo function does some additional work + that is inappropriate when handling queries. + + * utils.c + (GDB_FILE_ISATTY): new macro that takes a GDB_FILE param and + determines whether or not it's using a tty. + (gdb_file_isatty); called by the GDB_FILE_ISATTY macro. Does + the actual work + (init_page_info): Call GDB_FILE_ISATTY rather than ISATTY + (print_spaces): fix parameter to fputc. fix call to + gdb_file_adjust_strbuf. + (gdb_file_init_astring): fix parameter to xmalloc + (gdb_file_deallocate): new function to deallocate + a GDB_FILE object and possibly a string buffer + + * utils.c (gdb_file_init_astring): initialize buffer as the empty + string. + Indent GNU style. + + + * utils.c (gdb_fopen): gdb_fopen is called if the GDB_FILE object is + actually afile rather than astring. The routine now allocates space + for a GDB_FILE object and initializes its fields in addition to + performing an fopen. + (gdb_flush): fix the parameter passed into fflush. It's now stream->ts_filestream. + (gdb_fclose): Pass in an object of type GDB_FILE **. Fix parameter to fclose. It's + now tmpstream->ts_filestream. Make sure to free the GDB_FILE object and set the + GDB_FILE * object to NULL. + (gdb_adjust_strbuf): new function. Determine if the current ts_strbuf field + contains sufficient space to concatenate a string of length n on the end. + If not, then reallocate the ts_strbuf. + (print_spaces): Check to see if the GDB_FILE is afile or astring. If it is + astring, then adjust the size of the ts_strbuf field and concatenate the + correct number of spaces onto the end of the buffer. Otherwise continue to + use fputc. + (gdb_file_get_strbuf): new function. return a ptr to the ts_strbuf field in a + GDB_FILE object + (gdb_file_init_astring): new function to allocate space for and initialize + a GDB_FILE object when it is an astring. + + * utils.c (set_width): declare it. + (pagination_enabled): define it. + (query): tui changes. + (init_page_info, set_width): new functions. + (set_width_command): call set_width. + (_initialize_utils): replace termcap stuff with call to + init_page_info; if xdb_commands set, define am and sm commands; + define pagination as a set/show command. + + * utils.c (vfprintf_maybe_filtered): change FILE to GDB_FILE. + (fputs_maybe_filtered): ditto. + (print_spaces): ditto. + (gdb_printchar): ditto. + (gdb_flush): ditto. + (fputs_filtered): ditto. + (vfprintf_filtered): ditto. + (vfprintf_unfiltered): ditto. + (fprintf_filtered): ditto. + (fprintf_unfiltered): ditto. + (fprintfi_filtered): ditto. + (print_spaces_filtered): ditto. + (fprintf_symbol_filtered): ditto. + (gdb_fclose): new function. + + * valops.c (call_function_by_hand): assign to param_type only + if function has parameters. + + * valops.c (call_function_by_hand): ifdef the + HP_COMPILED_TARGET stuff. + (value_arg_coerce): ditto. + + * valops.c (call_function_by_hand): make sure param_type is + initialized to NULL. + + * valops.c (find_rt_vbase_offset): add parameter to value_at. + (value_rtti_type): ditto. + (value_full_object): ditto. + + * valops.c (search_struct_field_aux): fixed mismatching parenths + + + * valops.c (search_struct_field_aux): make sure TYPE_TAG_NAME + is not null before copying it. + + + * valops.c (search_struct_field_aux): set found_class_name to null + if class has no name (anon unions case). Adjust base_addr + computation. + + * valops.c (value_arg_coerce): change final arg to int. + + * valops.c (value_arg_coerce): remove the conditional on HP + compiled target, for doing coercion of float to double. Removed + third parameter, using_gcc. + (call_function_by_hand): do not use HP_COMPILED_TARGET, just + use the gcc_compiled variable. + + * valops.c (value_cast): take case of the enclosing_type and + pointer_to_offset fields. + (value_at): use VALUE_CONTENTS_ALL_RAW + (value_fetch_lazy): ditto + (value_assign): handle enclosing_type, embedded_offset and + pointed_to_offset fields. + (value_repeat): use VALUE_CONTENTS_ALL_RAW and VALUE_ENCLOSING_TYPE. + (value_ind): set enclosing_type and embedded_offset correctly, + for a pointer value being dereferenced. Target memory bytes + corresponding to the size of the enclosing type are retreived. + (value_addr): handle enclosing_type and pointed_to_offset. + (value_push): use VALUE_CONTENTS_ALL and VALUE_ENCLOSING_TYPE. + (value_arg_coerce): coerce floats to doubles only if gcc was not + used to compile the target. + (call_function_by_hand): handle pointers to functions as paramters. + (value_array): use VALUE_CONTENTS_ALL and VALUE_ENCLOSING_TYPE. + (search_struct_method): produce more informative error message. + (find_rt_vbase_offset): deal with negative offsets. + (value_find_oload_method_list): new function. Return the list of + overloaded methods of a specified name. + (find_method_list): new function. Search through the methods of an + object (and its bases) to find a specified method. + (value_full_object): new function. Given a value, check its real + run-time type. + (value_rtti_target_type): new function. Given a pointer value V, find + the real (RTTI) type of the object it points to. + (value_rtti_type): new function. Find the real run-time type of a + value using RTTI. + + * valops.c: include gdbcmd.h + Set global overload_resolution to 0. + (find_function_in_inferior): modify error message. + (value_allocate_space_in_inferior): modify error message. + (value_cast): deal with HP/aCC peculiarities. + (value_of_variable): use SYMBOL_SOURCE_NAME instead of SYMBOL_NAME. + (value_addr): modify address value by adding the embedded offset. + (value_ind): modify the address of the object by the pointed_to_offset. + (call_function_by_hand): do not do any extra alignment if not needed. + Fetch the return value from the stack rather then from the register, + for the hppa architecture. + (search_struct_field): rewritten. Now this function uses + search_struct_field_aux to do all the work. + (search_struct_field_aux): new function. This is the old + search_struct_field rewritten. + (find_rt_vbase_offset): give error if virtual table pointer is not good. + (find_overload_match): new function. Find the best function that + matches on the argument types according to the overload resolution + rules. + (_initialize_valops): add new set/show command for overload-resolution. + + * value.h (VALUE_POINTED_TO_OFFSET): new macro. + Add field pointed_to_offset to value structure. + Add prototypes for new functions in valops.c. + + * value.h (write_register_pid): change prototype to match + function. + + * value.h: HP merge, 4/15/98 snapshot + Added parameter to val_print func decl. + Added new macro, VALUE_EMBEDDED_OFFSET, and + new func decl, find_rt_vbase_offset, for C++ + support. + + * values.c (allocate_value): allocate also for value_embedded_offset + and value_enclosing_type. + (value_copy): copy value_embedded_offset and value_enclosing_type too. + Use all_raw in copying the value itself. + (value_primitive_field): add handling of base subobjects. + + * values.c (value_copy): copy the pointed_to_offset as well. + (allocate_value): allocate the pointed_to_offset as well. + (value_virtual_fn_field): rewrite. + + * values.c (value_primitive_field): adjust embedded offset and + offset calculation. + + * values.c (value_static_field): take into consideration that static + data members can be minimal symbols too. + + * values.c (value_virtual_fn_field): fix call to value_at. + + * win32-nat.c (child_ops): Remove unneeded settings. + + * win32-nat.c: + (handle_load_dll): added params to call to symbol_file_add. + + * win32-nat.c: + (init_child_ops): adding new target_ops vector fields and + removing a few. see list below + + * win32-nat.c: + (init_child_ops): initializing new target ops vector fields. see list below. + + Other changes have to do with XDB compatability. Leave oout + for now. + + defs.h (vfprintf_filtered): change FILE to GDB_FILE in decl. + (fprintf_filtered): ditto. + (fprintfi_filtered): ditto. + (vfprintf_unfiltered): ditto. + (fprintf_unfiltered): ditto. + + infcmd.c (_initialize_infcmd): if xdb_commands is set, make S an + alias for next and define R, lr, g. Define go. + + pyr-tdep.c (pyr_print_insn): change FILE to GDB_FILE. + + + * breakpoint.c (create_temp_exception_breakpoint): #if it out -- + nothing calls it. + (bpstat_stop_status): don't call SOLIB_HAVE_LOAD_EVENT if it's not + defined; don't call SOLIB_HAVE_UNLOAD_EVENT if it's not defined. + (bpstat_get_triggered_catchpoints): if we don't have shared + library support, then don't call SOLIB_LOADED_LIBRARY_PATHNAME nor + SOLIB_UNLOADED_LIBRARY_PATHNAME. + (watch_command_1): don't require a run before a watch command + unless we're on HP [it's an HP OS bug, not a generic limitation] + (catch_load_command_1): don't define if no shared libraries. + (catch_command_1): don't claim to support fork catchpoints unless + CHILD_INSERT_FORK_CATCHPOINT is defined, don't claim to support + vfork catchpoints unless CHILD_INSERT_VFORK_CATCHPOINT is defined, + don't clain to support shared library load catchpoints if shared + libraries aren't supported, and don't claim to support exec + catchpoints unless CHILD_INSERT_EXEC_CATCHPOINT is defined + + There are new target_ops vector fields that pertain + only to HPUX. Added the to_require_attach and + to_require_detach fields to exec_ops. These new + fields are ifdef'ed for HPUX_SNAP1. + + * breakpoint.h: + Fix compile error in enum bptype. + + * coff-solib.h: + Fixed a number of macro definitions. SOLIB_LOADED_LIBRARY_PATHNAME, + SOLIB_HAVE_LOAD_EVENT, SOLIB_HAVE_UNLOAD_EVENT, + SOLIB_UNLOADED_LIBRARY_PATHNAME, SOLIB_IN_DYNAMIC_LINKER. These + macros are only meaningful (for now) for SOM. So, all + the macros were defined as error(...), but were used in + conditions. This caused the compile to crap out. I redefined + these (for now) to be 0. + + * procfs.c: + (procfs_create_inferior): fix call to fork_inferior -- need another + parameter. + + * solib.h: + Fixed a number of macro definitions. SOLIB_LOADED_LIBRARY_PATHNAME, + SOLIB_HAVE_LOAD_EVENT, SOLIB_HAVE_UNLOAD_EVENT, + SOLIB_UNLOADED_LIBRARY_PATHNAME, SOLIB_IN_DYNAMIC_LINKER. These + macros are only meaningful (for now) for SOM. So, all + the macros were defined as error(...), but were used in + conditions. This caused the compile to crap out. I redefined + these (for now) to be 0. + + * valops.c: + (search_struct_field): undeclared local variable, "assigned". + (find_rt_vbase_offset): fixed call to value_at + + * value.h: + Fix signature for find_rt_vbase_offset funct decl (missing a param) + Wed Dec 30 17:48:12 1998 Stan Shebs <shebs@andros.cygnus.com> From J.T. Conklin <jtc@redbacknetworks.com>: diff --git a/gdb/acconfig.h b/gdb/acconfig.h index 565e692..10f8cb0 100644 --- a/gdb/acconfig.h +++ b/gdb/acconfig.h @@ -64,6 +64,9 @@ /* Define if your locale.h file contains LC_MESSAGES. */ #undef HAVE_LC_MESSAGES +/* Define if you want to use the full-screen terminal user interface. */ +#undef TUI + /* Define if <proc_service.h> on solaris uses int instead of size_t, and assorted other type changes. */ #undef PROC_SERVICE_IS_OLD diff --git a/gdb/breakpoint.c b/gdb/breakpoint.c index f6d860c..0613bf8 100644 --- a/gdb/breakpoint.c +++ b/gdb/breakpoint.c @@ -41,71 +41,124 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* Prototypes for local functions. */ -static void catch_command_1 PARAMS ((char *, int, int)); +static void +catch_command_1 PARAMS ((char *, int, int)); -static void enable_delete_command PARAMS ((char *, int)); +static void +enable_delete_command PARAMS ((char *, int)); -static void enable_delete_breakpoint PARAMS ((struct breakpoint *)); +static void +enable_delete_breakpoint PARAMS ((struct breakpoint *)); -static void enable_once_command PARAMS ((char *, int)); +static void +enable_once_command PARAMS ((char *, int)); -static void enable_once_breakpoint PARAMS ((struct breakpoint *)); +static void +enable_once_breakpoint PARAMS ((struct breakpoint *)); -static void disable_command PARAMS ((char *, int)); +static void +disable_command PARAMS ((char *, int)); -static void enable_command PARAMS ((char *, int)); +static void +enable_command PARAMS ((char *, int)); -static void map_breakpoint_numbers PARAMS ((char *, - void (*)(struct breakpoint *))); +static void +map_breakpoint_numbers PARAMS ((char *, void (*)(struct breakpoint *))); -static void ignore_command PARAMS ((char *, int)); +static void +ignore_command PARAMS ((char *, int)); -static int breakpoint_re_set_one PARAMS ((char *)); +static int +breakpoint_re_set_one PARAMS ((char *)); -static void clear_command PARAMS ((char *, int)); +static void +clear_command PARAMS ((char *, int)); -static void catch_command PARAMS ((char *, int)); +static void +catch_command PARAMS ((char *, int)); -static struct symtabs_and_lines get_catch_sals PARAMS ((int)); +static void +handle_gnu_4_16_catch_command PARAMS ((char *, int, int)); -static void watch_command PARAMS ((char *, int)); +static struct symtabs_and_lines +get_catch_sals PARAMS ((int)); -static int can_use_hardware_watchpoint PARAMS ((struct value *)); +static void +watch_command PARAMS ((char *, int)); -static void tbreak_command PARAMS ((char *, int)); +static int +can_use_hardware_watchpoint PARAMS ((struct value *)); -static void break_command_1 PARAMS ((char *, int, int)); +void +tbreak_command PARAMS ((char *, int)); -static void mention PARAMS ((struct breakpoint *)); +static void +break_command_1 PARAMS ((char *, int, int)); -struct breakpoint *set_raw_breakpoint PARAMS ((struct symtab_and_line)); +static void +mention PARAMS ((struct breakpoint *)); -static void check_duplicates PARAMS ((CORE_ADDR, asection *)); +struct breakpoint * +set_raw_breakpoint PARAMS ((struct symtab_and_line)); -static void describe_other_breakpoints PARAMS ((CORE_ADDR, asection *)); +static void +check_duplicates PARAMS ((CORE_ADDR, asection *)); -static void breakpoints_info PARAMS ((char *, int)); +static void +describe_other_breakpoints PARAMS ((CORE_ADDR, asection *)); -static void breakpoint_1 PARAMS ((int, int)); +static void +breakpoints_info PARAMS ((char *, int)); -static bpstat bpstat_alloc PARAMS ((struct breakpoint *, bpstat)); +static void +breakpoint_1 PARAMS ((int, int)); -static int breakpoint_cond_eval PARAMS ((char *)); +static bpstat +bpstat_alloc PARAMS ((struct breakpoint *, bpstat)); -static void cleanup_executing_breakpoints PARAMS ((PTR)); +static int +breakpoint_cond_eval PARAMS ((char *)); -static void commands_command PARAMS ((char *, int)); +static void +cleanup_executing_breakpoints PARAMS ((PTR)); -static void condition_command PARAMS ((char *, int)); +static void +commands_command PARAMS ((char *, int)); -static int get_number PARAMS ((char **)); +static void +condition_command PARAMS ((char *, int)); -static int remove_breakpoint PARAMS ((struct breakpoint *)); +static int +get_number PARAMS ((char **)); + +void +set_breakpoint_count PARAMS ((int)); + +#if 0 +static struct breakpoint * +create_temp_exception_breakpoint PARAMS ((CORE_ADDR)); +#endif + +typedef enum { + mark_inserted, + mark_uninserted, +} insertion_state_t; + +static int +remove_breakpoint PARAMS ((struct breakpoint *, insertion_state_t)); static int print_it_normal PARAMS ((bpstat)); +typedef struct { + enum exception_event_kind kind; + int enable; +} args_for_catchpoint_enable; + static int watchpoint_check PARAMS ((char *)); +static struct symtab_and_line * +cover_target_enable_exception_callback PARAMS ((args_for_catchpoint_enable *)); + static int print_it_done PARAMS ((bpstat)); static int print_it_noop PARAMS ((bpstat)); @@ -134,6 +187,16 @@ static void do_enable_breakpoint PARAMS ((struct breakpoint *, enum bpdisp)); /* Prototypes for exported functions. */ +static void +awatch_command PARAMS ((char *, int)); + +static void +do_enable_breakpoint PARAMS ((struct breakpoint *, enum bpdisp)); + +/* If FALSE, gdb will not use hardware support for watchpoints, even + if such is available. */ +static int can_use_hw_watchpoints; + void delete_command PARAMS ((char *, int)); void _initialize_breakpoint PARAMS ((void)); @@ -182,6 +245,18 @@ struct breakpoint *breakpoint_chain; int breakpoint_count; +/* Pointer to current exception event record */ +static struct exception_event_record * current_exception_event; + +/* Indicator of whether exception catchpoints should be nuked + between runs of a program */ +int exception_catchpoints_are_fragile = 0; + +/* Indicator of when exception catchpoints set-up should be + reinitialized -- e.g. when program is re-run */ +int exception_support_initialized = 0; + + /* Set breakpoint count to NUM. */ void @@ -360,8 +435,6 @@ commands_command (arg, from_tty) error ("No breakpoint number %d.", bnum); } -extern int memory_breakpoint_size; /* from mem-break.c */ - /* Like target_read_memory() but if breakpoints are inserted, return the shadow contents instead of the breakpoints themselves. @@ -378,84 +451,96 @@ read_memory_nobpt (memaddr, myaddr, len) { int status; struct breakpoint *b; + CORE_ADDR bp_addr = 0; + int bp_size = 0; - if (memory_breakpoint_size < 0) - /* No breakpoints on this machine. FIXME: This should be - dependent on the debugging target. Probably want - target_insert_breakpoint to return a size, saying how many - bytes of the shadow contents are used, or perhaps have - something like target_xfer_shadow. */ + if (BREAKPOINT_FROM_PC (&bp_addr, &bp_size) == NULL) + /* No breakpoints on this machine. */ return target_read_memory (memaddr, myaddr, len); ALL_BREAKPOINTS (b) { + if (b->type == bp_none) + warning ("attempted to read through apparently deleted breakpoint #%d?\n", b->number); + + /* memory breakpoint? */ if (b->type == bp_watchpoint || b->type == bp_hardware_watchpoint || b->type == bp_read_watchpoint - || b->type == bp_access_watchpoint - || !b->inserted) + || b->type == bp_access_watchpoint) + continue; + /* bp in memory? */ + if (!b->inserted) + continue; + /* Addresses and length of the part of the breakpoint that + we need to copy. */ + /* XXXX The m68k, sh and h8300 have different local and remote + breakpoint values. BREAKPOINT_FROM_PC still manages to + correctly determine the breakpoints memory address and size + for these targets. */ + bp_addr = b->address; + bp_size = 0; + if (BREAKPOINT_FROM_PC (&bp_addr, &bp_size) == NULL) continue; - else if (b->address + memory_breakpoint_size <= memaddr) - /* The breakpoint is entirely before the chunk of memory - we are reading. */ + if (bp_size == 0) + /* bp isn't valid */ continue; - else if (b->address >= memaddr + len) - /* The breakpoint is entirely after the chunk of memory we + if (bp_addr + bp_size <= memaddr) + /* The breakpoint is entirely before the chunk of memory we are reading. */ continue; - else - { - /* Copy the breakpoint from the shadow contents, and recurse - for the things before and after. */ - - /* Addresses and length of the part of the breakpoint that - we need to copy. */ - CORE_ADDR membpt = b->address; - unsigned int bptlen = memory_breakpoint_size; - /* Offset within shadow_contents. */ - int bptoffset = 0; + if (bp_addr >= memaddr + len) + /* The breakpoint is entirely after the chunk of memory we are + reading. */ + continue; + /* Copy the breakpoint from the shadow contents, and recurse for + the things before and after. */ + { + /* Offset within shadow_contents. */ + int bptoffset = 0; - if (membpt < memaddr) - { - /* Only copy the second part of the breakpoint. */ - bptlen -= memaddr - membpt; - bptoffset = memaddr - membpt; - membpt = memaddr; - } - - if (membpt + bptlen > memaddr + len) - { - /* Only copy the first part of the breakpoint. */ - bptlen -= (membpt + bptlen) - (memaddr + len); - } - - memcpy (myaddr + membpt - memaddr, - b->shadow_contents + bptoffset, bptlen); - - if (membpt > memaddr) - { - /* Copy the section of memory before the breakpoint. */ - status = read_memory_nobpt (memaddr, myaddr, membpt - memaddr); - if (status != 0) - return status; - } - - if (membpt + bptlen < memaddr + len) - { - /* Copy the section of memory after the breakpoint. */ - status = read_memory_nobpt - (membpt + bptlen, - myaddr + membpt + bptlen - memaddr, - memaddr + len - (membpt + bptlen)); - if (status != 0) - return status; - } - return 0; - } + if (bp_addr < memaddr) + { + /* Only copy the second part of the breakpoint. */ + bp_size -= memaddr - bp_addr; + bptoffset = memaddr - bp_addr; + bp_addr = memaddr; + } + + if (bp_addr + bp_size > memaddr + len) + { + /* Only copy the first part of the breakpoint. */ + bp_size -= (bp_addr + bp_size) - (memaddr + len); + } + + memcpy (myaddr + bp_addr - memaddr, + b->shadow_contents + bptoffset, bp_size); + + if (bp_addr > memaddr) + { + /* Copy the section of memory before the breakpoint. */ + status = read_memory_nobpt (memaddr, myaddr, bp_addr - memaddr); + if (status != 0) + return status; + } + + if (bp_addr + bp_size < memaddr + len) + { + /* Copy the section of memory after the breakpoint. */ + status = read_memory_nobpt + (bp_addr + bp_size, + myaddr + bp_addr + bp_size - memaddr, + memaddr + len - (bp_addr + bp_size)); + if (status != 0) + return status; + } + return 0; + } } /* Nothing overlaps. Just call read_memory_noerr. */ return target_read_memory (memaddr, myaddr, len); } + /* insert_breakpoints is used when starting or continuing the program. remove_breakpoints is used when the program stops. @@ -469,167 +554,258 @@ insert_breakpoints () int val = 0; int disabled_breaks = 0; + static char message1[] = "Error inserting catchpoint %d:\n"; + static char message[sizeof (message1) + 30]; + + ALL_BREAKPOINTS_SAFE (b, temp) - if (b->type != bp_watchpoint - && b->type != bp_hardware_watchpoint - && b->type != bp_read_watchpoint - && b->type != bp_access_watchpoint - && b->enable != disabled - && b->enable != shlib_disabled - && ! b->inserted - && ! b->duplicate) - { - if (b->type == bp_hardware_breakpoint) - val = target_insert_hw_breakpoint(b->address, b->shadow_contents); - else - { - /* Check to see if breakpoint is in an overlay section; - if so, we should set the breakpoint at the LMA address. - Only if the section is currently mapped should we ALSO - set a break at the VMA address. */ - if (overlay_debugging && b->section && - section_is_overlay (b->section)) - { - CORE_ADDR addr; - - addr = overlay_unmapped_address (b->address, b->section); - val = target_insert_breakpoint (addr, b->shadow_contents); - /* This would be the time to check val, to see if the - breakpoint write to the load address succeeded. - However, this might be an ordinary occurrance, eg. if - the unmapped overlay is in ROM. */ - val = 0; /* in case unmapped address failed */ - if (section_is_mapped (b->section)) - val = target_insert_breakpoint (b->address, - b->shadow_contents); - } - else /* ordinary (non-overlay) address */ - val = target_insert_breakpoint(b->address, b->shadow_contents); - } - if (val) - { - /* Can't set the breakpoint. */ + { + if (b->type != bp_watchpoint + && b->type != bp_hardware_watchpoint + && b->type != bp_read_watchpoint + && b->type != bp_access_watchpoint + && b->type != bp_catch_fork + && b->type != bp_catch_vfork + && b->type != bp_catch_exec + && b->type != bp_catch_throw + && b->type != bp_catch_catch + && b->enable != disabled + && b->enable != shlib_disabled + && b->enable != call_disabled + && ! b->inserted + && ! b->duplicate) + { + if (b->type == bp_hardware_breakpoint) + val = target_insert_hw_breakpoint(b->address, b->shadow_contents); + else + { + /* Check to see if breakpoint is in an overlay section; + if so, we should set the breakpoint at the LMA address. + Only if the section is currently mapped should we ALSO + set a break at the VMA address. */ + if (overlay_debugging && b->section && + section_is_overlay (b->section)) + { + CORE_ADDR addr; + + addr = overlay_unmapped_address (b->address, b->section); + val = target_insert_breakpoint (addr, b->shadow_contents); + /* This would be the time to check val, to see if the + breakpoint write to the load address succeeded. + However, this might be an ordinary occurrance, eg. if + the unmapped overlay is in ROM. */ + val = 0; /* in case unmapped address failed */ + if (section_is_mapped (b->section)) + val = target_insert_breakpoint (b->address, + b->shadow_contents); + } + else /* ordinary (non-overlay) address */ + val = target_insert_breakpoint(b->address, b->shadow_contents); + } + if (val) + { + /* Can't set the breakpoint. */ #if defined (DISABLE_UNSETTABLE_BREAK) - if (DISABLE_UNSETTABLE_BREAK (b->address)) - { - val = 0; - b->enable = shlib_disabled; - if (!disabled_breaks) - { - target_terminal_ours_for_output (); - fprintf_unfiltered (gdb_stderr, - "Cannot insert breakpoint %d:\n", b->number); - printf_filtered ("Temporarily disabling shared library breakpoints:\n"); - } - disabled_breaks = 1; - printf_filtered ("%d ", b->number); - } - else + if (DISABLE_UNSETTABLE_BREAK (b->address)) + { + /* See also: disable_breakpoints_in_shlibs. */ + val = 0; + b->enable = shlib_disabled; + if (!disabled_breaks) + { + target_terminal_ours_for_output (); + fprintf_unfiltered (gdb_stderr, + "Cannot insert breakpoint %d:\n", b->number); + printf_filtered ("Temporarily disabling shared library breakpoints:\n"); + } + disabled_breaks = 1; + printf_filtered ("%d ", b->number); + } + else #endif - { - target_terminal_ours_for_output (); - fprintf_unfiltered (gdb_stderr, "Cannot insert breakpoint %d:\n", b->number); + { + target_terminal_ours_for_output (); + fprintf_unfiltered (gdb_stderr, "Cannot insert breakpoint %d:\n", b->number); #ifdef ONE_PROCESS_WRITETEXT - fprintf_unfiltered (gdb_stderr, - "The same program may be running in another process.\n"); + fprintf_unfiltered (gdb_stderr, + "The same program may be running in another process.\n"); #endif - memory_error (val, b->address); /* which bombs us out */ - } - } - else - b->inserted = 1; - } - else if ((b->type == bp_hardware_watchpoint || - b->type == bp_read_watchpoint || - b->type == bp_access_watchpoint) - && b->enable == enabled - && ! b->inserted - && ! b->duplicate) + memory_error (val, b->address); /* which bombs us out */ + } + } + else + b->inserted = 1; + } + else if (ep_is_exception_catchpoint (b) + && b->enable != disabled + && b->enable != shlib_disabled + && b->enable != call_disabled + && ! b->inserted + && ! b->duplicate) + { - struct frame_info *saved_frame; - int saved_level, within_current_scope; - value_ptr mark = value_mark (); - value_ptr v; - - /* Save the current frame and level so we can restore it after - evaluating the watchpoint expression on its own frame. */ - saved_frame = selected_frame; - saved_level = selected_frame_level; - - /* Determine if the watchpoint is within scope. */ - if (b->exp_valid_block == NULL) - within_current_scope = 1; - else - { - struct frame_info *fi; - - /* There might be no current frame at this moment if we are - resuming from a step over a breakpoint. - Set up current frame before trying to find the watchpoint - frame. */ - get_current_frame (); - fi = find_frame_addr_in_frame_chain (b->watchpoint_frame); - within_current_scope = (fi != NULL); - if (within_current_scope) - select_frame (fi, -1); - } + /* If we get here, we must have a callback mechanism for exception + events -- with g++ style embedded label support, we insert + ordinary breakpoints and not catchpoints. */ + struct symtab_and_line * sal; + args_for_catchpoint_enable args; + sprintf (message, message1, b->number); /* Format possible error message */ + + val = target_insert_breakpoint(b->address, b->shadow_contents); + if (val) + { + /* Couldn't set breakpoint for some reason */ + target_terminal_ours_for_output (); + fprintf_unfiltered (gdb_stderr, + "Cannot insert catchpoint %d; disabling it\n", b->number); + b->enable = disabled; + } + else + { + /* Bp set, now make sure callbacks are enabled */ + args.kind = b->type == bp_catch_catch ? EX_EVENT_CATCH : EX_EVENT_THROW; + args.enable = 1; + sal = catch_errors ((int (*) PARAMS ((char *))) cover_target_enable_exception_callback, + (char *) &args, + message, RETURN_MASK_ALL); + if (sal && (sal != (struct symtab_and_line *) -1)) + { + b->inserted = 1; + } + /* Check if something went wrong; sal == 0 can be ignored */ + if (sal == (struct symtab_and_line *) -1) + { + /* something went wrong */ + target_terminal_ours_for_output (); + fprintf_unfiltered (gdb_stderr, "Cannot insert catchpoint %d; disabling it\n", b->number); + b->enable = disabled; + } + } + } + + else if ((b->type == bp_hardware_watchpoint || + b->type == bp_read_watchpoint || + b->type == bp_access_watchpoint) + && b->enable == enabled + && ! b->inserted + && ! b->duplicate) + { + struct frame_info *saved_frame; + int saved_level, within_current_scope; + value_ptr mark = value_mark (); + value_ptr v; + + /* Save the current frame and level so we can restore it after + evaluating the watchpoint expression on its own frame. */ + saved_frame = selected_frame; + saved_level = selected_frame_level; + + /* Determine if the watchpoint is within scope. */ + if (b->exp_valid_block == NULL) + within_current_scope = 1; + else + { + struct frame_info *fi; + + /* There might be no current frame at this moment if we are + resuming from a step over a breakpoint. + Set up current frame before trying to find the watchpoint + frame. */ + get_current_frame (); + fi = find_frame_addr_in_frame_chain (b->watchpoint_frame); + within_current_scope = (fi != NULL); + if (within_current_scope) + select_frame (fi, -1); + } - if (within_current_scope) - { - /* Evaluate the expression and cut the chain of values - produced off from the value chain. */ - v = evaluate_expression (b->exp); - value_release_to_mark (mark); + if (within_current_scope) + { + /* Evaluate the expression and cut the chain of values + produced off from the value chain. */ + v = evaluate_expression (b->exp); + value_release_to_mark (mark); - b->val_chain = v; - b->inserted = 1; + b->val_chain = v; + b->inserted = 1; - /* Look at each value on the value chain. */ - for ( ; v; v=v->next) - { - /* If it's a memory location, then we must watch it. */ - if (v->lval == lval_memory) - { - int addr, len, type; + /* Look at each value on the value chain. */ + for ( ; v; v=v->next) + { + /* If it's a memory location, then we must watch it. */ + if (v->lval == lval_memory) + { + int addr, len, type; - addr = VALUE_ADDRESS (v) + VALUE_OFFSET (v); - len = TYPE_LENGTH (VALUE_TYPE (v)); - type = 0; - if (b->type == bp_read_watchpoint) - type = 1; - else if (b->type == bp_access_watchpoint) - type = 2; - - val = target_insert_watchpoint (addr, len, type); - if (val == -1) - { - b->inserted = 0; - break; - } - val = 0; - } - } - /* Failure to insert a watchpoint on any memory value in the - value chain brings us here. */ - if (!b->inserted) - warning ("Hardware watchpoint %d: Could not insert watchpoint\n", - b->number); - } - else - { - printf_filtered ("\ + addr = VALUE_ADDRESS (v) + VALUE_OFFSET (v); + len = TYPE_LENGTH (VALUE_TYPE (v)); + type = 0; + if (b->type == bp_read_watchpoint) + type = 1; + else if (b->type == bp_access_watchpoint) + type = 2; + + val = target_insert_watchpoint (addr, len, type); + if (val == -1) + { + b->inserted = 0; + break; + } + val = 0; + } + } + /* Failure to insert a watchpoint on any memory value in the + value chain brings us here. */ + if (!b->inserted) + warning ("Hardware watchpoint %d: Could not insert watchpoint\n", + b->number); + } + else + { + printf_filtered ("\ Hardware watchpoint %d deleted because the program has left the block in\n\ which its expression is valid.\n", b->number); - if (b->related_breakpoint) - b->related_breakpoint->disposition = del_at_next_stop; - b->disposition = del_at_next_stop; - } + if (b->related_breakpoint) + b->related_breakpoint->disposition = del_at_next_stop; + b->disposition = del_at_next_stop; + } - /* Restore the frame and level. */ - select_frame (saved_frame, saved_level); - } + /* Restore the frame and level. */ + if ((saved_frame != selected_frame) || + (saved_level != selected_frame_level)) + select_and_print_frame (saved_frame, saved_level); + } + else if ((b->type == bp_catch_fork + || b->type == bp_catch_vfork + || b->type == bp_catch_exec) + && b->enable == enabled + && ! b->inserted + && ! b->duplicate) + { + val = -1; + switch (b->type) + { + case bp_catch_fork : + val = target_insert_fork_catchpoint (inferior_pid); + break; + case bp_catch_vfork : + val = target_insert_vfork_catchpoint (inferior_pid); + break; + case bp_catch_exec : + val = target_insert_exec_catchpoint (inferior_pid); + break; + } + if (val < 0) + { + target_terminal_ours_for_output (); + fprintf_unfiltered (gdb_stderr, "Cannot insert catchpoint %d:\n", b->number); + } + else + b->inserted = 1; + } + } if (disabled_breaks) printf_filtered ("\n"); + return val; } @@ -644,7 +820,7 @@ remove_breakpoints () { if (b->inserted) { - val = remove_breakpoint (b); + val = remove_breakpoint (b, mark_uninserted); if (val != 0) return val; } @@ -652,17 +828,189 @@ remove_breakpoints () return 0; } +int +reattach_breakpoints (pid) + int pid; +{ + register struct breakpoint *b; + int val; + int saved_inferior_pid = inferior_pid; + + inferior_pid = pid; /* Because remove_breakpoint will use this global. */ + ALL_BREAKPOINTS (b) + { + if (b->inserted) + { + remove_breakpoint (b, mark_inserted); + if (b->type == bp_hardware_breakpoint) + val = target_insert_hw_breakpoint(b->address, b->shadow_contents); + else + val = target_insert_breakpoint(b->address, b->shadow_contents); + if (val != 0) + { + inferior_pid = saved_inferior_pid; + return val; + } + } + } + inferior_pid = saved_inferior_pid; + return 0; +} + +void +update_breakpoints_after_exec () +{ + struct breakpoint * b; + struct breakpoint * temp; + + /* Doing this first prevents the badness of having delete_breakpoint() + write a breakpoint's current "shadow contents" to lift the bp. That + shadow is NOT valid after an exec()! */ + mark_breakpoints_out (); + + ALL_BREAKPOINTS_SAFE (b, temp) + { + /* Solib breakpoints must be explicitly reset after an exec(). */ + if (b->type == bp_shlib_event) + { + delete_breakpoint (b); + continue; + } + + /* Step-resume breakpoints are meaningless after an exec(). */ + if (b->type == bp_step_resume) + { + delete_breakpoint (b); + continue; + } + + /* Ditto the sigtramp handler breakpoints. */ + if (b->type == bp_through_sigtramp) + { + delete_breakpoint (b); + continue; + } + + /* Ditto the exception-handling catchpoints. */ + if ((b->type == bp_catch_catch) || (b->type == bp_catch_throw)) + { + delete_breakpoint (b); + continue; + } + + /* Don't delete an exec catchpoint, because else the inferior + won't stop when it ought! + + Similarly, we probably ought to keep vfork catchpoints, 'cause + on this target, we may not be able to stop when the vfork is seen, + but only when the subsequent exec is seen. (And because deleting + fork catchpoints here but not vfork catchpoints will seem mysterious + to users, keep those too.) + + ??rehrauer: Let's hope that merely clearing out this catchpoint's + target address field, if any, is sufficient to have it be reset + automagically. Certainly on HP-UX that's true. */ + if ((b->type == bp_catch_exec) || + (b->type == bp_catch_vfork) || + (b->type == bp_catch_fork)) + { + b->address = NULL; + continue; + } + + /* bp_finish is a special case. The only way we ought to be able + to see one of these when an exec() has happened, is if the user + caught a vfork, and then said "finish". Ordinarily a finish just + carries them to the call-site of the current callee, by setting + a temporary bp there and resuming. But in this case, the finish + will carry them entirely through the vfork & exec. + + We don't want to allow a bp_finish to remain inserted now. But + we can't safely delete it, 'cause finish_command has a handle to + the bp on a bpstat, and will later want to delete it. There's a + chance (and I've seen it happen) that if we delete the bp_finish + here, that its storage will get reused by the time finish_command + gets 'round to deleting the "use to be a bp_finish" breakpoint. + We really must allow finish_command to delete a bp_finish. + + In the absense of a general solution for the "how do we know it's + safe to delete something others may have handles to?" problem, what + we'll do here is just uninsert the bp_finish, and let finish_command + delete it. + + (We know the bp_finish is "doomed" in the sense that it's momentary, + and will be deleted as soon as finish_command sees the inferior stopped. + So it doesn't matter that the bp's address is probably bogus in the + new a.out, unlike e.g., the solib breakpoints.) */ + if (b->type == bp_finish) + { + continue; + } + + /* Without a symbolic address, we have little hope of the + pre-exec() address meaning the same thing in the post-exec() + a.out. */ + if (b->addr_string == NULL) + { + delete_breakpoint (b); + continue; + } + + /* If this breakpoint has survived the above battery of checks, then + it must have a symbolic address. Be sure that it gets reevaluated + to a target address, rather than reusing the old evaluation. */ + b->address = NULL; + } +} + +int +detach_breakpoints (pid) + int pid; +{ + register struct breakpoint *b; + int val; + int saved_inferior_pid = inferior_pid; + + if (pid == inferior_pid) + error ("Cannot detach breakpoints of inferior_pid"); + + inferior_pid = pid; /* Because remove_breakpoint will use this global. */ + ALL_BREAKPOINTS (b) + { + if (b->inserted) + { + val = remove_breakpoint (b, mark_inserted); + if (val != 0) + { + inferior_pid = saved_inferior_pid; + return val; + } + } + } + inferior_pid = saved_inferior_pid; + return 0; +} static int -remove_breakpoint (b) +remove_breakpoint (b, is) struct breakpoint *b; + insertion_state_t is; { int val; + if (b->type == bp_none) + warning ("attempted to remove apparently deleted breakpoint #%d?\n", b->number); + if (b->type != bp_watchpoint && b->type != bp_hardware_watchpoint && b->type != bp_read_watchpoint - && b->type != bp_access_watchpoint) + && b->type != bp_access_watchpoint + && b->type != bp_catch_fork + && b->type != bp_catch_vfork + && b->type != bp_catch_exec + && b->type != bp_catch_catch + && b->type != bp_catch_throw) + { if (b->type == bp_hardware_breakpoint) val = target_remove_hw_breakpoint(b->address, b->shadow_contents); @@ -693,7 +1041,7 @@ remove_breakpoint (b) } if (val) return val; - b->inserted = 0; + b->inserted = (is == mark_inserted); } else if ((b->type == bp_hardware_watchpoint || b->type == bp_read_watchpoint || @@ -703,7 +1051,7 @@ remove_breakpoint (b) { value_ptr v, n; - b->inserted = 0; + b->inserted = (is == mark_inserted); /* Walk down the saved value chain. */ for (v = b->val_chain; v; v = v->next) { @@ -728,7 +1076,7 @@ remove_breakpoint (b) } } /* Failure to remove any of the hardware watchpoints comes here. */ - if (b->inserted) + if ((is == mark_uninserted) && (b->inserted)) warning ("Hardware watchpoint %d: Could not remove watchpoint\n", b->number); @@ -741,6 +1089,53 @@ remove_breakpoint (b) } b->val_chain = NULL; } + else if ((b->type == bp_catch_fork || + b->type == bp_catch_vfork || + b->type == bp_catch_exec) + && b->enable == enabled + && ! b->duplicate) + { + val = -1; + switch (b->type) + { + case bp_catch_fork: + val = target_remove_fork_catchpoint (inferior_pid); + break; + case bp_catch_vfork : + val = target_remove_vfork_catchpoint (inferior_pid); + break; + case bp_catch_exec : + val = target_remove_exec_catchpoint (inferior_pid); + break; + } + if (val) + return val; + b->inserted = (is == mark_inserted); + } + else if ((b->type == bp_catch_catch || + b->type == bp_catch_throw) + && b->enable == enabled + && ! b->duplicate) + { + + val = target_remove_breakpoint(b->address, b->shadow_contents); + if (val) + return val; + b->inserted = (is == mark_inserted); + } + else if (ep_is_exception_catchpoint (b) + && b->inserted /* sometimes previous insert doesn't happen */ + && b->enable == enabled + && ! b->duplicate) + { + + val = target_remove_breakpoint(b->address, b->shadow_contents); + if (val) + return val; + + b->inserted = (is == mark_inserted); + } + return 0; } @@ -756,12 +1151,22 @@ mark_breakpoints_out () } /* Clear the "inserted" flag in all breakpoints and delete any breakpoints - which should go away between runs of the program. */ + which should go away between runs of the program. + + Plus other such housekeeping that has to be done for breakpoints + between runs. + + Note: this function gets called at the end of a run (by generic_mourn_inferior) + and when a run begins (by init_wait_for_inferior). */ + + void -breakpoint_init_inferior () +breakpoint_init_inferior (context) + enum inf_context context; { register struct breakpoint *b, *temp; + static int warning_needed = 0; ALL_BREAKPOINTS_SAFE (b, temp) { @@ -789,11 +1194,28 @@ breakpoint_init_inferior () if (b->exp_valid_block != NULL) delete_breakpoint (b); break; - default: - break; + /* Likewise for exception catchpoints in dynamic-linked + executables where required */ + if (ep_is_exception_catchpoint (b) && + exception_catchpoints_are_fragile) + { + warning_needed = 1; + delete_breakpoint (b); + } + break; } } + + if (exception_catchpoints_are_fragile) + exception_support_initialized = 0; + + /* Don't issue the warning unless it's really needed... */ + if (warning_needed && (context != inf_exited)) + { + warning ("Exception catchpoints from last run were deleted, you must reinsert them explicitly"); + warning_needed = 0; + } } /* breakpoint_here_p (PC) returns 1 if an enabled breakpoint exists at PC. @@ -808,6 +1230,8 @@ breakpoint_here_p (pc) ALL_BREAKPOINTS (b) if (b->enable == enabled + && b->enable != shlib_disabled + && b->enable != call_disabled && b->address == pc) /* bp is enabled and matches pc */ { if (overlay_debugging && @@ -896,6 +1320,7 @@ breakpoint_thread_match (pc, pid) ALL_BREAKPOINTS (b) if (b->enable != disabled && b->enable != shlib_disabled + && b->enable != call_disabled && b->address == pc && (b->thread == -1 || b->thread == thread)) { @@ -914,6 +1339,44 @@ breakpoint_thread_match (pc, pid) /* bpstat stuff. External routines' interfaces are documented in breakpoint.h. */ +int +ep_is_catchpoint (ep) + struct breakpoint * ep; +{ + return + (ep->type == bp_catch_load) + || (ep->type == bp_catch_unload) + || (ep->type == bp_catch_fork) + || (ep->type == bp_catch_vfork) + || (ep->type == bp_catch_exec) + || (ep->type == bp_catch_catch) + || (ep->type == bp_catch_throw) + + + /* ??rehrauer: Add more kinds here, as are implemented... */ + ; +} + +int +ep_is_shlib_catchpoint (ep) + struct breakpoint * ep; +{ + return + (ep->type == bp_catch_load) + || (ep->type == bp_catch_unload) + ; +} + +int +ep_is_exception_catchpoint (ep) + struct breakpoint * ep; +{ + return + (ep->type == bp_catch_catch) + || (ep->type == bp_catch_throw) + ; +} + /* Clear a bpstat so that it says we are not at any breakpoint. Also free any storage that is part of a bpstat. */ @@ -982,6 +1445,32 @@ bpstat_find_breakpoint(bsp, breakpoint) return NULL; } +/* Find a step_resume breakpoint associated with this bpstat. + (If there are multiple step_resume bp's on the list, this function + will arbitrarily pick one.) + + It is an error to use this function if BPSTAT doesn't contain a + step_resume breakpoint. + + See wait_for_inferior's use of this function. */ +struct breakpoint * +bpstat_find_step_resume_breakpoint (bsp) + bpstat bsp; +{ + if (bsp == NULL) + error ("Internal error (bpstat_find_step_resume_breakpoint)"); + + for (; bsp != NULL; bsp = bsp->next) + { + if ((bsp->breakpoint_at != NULL) && + (bsp->breakpoint_at->type == bp_step_resume)) + return bsp->breakpoint_at; + } + + error ("Internal error (no step_resume breakpoint found)"); +} + + /* Return the breakpoint number of the first breakpoint we are stopped at. *BSP upon return is a bpstat which points to the remaining breakpoints stopped at (but which is not guaranteed to be good for @@ -1055,6 +1544,15 @@ bpstat_do_actions (bsp) old_chain = make_cleanup (cleanup_executing_breakpoints, 0); top: + /* Note that (as of this writing), our callers all appear to + be passing us the address of global stop_bpstat. And, if + our calls to execute_control_command cause the inferior to + proceed, that global (and hence, *bsp) will change. + + We must be careful to not touch *bsp unless the inferior + has not proceeded. */ + + /* This pointer will iterate over the list of bpstat's. */ bs = *bsp; breakpoint_proceeded = 0; @@ -1064,7 +1562,11 @@ top: while (cmd != NULL) { execute_control_command (cmd); - cmd = cmd->next; + + if (breakpoint_proceeded) + break; + else + cmd = cmd->next; } if (breakpoint_proceeded) /* The inferior is proceeded by the command; bomb out now. @@ -1082,7 +1584,21 @@ top: /* This is the normal print_it function for a bpstat. In the future, much of this logic could (should?) be moved to bpstat_stop_status, - by having it set different print_it functions. */ + by having it set different print_it functions. + + Current scheme: When we stop, bpstat_print() is called. + It loops through the bpstat list of things causing this stop, + calling the print_it function for each one. The default + print_it function, used for breakpoints, is print_it_normal(). + (Also see print_it_noop() and print_it_done()). + + Return values from this routine (used by bpstat_print() to + decide what to do): + 1: Means we printed something, and we do *not* desire that + something to be followed by a location. + 0: Means we printed something, and we *do* desire that + something to be followed by a location. + -1: Means we printed nothing. */ static int print_it_normal (bs) @@ -1092,14 +1608,113 @@ print_it_normal (bs) which has since been deleted. */ if (bs->breakpoint_at == NULL || (bs->breakpoint_at->type != bp_breakpoint + && bs->breakpoint_at->type != bp_catch_load + && bs->breakpoint_at->type != bp_catch_unload + && bs->breakpoint_at->type != bp_catch_fork + && bs->breakpoint_at->type != bp_catch_vfork + && bs->breakpoint_at->type != bp_catch_exec + && bs->breakpoint_at->type != bp_catch_catch + && bs->breakpoint_at->type != bp_catch_throw && bs->breakpoint_at->type != bp_hardware_breakpoint && bs->breakpoint_at->type != bp_watchpoint && bs->breakpoint_at->type != bp_read_watchpoint && bs->breakpoint_at->type != bp_access_watchpoint && bs->breakpoint_at->type != bp_hardware_watchpoint)) - return 0; + return -1; - if (bs->breakpoint_at->type == bp_breakpoint || + if (ep_is_shlib_catchpoint (bs->breakpoint_at)) + { + annotate_catchpoint (bs->breakpoint_at->number); + printf_filtered ("\nCatchpoint %d (", bs->breakpoint_at->number); + if (bs->breakpoint_at->type == bp_catch_load) + printf_filtered ("loaded"); + else if (bs->breakpoint_at->type == bp_catch_unload) + printf_filtered ("unloaded"); + printf_filtered (" %s), ", bs->breakpoint_at->triggered_dll_pathname); + return 0; + } + else if (bs->breakpoint_at->type == bp_catch_fork || + bs->breakpoint_at->type == bp_catch_vfork) + { + annotate_catchpoint (bs->breakpoint_at->number); + printf_filtered ("\nCatchpoint %d (", bs->breakpoint_at->number); + if (bs->breakpoint_at->type == bp_catch_fork) + printf_filtered ("forked"); + else if (bs->breakpoint_at->type == bp_catch_vfork) + printf_filtered ("vforked"); + printf_filtered (" process %d), ", bs->breakpoint_at->forked_inferior_pid); + return 0; + } + else if (bs->breakpoint_at->type == bp_catch_exec) + { + annotate_catchpoint (bs->breakpoint_at->number); + printf_filtered ("\nCatchpoint %d (exec'd %s), ", + bs->breakpoint_at->number, + bs->breakpoint_at->exec_pathname); + return 0; + } + else if (bs->breakpoint_at->type == bp_catch_catch) + { + if (current_exception_event && (CURRENT_EXCEPTION_KIND == EX_EVENT_CATCH)) + { + annotate_catchpoint (bs->breakpoint_at->number); + printf_filtered ("\nCatchpoint %d (exception caught), ", bs->breakpoint_at->number); + printf_filtered ("throw location "); + if (CURRENT_EXCEPTION_THROW_PC && CURRENT_EXCEPTION_THROW_LINE) + printf_filtered ("%s:%d", + CURRENT_EXCEPTION_THROW_FILE, + CURRENT_EXCEPTION_THROW_LINE); + else + printf_filtered ("unknown"); + + printf_filtered (", catch location "); + if (CURRENT_EXCEPTION_CATCH_PC && CURRENT_EXCEPTION_CATCH_LINE) + printf_filtered ("%s:%d", + CURRENT_EXCEPTION_CATCH_FILE, + CURRENT_EXCEPTION_CATCH_LINE); + else + printf_filtered ("unknown"); + + printf_filtered ("\n"); + return 1; /* don't bother to print location frame info */ + } + else + { + return -1; /* really throw, some other bpstat will handle it */ + } + } + else if (bs->breakpoint_at->type == bp_catch_throw) + { + if (current_exception_event && (CURRENT_EXCEPTION_KIND == EX_EVENT_THROW)) + { + annotate_catchpoint (bs->breakpoint_at->number); + printf_filtered ("\nCatchpoint %d (exception thrown), ", bs->breakpoint_at->number); + printf_filtered ("throw location "); + if (CURRENT_EXCEPTION_THROW_PC && CURRENT_EXCEPTION_THROW_LINE) + printf_filtered ("%s:%d", + CURRENT_EXCEPTION_THROW_FILE, + CURRENT_EXCEPTION_THROW_LINE); + else + printf_filtered ("unknown"); + + printf_filtered (", catch location "); + if (CURRENT_EXCEPTION_CATCH_PC && CURRENT_EXCEPTION_CATCH_LINE) + printf_filtered ("%s:%d", + CURRENT_EXCEPTION_CATCH_FILE, + CURRENT_EXCEPTION_CATCH_LINE); + else + printf_filtered ("unknown"); + + printf_filtered ("\n"); + return 1; /* don't bother to print location frame info */ + } + else + { + return -1; /* really catch, some other bpstat willhandle it */ + } + } + + else if (bs->breakpoint_at->type == bp_breakpoint || bs->breakpoint_at->type == bp_hardware_breakpoint) { /* I think the user probably only wants to see one breakpoint @@ -1140,10 +1755,25 @@ print_it_normal (bs) return -1; } -/* Print a message indicating what happened. Returns nonzero to - say that only the source line should be printed after this (zero - return means print the frame as well as the source line). */ -/* Currently we always return zero. */ +/* Print a message indicating what happened. + This is called from normal_stop(). + The input to this routine is the head of the bpstat list - a list + of the eventpoints that caused this stop. + This routine calls the "print_it" routine(s) associated + with these eventpoints. This will print (for example) + the "Breakpoint n," part of the output. + The return value of this routine is one of: + + -1: Means we printed nothing + 0: Means we printed something, and expect subsequent + code to print the location. An example is + "Breakpoint 1, " which should be followed by + the location. + 1 : Means we printed something, but there is no need + to also print the location part of the message. + An example is the catch/throw messages, which + don't require a location appended to the end. */ + int bpstat_print (bs) bpstat bs; @@ -1151,7 +1781,7 @@ bpstat_print (bs) int val; if (bs == NULL) - return 0; + return -1; val = (*bs->print_it) (bs); if (val >= 0) @@ -1165,7 +1795,7 @@ bpstat_print (bs) return bpstat_print (bs->next); /* We reached the end of the chain without printing anything. */ - return 0; + return -1; } /* Evaluate the expression EXP and return 1 if value is zero. @@ -1293,6 +1923,15 @@ which its expression is valid.\n", bs->breakpoint_at->number); /* This is used when everything which needs to be printed has already been printed. But we still want to print the frame. */ + +/* Background: When we stop, bpstat_print() is called. + It loops through the bpstat list of things causing this stop, + calling the print_it function for each one. The default + print_it function, used for breakpoints, is print_it_normal(). + Also see print_it_noop() and print_it_done() are the other + two possibilities. See comments in bpstat_print() and + in header of print_it_normal() for more detail. */ + static int print_it_done (bs) bpstat bs; @@ -1301,6 +1940,13 @@ print_it_done (bs) } /* This is used when nothing should be printed for this bpstat entry. */ +/* Background: When we stop, bpstat_print() is called. + It loops through the bpstat list of things causing this stop, + calling the print_it function for each one. The default + print_it function, used for breakpoints, is print_it_normal(). + Also see print_it_noop() and print_it_done() are the other + two possibilities. See comments in bpstat_print() and + in header of print_it_normal() for more detail. */ static int print_it_noop (bs) @@ -1327,9 +1973,7 @@ print_it_noop (bs) several reasons concurrently.) Each element of the chain has valid next, breakpoint_at, - commands, FIXME??? fields. - - */ + commands, FIXME??? fields. */ bpstat bpstat_stop_status (pc, not_a_breakpoint) @@ -1354,14 +1998,20 @@ bpstat_stop_status (pc, not_a_breakpoint) ALL_BREAKPOINTS_SAFE (b, temp) { if (b->enable == disabled - || b->enable == shlib_disabled) + || b->enable == shlib_disabled + || b->enable == call_disabled) continue; if (b->type != bp_watchpoint && b->type != bp_hardware_watchpoint && b->type != bp_read_watchpoint && b->type != bp_access_watchpoint - && b->type != bp_hardware_breakpoint) /* a non-watchpoint bp */ + && b->type != bp_hardware_breakpoint + && b->type != bp_catch_fork + && b->type != bp_catch_vfork + && b->type != bp_catch_exec + && b->type != bp_catch_catch + && b->type != bp_catch_throw) /* a non-watchpoint bp */ if (b->address != bp_addr || /* address doesn't match or */ (overlay_debugging && /* overlay doesn't match */ section_is_overlay (b->section) && @@ -1379,19 +2029,55 @@ bpstat_stop_status (pc, not_a_breakpoint) && not_a_breakpoint) continue; - /* Come here if it's a watchpoint, or if the break address matches */ + /* Is this a catchpoint of a load or unload? If so, did we + get a load or unload of the specified library? If not, + ignore it. */ + if ((b->type == bp_catch_load) +#if defined(SOLIB_HAVE_LOAD_EVENT) + && (!SOLIB_HAVE_LOAD_EVENT(inferior_pid) + || ((b->dll_pathname != NULL) + && (strcmp (b->dll_pathname, SOLIB_LOADED_LIBRARY_PATHNAME(inferior_pid)) != 0))) +#endif + ) + continue; + + if ((b->type == bp_catch_unload) +#if defined(SOLIB_HAVE_UNLOAD_EVENT) + && (!SOLIB_HAVE_UNLOAD_EVENT(inferior_pid) + || ((b->dll_pathname != NULL) + && (strcmp (b->dll_pathname, SOLIB_UNLOADED_LIBRARY_PATHNAME(inferior_pid)) != 0))) +#endif + ) + continue; + + if ((b->type == bp_catch_fork) + && ! target_has_forked (inferior_pid, &b->forked_inferior_pid)) + continue; + + if ((b->type == bp_catch_vfork) + && ! target_has_vforked (inferior_pid, &b->forked_inferior_pid)) + continue; + + if ((b->type == bp_catch_exec) + && ! target_has_execd (inferior_pid, &b->exec_pathname)) + continue; - ++(b->hit_count); + if (ep_is_exception_catchpoint (b) && + !(current_exception_event = target_get_current_exception_event ())) + continue; + + /* Come here if it's a watchpoint, or if the break address matches */ bs = bpstat_alloc (b, bs); /* Alloc a bpstat to explain stop */ + /* Watchpoints may change this, if not found to have triggered. */ bs->stop = 1; bs->print = 1; sprintf (message, message1, b->number); if (b->type == bp_watchpoint || b->type == bp_hardware_watchpoint) { - switch (catch_errors (watchpoint_check, (char *) bs, message, + switch (catch_errors ((int (*) PARAMS ((char *))) watchpoint_check, (char *) bs, message, RETURN_MASK_ALL)) { case WP_DELETED: @@ -1401,6 +2087,7 @@ bpstat_stop_status (pc, not_a_breakpoint) break; case WP_VALUE_CHANGED: /* Stop. */ + ++(b->hit_count); break; case WP_VALUE_NOT_CHANGED: /* Don't stop. */ @@ -1445,7 +2132,7 @@ bpstat_stop_status (pc, not_a_breakpoint) } } if (found) - switch (catch_errors (watchpoint_check, (char *) bs, message, + switch (catch_errors ((int (*) PARAMS ((char *))) watchpoint_check, (char *) bs, message, RETURN_MASK_ALL)) { case WP_DELETED: @@ -1456,6 +2143,7 @@ bpstat_stop_status (pc, not_a_breakpoint) case WP_VALUE_CHANGED: case WP_VALUE_NOT_CHANGED: /* Stop. */ + ++(b->hit_count); break; default: /* Can't happen. */ @@ -1470,12 +2158,19 @@ bpstat_stop_status (pc, not_a_breakpoint) break; } } - else if (DECR_PC_AFTER_BREAK != 0 || must_shift_inst_regs) - real_breakpoint = 1; + else + { + /* By definition, an encountered breakpoint is a triggered + breakpoint. */ + ++(b->hit_count); + + if (DECR_PC_AFTER_BREAK != 0 || must_shift_inst_regs) + real_breakpoint = 1; + } if (b->frame && b->frame != (get_current_frame ())->frame && (b->type == bp_step_resume && - (get_current_frame ())->frame INNER_THAN b->frame)) + (INNER_THAN (get_current_frame ()->frame, b->frame)))) bs->stop = 0; else { @@ -1487,7 +2182,7 @@ bpstat_stop_status (pc, not_a_breakpoint) so that the conditions will have the right context. */ select_frame (get_current_frame (), 0); value_is_zero - = catch_errors (breakpoint_cond_eval, (char *)(b->cond), + = catch_errors ((int (*) PARAMS ((char *))) breakpoint_cond_eval, (char *)(b->cond), "Error in testing breakpoint condition:\n", RETURN_MASK_ALL); /* FIXME-someday, should give breakpoint # */ @@ -1512,7 +2207,9 @@ bpstat_stop_status (pc, not_a_breakpoint) bs->commands = b->commands; if (b->silent) bs->print = 0; - if (bs->commands && STREQ ("silent", bs->commands->line)) + if (bs->commands && + (STREQ ("silent", bs->commands->line) || + (xdb_commands && STREQ ("Q", bs->commands->line)))) { bs->commands = bs->commands->next; bs->print = 0; @@ -1593,6 +2290,9 @@ bpstat_what (bs) /* We hit the shared library event breakpoint. */ shlib_event, + /* We caught a shared library event. */ + catch_shlib_event, + /* This is just used to count how many enums there are. */ class_last }; @@ -1610,6 +2310,7 @@ bpstat_what (bs) #define sr BPSTAT_WHAT_STEP_RESUME #define ts BPSTAT_WHAT_THROUGH_SIGTRAMP #define shl BPSTAT_WHAT_CHECK_SHLIBS +#define shlr BPSTAT_WHAT_CHECK_SHLIBS_RESUME_FROM_HOOK /* "Can't happen." Might want to print an error message. abort() is not out of the question, but chances are GDB is just @@ -1634,20 +2335,22 @@ bpstat_what (bs) table[(int)class_last][(int)BPSTAT_WHAT_LAST] = { /* old action */ - /* kc ss sn sgl slr clr clrs sr ts shl - */ -/*no_effect*/ {kc, ss, sn, sgl, slr, clr, clrs, sr, ts, shl}, -/*wp_silent*/ {ss, ss, sn, ss, ss, ss, ss, sr, ts, shl}, -/*wp_noisy*/ {sn, sn, sn, sn, sn, sn, sn, sr, ts, shl}, -/*bp_nostop*/ {sgl, ss, sn, sgl, slr, clrs, clrs, sr, ts, shl}, -/*bp_silent*/ {ss, ss, sn, ss, ss, ss, ss, sr, ts, shl}, -/*bp_noisy*/ {sn, sn, sn, sn, sn, sn, sn, sr, ts, shl}, -/*long_jump*/ {slr, ss, sn, slr, err, err, err, sr, ts, shl}, -/*long_resume*/ {clr, ss, sn, clrs, err, err, err, sr, ts, shl}, -/*step_resume*/ {sr, sr, sr, sr, sr, sr, sr, sr, ts, shl}, -/*through_sig*/ {ts, ts, ts, ts, ts, ts, ts, ts, ts, shl}, -/*shlib*/ {shl, shl, shl, shl, shl, shl, shl, shl, ts, shl} - }; + /* kc ss sn sgl slr clr clrs sr ts shl shlr + */ +/*no_effect*/ {kc, ss, sn, sgl, slr, clr, clrs, sr, ts, shl, shlr}, +/*wp_silent*/ {ss, ss, sn, ss, ss, ss, ss, sr, ts, shl, shlr}, +/*wp_noisy*/ {sn, sn, sn, sn, sn, sn, sn, sr, ts, shl, shlr}, +/*bp_nostop*/ {sgl, ss, sn, sgl, slr, clrs, clrs, sr, ts, shl, shlr}, +/*bp_silent*/ {ss, ss, sn, ss, ss, ss, ss, sr, ts, shl, shlr}, +/*bp_noisy*/ {sn, sn, sn, sn, sn, sn, sn, sr, ts, shl, shlr}, +/*long_jump*/ {slr, ss, sn, slr, err, err, err, sr, ts, shl, shlr}, +/*long_resume*/ {clr, ss, sn, clrs, err, err, err, sr, ts, shl, shlr}, +/*step_resume*/ {sr, sr, sr, sr, sr, sr, sr, sr, ts, shl, shlr}, +/*through_sig*/ {ts, ts, ts, ts, ts, ts, ts, ts, ts, shl, shlr}, +/*shlib*/ {shl, shl, shl, shl, shl, shl, shl, shl, ts, shl, shlr}, +/*catch_shlib*/ {shlr, shlr, shlr, shlr, shlr, shlr, shlr, shlr, ts, shlr, shlr} + }; + #undef kc #undef ss #undef sn @@ -1659,6 +2362,7 @@ bpstat_what (bs) #undef sr #undef ts #undef shl +#undef shlr enum bpstat_what_main_action current_action = BPSTAT_WHAT_KEEP_CHECKING; struct bpstat_what retval; @@ -1672,6 +2376,9 @@ bpstat_what (bs) continue; switch (bs->breakpoint_at->type) { + case bp_none: + continue; + case bp_breakpoint: case bp_hardware_breakpoint: case bp_until: @@ -1726,6 +2433,43 @@ bpstat_what (bs) case bp_shlib_event: bs_class = shlib_event; break; + case bp_catch_load: + case bp_catch_unload: + /* Only if this catchpoint triggered should we cause the + step-out-of-dld behaviour. Otherwise, we ignore this + catchpoint. */ + if (bs->stop) + bs_class = catch_shlib_event; + else + bs_class = no_effect; + break; + case bp_catch_fork: + case bp_catch_vfork: + case bp_catch_exec: + if (bs->stop) + { + if (bs->print) + bs_class = bp_noisy; + else + bs_class = bp_silent; + } + else + /* There was a catchpoint, but we're not stopping. This requires + no further action. */ + bs_class = no_effect; + break; + case bp_catch_catch: + if (!bs->stop || CURRENT_EXCEPTION_KIND != EX_EVENT_CATCH) + bs_class = bp_nostop; + else if (bs->stop) + bs_class = bs->print ? bp_noisy : bp_silent; + break; + case bp_catch_throw: + if (!bs->stop || CURRENT_EXCEPTION_KIND != EX_EVENT_THROW) + bs_class = bp_nostop; + else if (bs->stop) + bs_class = bs->print ? bp_noisy : bp_silent; + break; case bp_call_dummy: /* Make sure the action is stop (silent or noisy), so infrun.c pops the dummy frame. */ @@ -1752,11 +2496,93 @@ bpstat_should_step () return 1; return 0; } + +/* Nonzero if there are enabled hardware watchpoints. */ +int +bpstat_have_active_hw_watchpoints () +{ + struct breakpoint *b; + ALL_BREAKPOINTS (b) + if ((b->enable == enabled) && + (b->inserted) && + ((b->type == bp_hardware_watchpoint) || + (b->type == bp_read_watchpoint) || + (b->type == bp_access_watchpoint))) + return 1; + return 0; +} + +/* Given a bpstat that records zero or more triggered eventpoints, this + function returns another bpstat which contains only the catchpoints + on that first list, if any. */ +void +bpstat_get_triggered_catchpoints (ep_list, cp_list) + bpstat ep_list; + bpstat * cp_list; +{ + struct bpstats root_bs[1]; + bpstat bs = root_bs; + struct breakpoint * ep; + char * dll_pathname; + + bpstat_clear (cp_list); + root_bs->next = NULL; + + for (; ep_list != NULL; ep_list = ep_list->next ) + { + /* Is this eventpoint a catchpoint? If not, ignore it. */ + ep = ep_list->breakpoint_at; + if (ep == NULL) + break; + if ((ep->type != bp_catch_load) && + (ep->type != bp_catch_unload) && + (ep->type != bp_catch_catch) && + (ep->type != bp_catch_throw)) /* pai: (temp) ADD fork/vfork here!! */ + continue; + + /* Yes; add it to the list. */ + bs = bpstat_alloc (ep, bs); + *bs = *ep_list; + bs->next = NULL; + bs = root_bs->next; + +#if defined(SOLIB_ADD) + /* Also, for each triggered catchpoint, tag it with the name of + the library that caused this trigger. (We copy the name now, + because it's only guaranteed to be available NOW, when the + catchpoint triggers. Clients who may wish to know the name + later must get it from the catchpoint itself.) */ + if (ep->triggered_dll_pathname != NULL) + free (ep->triggered_dll_pathname); + if (ep->type == bp_catch_load) + dll_pathname = SOLIB_LOADED_LIBRARY_PATHNAME (inferior_pid); + else + dll_pathname = SOLIB_UNLOADED_LIBRARY_PATHNAME (inferior_pid); +#else + dll_pathname = NULL; +#endif + if (dll_pathname) + { + ep->triggered_dll_pathname = (char *) xmalloc (strlen (dll_pathname) + 1); + strcpy (ep->triggered_dll_pathname, dll_pathname); + } + else + ep->triggered_dll_pathname = NULL; + } + + *cp_list = bs; +} + /* Print information on breakpoint number BNUM, or -1 if all. If WATCHPOINTS is zero, process only breakpoints; if WATCHPOINTS is nonzero, process only watchpoints. */ +typedef struct { + enum bptype type; + char * description; +} ep_type_description_t; + static void breakpoint_1 (bnum, allflag) int bnum; @@ -1767,14 +2593,33 @@ breakpoint_1 (bnum, allflag) register struct symbol *sym; CORE_ADDR last_addr = (CORE_ADDR)-1; int found_a_breakpoint = 0; - static char *bptypes[] = {"breakpoint", "hw breakpoint", - "until", "finish", "watchpoint", - "hw watchpoint", "read watchpoint", - "acc watchpoint", "longjmp", - "longjmp resume", "step resume", - "sigtramp", - "watchpoint scope", "call dummy", - "shlib events" }; + static ep_type_description_t bptypes[] = + { + {bp_none, "?deleted?"}, + {bp_breakpoint, "breakpoint"}, + {bp_hardware_breakpoint, "hw breakpoint"}, + {bp_until, "until"}, + {bp_finish, "finish"}, + {bp_watchpoint, "watchpoint"}, + {bp_hardware_watchpoint, "hw watchpoint"}, + {bp_read_watchpoint, "read watchpoint"}, + {bp_access_watchpoint, "acc watchpoint"}, + {bp_longjmp, "longjmp"}, + {bp_longjmp_resume, "longjmp resume"}, + {bp_step_resume, "step resume"}, + {bp_through_sigtramp, "sigtramp"}, + {bp_watchpoint_scope, "watchpoint scope"}, + {bp_call_dummy, "call dummy"}, + {bp_shlib_event, "shlib events"}, + {bp_catch_load, "catch load"}, + {bp_catch_unload, "catch unload"}, + {bp_catch_fork, "catch fork"}, + {bp_catch_vfork, "catch vfork"}, + {bp_catch_exec, "catch exec"}, + {bp_catch_catch, "catch catch"}, + {bp_catch_throw, "catch throw"} + }; + static char *bpdisps[] = {"del", "dstp", "dis", "keep"}; static char bpenables[] = "nyn"; char wrap_indent[80]; @@ -1786,6 +2631,13 @@ breakpoint_1 (bnum, allflag) /* We only print out user settable breakpoints unless the allflag is set. */ if (!allflag && b->type != bp_breakpoint + && b->type != bp_catch_load + && b->type != bp_catch_unload + && b->type != bp_catch_fork + && b->type != bp_catch_vfork + && b->type != bp_catch_exec + && b->type != bp_catch_catch + && b->type != bp_catch_throw && b->type != bp_hardware_breakpoint && b->type != bp_watchpoint && b->type != bp_read_watchpoint @@ -1820,7 +2672,11 @@ breakpoint_1 (bnum, allflag) annotate_field (0); printf_filtered ("%-3d ", b->number); annotate_field (1); - printf_filtered ("%-14s ", bptypes[(int)b->type]); + if ((int)b->type > (sizeof(bptypes)/sizeof(bptypes[0]))) + error ("bptypes table does not describe type #%d.", (int)b->type); + if ((int)b->type != bptypes[(int)b->type].type) + error ("bptypes table does not describe type #%d?", (int)b->type); + printf_filtered ("%-14s ", bptypes[(int)b->type].description); annotate_field (2); printf_filtered ("%-4s ", bpdisps[(int)b->disposition]); annotate_field (3); @@ -1841,6 +2697,51 @@ breakpoint_1 (bnum, allflag) annotate_field (5); print_expression (b->exp, gdb_stdout); break; + + case bp_catch_load: + case bp_catch_unload: + /* Field 4, the address, is omitted (which makes the columns + not line up too nicely with the headers, but the effect + is relatively readable). */ + annotate_field (5); + if (b->dll_pathname == NULL) + printf_filtered ("<any library> "); + else + printf_filtered ("library \"%s\" ", b->dll_pathname); + break; + + case bp_catch_fork: + case bp_catch_vfork: + /* Field 4, the address, is omitted (which makes the columns + not line up too nicely with the headers, but the effect + is relatively readable). */ + annotate_field (5); + if (b->forked_inferior_pid != 0) + printf_filtered ("process %d ", b->forked_inferior_pid); + break; + + case bp_catch_exec: + /* Field 4, the address, is omitted (which makes the columns + not line up too nicely with the headers, but the effect + is relatively readable). */ + annotate_field (5); + if (b->exec_pathname != NULL) + printf_filtered ("program \"%s\" ", b->exec_pathname); + break; + case bp_catch_catch: + /* Field 4, the address, is omitted (which makes the columns + not line up too nicely with the headers, but the effect + is relatively readable). */ + annotate_field (5); + printf_filtered ("exception catch "); + break; + case bp_catch_throw: + /* Field 4, the address, is omitted (which makes the columns + not line up too nicely with the headers, but the effect + is relatively readable). */ + annotate_field (5); + printf_filtered ("exception throw "); + break; case bp_breakpoint: case bp_hardware_breakpoint: @@ -1885,6 +2786,9 @@ breakpoint_1 (bnum, allflag) break; } + if (b->thread != -1) + printf_filtered (" thread %d", b->thread ); + printf_filtered ("\n"); if (b->frame) @@ -1914,9 +2818,12 @@ breakpoint_1 (bnum, allflag) if (show_breakpoint_hit_counts && b->hit_count) { /* FIXME should make an annotation for this */ - - printf_filtered ("\tbreakpoint already hit %d time%s\n", - b->hit_count, (b->hit_count == 1 ? "" : "s")); + if (ep_is_catchpoint (b)) + printf_filtered ("\tcatchpoint"); + else + printf_filtered ("\tbreakpoint"); + printf_filtered (" already hit %d time%s\n", + b->hit_count, (b->hit_count == 1 ? "" : "s")); } if (b->ignore_count) @@ -1971,7 +2878,7 @@ breakpoints_info (bnum_exp, from_tty) #if MAINTENANCE_CMDS /* ARGSUSED */ -static void +void maintenance_info_breakpoints (bnum_exp, from_tty) char *bnum_exp; int from_tty; @@ -2013,7 +2920,7 @@ describe_other_breakpoints (pc, section) printf_filtered ("%d%s%s ", b->number, - ((b->enable == disabled || b->enable == shlib_disabled) + ((b->enable == disabled || b->enable == shlib_disabled || b->enable == call_disabled) ? " (disabled)" : ""), (others > 1) ? "," : ((others == 1) ? " and" : "")); } @@ -2057,6 +2964,7 @@ check_duplicates (address, section) ALL_BREAKPOINTS (b) if (b->enable != disabled && b->enable != shlib_disabled + && b->enable != call_disabled && b->address == address && (overlay_debugging == 0 || b->section == section)) { @@ -2100,6 +3008,10 @@ set_raw_breakpoint (sal) b->ignore_count = 0; b->commands = NULL; b->frame = 0; + b->dll_pathname = NULL; + b->triggered_dll_pathname = NULL; + b->forked_inferior_pid = 0; + b->exec_pathname = NULL; /* Add this breakpoint to the end of the chain so that a list of breakpoints will come out in order @@ -2214,6 +3126,38 @@ create_solib_event_breakpoint (address) b->type = bp_shlib_event; } +void +disable_breakpoints_in_shlibs () +{ + struct breakpoint * b; + int disabled_shlib_breaks = 0; + + /* See also: insert_breakpoints, under DISABLE_UNSETTABLE_BREAK. */ + ALL_BREAKPOINTS (b) + { +#if defined (PC_SOLIB) + if (((b->type == bp_breakpoint) || + (b->type == bp_hardware_breakpoint)) && + (b->enable != shlib_disabled) && + (b->enable != call_disabled) && + ! b->duplicate && + PC_SOLIB (b->address)) + { + b->enable = shlib_disabled; + if (!disabled_shlib_breaks) + { + target_terminal_ours_for_output (); + printf_filtered ("Temporarily disabling shared library breakpoints:\n"); + } + disabled_shlib_breaks = 1; + printf_filtered ("%d ", b->number); + } +#endif + } + if (disabled_shlib_breaks) + printf_filtered ("\n"); +} + /* Try to reenable any breakpoints in shared libraries. */ void re_enable_breakpoints_in_shlibs () @@ -2234,6 +3178,192 @@ re_enable_breakpoints_in_shlibs () #endif +static void +create_solib_load_unload_event_breakpoint (hookname, tempflag, dll_pathname, cond_string, bp_kind) + char * hookname; + int tempflag; + char * dll_pathname; + char * cond_string; + enum bptype bp_kind; +{ + struct breakpoint * b; + struct symtabs_and_lines sals; + struct symtab_and_line sal; + struct cleanup * old_chain; + struct cleanup * canonical_strings_chain = NULL; + int i; + char * addr_start = hookname; + char * addr_end = NULL; + char ** canonical = (char **) NULL; + int thread = -1; /* All threads. */ + + /* Set a breakpoint on the specified hook. */ + sals = decode_line_1 (&hookname, 1, (struct symtab *) NULL, 0, &canonical); + addr_end = hookname; + + if (sals.nelts == 0) + { + warning ("Unable to set a breakpoint on dynamic linker callback."); + warning ("Suggest linking with /opt/langtools/lib/end.o."); + warning ("GDB will be unable to track shl_load/shl_unload calls"); + return; + } + if (sals.nelts != 1) + { + warning ("Unable to set a unique breakpoint on dynamic linker callback."); + warning ("GDB will be unable to track shl_load/shl_unload calls"); + return; + } + + /* Make sure that all storage allocated in decode_line_1 gets freed in case + the following errors out. */ + old_chain = make_cleanup (free, sals.sals); + if (canonical != (char **)NULL) + { + make_cleanup (free, canonical); + canonical_strings_chain = make_cleanup (null_cleanup, 0); + if (canonical[0] != NULL) + make_cleanup (free, canonical[0]); + } + + resolve_sal_pc (&sals.sals[0]); + + /* Remove the canonical strings from the cleanup, they are needed below. */ + if (canonical != (char **)NULL) + discard_cleanups (canonical_strings_chain); + + b = set_raw_breakpoint (sals.sals[0]); + set_breakpoint_count (breakpoint_count + 1); + b->number = breakpoint_count; + b->cond = NULL; + b->cond_string = (cond_string == NULL) ? NULL : savestring (cond_string, strlen (cond_string)); + b->thread = thread; + + if (canonical != (char **)NULL && canonical[0] != NULL) + b->addr_string = canonical[0]; + else if (addr_start) + b->addr_string = savestring (addr_start, addr_end - addr_start); + + b->enable = enabled; + b->disposition = tempflag ? del : donttouch; + + if (dll_pathname == NULL) + b->dll_pathname = NULL; + else + { + b->dll_pathname = (char *) xmalloc (strlen (dll_pathname) + 1); + strcpy (b->dll_pathname, dll_pathname); + } + b->type = bp_kind; + + mention (b); + do_cleanups (old_chain); +} + +void +create_solib_load_event_breakpoint (hookname, tempflag, dll_pathname, cond_string) + char * hookname; + int tempflag; + char * dll_pathname; + char * cond_string; +{ + create_solib_load_unload_event_breakpoint (hookname, + tempflag, + dll_pathname, + cond_string, + bp_catch_load); +} + +void +create_solib_unload_event_breakpoint (hookname, tempflag, dll_pathname, cond_string) + char * hookname; + int tempflag; + char * dll_pathname; + char * cond_string; +{ + create_solib_load_unload_event_breakpoint (hookname, + tempflag, + dll_pathname, + cond_string, + bp_catch_unload); +} + +static void +create_fork_vfork_event_catchpoint (tempflag, cond_string, bp_kind) + int tempflag; + char * cond_string; + enum bptype bp_kind; +{ + struct symtab_and_line sal; + struct breakpoint * b; + int thread = -1; /* All threads. */ + + INIT_SAL(&sal); + sal.pc = 0; + sal.symtab = NULL; + sal.line = 0; + + b = set_raw_breakpoint (sal); + set_breakpoint_count (breakpoint_count + 1); + b->number = breakpoint_count; + b->cond = NULL; + b->cond_string = (cond_string == NULL) ? NULL : savestring (cond_string, strlen (cond_string)); + b->thread = thread; + b->addr_string = NULL; + b->enable = enabled; + b->disposition = tempflag ? del : donttouch; + b->forked_inferior_pid = 0; + + b->type = bp_kind; + + mention (b); +} + +void +create_fork_event_catchpoint (tempflag, cond_string) + int tempflag; + char * cond_string; +{ + create_fork_vfork_event_catchpoint (tempflag, cond_string, bp_catch_fork); +} + +void +create_vfork_event_catchpoint (tempflag, cond_string) + int tempflag; + char * cond_string; +{ + create_fork_vfork_event_catchpoint (tempflag, cond_string, bp_catch_vfork); +} + +void +create_exec_event_catchpoint (tempflag, cond_string) + int tempflag; + char * cond_string; +{ + struct symtab_and_line sal; + struct breakpoint * b; + int thread = -1; /* All threads. */ + + INIT_SAL(&sal); + sal.pc = 0; + sal.symtab = NULL; + sal.line = 0; + + b = set_raw_breakpoint (sal); + set_breakpoint_count (breakpoint_count + 1); + b->number = breakpoint_count; + b->cond = NULL; + b->cond_string = (cond_string == NULL) ? NULL : savestring (cond_string, strlen (cond_string)); + b->thread = thread; + b->addr_string = NULL; + b->enable = enabled; + b->disposition = tempflag ? del : donttouch; + + b->type = bp_catch_exec; + + mention (b); +} + static int hw_breakpoint_used_count() { @@ -2277,8 +3407,7 @@ hw_watchpoint_used_count(type, other_type_used) breakpoint at the target of the jmp_buf. FIXME - This ought to be done by setting a temporary breakpoint that gets - deleted automatically... -*/ + deleted automatically... */ void set_longjmp_resume_breakpoint(pc, frame) @@ -2301,6 +3430,47 @@ set_longjmp_resume_breakpoint(pc, frame) } } +void +disable_watchpoints_before_interactive_call_start () +{ + struct breakpoint * b; + + ALL_BREAKPOINTS (b) + { + if (((b->type == bp_watchpoint) + || (b->type == bp_hardware_watchpoint) + || (b->type == bp_read_watchpoint) + || (b->type == bp_access_watchpoint) + || ep_is_exception_catchpoint (b)) + && (b->enable == enabled)) + { + b->enable = call_disabled; + check_duplicates (b->address, b->section); + } + } +} + +void +enable_watchpoints_after_interactive_call_stop () +{ + struct breakpoint * b; + + ALL_BREAKPOINTS (b) + { + if (((b->type == bp_watchpoint) + || (b->type == bp_hardware_watchpoint) + || (b->type == bp_read_watchpoint) + || (b->type == bp_access_watchpoint) + || ep_is_exception_catchpoint (b)) + && (b->enable == call_disabled)) + { + b->enable = enabled; + check_duplicates (b->address, b->section); + } + } +} + + /* Set a breakpoint that will evaporate an end of command at address specified by SAL. Restrict it to frame FRAME if FRAME is nonzero. */ @@ -2346,6 +3516,9 @@ mention (b) switch (b->type) { + case bp_none: + printf_filtered ("(apparently deleted?) Eventpoint %d: ", b->number); + break; case bp_watchpoint: printf_filtered ("Watchpoint %d: ", b->number); print_expression (b->exp, gdb_stdout); @@ -2359,7 +3532,7 @@ mention (b) print_expression (b->exp, gdb_stdout); break; case bp_access_watchpoint: - printf_filtered ("Hardware access(read/write) watchpoint %d: ",b->number); + printf_filtered ("Hardware access (read/write) watchpoint %d: ",b->number); print_expression (b->exp, gdb_stdout); break; case bp_breakpoint: @@ -2370,6 +3543,30 @@ mention (b) printf_filtered ("Hardware assisted breakpoint %d", b->number); say_where = 1; break; + case bp_catch_load: + case bp_catch_unload: + printf_filtered ("Catchpoint %d (%s %s)", + b->number, + (b->type == bp_catch_load) ? "load" : "unload", + (b->dll_pathname != NULL) ? b->dll_pathname : "<any library>"); + break; + case bp_catch_fork: + case bp_catch_vfork: + printf_filtered ("Catchpoint %d (%s)", + b->number, + (b->type == bp_catch_fork) ? "fork" : "vfork"); + break; + case bp_catch_exec: + printf_filtered ("Catchpoint %d (exec)", + b->number); + break; + case bp_catch_catch: + case bp_catch_throw: + printf_filtered ("Catchpoint %d (%s)", + b->number, + (b->type == bp_catch_catch) ? "catch" : "throw"); + break; + case bp_until: case bp_finish: case bp_longjmp: @@ -2391,6 +3588,8 @@ mention (b) if (b->source_file) printf_filtered (": file %s, line %d.", b->source_file, b->line_number); + TUIDO(((TuiOpaqueFuncPtr)tui_vAllSetHasBreakAt, b, 1)); + TUIDO(((TuiOpaqueFuncPtr)tuiUpdateAllExecInfos)); } printf_filtered ("\n"); } @@ -2497,6 +3696,24 @@ break_command_1 (arg, flag, from_tty) int toklen; resolve_sal_pc (&sals.sals[i]); + + /* It's possible for the PC to be nonzero, but still an illegal + value on some targets. + + For example, on HP-UX if you start gdb, and before running the + inferior you try to set a breakpoint on a shared library function + "foo" where the inferior doesn't call "foo" directly but does + pass its address to another function call, then we do find a + minimal symbol for the "foo", but it's address is invalid. + (Appears to be an index into a table that the loader sets up + when the inferior is run.) + + Give the target a chance to bless sals.sals[i].pc before we + try to make a breakpoint for it. */ + if (PC_REQUIRES_RUN_BEFORE_USE(sals.sals[i].pc)) + { + error ("Cannot break on %s without a running program.", addr_start); + } tok = arg; @@ -2588,6 +3805,174 @@ break_command_1 (arg, flag, from_tty) do_cleanups (old_chain); } +static void +break_at_finish_at_depth_command_1 (arg, flag, from_tty) + char *arg; + int flag; + int from_tty; +{ + struct frame_info *frame; + CORE_ADDR low, high, selected_pc = 0; + char *extra_args, *level_arg, *addr_string; + int extra_args_len = 0, if_arg = 0; + + if (!arg || + (arg[0] == 'i' && arg[1] == 'f' && (arg[2] == ' ' || arg[2] == '\t'))) + { + + if (default_breakpoint_valid) + { + if (selected_frame) + { + selected_pc = selected_frame->pc; + if (arg) + if_arg = 1; + } + else + error ("No selected frame."); + } + else + error ("No default breakpoint address now."); + } + else + { + extra_args = strchr (arg, ' '); + if (extra_args) + { + extra_args++; + extra_args_len = strlen (extra_args); + level_arg = (char *) xmalloc (extra_args - arg); + strncpy (level_arg, arg, extra_args - arg - 1); + level_arg[extra_args - arg - 1] = '\0'; + } + else + { + level_arg = (char *) xmalloc (strlen (arg) + 1); + strcpy (level_arg, arg); + } + + frame = parse_frame_specification (level_arg); + if (frame) + selected_pc = frame->pc; + else + selected_pc = 0; + } + if (if_arg) + { + extra_args = arg; + extra_args_len = strlen (arg); + } + + if (selected_pc) + { + if (find_pc_partial_function(selected_pc, (char **)NULL, &low, &high)) + { + addr_string = (char *) xmalloc (26 + extra_args_len); + if (extra_args_len) + sprintf (addr_string, "*0x%x %s", high, extra_args); + else + sprintf (addr_string, "*0x%x", high); + break_command_1 (addr_string, flag, from_tty); + free (addr_string); + } + else + error ("No function contains the specified address"); + } + else + error ("Unable to set breakpoint at procedure exit"); +} + + +static void +break_at_finish_command_1 (arg, flag, from_tty) + char *arg; + int flag; + int from_tty; +{ + char *addr_string, *break_string, *beg_addr_string; + CORE_ADDR low, high; + struct symtabs_and_lines sals; + struct symtab_and_line sal; + struct cleanup *old_chain; + char *extra_args; + int extra_args_len = 0; + int i, if_arg = 0; + + if (!arg || + (arg[0] == 'i' && arg[1] == 'f' && (arg[2] == ' ' || arg[2] == '\t'))) + { + if (default_breakpoint_valid) + { + if (selected_frame) + { + addr_string = (char *) xmalloc (15); + sprintf (addr_string, "*0x%x", selected_frame->pc); + if (arg) + if_arg = 1; + } + else + error ("No selected frame."); + } + else + error ("No default breakpoint address now."); + } + else + { + addr_string = (char *) xmalloc (strlen (arg) + 1); + strcpy (addr_string, arg); + } + + if (if_arg) + { + extra_args = arg; + extra_args_len = strlen (arg); + } + else + if (arg) + { + /* get the stuff after the function name or address */ + extra_args = strchr (arg, ' '); + if (extra_args) + { + extra_args++; + extra_args_len = strlen (extra_args); + } + } + + sals.sals = NULL; + sals.nelts = 0; + + beg_addr_string = addr_string; + sals = decode_line_1 (&addr_string, 1, (struct symtab *)NULL, 0, + (char ***)NULL); + + free (beg_addr_string); + old_chain = make_cleanup (free, sals.sals); + for (i = 0; (i < sals.nelts); i++) + { + sal = sals.sals[i]; + if (find_pc_partial_function (sal.pc, (char **)NULL, &low, &high)) + { + break_string = (char *) xmalloc (extra_args_len + 26); + if (extra_args_len) + sprintf (break_string, "*0x%x %s", high, extra_args); + else + sprintf (break_string, "*0x%x", high); + break_command_1 (break_string, flag, from_tty); + free(break_string); + } + else + error ("No function contains the specified address"); + } + if (sals.nelts > 1) + { + printf_filtered ("Multiple breakpoints were set.\n"); + printf_filtered ("Use the \"delete\" command to delete unwanted breakpoints.\n"); + } + do_cleanups(old_chain); +} + + /* Helper function for break_command_1 and disassemble_command. */ void @@ -2646,7 +4031,23 @@ break_command (arg, from_tty) break_command_1 (arg, 0, from_tty); } -static void +void +break_at_finish_command (arg, from_tty) + char *arg; + int from_tty; +{ + break_at_finish_command_1 (arg, 0, from_tty); +} + +void +break_at_finish_at_depth_command (arg, from_tty) + char *arg; + int from_tty; +{ + break_at_finish_at_depth_command_1 (arg, 0, from_tty); +} + +void tbreak_command (arg, from_tty) char *arg; int from_tty; @@ -2654,6 +4055,14 @@ tbreak_command (arg, from_tty) break_command_1 (arg, BP_TEMPFLAG, from_tty); } +void +tbreak_at_finish_command (arg, from_tty) + char *arg; + int from_tty; +{ + break_at_finish_command_1 (arg, BP_TEMPFLAG, from_tty); +} + static void hbreak_command (arg, from_tty) char *arg; @@ -2670,9 +4079,87 @@ thbreak_command (arg, from_tty) break_command_1 (arg, (BP_TEMPFLAG | BP_HARDWAREFLAG), from_tty); } +static void +stop_command (arg, from_tty) + char *arg; + int from_tty; +{ + printf_filtered ("Specify the type of breakpoint to set.\n\ +Usage: stop in <function | address>\n\ + stop at <line>\n"); +} + +static void +stopin_command (arg, from_tty) + char *arg; + int from_tty; +{ + int badInput = 0; + + if (arg == (char *)NULL) + badInput = 1; + else if (*arg != '*') + { + char *argptr = arg; + int hasColon = 0; + + /* look for a ':'. If this is a line number specification, then say + it is bad, otherwise, it should be an address or function/method + name */ + while (*argptr && !hasColon) + { + hasColon = (*argptr == ':'); + argptr++; + } + + if (hasColon) + badInput = (*argptr != ':'); /* Not a class::method */ + else + badInput = isdigit(*arg); /* a simple line number */ + } + + if (badInput) + printf_filtered("Usage: stop in <function | address>\n"); + else + break_command_1 (arg, 0, from_tty); +} + +static void +stopat_command (arg, from_tty) + char *arg; + int from_tty; +{ + int badInput = 0; + + if (arg == (char *)NULL || *arg == '*') /* no line number */ + badInput = 1; + else + { + char *argptr = arg; + int hasColon = 0; + + /* look for a ':'. If there is a '::' then get out, otherwise + it is probably a line number. */ + while (*argptr && !hasColon) + { + hasColon = (*argptr == ':'); + argptr++; + } + + if (hasColon) + badInput = (*argptr == ':'); /* we have class::method */ + else + badInput = !isdigit(*arg); /* not a line number */ + } + + if (badInput) + printf_filtered("Usage: stop at <line>\n"); + else + break_command_1 (arg, 0, from_tty); +} + /* ARGSUSED */ -/* accessflag: 0: watch write, 1: watch read, 2: watch access(read or write) -*/ +/* accessflag: 0: watch write, 1: watch read, 2: watch access(read or write) */ static void watch_command_1 (arg, accessflag, from_tty) char *arg; @@ -2745,6 +4232,28 @@ watch_command_1 (arg, accessflag, from_tty) if (target_resources_ok < 0 && bp_type != bp_hardware_watchpoint) error ("Target resources have been allocated for other types of watchpoints."); } + +#if defined(HPUXHPPA) + /* ??rehrauer: DTS #CHFts23014 notes that on HP-UX if you set a h/w + watchpoint before the "run" command, the inferior dies with a e.g., + SIGILL once you start it. I initially believed this was due to a + bad interaction between page protection traps and the initial + startup sequence by the dynamic linker. + + However, I tried avoiding that by having HP-UX's implementation of + TARGET_CAN_USE_HW_WATCHPOINT return FALSE if there was no inferior_pid + yet, which forced slow watches before a "run" or "attach", and it + still fails somewhere in the startup code. + + Until I figure out what's happening, I'm disallowing watches altogether + before the "run" or "attach" command. We'll tell the user they must + set watches after getting the program started. */ + if (! target_has_execution) + { + warning ("can't do that without a running program; try \"break main\", \"run\" first"); + return; + } +#endif /* HPUXHPPA */ /* Now set up the breakpoint. */ b = set_raw_breakpoint (sal); @@ -2818,11 +4327,20 @@ watch_command_1 (arg, accessflag, from_tty) in hardware. If the watchpoint can not be handled in hardware return zero. */ +#if !defined(TARGET_REGION_SIZE_OK_FOR_HW_WATCHPOINT) +#define TARGET_REGION_SIZE_OK_FOR_HW_WATCHPOINT(byte_size) \ + ((byte_size) <= (REGISTER_SIZE)) +#endif + static int can_use_hardware_watchpoint (v) struct value *v; { int found_memory_cnt = 0; + + /* Did the user specifically forbid us to use hardware watchpoints? */ + if (! can_use_hw_watchpoints) + return 0; /* Make sure all the intermediate values are in memory. Also make sure we found at least one memory expression. Guards against watch 0x12345, @@ -2832,7 +4350,7 @@ can_use_hardware_watchpoint (v) { if (v->lval == lval_memory) { - if (TYPE_LENGTH (VALUE_TYPE (v)) <= REGISTER_SIZE) + if (TARGET_REGION_SIZE_OK_FOR_HW_WATCHPOINT (TYPE_LENGTH (VALUE_TYPE (v)))) found_memory_cnt++; } else if (v->lval != not_lval && v->modifiable == 0) @@ -2952,7 +4470,10 @@ struct sal_chain struct symtab_and_line sal; }; -#if 0 +/* Not really used -- invocation in handle_gnu_4_16_catch_command + had been commented out in the v.4.16 sources, and stays + disabled there now because "catch NAME" syntax isn't allowed. + pai/1997-07-11 */ /* This isn't used; I don't know what it was for. */ /* For each catch clause identified in ARGS, run FUNCTION with that clause as an argument. */ @@ -3012,7 +4533,6 @@ map_catch_names (args, function) while (*p == ' ' || *p == '\t') p++; } } -#endif /* 0 */ /* This shares a lot of code with `print_frame_label_vars' from stack.c. */ @@ -3128,10 +4648,393 @@ get_catch_sals (this_level_only) return sals; } +static void +ep_skip_leading_whitespace (s) + char ** s; +{ + if ((s == NULL) || (*s == NULL)) + return; + while (isspace(**s)) + *s += 1; +} + +/* This function examines a string, and attempts to find a token + that might be an event name in the leading characters. If a + possible match is found, a pointer to the last character of + the token is returned. Else, NULL is returned. */ +static char * +ep_find_event_name_end (arg) + char * arg; +{ + char * s = arg; + char * event_name_end = NULL; + + /* If we could depend upon the presense of strrpbrk, we'd use that... */ + if (arg == NULL) + return NULL; + + /* We break out of the loop when we find a token delimiter. + Basically, we're looking for alphanumerics and underscores; + anything else delimites the token. */ + while (*s != '\0') + { + if (! isalnum(*s) && (*s != '_')) + break; + event_name_end = s; + s++; + } + + return event_name_end; +} + + +/* This function attempts to parse an optional "if <cond>" clause + from the arg string. If one is not found, it returns NULL. + + Else, it returns a pointer to the condition string. (It does not + attempt to evaluate the string against a particular block.) And, + it updates arg to point to the first character following the parsed + if clause in the arg string. */ +static char * +ep_parse_optional_if_clause (arg) + char ** arg; +{ + char * cond_string; + + if (((*arg)[0] != 'i') || ((*arg)[1] != 'f') || !isspace((*arg)[2])) + return NULL; + + /* Skip the "if" keyword. */ + (*arg) += 2; + + /* Skip any extra leading whitespace, and record the start of the + condition string. */ + ep_skip_leading_whitespace (arg); + cond_string = *arg; + + /* Assume that the condition occupies the remainder of the arg string. */ + (*arg) += strlen (cond_string); + + return cond_string; +} + +/* This function attempts to parse an optional filename from the arg + string. If one is not found, it returns NULL. + + Else, it returns a pointer to the parsed filename. (This function + makes no attempt to verify that a file of that name exists, or is + accessible.) And, it updates arg to point to the first character + following the parsed filename in the arg string. + + Note that clients needing to preserve the returned filename for + future access should copy it to their own buffers. */ +static char * +ep_parse_optional_filename (arg) + char ** arg; +{ + static char filename [1024]; + char * arg_p = *arg; + int i; + char c; + + if ((*arg_p == '\0') || isspace (*arg_p)) + return NULL; + + for (i=0; ; i++) + { + c = *arg_p; + if (isspace (c)) + c = '\0'; + filename[i] = c; + if (c == '\0') + break; + arg_p++; + } + *arg = arg_p; + + return filename; +} + +/* Commands to deal with catching events, such as signals, exceptions, + process start/exit, etc. */ + +typedef enum {catch_fork, catch_vfork} catch_fork_kind; + +static void +catch_fork_command_1 (fork_kind, arg, tempflag, from_tty) + catch_fork_kind fork_kind; + char * arg; + int tempflag; + int from_tty; +{ + char * cond_string = NULL; + + ep_skip_leading_whitespace (&arg); + + /* The allowed syntax is: + catch [v]fork + catch [v]fork if <cond> + + First, check if there's an if clause. */ + cond_string = ep_parse_optional_if_clause (&arg); + + if ((*arg != '\0') && !isspace (*arg)) + error ("Junk at end of arguments."); + + /* If this target supports it, create a fork or vfork catchpoint + and enable reporting of such events. */ + switch (fork_kind) { + case catch_fork : + create_fork_event_catchpoint (tempflag, cond_string); + break; + case catch_vfork : + create_vfork_event_catchpoint (tempflag, cond_string); + break; + default : + error ("unsupported or unknown fork kind; cannot catch it"); + break; + } +} + +static void +catch_exec_command_1 (arg, tempflag, from_tty) + char * arg; + int tempflag; + int from_tty; +{ + char * cond_string = NULL; + + ep_skip_leading_whitespace (&arg); + + /* The allowed syntax is: + catch exec + catch exec if <cond> + + First, check if there's an if clause. */ + cond_string = ep_parse_optional_if_clause (&arg); + + if ((*arg != '\0') && !isspace (*arg)) + error ("Junk at end of arguments."); + + /* If this target supports it, create an exec catchpoint + and enable reporting of such events. */ + create_exec_event_catchpoint (tempflag, cond_string); +} + +#if defined(SOLIB_ADD) +static void +catch_load_command_1 (arg, tempflag, from_tty) + char * arg; + int tempflag; + int from_tty; +{ + char * dll_pathname = NULL; + char * cond_string = NULL; + + ep_skip_leading_whitespace (&arg); + + /* The allowed syntax is: + catch load + catch load if <cond> + catch load <filename> + catch load <filename> if <cond> + + The user is not allowed to specify the <filename> after an + if clause. + + We'll ignore the pathological case of a file named "if". + + First, check if there's an if clause. If so, then there + cannot be a filename. */ + cond_string = ep_parse_optional_if_clause (&arg); + + /* If there was an if clause, then there cannot be a filename. + Else, there might be a filename and an if clause. */ + if (cond_string == NULL) + { + dll_pathname = ep_parse_optional_filename (&arg); + ep_skip_leading_whitespace (&arg); + cond_string = ep_parse_optional_if_clause (&arg); + } + + if ((*arg != '\0') && !isspace (*arg)) + error ("Junk at end of arguments."); + + /* Create a load breakpoint that only triggers when a load of + the specified dll (or any dll, if no pathname was specified) + occurs. */ + SOLIB_CREATE_CATCH_LOAD_HOOK (inferior_pid, tempflag, dll_pathname, cond_string); +} + +static void +catch_unload_command_1 (arg, tempflag, from_tty) + char * arg; + int tempflag; + int from_tty; +{ + char * dll_pathname = NULL; + char * cond_string = NULL; + + ep_skip_leading_whitespace (&arg); + + /* The allowed syntax is: + catch unload + catch unload if <cond> + catch unload <filename> + catch unload <filename> if <cond> + + The user is not allowed to specify the <filename> after an + if clause. + + We'll ignore the pathological case of a file named "if". + + First, check if there's an if clause. If so, then there + cannot be a filename. */ + cond_string = ep_parse_optional_if_clause (&arg); + + /* If there was an if clause, then there cannot be a filename. + Else, there might be a filename and an if clause. */ + if (cond_string == NULL) + { + dll_pathname = ep_parse_optional_filename (&arg); + ep_skip_leading_whitespace (&arg); + cond_string = ep_parse_optional_if_clause (&arg); + } + + if ((*arg != '\0') && !isspace (*arg)) + error ("Junk at end of arguments."); + + /* Create an unload breakpoint that only triggers when an unload of + the specified dll (or any dll, if no pathname was specified) + occurs. */ + SOLIB_CREATE_CATCH_UNLOAD_HOOK (inferior_pid, tempflag, dll_pathname, cond_string); +} +#endif /* SOLIB_ADD */ + /* Commands to deal with catching exceptions. */ +/* Set a breakpoint at the specified callback routine for an + exception event callback */ + static void -catch_command_1 (arg, tempflag, from_tty) +create_exception_catchpoint (tempflag, cond_string, ex_event, sal) + int tempflag; + char * cond_string; + enum exception_event_kind ex_event; + struct symtab_and_line * sal; +{ + struct breakpoint * b; + int i; + int thread = -1; /* All threads. */ + + if (!sal) /* no exception support? */ + return; + + b = set_raw_breakpoint (*sal); + set_breakpoint_count (breakpoint_count + 1); + b->number = breakpoint_count; + b->cond = NULL; + b->cond_string = (cond_string == NULL) ? NULL : savestring (cond_string, strlen (cond_string)); + b->thread = thread; + b->addr_string = NULL; + b->enable = enabled; + b->disposition = tempflag ? del : donttouch; + switch (ex_event) + { + case EX_EVENT_THROW: + b->type = bp_catch_throw; + break; + case EX_EVENT_CATCH: + b->type = bp_catch_catch; + break; + default: /* error condition */ + b->type = bp_none; + b->enable = disabled; + error ("Internal error -- invalid catchpoint kind"); + } + mention (b); +} + +/* Deal with "catch catch" and "catch throw" commands */ + +static void +catch_exception_command_1 (ex_event, arg, tempflag, from_tty) + enum exception_event_kind ex_event; + char * arg; + int tempflag; + int from_tty; +{ + char * cond_string = NULL; + struct symtab_and_line * sal = NULL; + + ep_skip_leading_whitespace (&arg); + + cond_string = ep_parse_optional_if_clause (&arg); + + if ((*arg != '\0') && !isspace (*arg)) + error ("Junk at end of arguments."); + + if ((ex_event != EX_EVENT_THROW) && + (ex_event != EX_EVENT_CATCH)) + error ("Unsupported or unknown exception event; cannot catch it"); + + /* See if we can find a callback routine */ + sal = target_enable_exception_callback (ex_event, 1); + + if (sal) + { + /* We have callbacks from the runtime system for exceptions. + Set a breakpoint on the sal found, if no errors */ + if (sal != (struct symtab_and_line *) -1) + create_exception_catchpoint (tempflag, cond_string, ex_event, sal); + else + return; /* something went wrong with setting up callbacks */ + } + else + { + /* No callbacks from runtime system for exceptions. + Try GNU C++ exception breakpoints using labels in debug info. */ + if (ex_event == EX_EVENT_CATCH) + { + handle_gnu_4_16_catch_command (arg, tempflag, from_tty); + } + else if (ex_event == EX_EVENT_THROW) + { + /* Set a breakpoint on __raise_exception () */ + + fprintf_filtered (gdb_stderr, "Unsupported with this platform/compiler combination.\n"); + fprintf_filtered (gdb_stderr, "Perhaps you can achieve the effect you want by setting\n"); + fprintf_filtered (gdb_stderr, "a breakpoint on __raise_exception().\n"); + } + } +} + +/* Cover routine to allow wrapping target_enable_exception_catchpoints + inside a catch_errors */ + +static struct symtab_and_line * +cover_target_enable_exception_callback (args) + args_for_catchpoint_enable * args; +{ + target_enable_exception_callback (args->kind, args->enable); +} + + + +/* This is the original v.4.16 and earlier version of the + catch_command_1() function. Now that other flavours of "catch" + have been introduced, and since exception handling can be handled + in other ways (through target ops) also, this is used only for the + GNU C++ exception handling system. + Note: Only the "catch" flavour of GDB 4.16 is handled here. The + "catch NAME" is now no longer allowed in catch_command_1(). Also, + there was no code in GDB 4.16 for "catch throw". + + Called from catch_exception_command_1 () */ + + +static void +handle_gnu_4_16_catch_command (arg, tempflag, from_tty) char *arg; int tempflag; int from_tty; @@ -3161,7 +5064,13 @@ catch_command_1 (arg, tempflag, from_tty) { /* Grab selected catch clauses. */ error ("catch NAME not implemented"); + #if 0 + /* Not sure why this code has been disabled. I'm leaving + it disabled. We can never come here now anyway + since we don't allow the "catch NAME" syntax. + pai/1997-07-11 */ + /* This isn't used; I don't know what it was for. */ sals = map_catch_names (arg, catch_breakpoint); #endif @@ -3197,7 +5106,13 @@ catch_command_1 (arg, tempflag, from_tty) b = set_raw_breakpoint (sal); set_breakpoint_count (breakpoint_count + 1); b->number = breakpoint_count; - b->type = bp_breakpoint; + b->type = bp_breakpoint; /* Important -- this is an ordinary breakpoint. + For platforms with callback support for exceptions, + create_exception_catchpoint() will create special + bp types (bp_catch_catch and bp_catch_throw), and + there is code in insert_breakpoints() and elsewhere + that depends on that. */ + b->cond = cond; b->enable = enabled; b->disposition = tempflag ? del : donttouch; @@ -3213,6 +5128,156 @@ catch_command_1 (arg, tempflag, from_tty) free ((PTR)sals.sals); } +#if 0 +/* This creates a temporary internal breakpoint + just to placate infrun */ +static struct breakpoint * +create_temp_exception_breakpoint (pc) + CORE_ADDR pc; +{ + struct symtab_and_line sal; + struct breakpoint *b; + + INIT_SAL(&sal); + sal.pc = pc; + sal.symtab = NULL; + sal.line = 0; + + b = set_raw_breakpoint (sal); + if (!b) + error ("Internal error -- couldn't set temp exception breakpoint"); + + b->type = bp_breakpoint; + b->disposition = del; + b->enable = enabled; + b->silent = 1; + b->number = internal_breakpoint_number--; + return b; +} +#endif + +static void +catch_command_1 (arg, tempflag, from_tty) + char *arg; + int tempflag; + int from_tty; +{ + + /* The first argument may be an event name, such as "start" or "load". + If so, then handle it as such. If it doesn't match an event name, + then attempt to interpret it as an exception name. (This latter is + the v4.16-and-earlier GDB meaning of the "catch" command.) + + First, try to find the bounds of what might be an event name. */ + char * arg1_start = arg; + char * arg1_end; + int arg1_length; + + if (arg1_start == NULL) + { + /* Old behaviour was to use pre-v-4.16 syntax */ + /* catch_throw_command_1 (arg1_start, tempflag, from_tty); */ + /* return; */ + /* Now, this is not allowed */ + error ("Catch requires an event name"); + + } + arg1_end = ep_find_event_name_end (arg1_start); + if (arg1_end == NULL) + error ("catch requires an event"); + arg1_length = arg1_end + 1 - arg1_start; + + /* Try to match what we found against known event names. */ + if (strncmp (arg1_start, "signal", arg1_length) == 0) + { + error ("Catch of signal not yet implemented"); + } + else if (strncmp (arg1_start, "catch", arg1_length) == 0) + { + catch_exception_command_1 (EX_EVENT_CATCH, arg1_end+1, tempflag, from_tty); + } + else if (strncmp (arg1_start, "throw", arg1_length) == 0) + { + catch_exception_command_1 (EX_EVENT_THROW, arg1_end+1, tempflag, from_tty); + } + else if (strncmp (arg1_start, "thread_start", arg1_length) == 0) + { + error ("Catch of thread_start not yet implemented"); + } + else if (strncmp (arg1_start, "thread_exit", arg1_length) == 0) + { + error ("Catch of thread_exit not yet implemented"); + } + else if (strncmp (arg1_start, "thread_join", arg1_length) == 0) + { + error ("Catch of thread_join not yet implemented"); + } + else if (strncmp (arg1_start, "start", arg1_length) == 0) + { + error ("Catch of start not yet implemented"); + } + else if (strncmp (arg1_start, "exit", arg1_length) == 0) + { + error ("Catch of exit not yet implemented"); + } + else if (strncmp (arg1_start, "fork", arg1_length) == 0) + { +#if defined(CHILD_INSERT_FORK_CATCHPOINT) + catch_fork_command_1 (catch_fork, arg1_end+1, tempflag, from_tty); +#else + error ("Catch of fork not yet implemented"); +#endif + } + else if (strncmp (arg1_start, "vfork", arg1_length) == 0) + { +#if defined(CHILD_INSERT_VFORK_CATCHPOINT) + catch_fork_command_1 (catch_vfork, arg1_end+1, tempflag, from_tty); +#else + error ("Catch of vfork not yet implemented"); +#endif + } + else if (strncmp (arg1_start, "exec", arg1_length) == 0) + { +#if defined(CHILD_INSERT_EXEC_CATCHPOINT) + catch_exec_command_1 (arg1_end+1, tempflag, from_tty); +#else + error ("Catch of exec not yet implemented"); +#endif + } + else if (strncmp (arg1_start, "load", arg1_length) == 0) + { +#if defined(SOLIB_ADD) + catch_load_command_1 (arg1_end+1, tempflag, from_tty); +#else + error ("Catch of load not implemented"); +#endif + } + else if (strncmp (arg1_start, "unload", arg1_length) == 0) + { +#if defined(SOLIB_ADD) + catch_unload_command_1 (arg1_end+1, tempflag, from_tty); +#else + error ("Catch of load not implemented"); +#endif + } + else if (strncmp (arg1_start, "stop", arg1_length) == 0) + { + error ("Catch of stop not yet implemented"); + } + + /* This doesn't appear to be an event name */ + + else + { + /* Pre-v.4.16 behaviour was to treat the argument + as the name of an exception */ + /* catch_throw_command_1 (arg1_start, tempflag, from_tty); */ + /* Now this is not allowed */ + error ("Unknown event kind specified for catch"); + + } +} + /* Used by the gui, could be made a worker for other things. */ struct breakpoint * @@ -3264,12 +5329,23 @@ catch_command (arg, from_tty) catch_command_1 (arg, 0, from_tty); } + +static void +tcatch_command (arg, from_tty) + char *arg; + int from_tty; +{ + catch_command_1 (arg, 1, from_tty); +} + + static void clear_command (arg, from_tty) char *arg; int from_tty; { register struct breakpoint *b, *b1; + int default_match; struct symtabs_and_lines sals; struct symtab_and_line sal; register struct breakpoint *found; @@ -3278,6 +5354,7 @@ clear_command (arg, from_tty) if (arg) { sals = decode_line_spec (arg, 1); + default_match = 0; } else { @@ -3286,30 +5363,59 @@ clear_command (arg, from_tty) INIT_SAL (&sal); /* initialize to zeroes */ sal.line = default_breakpoint_line; sal.symtab = default_breakpoint_symtab; + sal.pc = default_breakpoint_address; if (sal.symtab == 0) error ("No source file specified."); sals.sals[0] = sal; sals.nelts = 1; + + default_match = 1; } + /* For each line spec given, delete bps which correspond + to it. We do this in two loops: the first loop looks at + the initial bp(s) in the chain which should be deleted, + the second goes down the rest of the chain looking ahead + one so it can take those bps off the chain without messing + up the chain. */ + + for (i = 0; i < sals.nelts; i++) { /* If exact pc given, clear bpts at that pc. - But if sal.pc is zero, clear all bpts on specified line. */ + If line given (pc == 0), clear all bpts on specified line. + If defaulting, clear all bpts on default line + or at default pc. + + defaulting sal.pc != 0 tests to do + + 0 1 pc + 1 1 pc _and_ line + 0 0 line + 1 0 <can't happen> */ + sal = sals.sals[i]; found = (struct breakpoint *) 0; + while (breakpoint_chain - && (sal.pc - ? (breakpoint_chain->address == sal.pc && - (overlay_debugging == 0 || - breakpoint_chain->section == sal.section)) - : (breakpoint_chain->source_file != NULL - && sal.symtab != NULL - && STREQ (breakpoint_chain->source_file, - sal.symtab->filename) - && breakpoint_chain->line_number == sal.line))) + /* Why don't we check here that this is not + a watchpoint, etc., as we do below? + I can't make it fail, but don't know + what's stopping the failure: a watchpoint + of the same address as "sal.pc" should + wind up being deleted. */ + + && ( ((sal.pc && (breakpoint_chain->address == sal.pc)) && + (overlay_debugging == 0 || + breakpoint_chain->section == sal.section)) + || ((default_match || (0 == sal.pc)) + && breakpoint_chain->source_file != NULL + && sal.symtab != NULL + && STREQ (breakpoint_chain->source_file, sal.symtab->filename) + && breakpoint_chain->line_number == sal.line))) + { b1 = breakpoint_chain; breakpoint_chain = b1->next; @@ -3318,19 +5424,23 @@ clear_command (arg, from_tty) } ALL_BREAKPOINTS (b) - while (b->next - && b->next->type != bp_watchpoint - && b->next->type != bp_hardware_watchpoint - && b->next->type != bp_read_watchpoint - && b->next->type != bp_access_watchpoint - && (sal.pc - ? (b->next->address == sal.pc && - (overlay_debugging == 0 || - b->next->section == sal.section)) - : (b->next->source_file != NULL - && sal.symtab != NULL - && STREQ (b->next->source_file, sal.symtab->filename) - && b->next->line_number == sal.line))) + + while (b->next + && b->next->type != bp_none + && b->next->type != bp_watchpoint + && b->next->type != bp_hardware_watchpoint + && b->next->type != bp_read_watchpoint + && b->next->type != bp_access_watchpoint + && ( ((sal.pc && (b->next->address == sal.pc)) && + (overlay_debugging == 0 || + b->next->section == sal.section)) + || ((default_match || (0 == sal.pc)) + && b->next->source_file != NULL + && sal.symtab != NULL + && STREQ (b->next->source_file, sal.symtab->filename) + && b->next->line_number == sal.line))) + + { b1 = b->next; b->next = b1->next; @@ -3392,15 +5502,56 @@ delete_breakpoint (bpt) register struct breakpoint *b; register bpstat bs; + if (bpt == NULL) + error ("Internal error (attempted to delete a NULL breakpoint)"); + + + /* Has this bp already been deleted? This can happen because multiple + lists can hold pointers to bp's. bpstat lists are especial culprits. + + One example of this happening is a watchpoint's scope bp. When the + scope bp triggers, we notice that the watchpoint is out of scope, and + delete it. We also delete its scope bp. But the scope bp is marked + "auto-deleting", and is already on a bpstat. That bpstat is then + checked for auto-deleting bp's, which are deleted. + + A real solution to this problem might involve reference counts in bp's, + and/or giving them pointers back to their referencing bpstat's, and + teaching delete_breakpoint to only free a bp's storage when no more + references were extent. A cheaper bandaid was chosen. */ + if (bpt->type == bp_none) + return; + if (delete_breakpoint_hook) delete_breakpoint_hook (bpt); if (bpt->inserted) - remove_breakpoint (bpt); + remove_breakpoint (bpt, mark_uninserted); if (breakpoint_chain == bpt) breakpoint_chain = bpt->next; + /* If we have callback-style exception catchpoints, don't go through + the adjustments to the C++ runtime library etc. if the inferior + isn't actually running. target_enable_exception_callback for a + null target ops vector gives an undesirable error message, so we + check here and avoid it. Since currently (1997-09-17) only HP-UX aCC's + exceptions are supported in this way, it's OK for now. FIXME */ + if (ep_is_exception_catchpoint (bpt) && target_has_execution) + { + static char message1[] = "Error in deleting catchpoint %d:\n"; + static char message[sizeof (message1) + 30]; + args_for_catchpoint_enable args; + + sprintf (message, message1, bpt->number); /* Format possible error msg */ + args.kind = bpt->type == bp_catch_catch ? EX_EVENT_CATCH : EX_EVENT_THROW; + args.enable = 0; + (void) catch_errors ((int (*) PARAMS ((char *))) cover_target_enable_exception_callback, + (char *) &args, + message, RETURN_MASK_ALL); + } + + ALL_BREAKPOINTS (b) if (b->next == bpt) { @@ -3408,20 +5559,44 @@ delete_breakpoint (bpt) break; } + /* Before turning off the visuals for the bp, check to see that + there are no other bps at the same address. */ + if (tui_version) + { + int clearIt; + + ALL_BREAKPOINTS (b) + { + clearIt = (b->address != bpt->address); + if (!clearIt) + break; + } + + if (clearIt) + { + TUIDO(((TuiOpaqueFuncPtr)tui_vAllSetHasBreakAt, bpt, 0)); + TUIDO(((TuiOpaqueFuncPtr)tuiUpdateAllExecInfos)); + } + } + check_duplicates (bpt->address, bpt->section); /* If this breakpoint was inserted, and there is another breakpoint at the same address, we need to insert the other breakpoint. */ if (bpt->inserted && bpt->type != bp_hardware_watchpoint && bpt->type != bp_read_watchpoint - && bpt->type != bp_access_watchpoint) + && bpt->type != bp_access_watchpoint + && bpt->type != bp_catch_fork + && bpt->type != bp_catch_vfork + && bpt->type != bp_catch_exec) { ALL_BREAKPOINTS (b) if (b->address == bpt->address && b->section == bpt->section && !b->duplicate && b->enable != disabled - && b->enable != shlib_disabled) + && b->enable != shlib_disabled + && b->enable != call_disabled) { int val; val = target_insert_breakpoint (b->address, b->shadow_contents); @@ -3451,13 +5626,33 @@ delete_breakpoint (bpt) value_free (bpt->val); if (bpt->source_file != NULL) free (bpt->source_file); + if (bpt->dll_pathname != NULL) + free (bpt->dll_pathname); + if (bpt->triggered_dll_pathname != NULL) + free (bpt->triggered_dll_pathname); + if (bpt->exec_pathname != NULL) + free (bpt->exec_pathname); /* Be sure no bpstat's are pointing at it after it's been freed. */ /* FIXME, how can we find all bpstat's? We just check stop_bpstat for now. */ for (bs = stop_bpstat; bs; bs = bs->next) if (bs->breakpoint_at == bpt) - bs->breakpoint_at = NULL; + { + bs->breakpoint_at = NULL; + + /* we'd call bpstat_clear_actions, but that free's stuff and due + to the multiple pointers pointing to one item with no + reference counts found anywhere through out the bpstat's (how + do you spell fragile?), we don't want to free things twice -- + better a memory leak than a corrupt malloc pool! */ + bs->commands = NULL; + bs->old_val = NULL; + } + /* On the chance that someone will soon try again to delete this same + bp, we mark it as deleted before freeing its storage. */ + bpt->type = bp_none; + free ((PTR)bpt); } @@ -3477,7 +5672,9 @@ delete_command (arg, from_tty) have to be deleted with an explicit breakpoint number argument. */ ALL_BREAKPOINTS (b) { - if (b->type != bp_call_dummy && b->number >= 0) + if (b->type != bp_call_dummy && + b->type != bp_shlib_event && + b->number >= 0) breaks_to_delete = 1; } @@ -3487,7 +5684,9 @@ delete_command (arg, from_tty) { ALL_BREAKPOINTS_SAFE (b, temp) { - if (b->type != bp_call_dummy && b->number >= 0) + if (b->type != bp_call_dummy && + b->type != bp_shlib_event && + b->number >= 0) delete_breakpoint (b); } } @@ -3513,8 +5712,13 @@ breakpoint_re_set_one (bint) switch (b->type) { + case bp_none: + warning ("attempted to reset apparently deleted breakpoint #%d?\n", b->number); + return 0; case bp_breakpoint: case bp_hardware_breakpoint: + case bp_catch_load: + case bp_catch_unload: if (b->addr_string == NULL) { /* Anything without a string can't be re-set. */ @@ -3568,7 +5772,10 @@ breakpoint_re_set_one (bint) strlen (sals.sals[i].symtab->filename)); b->line_number = sals.sals[i].line; b->address = sals.sals[i].pc; - check_duplicates (b->address, b->section); + + /* Used to check for duplicates here, but that can + cause trouble, as it doesn't check for disable + breakpoints. */ mention (b); @@ -3578,6 +5785,12 @@ breakpoint_re_set_one (bint) } b->section = sals.sals[i].section; b->enable = save_enable; /* Restore it, this worked. */ + + + /* Now that this is re-enabled, check_duplicates + can be used. */ + check_duplicates (b->address, b->section); + } free ((PTR)sals.sals); break; @@ -3618,7 +5831,17 @@ breakpoint_re_set_one (bint) mention (b); value_free_to_mark (mark); break; - + case bp_catch_catch: + case bp_catch_throw: + break; + /* We needn't really do anything to reset these, since the mask + that requests them is unaffected by e.g., new libraries being + loaded. */ + case bp_catch_fork: + case bp_catch_vfork: + case bp_catch_exec: + break; + default: printf_filtered ("Deleting unknown breakpoint type %d\n", b->type); /* fall through */ @@ -3663,7 +5886,7 @@ breakpoint_re_set () ALL_BREAKPOINTS_SAFE (b, temp) { sprintf (message, message1, b->number); /* Format possible error msg */ - catch_errors (breakpoint_re_set_one, (char *) b, message, + catch_errors ((int (*) PARAMS ((char *))) breakpoint_re_set_one, (char *) b, message, RETURN_MASK_ALL); } set_language (save_language); @@ -3689,6 +5912,21 @@ breakpoint_re_set () If from_tty is nonzero, it prints a message to that effect, which ends with a period (no newline). */ +/* Reset the thread number of this breakpoint: + + - If the breakpoint is for all threads, leave it as-is. + - Else, reset it to the current thread for inferior_pid. */ +void +breakpoint_re_set_thread (b) + struct breakpoint * b; +{ + if (b->thread != -1) + { + if (in_thread_list (inferior_pid)) + b->thread = pid_to_thread_id (inferior_pid); + } +} + void set_ignore_count (bptnum, count, from_tty) int bptnum, count, from_tty; @@ -3821,7 +6059,17 @@ disable_command (args, from_tty) ALL_BREAKPOINTS (bpt) switch (bpt->type) { + case bp_none: + warning ("attempted to disable apparently deleted breakpoint #%d?\n", bpt->number); + continue; case bp_breakpoint: + case bp_catch_load: + case bp_catch_unload: + case bp_catch_fork: + case bp_catch_vfork: + case bp_catch_exec: + case bp_catch_catch: + case bp_catch_throw: case bp_hardware_breakpoint: case bp_watchpoint: case bp_hardware_watchpoint: @@ -3868,7 +6116,12 @@ do_enable_breakpoint (bpt, disposition) if (bpt->exp_valid_block != NULL) { struct frame_info *fr = - find_frame_addr_in_frame_chain (bpt->watchpoint_frame); + + /* Ensure that we have the current frame. Else, this + next query may pessimistically be answered as, "No, + not within current scope". */ + get_current_frame (); + fr = find_frame_addr_in_frame_chain (bpt->watchpoint_frame); if (fr == NULL) { printf_filtered ("\ @@ -3915,7 +6168,7 @@ have been allocated for other watchpoints.\n", bpt->number); } if (save_selected_frame_level >= 0) - select_frame (save_selected_frame, save_selected_frame_level); + select_and_print_frame (save_selected_frame, save_selected_frame_level); value_free_to_mark (mark); } if (modify_breakpoint_hook) @@ -3944,7 +6197,17 @@ enable_command (args, from_tty) ALL_BREAKPOINTS (bpt) switch (bpt->type) { + case bp_none: + warning ("attempted to enable apparently deleted breakpoint #%d?\n", bpt->number); + continue; case bp_breakpoint: + case bp_catch_load: + case bp_catch_unload: + case bp_catch_fork: + case bp_catch_vfork: + case bp_catch_exec: + case bp_catch_catch: + case bp_catch_throw: case bp_hardware_breakpoint: case bp_watchpoint: case bp_hardware_watchpoint: @@ -4015,6 +6278,8 @@ decode_line_spec_1 (string, funfirstline) void _initialize_breakpoint () { + struct cmd_list_element *c; + breakpoint_chain = 0; /* Don't bother to call set_breakpoint_count. $bpnum isn't useful before a breakpoint is set. */ @@ -4023,6 +6288,8 @@ _initialize_breakpoint () add_com ("ignore", class_breakpoint, ignore_command, "Set ignore-count of breakpoint number N to COUNT.\n\ Usage is `ignore N COUNT'."); + if (xdb_commands) + add_com_alias("bc", "ignore", class_breakpoint, 1); add_com ("commands", class_breakpoint, commands_command, "Set commands to be executed when a breakpoint is hit.\n\ @@ -4043,6 +6310,9 @@ expression to be evaluated whenever breakpoint N is reached. "); Like \"break\" except the breakpoint is only temporary,\n\ so it will be deleted when hit. Equivalent to \"break\" followed\n\ by using \"enable delete\" on the breakpoint number."); + add_com("txbreak", class_breakpoint, tbreak_at_finish_command, + "Set temporary breakpoint at procedure exit. Either there should\n\ +be no argument or the argument must be a depth.\n"); add_com ("hbreak", class_breakpoint, hbreak_command, "Set a hardware assisted breakpoint. Args like \"break\" command.\n\ @@ -4061,6 +6331,13 @@ With no subcommand, breakpoints are enabled until you command otherwise.\n\ This is used to cancel the effect of the \"disable\" command.\n\ With a subcommand you can enable temporarily.", &enablelist, "enable ", 1, &cmdlist); + if (xdb_commands) + add_com("ab", class_breakpoint, enable_command, + "Enable some breakpoints.\n\ +Give breakpoint numbers (separated by spaces) as arguments.\n\ +With no subcommand, breakpoints are enabled until you command otherwise.\n\ +This is used to cancel the effect of the \"disable\" command.\n\ +With a subcommand you can enable temporarily."); add_com_alias ("en", "enable", class_breakpoint, 1); @@ -4099,6 +6376,12 @@ A disabled breakpoint is not forgotten, but has no effect until reenabled.", &disablelist, "disable ", 1, &cmdlist); add_com_alias ("dis", "disable", class_breakpoint, 1); add_com_alias ("disa", "disable", class_breakpoint, 1); + if (xdb_commands) + add_com("sb", class_breakpoint, disable_command, + "Disable some breakpoints.\n\ +Arguments are breakpoint numbers with spaces in between.\n\ +To disable all breakpoints, give no argument.\n\ +A disabled breakpoint is not forgotten, but has no effect until reenabled."); add_cmd ("breakpoints", class_alias, disable_command, "Disable some breakpoints.\n\ @@ -4117,6 +6400,11 @@ Also a prefix command for deletion of other GDB objects.\n\ The \"unset\" command is also an alias for \"delete\".", &deletelist, "delete ", 1, &cmdlist); add_com_alias ("d", "delete", class_breakpoint, 1); + if (xdb_commands) + add_com ("db", class_breakpoint, delete_command, + "Delete some breakpoints.\n\ +Arguments are breakpoint numbers with spaces in between.\n\ +To delete all breakpoints, give no argument.\n"); add_cmd ("breakpoints", class_alias, delete_command, "Delete some breakpoints or auto-display expressions.\n\ @@ -4153,6 +6441,56 @@ Do \"help breakpoints\" for info on other commands dealing with breakpoints.", N add_com_alias ("bre", "break", class_run, 1); add_com_alias ("brea", "break", class_run, 1); + add_com("xbreak", class_breakpoint, break_at_finish_command, + concat("Set breakpoint at procedure exit. \n\ +Argument may be function name, or \"*\" and an address.\n\ +If function is specified, break at end of code for that function.\n\ +If an address is specified, break at the end of the function that contains \n\ +that exact address.\n", +"With no arg, uses current execution address of selected stack frame.\n\ +This is useful for breaking on return to a stack frame.\n\ +\n\ +Multiple breakpoints at one place are permitted, and useful if conditional.\n\ +\n\ +Do \"help breakpoints\" for info on other commands dealing with breakpoints.", NULL)); + add_com_alias ("xb", "xbreak", class_breakpoint, 1); + add_com_alias ("xbr", "xbreak", class_breakpoint, 1); + add_com_alias ("xbre", "xbreak", class_breakpoint, 1); + add_com_alias ("xbrea", "xbreak", class_breakpoint, 1); + + if (xdb_commands) + { + add_com_alias ("ba", "break", class_breakpoint, 1); + add_com_alias ("bu", "ubreak", class_breakpoint, 1); + add_com ("bx", class_breakpoint, break_at_finish_at_depth_command, + "Set breakpoint at procedure exit. Either there should\n\ +be no argument or the argument must be a depth.\n"); + } + + if (dbx_commands) + { + add_abbrev_prefix_cmd("stop", class_breakpoint, stop_command, + "Break in function/address or break at a line in the current file.", + &stoplist, "stop ", 1, &cmdlist); + add_cmd("in", class_breakpoint, stopin_command, + "Break in function or address.\n", &stoplist); + add_cmd("at", class_breakpoint, stopat_command, + "Break at a line in the current file.\n", &stoplist); + add_com("status", class_info, breakpoints_info, + concat ("Status of user-settable breakpoints, or breakpoint number NUMBER.\n\ +The \"Type\" column indicates one of:\n\ +\tbreakpoint - normal breakpoint\n\ +\twatchpoint - watchpoint\n\ +The \"Disp\" column contains one of \"keep\", \"del\", or \"dis\" to indicate\n\ +the disposition of the breakpoint after it gets hit. \"dis\" means that the\n\ +breakpoint will be disabled. The \"Address\" and \"What\" columns indicate the\n\ +address and file/line number respectively.\n\n", +"Convenience variable \"$_\" and default examine address for \"x\"\n\ +are set to the address of the last breakpoint listed.\n\n\ +Convenience variable \"$bpnum\" contains the number of the last\n\ +breakpoint set.", NULL)); + } + add_info ("breakpoints", breakpoints_info, concat ("Status of user-settable breakpoints, or breakpoint number NUMBER.\n\ The \"Type\" column indicates one of:\n\ @@ -4167,6 +6505,21 @@ are set to the address of the last breakpoint listed.\n\n\ Convenience variable \"$bpnum\" contains the number of the last\n\ breakpoint set.", NULL)); + if (xdb_commands) + add_com("lb", class_breakpoint, breakpoints_info, + concat ("Status of user-settable breakpoints, or breakpoint number NUMBER.\n\ +The \"Type\" column indicates one of:\n\ +\tbreakpoint - normal breakpoint\n\ +\twatchpoint - watchpoint\n\ +The \"Disp\" column contains one of \"keep\", \"del\", or \"dis\" to indicate\n\ +the disposition of the breakpoint after it gets hit. \"dis\" means that the\n\ +breakpoint will be disabled. The \"Address\" and \"What\" columns indicate the\n\ +address and file/line number respectively.\n\n", +"Convenience variable \"$_\" and default examine address for \"x\"\n\ +are set to the address of the last breakpoint listed.\n\n\ +Convenience variable \"$bpnum\" contains the number of the last\n\ +breakpoint set.", NULL)); + #if MAINTENANCE_CMDS add_cmd ("breakpoints", class_maintenance, maintenance_info_breakpoints, @@ -4191,18 +6544,49 @@ breakpoint set.", NULL), #endif /* MAINTENANCE_CMDS */ add_com ("catch", class_breakpoint, catch_command, - "Set breakpoints to catch exceptions that are raised.\n\ -Argument may be a single exception to catch, multiple exceptions\n\ -to catch, or the default exception \"default\". If no arguments\n\ -are given, breakpoints are set at all exception handlers catch clauses\n\ -within the current scope.\n\ -\n\ -A condition specified for the catch applies to all breakpoints set\n\ -with this command\n\ + "Set catchpoints to catch events.\n\ +Raised signals may be caught:\n\ +\tcatch signal - all signals\n\ +\tcatch signal <signame> - a particular signal\n\ +Raised exceptions may be caught:\n\ +\tcatch throw - all exceptions, when thrown\n\ +\tcatch throw <exceptname> - a particular exception, when thrown\n\ +\tcatch catch - all exceptions, when caught\n\ +\tcatch catch <exceptname> - a particular exception, when caught\n\ +Thread or process events may be caught:\n\ +\tcatch thread_start - any threads, just after creation\n\ +\tcatch thread_exit - any threads, just before expiration\n\ +\tcatch thread_join - any threads, just after joins\n\ +Process events may be caught:\n\ +\tcatch start - any processes, just after creation\n\ +\tcatch exit - any processes, just before expiration\n\ +\tcatch fork - calls to fork()\n\ +\tcatch vfork - calls to vfork()\n\ +\tcatch exec - calls to exec()\n\ +Dynamically-linked library events may be caught:\n\ +\tcatch load - loads of any library\n\ +\tcatch load <libname> - loads of a particular library\n\ +\tcatch unload - unloads of any library\n\ +\tcatch unload <libname> - unloads of a particular library\n\ +The act of your program's execution stopping may also be caught:\n\ +\tcatch stop\n\n\ +C++ exceptions may be caught:\n\ +\tcatch throw - all exceptions, when thrown\n\ +\tcatch catch - all exceptions, when caught\n\ \n\ +Do \"help set follow-fork-mode\" for info on debugging your program\n\ +after a fork or vfork is caught.\n\n\ Do \"help breakpoints\" for info on other commands dealing with breakpoints."); + + add_com ("tcatch", class_breakpoint, tcatch_command, + "Set temporary catchpoints to catch events.\n\ +Args like \"catch\" command.\n\ +Like \"catch\" except the catchpoint is only temporary,\n\ +so it will be deleted when hit. Equivalent to \"catch\" followed\n\ +by using \"enable delete\" on the catchpoint number."); + +add_com ("watch", class_breakpoint, watch_command, - add_com ("watch", class_breakpoint, watch_command, "Set a watchpoint for an expression.\n\ A watchpoint stops execution of your program whenever the value of\n\ an expression changes."); @@ -4220,4 +6604,16 @@ an expression is either read or written."); add_info ("watchpoints", breakpoints_info, "Synonym for ``info breakpoints''."); + + c = add_set_cmd ("can-use-hw-watchpoints", class_support, var_zinteger, + (char *) &can_use_hw_watchpoints, + "Set debugger's willingness to use watchpoint hardware.\n\ +If zero, gdb will not use hardware for new watchpoints, even if\n\ +such is available. (However, any hardware watchpoints that were\n\ +created before setting this to nonzero, will continue to use watchpoint\n\ +hardware.)", + &setlist); + add_show_from_set (c, &showlist); + + can_use_hw_watchpoints = 1; } diff --git a/gdb/breakpoint.h b/gdb/breakpoint.h index 0aa42f5..0b6fff5 100644 --- a/gdb/breakpoint.h +++ b/gdb/breakpoint.h @@ -1,5 +1,5 @@ /* Data structures associated with breakpoints in GDB. - Copyright (C) 1992, 1993, 1994, 1995, 1996 Free Software Foundation, Inc. + Copyright (C) 1992, 93, 94, 95, 96, 1998 Free Software Foundation, Inc. This file is part of GDB. @@ -38,6 +38,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ possible like a single-step to wait_for_inferior). */ enum bptype { + bp_none = 0, /* Eventpoint has been deleted. */ bp_breakpoint, /* Normal breakpoint */ bp_hardware_breakpoint, /* Hardware assisted breakpoint */ bp_until, /* used by until command */ @@ -87,12 +88,50 @@ enum bptype { when these significant events occur. GDB can then re-examine the dynamic linker's data structures to discover any newly loaded dynamic libraries. */ - bp_shlib_event + bp_shlib_event, + + /* These breakpoints are used to implement the "catch load" command + on platforms whose dynamic linkers support such functionality. */ + bp_catch_load, + + /* These breakpoints are used to implement the "catch unload" command + on platforms whose dynamic linkers support such functionality. */ + bp_catch_unload, + + /* These are not really breakpoints, but are catchpoints that + implement the "catch fork", "catch vfork" and "catch exec" commands + on platforms whose kernel support such functionality. (I.e., + kernels which can raise an event when a fork or exec occurs, as + opposed to the debugger setting breakpoints on functions named + "fork" or "exec".) */ + bp_catch_fork, + bp_catch_vfork, + bp_catch_exec, + + /* These are catchpoints to implement "catch catch" and "catch throw" + commands for C++ exception handling. */ + bp_catch_catch, + bp_catch_throw, + + }; /* States of enablement of breakpoint. */ -enum enable { disabled, enabled, shlib_disabled}; +enum enable { + disabled, /* The eventpoint is inactive, and cannot trigger. */ + enabled, /* The eventpoint is active, and can trigger. */ + shlib_disabled, /* The eventpoint's address is within an unloaded solib. + The eventpoint will be automatically enabled & reset + when that solib is loaded. */ + call_disabled /* The eventpoint has been disabled while a call into + the inferior is "in flight", because some eventpoints + interfere with the implementation of a call on some + targets. The eventpoint will be automatically enabled + & reset when the call "lands" (either completes, or + stops at another eventpoint). */ +}; + /* Disposition of breakpoint. Ie: what to do after hitting it. */ @@ -205,6 +244,24 @@ struct breakpoint aborting, so you can back up to just before the abort. */ int hit_count; + /* Filename of a dynamically-linked library (dll), used for bp_catch_load + and bp_catch_unload (malloc'd), or NULL if any library is significant. */ + char * dll_pathname; + + /* Filename of a dll whose state change (e.g., load or unload) + triggered this catchpoint. This field is only vaid immediately + after this catchpoint has triggered. */ + char * triggered_dll_pathname; + + /* Process id of a child process whose forking triggered this catchpoint. + This field is only vaid immediately after this catchpoint has triggered. */ + int forked_inferior_pid; + + /* Filename of a program whose exec triggered this catchpoint. This + field is only vaid immediately after this catchpoint has triggered. */ + char * exec_pathname; + + asection *section; }; /* The following stuff is an abstract data type "bpstat" ("breakpoint status"). @@ -275,6 +332,10 @@ enum bpstat_what_main_action { keep checking. */ BPSTAT_WHAT_CHECK_SHLIBS, + /* Check the dynamic linker's data structures for new libraries, then + resume out of the dynamic linker's callback, stop and print. */ + BPSTAT_WHAT_CHECK_SHLIBS_RESUME_FROM_HOOK, + /* This is just used to keep track of how many enums there are. */ BPSTAT_WHAT_LAST }; @@ -295,6 +356,18 @@ struct bpstat_what bpstat_what PARAMS ((bpstat)); /* Find the bpstat associated with a breakpoint. NULL otherwise. */ bpstat bpstat_find_breakpoint PARAMS ((bpstat, struct breakpoint *)); +/* Find a step_resume breakpoint associated with this bpstat. + (If there are multiple step_resume bp's on the list, this function + will arbitrarily pick one.) + + It is an error to use this function if BPSTAT doesn't contain a + step_resume breakpoint. + + See wait_for_inferior's use of this function. + */ +extern struct breakpoint * +bpstat_find_step_resume_breakpoint PARAMS ((bpstat)); + /* Nonzero if a signal that we got in wait() was due to circumstances explained by the BS. */ /* Currently that is true if we have hit a breakpoint, or if there is @@ -306,6 +379,9 @@ bpstat bpstat_find_breakpoint PARAMS ((bpstat, struct breakpoint *)); just to things like whether watchpoints are set. */ extern int bpstat_should_step PARAMS ((void)); +/* Nonzero if there are enabled hardware watchpoints. */ +extern int bpstat_have_active_hw_watchpoints PARAMS ((void)); + /* Print a message indicating what happened. Returns nonzero to say that only the source line should be printed after this (zero return means print the frame as well as the source line). */ @@ -326,6 +402,12 @@ extern void bpstat_do_actions PARAMS ((bpstat *)); /* Modify BS so that the actions will not be performed. */ extern void bpstat_clear_actions PARAMS ((bpstat)); +/* Given a bpstat that records zero or more triggered eventpoints, this + function returns another bpstat which contains only the catchpoints + on that first list, if any. + */ +extern void bpstat_get_triggered_catchpoints PARAMS ((bpstat, bpstat *)); + /* Implementation: */ struct bpstats { @@ -350,6 +432,14 @@ struct bpstats bpstat_print, or -1 if it can't deal with it. */ int (*print_it) PARAMS((bpstat bs)); }; + +enum inf_context +{ + inf_starting, + inf_running, + inf_exited +}; + /* Prototypes for breakpoint-related functions. */ @@ -359,6 +449,8 @@ struct frame_info; extern int breakpoint_here_p PARAMS ((CORE_ADDR)); +extern int breakpoint_inserted_here_p PARAMS ((CORE_ADDR)); + extern int frame_in_dummy PARAMS ((struct frame_info *)); extern int breakpoint_thread_match PARAMS ((CORE_ADDR, int)); @@ -367,7 +459,8 @@ extern void until_break_command PARAMS ((char *, int)); extern void breakpoint_re_set PARAMS ((void)); -extern void clear_momentary_breakpoints PARAMS ((void)); +extern void breakpoint_re_set_thread PARAMS ((struct breakpoint *)); + extern struct breakpoint *set_momentary_breakpoint PARAMS ((struct symtab_and_line, struct frame_info *, enum bptype)); @@ -378,7 +471,7 @@ extern void set_default_breakpoint PARAMS ((int, CORE_ADDR, struct symtab *, int extern void mark_breakpoints_out PARAMS ((void)); -extern void breakpoint_init_inferior PARAMS ((void)); +extern void breakpoint_init_inferior PARAMS ((enum inf_context)); extern void delete_breakpoint PARAMS ((struct breakpoint *)); @@ -388,16 +481,80 @@ extern void breakpoint_clear_ignore_counts PARAMS ((void)); extern void break_command PARAMS ((char *, int)); +extern void tbreak_command PARAMS ((char *, int)); + extern int insert_breakpoints PARAMS ((void)); extern int remove_breakpoints PARAMS ((void)); +/* This function can be used to physically insert eventpoints from the + specified traced inferior process, without modifying the breakpoint + package's state. This can be useful for those targets which support + following the processes of a fork() or vfork() system call, when both + of the resulting two processes are to be followed. */ +extern int reattach_breakpoints PARAMS ((int)); + +/* This function can be used to update the breakpoint package's state + after an exec() system call has been executed. + + This function causes the following: + + - All eventpoints are marked "not inserted". + - All eventpoints with a symbolic address are reset such that + the symbolic address must be reevaluated before the eventpoints + can be reinserted. + - The solib breakpoints are explicitly removed from the breakpoint + list. + - A step-resume breakpoint, if any, is explicitly removed from the + breakpoint list. + - All eventpoints without a symbolic address are removed from the + breakpoint list. */ +extern void update_breakpoints_after_exec PARAMS ((void)); + +/* This function can be used to physically remove hardware breakpoints + and watchpoints from the specified traced inferior process, without + modifying the breakpoint package's state. This can be useful for + those targets which support following the processes of a fork() or + vfork() system call, when one of the resulting two processes is to + be detached and allowed to run free. + + It is an error to use this function on the process whose id is + inferior_pid. */ +extern int detach_breakpoints PARAMS ((int)); + extern void enable_longjmp_breakpoint PARAMS ((void)); extern void disable_longjmp_breakpoint PARAMS ((void)); extern void set_longjmp_resume_breakpoint PARAMS ((CORE_ADDR, struct frame_info *)); +/* These functions respectively disable or reenable all currently + enabled watchpoints. When disabled, the watchpoints are marked + call_disabled. When reenabled, they are marked enabled. + + The intended client of these functions is infcmd.c\run_stack_dummy. + + The inferior must be stopped, and all breakpoints removed, when + these functions are used. + + The need for these functions is that on some targets (e.g., HP-UX), + gdb is unable to unwind through the dummy frame that is pushed as + part of the implementation of a call command. Watchpoints can + cause the inferior to stop in places where this frame is visible, + and that can cause execution control to become very confused. + + Note that if a user sets breakpoints in an interactively call + function, the call_disabled watchpoints will have been reenabled + when the first such breakpoint is reached. However, on targets + that are unable to unwind through the call dummy frame, watches + of stack-based storage may then be deleted, because gdb will + believe that their watched storage is out of scope. (Sigh.) */ +extern void +disable_watchpoints_before_interactive_call_start PARAMS ((void)); + +extern void +enable_watchpoints_after_interactive_call_stop PARAMS ((void)); + extern void clear_breakpoint_hit_counts PARAMS ((void)); @@ -420,6 +577,28 @@ extern void create_solib_event_breakpoint PARAMS ((CORE_ADDR)); extern void remove_solib_event_breakpoints PARAMS ((void)); +extern void disable_breakpoints_in_shlibs PARAMS ((void)); + extern void re_enable_breakpoints_in_shlibs PARAMS ((void)); +extern void create_solib_load_event_breakpoint PARAMS ((char *, int, char *, char *)); + +extern void create_solib_unload_event_breakpoint PARAMS ((char *, int, char *, char *)); + +extern void create_fork_event_catchpoint PARAMS ((int, char *)); + +extern void create_vfork_event_catchpoint PARAMS ((int, char *)); + +extern void create_exec_event_catchpoint PARAMS ((int, char *)); + +/* This function returns TRUE if ep is a catchpoint. */ +extern int ep_is_catchpoint PARAMS ((struct breakpoint *)); + +/* This function returns TRUE if ep is a catchpoint of a + shared library (aka dynamically-linked library) event, + such as a library load or unload. */ +extern int ep_is_shlib_catchpoint PARAMS ((struct breakpoint *)); + +extern struct breakpoint *set_breakpoint_sal PARAMS ((struct symtab_and_line)); + #endif /* !defined (BREAKPOINT_H) */ diff --git a/gdb/buildsym.c b/gdb/buildsym.c index 7ba424e..f494b54 100644 --- a/gdb/buildsym.c +++ b/gdb/buildsym.c @@ -244,6 +244,7 @@ finish_block (symbol, listhead, old_blocks, start, end, objfile) BLOCK_END (block) = end; /* Superblock filled in when containing block is made */ BLOCK_SUPERBLOCK (block) = NULL; + BLOCK_GCC_COMPILED (block) = processing_gcc_compilation; /* Put the block in as the value of the symbol that names it. */ @@ -276,6 +277,7 @@ finish_block (symbol, listhead, old_blocks, start, end, objfile) case LOC_UNDEF: case LOC_CONST: case LOC_STATIC: + case LOC_INDIRECT: case LOC_REGISTER: case LOC_LOCAL: case LOC_TYPEDEF: @@ -312,6 +314,7 @@ finish_block (symbol, listhead, old_blocks, start, end, objfile) case LOC_UNDEF: case LOC_CONST: case LOC_STATIC: + case LOC_INDIRECT: case LOC_REGISTER: case LOC_LOCAL: case LOC_TYPEDEF: @@ -1046,11 +1049,13 @@ push_context (desc, valu) new = &context_stack[context_stack_depth++]; new->depth = desc; new->locals = local_symbols; + new->params = param_symbols; new->old_blocks = pending_blocks; new->start_addr = valu; new->name = NULL; local_symbols = NULL; + param_symbols = NULL; return (new); } @@ -1094,6 +1099,40 @@ record_debugformat (format) current_subfile -> debugformat = savestring (format, strlen (format)); } + +/* Merge the first symbol list SRCLIST into the second symbol list + TARGETLIST by repeated calls to add_symbol_to_list(). This + procedure "frees" each link of SRCLIST by adding it to the + free_pendings list. Caller must set SRCLIST to a null list after + calling this function. + + Void return. */ + +void +merge_symbol_lists (srclist, targetlist) + struct pending ** srclist; + struct pending ** targetlist; +{ + register int i; + register struct pending * link; + + if (!srclist || !*srclist) + return; + + /* Merge in elements from current link */ + for (i=0; i < (*srclist)->nsyms; i++) + add_symbol_to_list ((*srclist)->symbol[i], targetlist); + + /* Recurse on next */ + merge_symbol_lists (&(*srclist)->next, targetlist); + + /* "Free" the current link */ + (*srclist)->next = free_pendings; + free_pendings = (*srclist); +} + + + /* Initialize anything that needs initializing when starting to read a fresh piece of a symbol file, e.g. reading in the stuff corresponding diff --git a/gdb/c-lang.c b/gdb/c-lang.c index 251b0b2..e7aa055 100644 --- a/gdb/c-lang.c +++ b/gdb/c-lang.c @@ -245,10 +245,17 @@ c_create_fundamental_type (objfile, typeid) TARGET_CHAR_BIT / TARGET_CHAR_BIT, 0, "void", objfile); break; + case FT_BOOLEAN: + type = init_type (TYPE_CODE_BOOL, + TARGET_CHAR_BIT / TARGET_CHAR_BIT, + 0, "bool", objfile); + + break; case FT_CHAR: type = init_type (TYPE_CODE_INT, TARGET_CHAR_BIT / TARGET_CHAR_BIT, 0, "char", objfile); + TYPE_FLAGS (type) |= TYPE_FLAG_NOSIGN; break; case FT_SIGNED_CHAR: type = init_type (TYPE_CODE_INT, @@ -334,6 +341,12 @@ c_create_fundamental_type (objfile, typeid) type = init_type (TYPE_CODE_FLT, TARGET_LONG_DOUBLE_BIT / TARGET_CHAR_BIT, 0, "long double", objfile); + break; + case FT_TEMPLATE_ARG: + type = init_type (TYPE_CODE_TEMPLATE_ARG, + 0, + 0, "<template arg>", objfile); + break; } return (type); @@ -428,10 +441,33 @@ const struct language_defn c_language_defn = { LANG_MAGIC }; +struct type ** const (cplus_builtin_types[]) = +{ + &builtin_type_int, + &builtin_type_long, + &builtin_type_short, + &builtin_type_char, + &builtin_type_float, + &builtin_type_double, + &builtin_type_void, + &builtin_type_long_long, + &builtin_type_signed_char, + &builtin_type_unsigned_char, + &builtin_type_unsigned_short, + &builtin_type_unsigned_int, + &builtin_type_unsigned_long, + &builtin_type_unsigned_long_long, + &builtin_type_long_double, + &builtin_type_complex, + &builtin_type_double_complex, + &builtin_type_bool, + 0 +}; + const struct language_defn cplus_language_defn = { "c++", /* Language name */ language_cplus, - c_builtin_types, + cplus_builtin_types, range_check_off, type_check_off, c_parse, diff --git a/gdb/c-typeprint.c b/gdb/c-typeprint.c index 689988d..120511e 100644 --- a/gdb/c-typeprint.c +++ b/gdb/c-typeprint.c @@ -38,6 +38,9 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include <errno.h> #include <ctype.h> +/* Flag indicating target was compiled by HP compiler */ +extern int hp_som_som_object_present; + static void c_type_print_args PARAMS ((struct type *, GDB_FILE *)); @@ -50,6 +53,10 @@ cp_type_print_derivation_info PARAMS ((GDB_FILE *, struct type *)); void c_type_print_varspec_prefix PARAMS ((struct type *, GDB_FILE *, int, int)); +static void +c_type_print_cv_qualifier PARAMS ((struct type *, GDB_FILE *, int, int)); + + /* Print a description of a type in the format of a typedef for the current language. @@ -169,7 +176,10 @@ c_print_type (type, varstring, stream, show, level) } In general, gdb should try to print the types as closely as possible to - the form that they appear in the source code. */ + the form that they appear in the source code. + Note that in case of protected derivation gcc will not say 'protected' + but 'private'. The HP's aCC compiler emits specific information for + derivation via protected inheritance, so gdb can print it out */ static void cp_type_print_derivation_info (stream, type) @@ -183,7 +193,8 @@ cp_type_print_derivation_info (stream, type) { fputs_filtered (i == 0 ? ": " : ", ", stream); fprintf_filtered (stream, "%s%s ", - BASETYPE_VIA_PUBLIC (type, i) ? "public" : "private", + BASETYPE_VIA_PUBLIC (type, i) ? "public" + : (TYPE_FIELD_PROTECTED (type, i) ? "protected" : "private"), BASETYPE_VIA_VIRTUAL(type, i) ? " virtual" : ""); name = type_name_no_tag (TYPE_BASECLASS (type, i)); fprintf_filtered (stream, "%s", name ? name : "(null)"); @@ -193,6 +204,47 @@ cp_type_print_derivation_info (stream, type) fputs_filtered (" ", stream); } } +/* Print the C++ method arguments ARGS to the file STREAM. */ + +void +cp_type_print_method_args (args, prefix, varstring, staticp, stream) + struct type **args; + char *prefix; + char *varstring; + int staticp; + GDB_FILE *stream; +{ + int i; + + fprintf_symbol_filtered (stream, prefix, language_cplus, DMGL_ANSI); + fprintf_symbol_filtered (stream, varstring, language_cplus, DMGL_ANSI); + fputs_filtered ("(", stream); + if (args && args[!staticp] && args[!staticp]->code != TYPE_CODE_VOID) + { + i = !staticp; /* skip the class variable */ + while (1) + { + type_print (args[i++], "", stream, 0); + if (!args[i]) + { + fprintf_filtered (stream, " ..."); + break; + } + else if (args[i]->code != TYPE_CODE_VOID) + { + fprintf_filtered (stream, ", "); + } + else break; + } + } + else if (current_language->la_language == language_cplus) + { + fprintf_filtered (stream, "void"); + } + + fprintf_filtered (stream, ")"); +} + /* Print any asterisks or open-parentheses needed before the variable name (to describe its type). @@ -223,6 +275,7 @@ c_type_print_varspec_prefix (type, stream, show, passed_a_ptr) case TYPE_CODE_PTR: c_type_print_varspec_prefix (TYPE_TARGET_TYPE (type), stream, 0, 1); fprintf_filtered (stream, "*"); + c_type_print_cv_qualifier (type, stream, 1, 0); break; case TYPE_CODE_MEMBER: @@ -253,6 +306,7 @@ c_type_print_varspec_prefix (type, stream, show, passed_a_ptr) case TYPE_CODE_REF: c_type_print_varspec_prefix (TYPE_TARGET_TYPE (type), stream, 0, 1); fprintf_filtered (stream, "&"); + c_type_print_cv_qualifier (type, stream, 1, 0); break; case TYPE_CODE_FUNC: @@ -289,6 +343,43 @@ c_type_print_varspec_prefix (type, stream, show, passed_a_ptr) } } +/* Print out "const" and "volatile" attributes. + TYPE is a pointer to the type being printed out. + STREAM is the output destination. + NEED_SPACE = 1 indicates an initial white space is needed */ + +static void +c_type_print_cv_qualifier (type, stream, need_pre_space, need_post_space) + struct type *type; + GDB_FILE *stream; + int need_pre_space; + int need_post_space; +{ + int flag = 0; + + if (TYPE_CONST (type)) + { + if (need_pre_space) + fprintf_filtered (stream, " "); + fprintf_filtered (stream, "const"); + flag = 1; + } + + if (TYPE_VOLATILE (type)) + { + if (flag || need_pre_space) + fprintf_filtered (stream, " "); + fprintf_filtered (stream, "volatile"); + flag = 1; + } + + if (flag && need_post_space) + fprintf_filtered (stream, " "); +} + + + + static void c_type_print_args (type, stream) struct type *type; @@ -305,6 +396,11 @@ c_type_print_args (type, stream) { fprintf_filtered (stream, "..."); } + else if ((args[1]->code == TYPE_CODE_VOID) && + (current_language->la_language == language_cplus)) + { + fprintf_filtered (stream, "void"); + } else { for (i = 1; @@ -324,6 +420,11 @@ c_type_print_args (type, stream) } } } + else if (current_language->la_language == language_cplus) + { + fprintf_filtered (stream, "void"); + } + fprintf_filtered (stream, ")"); } @@ -391,15 +492,20 @@ c_type_print_varspec_suffix (type, stream, show, passed_a_ptr, demangled_args) if (!demangled_args) { int i, len = TYPE_NFIELDS (type); fprintf_filtered (stream, "("); - for (i = 0; i < len; i++) - { - if (i > 0) - { - fputs_filtered (", ", stream); - wrap_here (" "); - } - c_print_type (TYPE_FIELD_TYPE (type, i), "", stream, -1, 0); - } + if ((len == 0) && (current_language->la_language == language_cplus)) + { + fprintf_filtered (stream, "void"); + } + else + for (i = 0; i < len; i++) + { + if (i > 0) + { + fputs_filtered (", ", stream); + wrap_here (" "); + } + c_print_type (TYPE_FIELD_TYPE (type, i), "", stream, -1, 0); + } fprintf_filtered (stream, ")"); } c_type_print_varspec_suffix (TYPE_TARGET_TYPE (type), stream, 0, @@ -456,7 +562,11 @@ c_type_print_base (type, stream, show, level) register int lastval; char *mangled_name; char *demangled_name; + char *demangled_no_static; enum {s_none, s_public, s_private, s_protected} section_type; + int need_access_label = 0; + int j, len2; + QUIT; wrap_here (" "); @@ -475,6 +585,7 @@ c_type_print_base (type, stream, show, level) if (show <= 0 && TYPE_NAME (type) != NULL) { + c_type_print_cv_qualifier (type, stream, 0, 1); fputs_filtered (TYPE_NAME (type), stream); return; } @@ -494,21 +605,48 @@ c_type_print_base (type, stream, show, level) break; case TYPE_CODE_STRUCT: - if (HAVE_CPLUS_STRUCT (type)) - { - fprintf_filtered (stream, "class "); - } - else - { - fprintf_filtered (stream, "struct "); - } + c_type_print_cv_qualifier (type, stream, 0, 1); + /* Note TYPE_CODE_STRUCT and TYPE_CODE_CLASS have the same value, + * so we use another means for distinguishing them. + */ + if (HAVE_CPLUS_STRUCT (type)) { + switch (TYPE_DECLARED_TYPE(type)) { + case DECLARED_TYPE_CLASS: + fprintf_filtered (stream, "class "); + break; + case DECLARED_TYPE_UNION: + fprintf_filtered (stream, "union "); + break; + case DECLARED_TYPE_STRUCT: + fprintf_filtered (stream, "struct "); + break; + default: + /* If there is a CPLUS_STRUCT, assume class if not + * otherwise specified in the declared_type field. + */ + fprintf_filtered (stream, "class "); + break; + } /* switch */ + } else { + /* If not CPLUS_STRUCT, then assume it's a C struct */ + fprintf_filtered (stream, "struct "); + } goto struct_union; case TYPE_CODE_UNION: + c_type_print_cv_qualifier (type, stream, 0, 1); fprintf_filtered (stream, "union "); struct_union: - if (TYPE_TAG_NAME (type) != NULL) + + /* Print the tag if it exists. + * The HP aCC compiler emits + * a spurious "{unnamed struct}"/"{unnamed union}"/"{unnamed enum}" + * tag for unnamed struct/union/enum's, which we don't + * want to print. + */ + if (TYPE_TAG_NAME (type) != NULL && + strncmp(TYPE_TAG_NAME(type), "{unnamed", 8)) { fputs_filtered (TYPE_TAG_NAME (type), stream); if (show > 0) @@ -540,6 +678,72 @@ c_type_print_base (type, stream, show, level) section_type = s_none; + /* For a class, if all members are private, there's no need + for a "private:" label; similarly, for a struct or union + masquerading as a class, if all members are public, there's + no need for a "public:" label. */ + + if ((TYPE_DECLARED_TYPE (type) == DECLARED_TYPE_CLASS) || + (TYPE_DECLARED_TYPE (type) == DECLARED_TYPE_TEMPLATE)) + { + QUIT; + len = TYPE_NFIELDS (type); + for (i = TYPE_N_BASECLASSES (type); i < len; i++) + if (!TYPE_FIELD_PRIVATE (type, i)) + { + need_access_label = 1; + break; + } + QUIT; + if (!need_access_label) + { + len2 = TYPE_NFN_FIELDS (type); + for (j = 0; j < len2; j++) + { + len = TYPE_FN_FIELDLIST_LENGTH (type, j); + for (i = 0; i < len; i++) + if (!TYPE_FN_FIELD_PRIVATE (TYPE_FN_FIELDLIST1 (type, j), i)) + { + need_access_label = 1; + break; + } + if (need_access_label) + break; + } + } + } + else if ((TYPE_DECLARED_TYPE (type) == DECLARED_TYPE_STRUCT) || + (TYPE_DECLARED_TYPE (type) == DECLARED_TYPE_UNION)) + { + QUIT; + len = TYPE_NFIELDS (type); + for (i = TYPE_N_BASECLASSES (type); i < len; i++) + if (TYPE_FIELD_PRIVATE (type, i) || TYPE_FIELD_PROTECTED (type, i)) + { + need_access_label = 1; + break; + } + QUIT; + if (!need_access_label) + { + len2 = TYPE_NFN_FIELDS (type); + for (j = 0; j < len2; j++) + { + QUIT; + len = TYPE_FN_FIELDLIST_LENGTH (type, j); + for (i = 0; i < len; i++) + if (TYPE_FN_FIELD_PRIVATE (TYPE_FN_FIELDLIST1 (type, j), i) || + TYPE_FN_FIELD_PROTECTED (TYPE_FN_FIELDLIST1 (type, j), i)) + { + need_access_label = 1; + break; + } + if (need_access_label) + break; + } + } + } + /* If there is a base class for this type, do not print the field that it occupies. */ @@ -548,6 +752,11 @@ c_type_print_base (type, stream, show, level) { QUIT; /* Don't print out virtual function table. */ + /* HP ANSI C++ case */ + if (TYPE_HAS_VTABLE(type) && (STREQN (TYPE_FIELD_NAME (type, i), "__vfp", 5))) + continue; + /* Other compilers */ + /* pai:: FIXME : check for has_vtable < 0 */ if (STREQN (TYPE_FIELD_NAME (type, i), "_vptr", 5) && is_cplus_marker ((TYPE_FIELD_NAME (type, i))[5])) continue; @@ -555,7 +764,7 @@ c_type_print_base (type, stream, show, level) /* If this is a C++ class we can print the various C++ section labels. */ - if (HAVE_CPLUS_STRUCT (type)) + if (HAVE_CPLUS_STRUCT (type) && need_access_label) { if (TYPE_FIELD_PROTECTED (type, i)) { @@ -612,7 +821,6 @@ c_type_print_base (type, stream, show, level) fprintf_filtered (stream, "\n"); /* C++: print out the methods */ - for (i = 0; i < len; i++) { struct fn_field *f = TYPE_FN_FIELDLIST1 (type, i); @@ -669,7 +877,10 @@ c_type_print_base (type, stream, show, level) TYPE_FN_FIELD_PHYSNAME (f, j)); break; } - else if (!is_constructor && !is_full_physname_constructor) + else if (!is_constructor && /* constructors don't have declared types */ + !is_full_physname_constructor && /* " " */ + !strstr (method_name, "operator ")) /* Not a type conversion operator */ + /* (note space -- other operators don't have it) */ { type_print (TYPE_TARGET_TYPE (TYPE_FN_FIELD_TYPE (f, j)), "", stream, -1); @@ -685,21 +896,45 @@ c_type_print_base (type, stream, show, level) cplus_demangle (mangled_name, DMGL_ANSI | DMGL_PARAMS); if (demangled_name == NULL) - fprintf_filtered (stream, "<badly mangled name '%s'>", - mangled_name); - else { - char *demangled_no_class = - strchr (demangled_name, ':'); - - if (demangled_no_class == NULL) - demangled_no_class = demangled_name; + /* in some cases (for instance with the HP demangling), + if a function has more than 10 arguments, + the demangling will fail. + Let's try to reconstruct the function signature from + the symbol information */ + if (!TYPE_FN_FIELD_STUB (f, j)) + cp_type_print_method_args (TYPE_FN_FIELD_ARGS (f, j), "", + method_name, + TYPE_FN_FIELD_STATIC_P (f, j), + stream); else + fprintf_filtered (stream, "<badly mangled name '%s'>", + mangled_name); + } + else + { + char *p; + char *demangled_no_class = demangled_name; + + while (p = strchr (demangled_no_class, ':')) { + demangled_no_class = p; if (*++demangled_no_class == ':') ++demangled_no_class; } - fputs_filtered (demangled_no_class, stream); + /* get rid of the static word appended by the demangler */ + p = strstr (demangled_no_class, " static"); + if (p != NULL) + { + int length = p - demangled_no_class; + demangled_no_static = (char *) xmalloc (length + 1); + strncpy (demangled_no_static, demangled_no_class, length); + *(demangled_no_static + length) = '\0'; + fputs_filtered (demangled_no_static, stream); + free (demangled_no_static); + } + else + fputs_filtered (demangled_no_class, stream); free (demangled_name); } @@ -710,13 +945,41 @@ c_type_print_base (type, stream, show, level) } } + if (TYPE_LOCALTYPE_PTR (type) && show >= 0) + fprintfi_filtered (level, stream, " (Local at %s:%d)\n", + TYPE_LOCALTYPE_FILE (type), + TYPE_LOCALTYPE_LINE (type)); + fprintfi_filtered (level, stream, "}"); } + if (TYPE_CODE(type) == TYPE_CODE_TEMPLATE) + goto go_back; break; case TYPE_CODE_ENUM: - fprintf_filtered (stream, "enum "); - if (TYPE_TAG_NAME (type) != NULL) + c_type_print_cv_qualifier (type, stream, 0, 1); + /* HP C supports sized enums */ + if (hp_som_som_object_present) + switch (TYPE_LENGTH (type)) + { + case 1: + fputs_filtered ("char ", stream); + break; + case 2: + fputs_filtered ("short ", stream); + break; + default: + break; + } + fprintf_filtered (stream, "enum "); + /* Print the tag name if it exists. + * Fix for CHFts22718 (RT): The aCC compiler emits + * a spurious "{unnamed struct}"/"{unnamed union}"/"{unnamed enum}" + * tag for unnamed struct/union/enum's, which we don't + * want to print. + */ + if (TYPE_TAG_NAME (type) != NULL && + strncmp(TYPE_TAG_NAME(type), "{unnamed", 8)) { fputs_filtered (TYPE_TAG_NAME (type), stream); if (show > 0) @@ -769,6 +1032,38 @@ c_type_print_base (type, stream, show, level) fprintf_filtered (stream, "<range type>"); break; + case TYPE_CODE_TEMPLATE: + /* Called on "ptype t" where "t" is a template. + * Prints the template header (with args), e.g.: + * template <class T1, class T2> class " + * and then merges with the struct/union/class code to + * print the rest of the definition. + */ + c_type_print_cv_qualifier (type, stream, 0, 1); + fprintf_filtered (stream, "template <"); + for (i = 0; i < TYPE_NTEMPLATE_ARGS(type); i++) { + struct template_arg templ_arg; + templ_arg = TYPE_TEMPLATE_ARG(type, i); + fprintf_filtered (stream, "class %s", templ_arg.name); + if (i < TYPE_NTEMPLATE_ARGS(type)-1) + fprintf_filtered (stream, ", "); + } + fprintf_filtered (stream, "> class "); + /* Yuck, factor this out to a subroutine so we can call + * it and return to the point marked with the "goback:" label... - RT + */ + goto struct_union; +go_back: + if (TYPE_NINSTANTIATIONS(type) > 0) { + fprintf_filtered (stream, "\ntemplate instantiations:\n"); + for (i = 0; i < TYPE_NINSTANTIATIONS(type); i++) { + fprintf_filtered(stream, " "); + c_type_print_base (TYPE_INSTANTIATION(type, i), stream, 0, level); + if (i < TYPE_NINSTANTIATIONS(type)-1) fprintf_filtered(stream, "\n"); + } + } + break; + default: /* Handle types not explicitly handled by the other cases, such as fundamental types. For these, just print whatever @@ -776,6 +1071,7 @@ c_type_print_base (type, stream, show, level) is no type name, then complain. */ if (TYPE_NAME (type) != NULL) { + c_type_print_cv_qualifier (type, stream, 0, 1); fputs_filtered (TYPE_NAME (type), stream); } else @@ -789,3 +1085,6 @@ c_type_print_base (type, stream, show, level) } } + + + diff --git a/gdb/c-valprint.c b/gdb/c-valprint.c index a95f44d..6bc49c3 100644 --- a/gdb/c-valprint.c +++ b/gdb/c-valprint.c @@ -474,7 +474,11 @@ c_value_print (val, stream, format, pretty) if (TYPE_CODE (type) == TYPE_CODE_PTR && TYPE_NAME (type) == NULL && TYPE_NAME (TYPE_TARGET_TYPE (type)) != NULL && - STREQ (TYPE_NAME (TYPE_TARGET_TYPE (type)), "char")) + STREQ (TYPE_NAME (TYPE_TARGET_TYPE (type)), "char") && + !TYPE_VOLATILE (TYPE_TARGET_TYPE (type)) && + (TYPE_UNSIGNED (TYPE_TARGET_TYPE (type)) || TYPE_NOSIGN (TYPE_TARGET_TYPE (type))) && + !TYPE_VOLATILE (type) && + !TYPE_CONST (type)) { /* Print nothing */ } diff --git a/gdb/coff-solib.c b/gdb/coff-solib.c index c0cfcf5..6be6869 100644 --- a/gdb/coff-solib.c +++ b/gdb/coff-solib.c @@ -94,7 +94,10 @@ coff_solib_add (arg_string, from_tty, target) 0, /* addr */ 0, /* not mainline */ 0, /* not mapped */ - 0); /* Not readnow */ + 0, /* Not readnow */ + 0, /* Not user loaded */ + 1); /* Is a solib */ + libsize -= len * 4; lib += len * 4; } diff --git a/gdb/command.c b/gdb/command.c index f368620..58af567 100644 --- a/gdb/command.c +++ b/gdb/command.c @@ -559,7 +559,38 @@ help_cmd_list (list, class, prefix, recurse, stream) help_cmd_list (*c->prefixlist, class, c->prefixname, 1, stream); } } + +/* Search the input clist for 'command'. Return the command if + found (or NULL if not), and return the number of commands + found in nfound */ + +static struct cmd_list_element * +find_cmd(command, len, clist, ignore_help_classes, nfound) + char *command; + struct cmd_list_element *clist; + int ignore_help_classes; + int *nfound; +{ + struct cmd_list_element *found, *c; + + found = (struct cmd_list_element *)NULL; + *nfound = 0; + for (c = clist; c; c = c->next) + if (!strncmp (command, c->name, len) + && (!ignore_help_classes || c->function.cfunc)) + { + found = c; + (*nfound)++; + if (c->name[len] == '\0') + { + *nfound = 1; + break; + } + } + return found; +} + /* This routine takes a line of TEXT and a CLIST in which to start the lookup. When it returns it will have incremented the text pointer past the section of text it matched, set *RESULT_LIST to point to the list in @@ -613,7 +644,10 @@ lookup_cmd_1 (text, clist, result_list, ignore_help_classes) so that "set args_foo()" doesn't get interpreted as "set args _foo()". */ for (p = *text; - *p && (isalnum(*p) || *p == '-' || *p == '_'); + *p && (isalnum(*p) || *p == '-' || *p == '_' || + (tui_version && + (*p == '+' || *p == '<' || *p == '>' || *p == '$')) || + (xdb_commands && (*p == '!' || *p == '/' || *p == '?'))); p++) ; @@ -624,32 +658,35 @@ lookup_cmd_1 (text, clist, result_list, ignore_help_classes) len = p - *text; /* *text and p now bracket the first command word to lookup (and - it's length is len). We copy this into a local temporary, - converting to lower case as we go. */ + it's length is len). We copy this into a local temporary */ + command = (char *) alloca (len + 1); for (tmp = 0; tmp < len; tmp++) { char x = (*text)[tmp]; - command[tmp] = isupper(x) ? tolower(x) : x; + command[tmp] = x; } command[len] = '\0'; /* Look it up. */ found = 0; nfound = 0; - for (c = clist; c; c = c->next) - if (!strncmp (command, c->name, len) - && (!ignore_help_classes || c->function.cfunc)) - { - found = c; - nfound++; - if (c->name[len] == '\0') - { - nfound = 1; - break; - } - } + found = find_cmd(command, len, clist, ignore_help_classes, &nfound); + + /* + ** We didn't find the command in the entered case, so lower case it + ** and search again. + */ + if (!found || nfound == 0) + { + for (tmp = 0; tmp < len; tmp++) + { + char x = command[tmp]; + command[tmp] = isupper(x) ? tolower(x) : x; + } + found = find_cmd(command, len, clist, ignore_help_classes, &nfound); + } /* If nothing matches, we have a simple failure. */ if (nfound == 0) @@ -1514,6 +1551,10 @@ _initialize_command () add_com ("shell", class_support, shell_escape, "Execute the rest of the line as a shell command. \n\ With no arguments, run an inferior shell."); + + if (xdb_commands) + add_com_alias("!", "shell", class_support, 0); + add_com ("make", class_support, make_command, "Run the ``make'' program using the rest of the line as arguments."); add_cmd ("user", no_class, show_user, diff --git a/gdb/convex-tdep.c b/gdb/convex-tdep.c index 7ff8842..8eeb2b3 100644 --- a/gdb/convex-tdep.c +++ b/gdb/convex-tdep.c @@ -431,7 +431,7 @@ set_trapped_internalvar (var, val, bitpos, bitsize, offset) If it looks like an address, print it in hex instead. */ decout (stream, type, val) - FILE *stream; + GDB_FILE *stream; struct type *type; LONGEST val; { diff --git a/gdb/corefile.c b/gdb/corefile.c index 8ffe49d..94e56eb 100644 --- a/gdb/corefile.c +++ b/gdb/corefile.c @@ -34,6 +34,8 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "dis-asm.h" #include "language.h" #include "gdb_stat.h" +#include "symfile.h" +#include "objfiles.h" extern char registers[]; @@ -76,7 +78,29 @@ core_file_command (filename, from_tty) if (!filename) (t->to_detach) (filename, from_tty); else - (t->to_open) (filename, from_tty); + { + /* Yes, we were given the path of a core file. Do we already + have a symbol file? If not, can we determine it from the + core file? If we can, do so. + */ +#ifdef HPUXHPPA + if (symfile_objfile == NULL) + { + char * symfile; + symfile = t->to_core_file_to_sym_file (filename); + if (symfile) + { + char * symfile_copy = strdup (symfile); + + make_cleanup (free, symfile_copy); + symbol_file_command (symfile_copy, from_tty); + } + else + warning ("Unknown symbols for '%s'; use the 'symbol-file' command.", filename); + } +#endif + (t->to_open) (filename, from_tty); + } else error ("GDB can't read core files on this machine."); } diff --git a/gdb/cxux-nat.c b/gdb/cxux-nat.c index 2ed1430..2c4b6f1 100644 --- a/gdb/cxux-nat.c +++ b/gdb/cxux-nat.c @@ -368,7 +368,7 @@ add_shared_symbol_files () return; } - objfile = symbol_file_add (LIBC_FILE, 0, 0, 0, 0, 1); + objfile = symbol_file_add (LIBC_FILE, 0, 0, 0, 0, 1, 0, 0); minsym = lookup_minimal_symbol (LINKS_MAP_POINTER, objfile); ld_map = (struct link_map *) @@ -384,7 +384,7 @@ add_shared_symbol_files () if (target_read_string ((CORE_ADDR)lms.l_name, &path_name, PATH_MAX, &local_errno)) { - symbol_file_add (path_name, 1, lms.l_addr, 0, 0, 0); + symbol_file_add (path_name, 1, lms.l_addr, 0, 0, 0, 0, 0); free(path_name); } } @@ -94,6 +94,16 @@ typedef bfd_vma CORE_ADDR; /* Check if a character is one of the commonly used C++ marker characters. */ extern int is_cplus_marker PARAMS ((int)); +/* use tui interface if non-zero */ +extern int tui_version; + +#if defined(TUI) +/* all invocations of TUIDO should have two sets of parens */ +#define TUIDO(x) tuiDo x +#else +#define TUIDO(x) +#endif + /* enable xdb commands if set */ extern int xdb_commands; @@ -156,6 +166,13 @@ enum language language_scm /* Scheme / Guile */ }; +enum precision_type +{ + single_precision, + double_precision, + unspecified_precision +}; + /* the cleanup list records things that have to be undone if an error happens (descriptors to be closed, memory to be freed, etc.) Each link in the chain records a function to call and an @@ -311,9 +328,41 @@ extern void wrap_here PARAMS ((char *)); extern void reinitialize_more_filter PARAMS ((void)); +/* new */ +enum streamtype +{ + afile, + astring +}; + +/* new */ +typedef struct tui_stream +{ + enum streamtype ts_streamtype; + FILE *ts_filestream; + char *ts_strbuf; + int ts_buflen; +} GDB_FILE; + +extern GDB_FILE *gdb_stdout; +extern GDB_FILE *gdb_stderr; + +#if 0 typedef FILE GDB_FILE; #define gdb_stdout stdout #define gdb_stderr stderr +#endif + +#if defined(TUI) +#include "tui.h" +#include "tuiCommand.h" +#include "tuiData.h" +#include "tuiIO.h" +#include "tuiLayout.h" +#include "tuiWin.h" +#endif + +extern void gdb_fclose PARAMS ((GDB_FILE **)); extern void gdb_flush PARAMS ((GDB_FILE *)); @@ -338,13 +387,13 @@ extern void puts_debug PARAMS ((char *prefix, char *string, char *suffix)); extern void vprintf_filtered PARAMS ((const char *, va_list)) ATTR_FORMAT(printf, 1, 0); -extern void vfprintf_filtered PARAMS ((FILE *, const char *, va_list)) +extern void vfprintf_filtered PARAMS ((GDB_FILE *, const char *, va_list)) ATTR_FORMAT(printf, 2, 0); -extern void fprintf_filtered PARAMS ((FILE *, const char *, ...)) +extern void fprintf_filtered PARAMS ((GDB_FILE *, const char *, ...)) ATTR_FORMAT(printf, 2, 3); -extern void fprintfi_filtered PARAMS ((int, FILE *, const char *, ...)) +extern void fprintfi_filtered PARAMS ((int, GDB_FILE *, const char *, ...)) ATTR_FORMAT(printf, 3, 4); extern void printf_filtered PARAMS ((const char *, ...)) @@ -356,15 +405,25 @@ extern void printfi_filtered PARAMS ((int, const char *, ...)) extern void vprintf_unfiltered PARAMS ((const char *, va_list)) ATTR_FORMAT(printf, 1, 0); -extern void vfprintf_unfiltered PARAMS ((FILE *, const char *, va_list)) +extern void vfprintf_unfiltered PARAMS ((GDB_FILE *, const char *, va_list)) ATTR_FORMAT(printf, 2, 0); -extern void fprintf_unfiltered PARAMS ((FILE *, const char *, ...)) +extern void fprintf_unfiltered PARAMS ((GDB_FILE *, const char *, ...)) ATTR_FORMAT(printf, 2, 3); extern void printf_unfiltered PARAMS ((const char *, ...)) ATTR_FORMAT(printf, 1, 2); +extern int gdb_file_isatty PARAMS ((GDB_FILE *)); + +extern GDB_FILE *gdb_file_init_astring PARAMS ((int)); + +extern void gdb_file_deallocate PARAMS ((GDB_FILE **)); + +extern char *gdb_file_get_strbuf PARAMS ((GDB_FILE *)); + +extern void gdb_file_adjust_strbuf PARAMS ((int, GDB_FILE *)); + extern void print_spaces PARAMS ((int, GDB_FILE *)); extern void print_spaces_filtered PARAMS ((int, GDB_FILE *)); @@ -431,6 +490,8 @@ extern void print_address PARAMS ((CORE_ADDR, GDB_FILE *)); extern int openp PARAMS ((char *, int, char *, int, int, char **)); +extern int source_full_path_of PARAMS ((char *, char **)); + extern void mod_path PARAMS ((char *, char **)); extern void directory_command PARAMS ((char *, int)); @@ -785,7 +846,6 @@ extern void free (); #endif /* Dynamic target-system-dependent parameters for GDB. */ - #include "gdbarch.h" /* Static target-system-dependent parameters for GDB. */ @@ -865,11 +925,11 @@ extern int extract_long_unsigned_integer PARAMS ((void *, int, LONGEST *)); extern CORE_ADDR extract_address PARAMS ((void *, int)); -extern void store_signed_integer PARAMS ((void *, int, LONGEST)); +extern void store_signed_integer PARAMS ((PTR, int, LONGEST)); -extern void store_unsigned_integer PARAMS ((void *, int, ULONGEST)); +extern void store_unsigned_integer PARAMS ((PTR, int, ULONGEST)); -extern void store_address PARAMS ((void *, int, CORE_ADDR)); +extern void store_address PARAMS ((PTR, int, LONGEST)); /* Setup definitions for host and target floating point formats. We need to consider the format for `float', `double', and `long double' for both target @@ -989,13 +1049,14 @@ struct cmd_list_element; extern void (*init_ui_hook) PARAMS ((char *argv0)); extern void (*command_loop_hook) PARAMS ((void)); extern void (*fputs_unfiltered_hook) PARAMS ((const char *linebuffer, - FILE *stream)); + GDB_FILE *stream)); extern void (*print_frame_info_listing_hook) PARAMS ((struct symtab *s, int line, int stopline, int noerror)); +extern struct frame_info *parse_frame_specification PARAMS ((char *frame_exp)); extern int (*query_hook) PARAMS ((const char *, va_list)); extern void (*warning_hook) PARAMS ((const char *, va_list)); -extern void (*flush_hook) PARAMS ((FILE *stream)); +extern void (*flush_hook) PARAMS ((GDB_FILE *stream)); extern void (*create_breakpoint_hook) PARAMS ((struct breakpoint *b)); extern void (*delete_breakpoint_hook) PARAMS ((struct breakpoint *bpt)); extern void (*modify_breakpoint_hook) PARAMS ((struct breakpoint *bpt)); diff --git a/gdb/demangle.c b/gdb/demangle.c index 2d1ad2a..9c5e521 100644 --- a/gdb/demangle.c +++ b/gdb/demangle.c @@ -31,7 +31,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* Select the default C++ demangling style to use. The default is "auto", which allows gdb to attempt to pick an appropriate demangling style for the executable it has loaded. It can be set to a specific style ("gnu", - "lucid", "arm", etc.) in which case gdb will never attempt to do auto + "lucid", "arm", "hp", etc.) in which case gdb will never attempt to do auto selection of the style unless you do an explicit "set demangle auto". To select one of these as the default, set DEFAULT_DEMANGLING_STYLE in the appropriate target configuration file. */ @@ -67,6 +67,12 @@ static const struct demangler {ARM_DEMANGLING_STYLE_STRING, arm_demangling, "ARM style demangling"}, + {HP_DEMANGLING_STYLE_STRING, + hp_demangling, + "HP (aCC) style demangling"}, + {EDG_DEMANGLING_STYLE_STRING, + edg_demangling, + "EDG style demangling"}, {NULL, unknown_demangling, NULL} }; diff --git a/gdb/f-lang.c b/gdb/f-lang.c index 1590ac8..4ee66d5 100644 --- a/gdb/f-lang.c +++ b/gdb/f-lang.c @@ -144,7 +144,7 @@ f_emit_char (c, stream, quoter) static void f_printchar (c, stream) int c; - FILE *stream; + GDB_FILE *stream; { fputs_filtered ("'", stream); LA_EMIT_CHAR (c, stream, '\''); @@ -160,7 +160,7 @@ f_printchar (c, stream) static void f_printstr (stream, string, length, width, force_ellipses) - FILE *stream; + GDB_FILE *stream; char *string; unsigned int length; int width; @@ -176,7 +176,7 @@ f_printstr (stream, string, length, width, force_ellipses) if (length == 0) { - fputs_filtered ("''", stdout); + fputs_filtered ("''", gdb_stdout); return; } diff --git a/gdb/f-typeprint.c b/gdb/f-typeprint.c index 4bca773..09bb3eb 100644 --- a/gdb/f-typeprint.c +++ b/gdb/f-typeprint.c @@ -40,17 +40,19 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include <errno.h> #if 0 /* Currently unused */ -static void f_type_print_args PARAMS ((struct type *, FILE *)); +static void f_type_print_args PARAMS ((struct type *, GDB_FILE *)); #endif -static void print_equivalent_f77_float_type PARAMS ((struct type *, FILE *)); +static void print_equivalent_f77_float_type PARAMS ((struct type *, + GDB_FILE *)); -static void f_type_print_varspec_suffix PARAMS ((struct type *, FILE *, +static void f_type_print_varspec_suffix PARAMS ((struct type *, GDB_FILE *, int, int, int)); -void f_type_print_varspec_prefix PARAMS ((struct type *, FILE *, int, int)); +void f_type_print_varspec_prefix PARAMS ((struct type *, GDB_FILE *, + int, int)); -void f_type_print_base PARAMS ((struct type *, FILE *, int, int)); +void f_type_print_base PARAMS ((struct type *, GDB_FILE *, int, int)); /* LEVEL is the depth to indent lines by. */ @@ -59,7 +61,7 @@ void f_print_type (type, varstring, stream, show, level) struct type *type; char *varstring; - FILE *stream; + GDB_FILE *stream; int show; int level; { @@ -102,7 +104,7 @@ f_print_type (type, varstring, stream, show, level) void f_type_print_varspec_prefix (type, stream, show, passed_a_ptr) struct type *type; - FILE *stream; + GDB_FILE *stream; int show; int passed_a_ptr; { @@ -160,7 +162,7 @@ f_type_print_varspec_prefix (type, stream, show, passed_a_ptr) static void f_type_print_args (type, stream) struct type *type; - FILE *stream; + GDB_FILE *stream; { int i; struct type **args; @@ -200,7 +202,7 @@ f_type_print_args (type, stream) static void f_type_print_varspec_suffix (type, stream, show, passed_a_ptr, demangled_args) struct type *type; - FILE *stream; + GDB_FILE *stream; int show; int passed_a_ptr; int demangled_args; @@ -312,7 +314,7 @@ f_type_print_varspec_suffix (type, stream, show, passed_a_ptr, demangled_args) static void print_equivalent_f77_float_type (type, stream) struct type *type; - FILE *stream; + GDB_FILE *stream; { /* Override type name "float" and make it the appropriate real. XLC stupidly outputs -12 as a type @@ -337,7 +339,7 @@ print_equivalent_f77_float_type (type, stream) void f_type_print_base (type, stream, show, level) struct type *type; - FILE *stream; + GDB_FILE *stream; int show; int level; { diff --git a/gdb/fork-child.c b/gdb/fork-child.c index 243bed4..e262f89 100644 --- a/gdb/fork-child.c +++ b/gdb/fork-child.c @@ -33,12 +33,67 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include <unistd.h> #endif -extern char **environ; +#define DEBUGGING 0 +/* This just gets used as a default if we can't find SHELL */ #ifndef SHELL_FILE #define SHELL_FILE "/bin/sh" #endif +extern char **environ; + +/* This function breaks up an argument string into an argument + * vector suitable for passing to execvp(). + * E.g., on "run a b c d" this routine would get as input + * the string "a b c d", and as output it would fill in argv with + * the four arguments "a", "b", "c", "d". + */ +static void +breakup_args ( + scratch, + argv) +char *scratch; +char **argv; +{ + char *cp = scratch; + +#if DEBUGGING + printf("breakup_args: input = %s\n", scratch); +#endif + for (;;) { + + /* Scan past leading separators */ + while (*cp == ' ' || *cp == '\t' || *cp == '\n') { + cp++; + } + + /* Break if at end of string */ + if (*cp == '\0') break; + + /* Take an arg */ + *argv++ = cp; + + /* Scan for next arg separator */ + cp = strchr (cp, ' '); + if (cp == NULL) + cp = strchr (cp, '\t'); + if (cp == NULL) + cp = strchr (cp, '\n'); + + /* No separators => end of string => break */ + if (cp == NULL) + break; + + /* Replace the separator with a terminator */ + *cp++ = '\0'; + } + + /* execv requires a null-terminated arg vector */ + *argv = NULL; + +} + + /* Start an inferior Unix child process and sets inferior_pid to its pid. EXEC_FILE is the file to run. ALLARGS is a string containing the arguments to the program. @@ -47,12 +102,13 @@ extern char **environ; void fork_inferior (exec_file, allargs, env, traceme_fun, init_trace_fun, - shell_file) + pre_trace_fun, shell_file) char *exec_file; char *allargs; char **env; void (*traceme_fun) PARAMS ((void)); - int (*init_trace_fun) PARAMS ((int)); + void (*init_trace_fun) PARAMS ((int)); + void (*pre_trace_fun) PARAMS ((void)); char *shell_file; { int pid; @@ -65,22 +121,31 @@ fork_inferior (exec_file, allargs, env, traceme_fun, init_trace_fun, to you in the parent process. It's only used by humans for debugging. */ static int debug_setpgrp = 657473; char **save_our_env; + int shell = 0; + char **argv; + char *tryname; /* If no exec file handed to us, get it from the exec-file command -- with a good, common error message if none is specified. */ if (exec_file == 0) exec_file = get_exec_file(1); - /* The user might want tilde-expansion, and in general probably wants - the program to behave the same way as if run from - his/her favorite shell. So we let the shell run it for us. - FIXME-maybe, we might want a "set shell" command so the user can change - the shell from within GDB (if so, change callers which pass in a non-NULL - shell_file too). */ - if (shell_file == NULL) - shell_file = getenv ("SHELL"); - if (shell_file == NULL) - shell_file = default_shell_file; + /* STARTUP_WITH_SHELL is defined in inferior.h. + * If 0, we'll just do a fork/exec, no shell, so don't + * bother figuring out what shell. + */ + if (STARTUP_WITH_SHELL) { + /* Figure out what shell to start up the user program under. */ + if (shell_file == NULL) + shell_file = getenv ("SHELL"); + if (shell_file == NULL) + shell_file = default_shell_file; + shell = 1; + } + +#if DEBUGGING + printf("shell is %s\n", shell_file); +#endif /* Multiplying the length of exec_file by 4 is to account for the fact that it may expand when quoted; it is a worst-case number based on @@ -95,20 +160,40 @@ fork_inferior (exec_file, allargs, env, traceme_fun, init_trace_fun, shell_command = (char *) alloca (len); shell_command[0] = '\0'; #endif - strcat (shell_command, "exec "); - /* Now add exec_file, quoting as necessary. */ - { + if (!shell) { + /* We're going to call execvp. Create argv */ + /* Largest case: every other character is a separate arg */ +#if DEBUGGING + printf("allocating argv, length = %d\n", + ( + (strlen (allargs) + 1) / (unsigned) 2 + + 2 + ) * sizeof (*argv) + ); +#endif + argv = (char **) xmalloc(((strlen (allargs) + 1) / (unsigned) 2 + 2) * sizeof (*argv)); + argv[0] = exec_file; + breakup_args(allargs, &argv[1]); + + } else { + + /* We're going to call a shell */ + + /* Now add exec_file, quoting as necessary. */ + char *p; int need_to_quote; + strcat (shell_command, "exec "); + /* Quoting in this style is said to work with all shells. But csh on IRIX 4.0.1 can't deal with it. So we only quote it if we need to. */ p = exec_file; while (1) { - switch (*p) + switch (*p) { case '\'': case '"': @@ -137,8 +222,8 @@ fork_inferior (exec_file, allargs, env, traceme_fun, init_trace_fun, end_scan: if (need_to_quote) { - strcat (shell_command, "'"); - for (p = exec_file; *p != '\0'; ++p) + strcat (shell_command, "'"); + for (p = exec_file; *p != '\0'; ++p) { if (*p == '\'') strcat (shell_command, "'\\''"); @@ -149,10 +234,11 @@ fork_inferior (exec_file, allargs, env, traceme_fun, init_trace_fun, } else strcat (shell_command, exec_file); - } + + strcat (shell_command, " "); + strcat (shell_command, allargs); - strcat (shell_command, " "); - strcat (shell_command, allargs); + } /* exec is said to fail if the executable is open. */ close_exec_file (); @@ -174,6 +260,12 @@ fork_inferior (exec_file, allargs, env, traceme_fun, init_trace_fun, gdb_flush (gdb_stdout); gdb_flush (gdb_stderr); + /* If there's any initialization of the target layers that must happen + to prepare to handle the child we're about fork, do it now... + */ + if (pre_trace_fun != NULL) + (*pre_trace_fun) (); + #if defined(USG) && !defined(HAVE_VFORK) pid = fork (); #else @@ -209,6 +301,14 @@ fork_inferior (exec_file, allargs, env, traceme_fun, init_trace_fun, /* "Trace me, Dr. Memory!" */ (*traceme_fun) (); + /* The call above set this process (the "child") as debuggable + * by the original gdb process (the "parent"). Since processes + * (unlike people) can have only one parent, if you are + * debugging gdb itself (and your debugger is thus _already_ the + * controller/parent for this child), code from here on out + * is undebuggable. Indeed, you probably got an error message + * saying "not parent". Sorry--you'll have to use print statements! + */ /* There is no execlpe call, so we have to set the environment for our child in the global variable. If we've vforked, this @@ -216,12 +316,65 @@ fork_inferior (exec_file, allargs, env, traceme_fun, init_trace_fun, in the parent. By the way, yes we do need to look down the path to find $SHELL. Rich Pixley says so, and I agree. */ environ = env; - execlp (shell_file, shell_file, "-c", shell_command, (char *)0); - fprintf_unfiltered (gdb_stderr, "Cannot exec %s: %s.\n", shell_file, - safe_strerror (errno)); - gdb_flush (gdb_stderr); - _exit (0177); + /* If we decided above to start up with a shell, + * we exec the shell, + * "-c" says to interpret the next arg as a shell command + * to execute, and this command is "exec <target-program> <args>". + * "-f" means "fast startup" to the c-shell, which means + * don't do .cshrc file. Doing .cshrc may cause fork/exec + * events which will confuse debugger start-up code. + */ + if (shell) { +#if 0 + + /* HP change is problematic. The -f option has different meanings + for different shells. It is particularly inappropriate for + bourne shells. */ + execlp (shell_file, shell_file, "-f", "-c", shell_command, (char *)0); +#else + execlp (shell_file, shell_file, "-c", shell_command, (char *)0); +#endif + + + /* If we get here, it's an error */ + fprintf_unfiltered (gdb_stderr, "Cannot exec %s: %s.\n", shell_file, + safe_strerror (errno)); + gdb_flush (gdb_stderr); + _exit (0177); + } else { + /* Otherwise, we directly exec the target program with execvp. */ + int i; + char * errstring; +#if DEBUGGING + printf("about to exec target, exec_file = %s\n", exec_file); + i = 0; + while (argv[i] != NULL) { + printf("strlen(argv[%d]) is %d\n", i, strlen(argv[i])); + printf("argv[%d] is %s\n", i, argv[i]); + i++; + } +#endif + execvp (exec_file, argv); + + /* If we get here, it's an error */ + errstring = safe_strerror (errno); + fprintf_unfiltered (gdb_stderr, "Cannot exec %s ", exec_file); + + i = 1; + while (argv[i] != NULL) { + if (i != 1) + fprintf_unfiltered (gdb_stderr, " "); + fprintf_unfiltered (gdb_stderr, "%s", argv[i]); + i++; + } + fprintf_unfiltered (gdb_stderr, ".\n"); + /* This extra info seems to be useless + fprintf_unfiltered (gdb_stderr, "Got error %s.\n", errstring); + */ + gdb_flush (gdb_stderr); + _exit (0177); + } } /* Restore our environment in case a vforked child clob'd it. */ @@ -234,8 +387,7 @@ fork_inferior (exec_file, allargs, env, traceme_fun, init_trace_fun, /* Now that we have a child process, make it our target, and initialize anything target-vector-specific that needs initializing. */ - /* Note that pid may be modified by this function. */ - inferior_pid = pid = (*init_trace_fun)(pid); + (*init_trace_fun)(pid); /* We are now in the child process of interest, having exec'd the correct program, and are poised at the first instruction of the @@ -251,6 +403,137 @@ fork_inferior (exec_file, allargs, env, traceme_fun, init_trace_fun, #endif } +/* An inferior Unix process CHILD_PID has been created by a call to + fork() (or variants like vfork). It is presently stopped, and waiting + to be resumed. clone_and_follow_inferior will fork the debugger, + and that clone will "follow" (attach to) CHILD_PID. The original copy + of the debugger will not touch CHILD_PID again. + + Also, the original debugger will set FOLLOWED_CHILD FALSE, while the + clone will set it TRUE. + */ +void +clone_and_follow_inferior (child_pid, followed_child) + int child_pid; + int *followed_child; +{ + extern int auto_solib_add; + + int debugger_pid; + int status; + char pid_spelling [100]; /* Arbitrary but sufficient length. */ + + /* This semaphore is used to coordinate the two debuggers' handoff + of CHILD_PID. The original debugger will detach from CHILD_PID, + and then the clone debugger will attach to it. (It must be done + this way because on some targets, only one process at a time can + trace another. Thus, the original debugger must relinquish its + tracing rights before the clone can pick them up.) + */ +#define SEM_TALK (1) +#define SEM_LISTEN (0) + int handoff_semaphore[2]; /* Original "talks" to [1], clone "listens" to [0] */ + int talk_value = 99; + int listen_value; + + /* Set debug_fork then attach to the child while it sleeps, to debug. */ + static int debug_fork = 0; + + /* It is generally good practice to flush any possible pending stdio + output prior to doing a fork, to avoid the possibility of both the + parent and child flushing the same data after the fork. */ + + gdb_flush (gdb_stdout); + gdb_flush (gdb_stderr); + + /* Open the semaphore pipes. + */ + status = pipe (handoff_semaphore); + if (status < 0) + error ("error getting pipe for handoff semaphore"); + + /* Clone the debugger. */ +#if defined(USG) && !defined(HAVE_VFORK) + debugger_pid = fork (); +#else + if (debug_fork) + debugger_pid = fork (); + else + debugger_pid = vfork (); +#endif + + if (debugger_pid < 0) + perror_with_name ("fork"); + + /* Are we the original debugger? If so, we must relinquish all claims + to CHILD_PID. */ + if (debugger_pid != 0) + { + char signal_spelling[100]; /* Arbitrary but sufficient length */ + + /* Detach from CHILD_PID. Deliver a "stop" signal when we do, though, + so that it remains stopped until the clone debugger can attach + to it. + */ + detach_breakpoints (child_pid); + + sprintf (signal_spelling, "%d", target_signal_to_host (TARGET_SIGNAL_STOP)); + target_require_detach (child_pid, signal_spelling, 1); + + /* Notify the clone debugger that it should attach to CHILD_PID. */ + write (handoff_semaphore[SEM_TALK], &talk_value, sizeof (talk_value)); + + *followed_child = 0; + } + + /* We're the child. */ + else + { + if (debug_fork) + sleep (debug_fork); + + /* The child (i.e., the cloned debugger) must now attach to + CHILD_PID. inferior_pid is presently set to the parent process + of the fork, while CHILD_PID should be the child process of the + fork. + + Wait until the original debugger relinquishes control of CHILD_PID, + though. + */ + read (handoff_semaphore[SEM_LISTEN], &listen_value, sizeof (listen_value)); + + /* Note that we DON'T want to actually detach from inferior_pid, + because that would allow it to run free. The original + debugger wants to retain control of the process. So, we + just reset inferior_pid to CHILD_PID, and then ensure that all + breakpoints are really set in CHILD_PID. + */ + target_mourn_inferior (); + + /* Ask the tty subsystem to switch to the one we specified earlier + (or to share the current terminal, if none was specified). */ + + new_tty (); + + dont_repeat (); + sprintf(pid_spelling, "%d", child_pid); + target_require_attach (pid_spelling, 1); + + /* Perform any necessary cleanup, after attachment. (This form + of attaching can behave differently on some targets than the + standard method, where a process formerly not under debugger + control was suddenly attached to..) + */ + target_post_follow_inferior_by_clone (); + + *followed_child = 1; + } + + /* Discard the handoff sempahore. */ + (void) close (handoff_semaphore[SEM_LISTEN]); + (void) close (handoff_semaphore[SEM_TALK]); +} + /* Accept NTRAPS traps from the inferior. */ void @@ -270,6 +553,13 @@ startup_inferior (ntraps) terminal_initted = 0; + if (STARTUP_WITH_SHELL) + inferior_ignoring_startup_exec_events = ntraps; + else + inferior_ignoring_startup_exec_events = 0; + inferior_ignoring_leading_exec_events = + target_reported_exec_events_per_exec_call () - 1; + #ifdef STARTUP_INFERIOR STARTUP_INFERIOR (pending_execs); #else @@ -301,9 +591,12 @@ startup_inferior (ntraps) terminal_initted = 1; } - if (0 == --pending_execs) + + pending_execs = pending_execs - 1; + if (0 == pending_execs) break; - resume (0, TARGET_SIGNAL_0); /* Just make it go on */ + + resume (0, TARGET_SIGNAL_0); /* Just make it go on */ } } #endif /* STARTUP_INFERIOR */ diff --git a/gdb/gdbthread.h b/gdb/gdbthread.h index 20aee14..653dd65 100644 --- a/gdb/gdbthread.h +++ b/gdb/gdbthread.h @@ -23,6 +23,9 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef GDBTHREAD_H #define GDBTHREAD_H +/* For bpstat */ +#include "breakpoint.h" + extern void init_thread_list PARAMS ((void)); extern void add_thread PARAMS ((int pid)); @@ -40,12 +43,14 @@ extern int valid_thread_id PARAMS ((int thread)); extern void load_infrun_state PARAMS ((int, CORE_ADDR *, CORE_ADDR *, char **, int *, struct breakpoint **, struct breakpoint **, CORE_ADDR *, - CORE_ADDR *, CORE_ADDR *, int *, int *)); + CORE_ADDR *, CORE_ADDR *, int *, int *, + int *, bpstat *, int *)); extern void save_infrun_state PARAMS ((int, CORE_ADDR, CORE_ADDR, char *, int, struct breakpoint *, struct breakpoint *, CORE_ADDR, - CORE_ADDR, CORE_ADDR, int, int)); + CORE_ADDR, CORE_ADDR, int, int, + int, bpstat, int)); /* Commands with a prefix of `thread'. */ extern struct cmd_list_element *thread_cmd_list; diff --git a/gdb/gdbtk-hooks.c b/gdb/gdbtk-hooks.c index 54eacb6..7bccc4f 100644 --- a/gdb/gdbtk-hooks.c +++ b/gdb/gdbtk-hooks.c @@ -106,7 +106,7 @@ gdbtk_readline_begin (char *format, ...); gdbtk_readline_begin (); #endif static void gdbtk_readline_end PARAMS ((void)); -static void gdbtk_flush PARAMS ((FILE *)); +static void gdbtk_flush PARAMS ((GDB_FILE *)); static void gdbtk_pre_add_symbol PARAMS ((char *)); static void gdbtk_print_frame_info PARAMS ((struct symtab *, int, int, int)); static void gdbtk_post_add_symbol PARAMS ((void)); @@ -120,7 +120,7 @@ static void gdbtk_context_change PARAMS ((int)); * See note there for details. */ -void gdbtk_fputs PARAMS ((const char *, FILE *)); +void gdbtk_fputs PARAMS ((const char *, GDB_FILE *)); int gdbtk_load_hash PARAMS ((char *, unsigned long)); static void breakpoint_notify PARAMS ((struct breakpoint *, const char *)); @@ -214,7 +214,7 @@ int gdbtk_two_elem_cmd (cmd_name, argv1) static void gdbtk_flush (stream) - FILE *stream; + GDB_FILE *stream; { #if 0 /* Force immediate screen update */ @@ -238,7 +238,7 @@ gdbtk_flush (stream) * We place the data into the result_ptr, either as a string, * or a list, depending whether the GDBTK_MAKES_LIST bit is set. * 3) The GDBTK_TO_RESULT flag is unset - We route the data to gdbtk_tcl_fputs - * UNLESS it was coming to stderr. Then we place it in the result_ptr + * UNLESS it was coming to gdb_stderr. Then we place it in the result_ptr * anyway, so it can be dealt with. * */ @@ -246,7 +246,7 @@ gdbtk_flush (stream) void gdbtk_fputs (ptr, stream) const char *ptr; - FILE *stream; + GDB_FILE *stream; { in_fputs = 1; @@ -329,7 +329,7 @@ pc_changed() static void tk_command_loop () { - extern GDB_FILE *instream; + extern FILE *instream; /* We no longer want to use stdin as the command input stream */ instream = NULL; diff --git a/gdb/gdbtk.c b/gdb/gdbtk.c index 1ebf902..a15eccb 100644 --- a/gdb/gdbtk.c +++ b/gdb/gdbtk.c @@ -99,7 +99,7 @@ int gdbtk_test PARAMS ((char *)); * way errors will go to stdout. */ -extern void gdbtk_fputs PARAMS ((const char *, FILE *)); +extern void gdbtk_fputs PARAMS ((const char *, GDB_FILE *)); /* Handle for TCL interpreter */ Tcl_Interp *gdbtk_interp = NULL; @@ -569,14 +569,17 @@ gdbtk_find_main"; fputs_unfiltered_hook = NULL; /* Force errors to stdout/stderr */ if (getenv("GDBTK_LIBRARY")) { - fprintf_unfiltered (stderr, "Unable to find main.tcl in %s\n",getenv("GDBTK_LIBRARY")); - fprintf_unfiltered (stderr, + fprintf_unfiltered (gdb_stderr, "Unable to find main.tcl in %s\n", + getenv("GDBTK_LIBRARY")); + fprintf_unfiltered (gdb_stderr, "Please set GDBTK_LIBRARY to a path that includes the GDB tcl files.\n"); } else { - fprintf_unfiltered (stderr, "Unable to find main.tcl in %s\n", GDBTK_LIBRARY); - fprintf_unfiltered (stderr, "You might want to set GDBTK_LIBRARY\n"); + fprintf_unfiltered (gdb_stderr, + "Unable to find main.tcl in %s\n", GDBTK_LIBRARY); + fprintf_unfiltered (gdb_stderr, + "You might want to set GDBTK_LIBRARY\n"); } error(""); } diff --git a/gdb/gnu-nat.c b/gdb/gnu-nat.c index 1cad0aa..156d981 100644 --- a/gdb/gnu-nat.c +++ b/gdb/gnu-nat.c @@ -1936,7 +1936,7 @@ gnu_create_inferior (exec_file, allargs, env) inf_debug (inf, "creating inferior"); - fork_inferior (exec_file, allargs, env, trace_me, attach_to_child, NULL); + fork_inferior (exec_file, allargs, env, trace_me, attach_to_child, NULL, NULL); inf_update_signal_thread (inf); inf_set_traced (inf, inf->want_signals); diff --git a/gdb/hp-psymtab-read.c b/gdb/hp-psymtab-read.c new file mode 100644 index 0000000..b664ec7 --- /dev/null +++ b/gdb/hp-psymtab-read.c @@ -0,0 +1,2425 @@ +/* Read hp debug symbols and convert to internal format, for GDB. + Copyright 1993, 1996 Free Software Foundation, Inc. + + This file is part of GDB. + + 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. + + Written by the Center for Software Science at the University of Utah + and by Cygnus Support. */ + +/* Common include file for hp_symtab_read.c and hp_psymtab_read.c. + * This has nested includes of a bunch of stuff. + */ +#include "hpread.h" +#include "demangle.h" + +/* To generate dumping code, uncomment this define. The dumping + * itself is controlled by routine-local statics called "dumping". + */ +/* #define DUMPING 1 */ + +/* To use the quick look-up tables, uncomment this define. + */ +#define QUICK_LOOK_UP 0 + +/* To call PXDB to process un-processed files, uncomment this define. + */ +#define USE_PXDB 1 + +/* Forward procedure declarations */ + +void hpread_symfile_init + PARAMS ((struct objfile *)); + +void hpread_build_psymtabs + PARAMS ((struct objfile *, struct section_offsets *, int)); + +void hpread_symfile_finish + PARAMS ((struct objfile *)); + +static union dnttentry *hpread_get_gntt + PARAMS ((int, struct objfile *)); + +static unsigned long hpread_get_textlow + PARAMS ((int, int, struct objfile *, int)); + +static struct partial_symtab *hpread_start_psymtab + PARAMS ((struct objfile *, struct section_offsets *, char *, CORE_ADDR, int, + struct partial_symbol **, struct partial_symbol **)); + +static struct partial_symtab *hpread_end_psymtab + PARAMS ((struct partial_symtab *, char **, int, int, CORE_ADDR, + struct partial_symtab **, int)); + +/* End of forward routine declarations */ + +#ifdef USE_PXDB + +/************************************************** + * + * NOTE use of system files! May not be portable. + */ + +#define PXDB_SVR4 "/opt/langtools/bin/pxdb" +#define PXDB_BSD "/usr/bin/pxdb" + +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +/* check for the existance of a file, given its full pathname */ +int +file_exists (filename) + char *filename; +{ + if (filename) + return (access (filename, F_OK) == 0); + return 0; +} + + +/* Translate from the "hp_language" enumeration in hp-symtab.h + used in the debug info to gdb's generic enumeration in defs.h. */ +static enum language +trans_lang (in_lang) + enum hp_language in_lang; +{ + if (in_lang == HP_LANGUAGE_C) + return language_c; + + else if (in_lang == HP_LANGUAGE_CPLUSPLUS) + return language_cplus; + + else if (in_lang == HP_LANGUAGE_F77) + return language_fortran; + + else return language_unknown; +} + +static char main_string[] = "main"; + + +/* Call PXDB to process our file. + + Approach copied from DDE's "dbgk_run_pxdb". Note: we + don't check for BSD location of pxdb, nor for existance + of pxdb itself, etc. + + NOTE: uses system function and string functions directly. + + Return value: 1 if ok, 0 if not */ +int +hpread_call_pxdb (file_name) + char *file_name; +{ + char *p; + int status; + int retval; + + if (file_exists (PXDB_SVR4)) + { + p = malloc (strlen (PXDB_SVR4) + strlen (file_name) + 2); + strcpy (p, PXDB_SVR4); + strcat (p, " "); + strcat (p, file_name); + + warning ("File not processed by pxdb--about to process now.\n"); + status = system (p); + + retval = (status == 0); + } + else + { + warning ("pxdb not found at standard location: /opt/langtools/bin\ngdb will not be able to debug %s.\nPlease install pxdb at the above location and then restart gdb.\nYou can also run pxdb on %s with the command\n\"pxdb %s\" and then restart gdb.", file_name, file_name, file_name); + + retval = 0; + } + return retval; +} /* hpread_call_pxdb */ + + +/* Return 1 if the file turns out to need pre-processing + by PXDB, and we have thus called PXDB to do this processing + and the file therefore needs to be re-loaded. Otherwise + return 0. */ +int +hpread_pxdb_needed (sym_bfd) + bfd *sym_bfd; +{ + asection *pinfo_section, *debug_section, *header_section; + unsigned int do_pxdb; + char *buf; + bfd_size_type header_section_size; + + unsigned long tmp; + unsigned int pxdbed; + + header_section = bfd_get_section_by_name (sym_bfd, "$HEADER$"); + if (!header_section) + { + return 0; /* No header at all, can't recover... */ + } + + debug_section = bfd_get_section_by_name (sym_bfd, "$DEBUG$"); + pinfo_section = bfd_get_section_by_name (sym_bfd, "$PINFO$"); + + if (pinfo_section && !debug_section) + { + /* Debug info with DOC, has different header format. */ + /* this only happens if the file was pxdbed and compiled optimized + otherwise the PINFO section is not there. */ + header_section_size = bfd_section_size (objfile->obfd, header_section); + + if (header_section_size == (bfd_size_type)sizeof (DOC_info_PXDB_header)) + { + buf = alloca (sizeof (DOC_info_PXDB_header)); + + if (!bfd_get_section_contents (sym_bfd, + header_section, + buf, 0, + header_section_size)) + error ("bfd_get_section_contents\n"); + + tmp = bfd_get_32 (sym_bfd, (bfd_byte *)(buf + sizeof (int)*4)); + pxdbed = (tmp >> 31) & 0x1; + + if (!pxdbed) error ("file debug header info invalid\n"); + do_pxdb = 0; + } + + else error ("invalid $HEADER$ size in executable \n"); + } + + else { + + /* this can be three different cases: + 1. pxdbed and not doc + - DEBUG and HEADER sections are there + - header is PXDB_header type + - pxdbed flag is set to 1 + + 2. not pxdbed and doc + - DEBUG and HEADER sections are there + - header is DOC_info_header type + - pxdbed flag is set to 0 + + 3. not pxdbed and not doc + - DEBUG and HEADER sections are there + - header is XDB_header type + - pxdbed flag is set to 0 + + NOTE: the pxdbed flag is meaningful also in the not + already pxdb processed version of the header, + because in case on non-already processed by pxdb files + that same bit in the header would be always zero. + Why? Because the bit is the leftmost bit of a word + which contains a 'length' which is always a positive value + so that bit is never set to 1 (otherwise it would be negative) + + Given the above, we have two choices : either we ignore the + size of the header itself and just look at the pxdbed field, + or we check the size and then we (for safety and paranoia related + issues) check the bit. + The first solution is used by DDE, the second by PXDB itself. + I am using the second one here, because I already wrote it, + and it is the end of a long day. + Also, using the first approach would still involve size issues + because we need to read in the contents of the header section, and + give the correct amount of stuff we want to read to the + get_bfd_section_contents function. */ + + /* decide which case depending on the size of the header section. + The size is as defined in hp-symtab.h */ + + header_section_size = bfd_section_size (objfile->obfd, header_section); + + if (header_section_size == (bfd_size_type)sizeof (PXDB_header)) /* pxdb and not doc*/ + { + + buf = alloca (sizeof(PXDB_header)); + if (!bfd_get_section_contents (sym_bfd, + header_section, + buf, 0, + header_section_size)) + error ("bfd_get_section_contents\n"); + + tmp = bfd_get_32 (sym_bfd, (bfd_byte *)(buf + sizeof(int)*3)); + pxdbed = (tmp >> 31) & 0x1; + + if (pxdbed) + do_pxdb = 0; + else error ("file debug header invalid\n"); + } + else /*not pxdbed and doc OR not pxdbed and non doc*/ + do_pxdb = 1; + } + + if (do_pxdb) + { + return 1; + } + else + { + return 0; + } +} /* hpread_pxdb_needed */ + +#endif + +#ifdef QUICK_LOOK_UP + +/******************** + * + * This flag can be set to zero to use the old + * style psymtab (build from a scan of the LNTT) + * or to one to try to use the quick look-up + * tables. + */ +int psym_new_style = 1; + + + +/* Code to handle quick lookup-tables follows */ + + +/* Some useful macros */ +#define VALID_FILE(i) ((i) < pxdb_header_p->fd_entries) +#define VALID_MODULE(i) ((i) < pxdb_header_p->md_entries) +#define VALID_PROC(i) ((i) < pxdb_header_p->pd_entries) +#define VALID_CLASS(i) ((i) < pxdb_header_p->cd_entries) + +#define FILE_START(i) (qFD[i].adrStart) +#define MODULE_START(i) (qMD[i].adrStart) +#define PROC_START(i) (qPD[i].adrStart) + +#define FILE_END(i) (qFD[i].adrEnd) +#define MODULE_END(i) (qMD[i].adrEnd) +#define PROC_END(i) (qPD[i].adrEnd) + +#define FILE_ISYM(i) (qFD[i].isym) +#define MODULE_ISYM(i) (qMD[i].isym) +#define PROC_ISYM(i) (qPD[i].isym) + +#define VALID_CURR_FILE (curr_fd < pxdb_header_p->fd_entries) +#define VALID_CURR_MODULE (curr_md < pxdb_header_p->md_entries) +#define VALID_CURR_PROC (curr_pd < pxdb_header_p->pd_entries) +#define VALID_CURR_CLASS (curr_cd < pxdb_header_p->cd_entries) + +#define CURR_FILE_START (qFD[curr_fd].adrStart) +#define CURR_MODULE_START (qMD[curr_md].adrStart) +#define CURR_PROC_START (qPD[curr_pd].adrStart) + +#define CURR_FILE_END (qFD[curr_fd].adrEnd) +#define CURR_MODULE_END (qMD[curr_md].adrEnd) +#define CURR_PROC_END (qPD[curr_pd].adrEnd) + +#define CURR_FILE_ISYM (qFD[curr_fd].isym) +#define CURR_MODULE_ISYM (qMD[curr_md].isym) +#define CURR_PROC_ISYM (qPD[curr_pd].isym) + +#define TELL_OBJFILE \ + do { \ + if( !told_objfile ) { \ + told_objfile = 1; \ + warning ("\nIn object file \"%s\":\n", \ + objfile->name); \ + } \ + } while (0) + + + +/* Keeping track of the start/end symbol table (LNTT) indices of + psymtabs created so far */ + +typedef struct { + int start; + int end; +} pst_syms_struct; + +static pst_syms_struct * pst_syms_array = 0; + +static pst_syms_count = 0; +static pst_syms_size = 0; + +/* used by the TELL_OBJFILE macro */ +static boolean told_objfile = 0; + +/* Set up psymtab symbol index stuff */ +static void +init_pst_syms () +{ + pst_syms_count = 0; + pst_syms_size = 20; + pst_syms_array = (pst_syms_struct *) xmalloc (20 * sizeof (pst_syms_struct)); +} + +/* Clean up psymtab symbol index stuff */ +static void +clear_pst_syms () +{ + pst_syms_count = 0; + pst_syms_size = 0; + free (pst_syms_array); + pst_syms_array = 0; +} + +/* Add information about latest psymtab to symbol index table */ +static void +record_pst_syms (start_sym, end_sym) + int start_sym; + int end_sym; +{ + if (++pst_syms_count > pst_syms_size) + { + pst_syms_array = (pst_syms_struct *) xrealloc (pst_syms_array, + 2 * pst_syms_size * sizeof (pst_syms_struct)); + pst_syms_size *= 2; + } + pst_syms_array[pst_syms_count - 1].start = start_sym; + pst_syms_array[pst_syms_count - 1].end = end_sym; +} + +/* Find a suitable symbol table index which can serve as the upper + bound of a psymtab that starts at INDEX + + This scans backwards in the psymtab symbol index table to find a + "hole" in which the given index can fit. This is a heuristic!! + We don't search the entire table to check for multiple holes, + we don't care about overlaps, etc. + + Return 0 => not found */ + +static int +find_next_pst_start (index) + int index; +{ + int i; + + for (i = pst_syms_count - 1; i >= 0; i--) + if (pst_syms_array[i].end <= index) + return (i == pst_syms_count - 1) ? 0 : pst_syms_array[i+1].start - 1; + + if (pst_syms_array[0].start > index) + return pst_syms_array[0].start - 1; + + return 0; +} + + + +/* Utility functions to find the ending symbol index for a psymtab */ + +/* Find the next file entry that begins beyond INDEX, and return + its starting symbol index - 1. + QFD is the file table, CURR_FD is the file entry from where to start, + PXDB_HEADER_P as in hpread_quick_traverse (to allow macros to work). + + Return 0 => not found */ + +static int +find_next_file_isym (index, qFD, curr_fd, pxdb_header_p) + int index; + quick_file_entry *qFD; + int curr_fd; + PXDB_header_ptr pxdb_header_p; +{ + while (VALID_CURR_FILE) { + if (CURR_FILE_ISYM >= index) + return CURR_FILE_ISYM - 1; + curr_fd++; + } + return 0; +} + +/* Find the next procedure entry that begins beyond INDEX, and return + its starting symbol index - 1. + QPD is the procedure table, CURR_PD is the proc entry from where to start, + PXDB_HEADER_P as in hpread_quick_traverse (to allow macros to work). + + Return 0 => not found */ + +static int +find_next_proc_isym (index, qPD, curr_pd, pxdb_header_p) + int index; + quick_procedure_entry *qPD; + int curr_pd; + PXDB_header_ptr pxdb_header_p; +{ + while (VALID_CURR_PROC) { + if (CURR_PROC_ISYM >= index) + return CURR_PROC_ISYM - 1; + curr_pd++; + } + return 0; +} + +/* Find the next module entry that begins beyond INDEX, and return + its starting symbol index - 1. + QMD is the module table, CURR_MD is the modue entry from where to start, + PXDB_HEADER_P as in hpread_quick_traverse (to allow macros to work). + + Return 0 => not found */ + +static int +find_next_module_isym (index, qMD, curr_md, pxdb_header_p) + int index; + quick_module_entry *qMD; + int curr_md; + PXDB_header_ptr pxdb_header_p; +{ + while (VALID_CURR_MODULE) { + if (CURR_MODULE_ISYM >= index) + return CURR_MODULE_ISYM - 1; + curr_md++; + } + return 0; +} + +/* Scan and record partial symbols for all functions starting from index + pointed to by CURR_PD_P, and between code addresses START_ADR and END_ADR. + Other parameters are explained in comments below. */ + +/* This used to be inline in hpread_quick_traverse, but now that we do essentially the + same thing for two different cases (modules and module-less files), it's better + organized in a separate routine, although it does take lots of arguments. pai/1997-10-08 */ + +static int +scan_procs (curr_pd_p, qPD, max_procs, start_adr, end_adr, pst, vt_bits, objfile, section_offsets) + int * curr_pd_p; /* pointer to current proc index */ + quick_procedure_entry * qPD; /* the procedure quick lookup table */ + int max_procs; /* number of entries in proc. table */ + CORE_ADDR start_adr; /* beginning of code range for current psymtab */ + CORE_ADDR end_adr; /* end of code range for current psymtab */ + struct partial_symtab * pst; /* current psymtab */ + char * vt_bits; /* strings table of SOM debug space */ + struct objfile * objfile; /* current object file */ + struct section_offsets * section_offsets; /* not really used for HP-UX currently */ +{ + union dnttentry *dn_bufp; + int symbol_count = 0; /* Total number of symbols in this psymtab */ + int curr_pd = *curr_pd_p; /* Convenience variable -- avoid dereferencing pointer all the time */ + +#ifdef DUMPING + /* Turn this on for lots of debugging information in this routine */ + static int dumping = 0; +#endif + +#ifdef DUMPING + if (dumping) { + printf ("Scan_procs called, addresses %x to %x, proc %x\n", start_adr, end_adr, curr_pd); + } +#endif + + while((CURR_PROC_START <= end_adr) && (curr_pd < max_procs)) { + + char *rtn_name; /* mangled name */ + char *rtn_dem_name; /* qualified demangled name */ + char *class_name; + int class; + + if ((trans_lang ((enum hp_language) qPD[curr_pd].language) == language_cplus) && + vt_bits[ (long) qPD[curr_pd].sbAlias]) /* not a null string */ + { + /* Get mangled name for the procedure, and demangle it */ + rtn_name = &vt_bits[ (long)qPD[curr_pd].sbAlias ]; + rtn_dem_name = cplus_demangle (rtn_name, DMGL_ANSI | DMGL_PARAMS); + } + else + { + rtn_name = &vt_bits[ (long)qPD[curr_pd].sbProc ]; + rtn_dem_name = NULL; + } + + /* Hack to get around HP C/C++ compilers' insistence on providing + * "_MAIN_" as an alternate name for "main" */ + if ((strcmp (rtn_name, "_MAIN_") == 0) && + (strcmp (&vt_bits[ (long)qPD[curr_pd].sbProc ], "main") == 0)) + rtn_dem_name = rtn_name = main_string; + +#ifdef DUMPING + if( dumping ) { + printf( "..add %s (demangled %s), index %x to this psymtab\n", rtn_name, rtn_dem_name, curr_pd); + } +#endif + + /* Check for module-spanning routines. + */ + if(CURR_PROC_END > end_adr ) { + TELL_OBJFILE; + warning ("Procedure \"%s\" [0x%x] spans file or module boundaries.", rtn_name, curr_pd); + } + +/* I asked for this in the hope it would fix bug CHFts22228, but +* later decided it's not the right fix. I'm leaving the code +* commented out for now in case we decide we actually want to do this. +* - RT +*/ +#if 0 + /* Check this routine--if it's a class member function, + * add the class to the psymtab. We only need to add + * the class once in each module, so check. + */ + if( qPD[curr_pd].member ) { + + class = qPD[curr_pd].icd; + if( ! B_TST( class_entered, class)) { /* pai: (temp) class_entered not a parameter */ + + class_name = &vt_bits[ (long)qCD[class].sbClass ]; + + /* Add to both the struct and var namespace */ + + add_psymbol_to_list ( + class_name, + strlen( class_name ), + STRUCT_NAMESPACE, + LOC_UNDEF, /* "I have no storage" */ + &objfile->global_psymbols, /* assume classname is global */ + 0, 0, + trans_lang ((enum hp_language) qPD[curr_pd].language), + objfile ); + + add_psymbol_to_list ( + class_name, + strlen( class_name ), + VAR_NAMESPACE, + LOC_UNDEF, /* "I have no storage" */ + &objfile->global_psymbols, /* assume classname is global */ + 0, 0, + trans_lang ((enum hp_language) qPD[curr_pd].language), + objfile ); + + B_SET( class_entered, class ); /* pai: (temp) class_entered not a parameter */ + symbol_count++; + } + } +#endif + + /* Add this routine symbol to the list in the objfile. */ + /* Unfortunately we have to go to the LNTT to determine the + * correct list to put it on. An alternative (which the + * code used to do) would be to not check and always throw + * it on the "static" list. But if we go that route, then + * symbol_lookup() needs to be tweaked a bit to account + * for the fact that the function might not be found on + * the correct list in the psymtab. - RT + */ + dn_bufp = hpread_get_lntt (qPD[curr_pd].isym, objfile); + if (dn_bufp->dfunc.global) + add_psymbol_with_dem_name_to_list ( + rtn_name, + strlen( rtn_name ), + rtn_dem_name, + strlen( rtn_dem_name ), + VAR_NAMESPACE, + LOC_BLOCK, /* "I am a routine" */ + &objfile->global_psymbols, + ( qPD[curr_pd].adrStart + /* Starting address of rtn */ + ANOFFSET (section_offsets, SECT_OFF_TEXT)), + 0, /* core addr?? */ + trans_lang ((enum hp_language) qPD[curr_pd].language), + objfile ); + else + add_psymbol_with_dem_name_to_list ( + rtn_name, + strlen( rtn_name ), + rtn_dem_name, + strlen( rtn_dem_name ), + VAR_NAMESPACE, + LOC_BLOCK, /* "I am a routine" */ + &objfile->static_psymbols, + ( qPD[curr_pd].adrStart + /* Starting address of rtn */ + ANOFFSET (section_offsets, SECT_OFF_TEXT)), + 0, /* core addr?? */ + trans_lang ((enum hp_language) qPD[curr_pd].language), + objfile ); + + symbol_count++; + *curr_pd_p = ++curr_pd; /* bump up count & reflect in caller */ + } /* loop over procedures */ + +#ifdef DUMPING + if( dumping ) { + if( symbol_count == 0 ) + printf( "Scan_procs: no symbols found!\n"); + } +#endif + + return symbol_count; +} + + +/* Traverse the quick look-up tables, building a set of psymtabs. + * + * This constructs a psymtab for modules and files in the quick lookup + * tables. + * + * Mostly, modules correspond to compilation units, so we try to + * create psymtabs that correspond to modules; however, in some cases + * a file can result in a compiled object which does not have a module + * entry for it, so in such cases we create a psymtab for the file. + */ + +int +hpread_quick_traverse( + objfile, /* The object file descriptor */ + section_offsets, /* ?? Null for HP */ + gntt_bits, /* GNTT entries, loaded in from the file */ + vt_bits, /* VT (string) entries ditto. */ + pxdb_header_p ) /* Pointer to pxdb header ditto */ + + struct objfile *objfile; + struct section_offsets *section_offsets; + char *gntt_bits; + char *vt_bits; + PXDB_header_ptr pxdb_header_p; +{ + struct partial_symtab *pst; + + char *addr; + + quick_procedure_entry *qPD; + quick_file_entry *qFD; + quick_module_entry *qMD; + quick_class_entry *qCD; + + int idx; + int i; + CORE_ADDR start_adr; /* current psymtab's starting code addr */ + CORE_ADDR end_adr; /* current psymtab's ending code addr */ + CORE_ADDR next_mod_adr; /* next module's starting code addr */ + int curr_pd; /* current procedure */ + int curr_fd; /* current file */ + int curr_md; /* current module */ + int start_sym; /* current psymtab's starting symbol index */ + int end_sym; /* current psymtab's ending symbol index */ + int max_LNTT_sym_index; + int syms_in_pst; + B_TYPE *class_entered; + + struct partial_symbol **global_syms; /* We'll be filling in the "global" */ + struct partial_symbol **static_syms; /* and "static" tables in the objfile */ + /* as we go, so we need a pair of */ + /* current pointers. */ + +#ifdef DUMPING + /* Turn this on for lots of debugging information in this routine. + You get a blow-by-blow account of quick lookup table reading */ + static int dumping = 0; +#endif + + pst = (struct partial_symtab *) 0; + + /* Clear out some globals */ + init_pst_syms (); + told_objfile = 0; + + /* Demangling style -- if EDG style already set, don't change it, + as HP style causes some problems with the KAI EDG compiler */ + if (current_demangling_style != edg_demangling) { + /* Otherwise, ensure that we are using HP style demangling */ + set_demangling_style (HP_DEMANGLING_STYLE_STRING); + } + + /* First we need to find the starting points of the quick + * look-up tables in the GNTT. */ + + addr = gntt_bits; + + qPD = (quick_procedure_entry_ptr) addr; + addr += pxdb_header_p->pd_entries * sizeof( quick_procedure_entry ); + +#ifdef DUMPING + if( dumping ) { + printf( "\n Printing routines as we see them\n" ); + for( i = 0; VALID_PROC (i); i++ ){ + idx = (long) qPD[i].sbProc; + printf( "%s %x..%x\n", &vt_bits[idx], + (int) PROC_START (i), + (int) PROC_END (i) ); + } + } +#endif + + qFD = (quick_file_entry_ptr) addr; + addr += pxdb_header_p->fd_entries * sizeof( quick_file_entry); + +#ifdef DUMPING + if( dumping ) { + printf( "\n Printing files as we see them\n" ); + for( i = 0; VALID_FILE (i); i++ ){ + idx = (long) qFD[i].sbFile; + printf( "%s %x..%x\n", &vt_bits[idx], + (int) FILE_START(i), + (int) FILE_END(i) ); + } + } +#endif + + qMD = (quick_module_entry_ptr) addr; + addr += pxdb_header_p->md_entries * sizeof( quick_module_entry ); + +#ifdef DUMPING + if( dumping ) { + printf( "\n Printing modules as we see them\n" ); + for( i = 0; i < pxdb_header_p->md_entries; i++ ){ + idx = (long) qMD[i].sbMod; + printf( "%s\n", &vt_bits[idx] ); + } + } +#endif + + qCD = (quick_class_entry_ptr) addr; + addr += pxdb_header_p->cd_entries * sizeof( quick_class_entry ); + +#ifdef DUMPING + if( dumping ) { + printf( "\n Printing classes as we see them\n" ); + for( i = 0; VALID_CLASS (i); i++ ){ + idx = (long) qCD[i].sbClass; + printf( "%s\n", &vt_bits[idx] ); + } + + printf( "\n Done with dump, on to build!\n" ); + } +#endif + + /* We need this index only while hp-symtab-read.c expects + * a byte offset to the end of the LNTT entries for a given + * psymtab. Thus the need for it should go away someday. + * + * When it goes away, then we won't have any need to load the + * LNTT from the objfile at psymtab-time, and start-up will be + * faster. To make that work, we'll need some way to create + * a null pst for the "globals" pseudo-module. + */ + max_LNTT_sym_index = LNTT_SYMCOUNT (objfile); + + /* Scan the module descriptors and make a psymtab for each. + * + * We know the MDs, FDs and the PDs are in order by starting + * address. We use that fact to traverse all three arrays in + * parallel, knowing when the next PD is in a new file + * and we need to create a new psymtab. + */ + curr_pd = 0; /* Current procedure entry */ + curr_fd = 0; /* Current file entry */ + curr_md = 0; /* Current module entry */ + + start_adr = 0; /* Current psymtab code range */ + end_adr = 0; + + start_sym = 0; /* Current psymtab symbol range */ + end_sym = 0; + + syms_in_pst = 0; /* Symbol count for psymtab */ + + /* Psts actually just have pointers into the objfile's + * symbol table, not their own symbol tables. + */ + global_syms = objfile -> global_psymbols.list; + static_syms = objfile -> static_psymbols.list; + +#if 0 /* pai: (temp) we don't need this any more */ + /* elz: if the first module we see in the table is for + end.c, then return immediately with false. This happens + for F77 programs, for which there is no MODULE information + produced in the debug info. + Returning false from this function will make the caller + (build_psymbols) scan the table from the beginning and + not use the quick lookup tables. + F90 has modules so this poses no porblem. + */ + if (!strcmp (&vt_bits[(long)qMD[0].sbMod], "end.c")) + return 0; +#endif + + /* First skip over pseudo-entries with address 0. These represent inlined + routines and abstract (uninstantiated) template routines. + FIXME: These should be read in and available -- even if we can't set + breakpoints, etc., there's some information that can be presented + to the user. pai/1997-10-08 */ + + while (VALID_CURR_PROC && (CURR_PROC_START == 0)) + curr_pd++; + + /* Loop over files, modules, and procedures in code address order. Each + time we enter an iteration of this loop, curr_pd points to the first + unprocessed procedure, curr_fd points to the first unprocessed file, and + curr_md to the first unprocessed module. Each iteration of this loop + updates these as required -- any or all of them may be bumpd up + each time around. When we exit this loop, we are done with all files + and modules in the tables -- there may still be some procedures, however. + + Note: This code used to loop only over module entries, under the assumption + that files can occur via inclusions and are thus unreliable, while a + compiled object always corresponds to a module. With CTTI in the HP aCC + compiler, it turns out that compiled objects may have only files and no + modules; so we have to loop over files and modules, creating psymtabs for + either as appropriate. Unfortunately there are some problems (notably: + 1. the lack of "SRC_FILE_END" entries in the LNTT, 2. the lack of pointers + to the ending symbol indices of a module or a file) which make it quite hard + to do this correctly. Currently it uses a bunch of heuristics to start and + end psymtabs; they seem to work well with most objects generated by aCC, but + who knows when that will change... */ + + while (VALID_CURR_FILE || VALID_CURR_MODULE) { + + char *mod_name_string; + char *full_name_string; + + /* First check for modules like "version.c", which have no code + * in them but still have qMD entries. They also have no qFD or + * qPD entries. Their start address is -1 and their end address + * is 0. */ + if(VALID_CURR_MODULE && (CURR_MODULE_START == -1) && (CURR_MODULE_END == NULL)) { + + mod_name_string = &vt_bits[(long) qMD[curr_md].sbMod]; + +#ifdef DUMPING + if( dumping ) + printf( "Module with data only %s\n", mod_name_string ); +#endif + + /* We'll skip the rest (it makes error-checking easier), and + * just make an empty pst. Right now empty psts are not put + * in the pst chain, so all this is for naught, but later it + * might help. */ + + pst = hpread_start_psymtab( objfile, + section_offsets, /* ?? */ + mod_name_string, + CURR_MODULE_START, /* Low text address: bogus! */ + (CURR_MODULE_ISYM * sizeof (struct dntt_type_block)), + /* ldsymoff */ + global_syms, + static_syms ); + + pst = hpread_end_psymtab ( + pst, + NULL, /* psymtab_include_list */ + 0, /* includes_used */ + end_sym * sizeof (struct dntt_type_block), + /* byte index in LNTT of end */ + /* = capping symbol offset */ + /* = LDSYMOFF of nextfile */ + NULL, /* text high */ + NULL, /* dependency_list */ + 0 ); /* dependencies_used */ + + global_syms = objfile->global_psymbols.next; + static_syms = objfile->static_psymbols.next; + + curr_md++; + } + else if (VALID_CURR_MODULE && + ((CURR_MODULE_START == 0) || (CURR_MODULE_START == -1) || + (CURR_MODULE_END == 0) || (CURR_MODULE_END == -1))) { + TELL_OBJFILE; + warning ("Module \"%s\" [0x%x] has non-standard addresses. It starts at 0x%x, ends at 0x%x, and will be skipped.", + mod_name_string, curr_md, start_adr, end_adr); + /* On to next module */ + curr_md ++; + } + else { + /* First check if we are looking at a file with code in it + that does not overlap the current module's code range */ + + if (VALID_CURR_FILE ? (VALID_CURR_MODULE ? (CURR_FILE_END < CURR_MODULE_START) : 1) : 0) { + + /* Looking at file not corresponding to any module, + create a psymtab for it */ + full_name_string = &vt_bits[(long) qFD[curr_fd].sbFile]; + start_adr = CURR_FILE_START; + end_adr = CURR_FILE_END; + start_sym = CURR_FILE_ISYM; + + /* Check if there are any procedures not handled until now, that + begin before the start address of this file, and if so, adjust + this module's start address to include them. This handles routines that + are in between file or module ranges for some reason (probably + indicates a compiler bug */ + + if (CURR_PROC_START < start_adr) { + TELL_OBJFILE; + warning ("Found procedure \"%s\" [0x%x] that is not in any file or module.", + &vt_bits[(long) qPD[curr_pd].sbProc], curr_pd); + start_adr = CURR_PROC_START; + if (CURR_PROC_ISYM < start_sym) + start_sym = CURR_PROC_ISYM; + } + + /* Sometimes (compiler bug -- COBOL) the module end address is higher + than the start address of the next module, so check for that and + adjust accordingly */ + + if (VALID_FILE (curr_fd + 1) && (FILE_START (curr_fd + 1) <= end_adr)) { + TELL_OBJFILE; + warning("File \"%s\" [0x%x] has ending address after starting address of next file; adjusting ending address down.", + full_name_string, curr_fd); + end_adr = FILE_START (curr_fd + 1) - 1; /* Is -4 (or -8 for 64-bit) better? */ + } + if (VALID_MODULE (curr_md) && (CURR_MODULE_START <= end_adr)) { + TELL_OBJFILE; + warning("File \"%s\" [0x%x] has ending address after starting address of next module; adjusting ending address down.", + full_name_string, curr_fd); + end_adr = CURR_MODULE_START - 1; /* Is -4 (or -8 for 64-bit) better? */ + } + + +#ifdef DUMPING + if( dumping ) { + printf( "Make new psymtab for file %s (%x to %x).\n", + full_name_string, start_adr, end_adr ); + } +#endif + /* Create the basic psymtab, connecting it in the list + * for this objfile and pointing its symbol entries + * to the current end of the symbol areas in the objfile. + * + * The "ldsymoff" parameter is the byte offset in the LNTT + * of the first symbol in this file. Some day we should + * turn this into an index (fix in hp-symtab-read.c as well). + * And it's not even the right byte offset, as we're using + * the size of a union! FIXME! */ + pst = hpread_start_psymtab( objfile, + section_offsets, /* ?? */ + full_name_string, + start_adr, /* Low text address */ + (start_sym * sizeof (struct dntt_type_block)), + /* ldsymoff */ + global_syms, + static_syms ); + + /* Set up to only enter each class referenced in this module once. */ + class_entered = malloc( B_BYTES( pxdb_header_p->cd_entries )); + B_CLRALL( class_entered, pxdb_header_p->cd_entries ); + + /* Scan the procedure descriptors for procedures in the current + * file, based on the starting addresses. */ + + syms_in_pst = scan_procs (&curr_pd, qPD, pxdb_header_p->pd_entries, + start_adr, end_adr, + pst, vt_bits, objfile, section_offsets); + + /* Get ending symbol offset */ + + end_sym = 0; + /* First check for starting index before previous psymtab */ + if (pst_syms_count && start_sym < pst_syms_array[pst_syms_count - 1].end) { + end_sym = find_next_pst_start (start_sym); + } + /* Look for next start index of a file or module, or procedure */ + if (!end_sym) { + int next_file_isym = find_next_file_isym (start_sym, qFD, curr_fd + 1, pxdb_header_p); + int next_module_isym = find_next_module_isym (start_sym, qMD, curr_md, pxdb_header_p); + int next_proc_isym = find_next_proc_isym (start_sym, qPD, curr_pd, pxdb_header_p); + + if (next_file_isym && next_module_isym) { + /* pick lower of next file or module start index */ + end_sym = min (next_file_isym, next_module_isym); + } + else { + /* one of them is zero, pick the other */ + end_sym = max (next_file_isym, next_module_isym); + } + + /* As a precaution, check next procedure index too */ + if (!end_sym) + end_sym = next_proc_isym; + else + end_sym = min (end_sym, next_proc_isym); + } + + /* Couldn't find procedure, file, or module, use globals as default */ + if (!end_sym) + end_sym = pxdb_header_p->globals; + +#ifdef DUMPING + if (dumping) { + printf ("File psymtab indices: %x to %x\n", start_sym, end_sym); + } +#endif + + pst = hpread_end_psymtab ( + pst, + NULL, /* psymtab_include_list */ + 0, /* includes_used */ + end_sym * sizeof (struct dntt_type_block), + /* byte index in LNTT of end */ + /* = capping symbol offset */ + /* = LDSYMOFF of nextfile */ + end_adr, /* text high */ + NULL, /* dependency_list */ + 0 ); /* dependencies_used */ + + record_pst_syms (start_sym, end_sym); + + if( NULL == pst ) + warning ("No symbols in psymtab for file \"%s\" [0x%x].", full_name_string, curr_fd); + +#ifdef DUMPING + if( dumping ) { + printf( "Made new psymtab for file %s (%x to %x), sym %x to %x.\n", + full_name_string, start_adr, end_adr, CURR_FILE_ISYM, end_sym); + } +#endif + /* Prepare for the next psymtab. + */ + global_syms = objfile->global_psymbols.next; + static_syms = objfile->static_psymbols.next; + free( class_entered ); + + curr_fd++; + } /* Psymtab for file */ + else { + /* We have a module for which we create a psymtab */ + + mod_name_string = &vt_bits[(long) qMD[curr_md].sbMod]; + + /* We will include the code ranges of any files that happen to + overlap with this module */ + + /* So, first pick the lower of the file's and module's start addresses */ + start_adr = CURR_MODULE_START; + if (VALID_CURR_FILE) { + if (CURR_FILE_START < CURR_MODULE_START) { + TELL_OBJFILE; + warning ("File \"%s\" [0x%x] crosses beginning of module \"%s\".", + &vt_bits[ (long) qFD[curr_fd].sbFile], + curr_fd, mod_name_string); + + start_adr = CURR_FILE_START; + } + } + + /* Also pick the lower of the file's and the module's start symbol indices */ + start_sym = CURR_MODULE_ISYM; + if (VALID_CURR_FILE && (CURR_FILE_ISYM < CURR_MODULE_ISYM)) + start_sym = CURR_FILE_ISYM; + + /* For the end address, we scan through the files till we find one + that overlaps the current module but ends beyond it; if no such file exists we + simply use the module's start address. (Note, if file entries themselves overlap + we take the longest overlapping extension beyond the end of the module...) + We assume that modules never overlap. */ + + end_adr = CURR_MODULE_END; + + if (VALID_CURR_FILE) { + while (VALID_CURR_FILE && (CURR_FILE_START < end_adr)) { + +#ifdef DUMPING + if( dumping ) + printf( "Maybe skipping file %s which overlaps with module %s\n", + &vt_bits[ (long) qFD[ curr_fd ].sbFile], mod_name_string ); +#endif + if (CURR_FILE_END > end_adr) { + TELL_OBJFILE; + warning ("File \"%s\" [0x%x] crosses end of module \"%s\".", + &vt_bits[ (long) qFD[curr_fd].sbFile], + curr_fd, mod_name_string); + end_adr = CURR_FILE_END; + } + curr_fd++; + } + curr_fd--; /* back up after going too far */ + } + + /* Sometimes (compiler bug -- COBOL) the module end address is higher + than the start address of the next module, so check for that and + adjust accordingly */ + + if (VALID_MODULE (curr_md + 1) && (MODULE_START (curr_md + 1) <= end_adr)) { + TELL_OBJFILE; + warning ("Module \"%s\" [0x%x] has ending address after starting address of next module; adjusting ending address down.", + mod_name_string, curr_md); + end_adr = MODULE_START (curr_md + 1) - 1; /* Is -4 (or -8 for 64-bit) better? */ + } + if (VALID_FILE (curr_fd + 1) && (FILE_START (curr_fd + 1) <= end_adr)) { + TELL_OBJFILE; + warning ("Module \"%s\" [0x%x] has ending address after starting address of next file; adjusting ending address down.", + mod_name_string, curr_md); + end_adr = FILE_START (curr_fd + 1) - 1; /* Is -4 (or -8 for 64-bit) better? */ + } + + /* Use one file to get the full name for the module. This + * situation can arise if there is executable code in a #include + * file. Each file with code in it gets a qFD. Files which don't + * contribute code don't get a qFD, even if they include files + * which do, e.g.: + * + * body.c: rtn.h: + * int x; int main() { + * #include "rtn.h" return x; + * } + * + * There will a qFD for "rtn.h",and a qMD for "body.c", + * but no qMD for "rtn.h" or qFD for "body.c"! + * + * We pick the name of the last file to overlap with this + * module. C convention is to put include files first. In a + * perfect world, we could check names and use the file whose full + * path name ends with the module name. */ + + if (VALID_CURR_FILE) + full_name_string = &vt_bits[ (long) qFD[curr_fd].sbFile ]; + else + full_name_string = mod_name_string; + + /* Check if there are any procedures not handled until now, that + begin before the start address we have now, and if so, adjust + this psymtab's start address to include them. This handles routines that + are in between file or module ranges for some reason (probably + indicates a compiler bug */ + + if (CURR_PROC_START < start_adr) { + TELL_OBJFILE; + warning ("Found procedure \"%s\" [0x%x] that is not in any file or module.", + &vt_bits[(long) qPD[curr_pd].sbProc], curr_pd); + start_adr = CURR_PROC_START; + if (CURR_PROC_ISYM < start_sym) + start_sym = CURR_PROC_ISYM; + } + +#ifdef DUMPING + if( dumping ) { + printf( "Make new psymtab for module %s (%x to %x), using file %s\n", + mod_name_string, start_adr, end_adr, full_name_string); + } +#endif + /* Create the basic psymtab, connecting it in the list + * for this objfile and pointing its symbol entries + * to the current end of the symbol areas in the objfile. + * + * The "ldsymoff" parameter is the byte offset in the LNTT + * of the first symbol in this file. Some day we should + * turn this into an index (fix in hp-symtab-read.c as well). + * And it's not even the right byte offset, as we're using + * the size of a union! FIXME! */ + pst = hpread_start_psymtab( objfile, + section_offsets, /* ?? */ + full_name_string, + start_adr, /* Low text address */ + (start_sym * sizeof (struct dntt_type_block)), + /* ldsymoff */ + global_syms, + static_syms ); + + /* Set up to only enter each class referenced in this module once. */ + class_entered = malloc( B_BYTES( pxdb_header_p->cd_entries )); + B_CLRALL( class_entered, pxdb_header_p->cd_entries ); + + /* Scan the procedure descriptors for procedures in the current + * module, based on the starting addresses. */ + + syms_in_pst = scan_procs (&curr_pd, qPD, pxdb_header_p->pd_entries, + start_adr, end_adr, + pst, vt_bits, objfile, section_offsets); + + /* Get ending symbol offset */ + + end_sym = 0; + /* First check for starting index before previous psymtab */ + if (pst_syms_count && start_sym < pst_syms_array[pst_syms_count - 1].end) { + end_sym = find_next_pst_start (start_sym); + } + /* Look for next start index of a file or module, or procedure */ + if (!end_sym) { + int next_file_isym = find_next_file_isym (start_sym, qFD, curr_fd + 1, pxdb_header_p); + int next_module_isym = find_next_module_isym (start_sym, qMD, curr_md + 1, pxdb_header_p); + int next_proc_isym = find_next_proc_isym (start_sym, qPD, curr_pd, pxdb_header_p); + + if (next_file_isym && next_module_isym) { + /* pick lower of next file or module start index */ + end_sym = min (next_file_isym, next_module_isym); + } + else { + /* one of them is zero, pick the other */ + end_sym = max (next_file_isym, next_module_isym); + } + + /* As a precaution, check next procedure index too */ + if (!end_sym) + end_sym = next_proc_isym; + else + end_sym = min (end_sym, next_proc_isym); + } + + /* Couldn't find procedure, file, or module, use globals as default */ + if (!end_sym) + end_sym = pxdb_header_p->globals; + +#ifdef DUMPING + if (dumping) { + printf ("Module psymtab indices: %x to %x\n", start_sym, end_sym); + } +#endif + + pst = hpread_end_psymtab ( + pst, + NULL, /* psymtab_include_list */ + 0, /* includes_used */ + end_sym * sizeof (struct dntt_type_block), + /* byte index in LNTT of end */ + /* = capping symbol offset */ + /* = LDSYMOFF of nextfile */ + end_adr, /* text high */ + NULL, /* dependency_list */ + 0 ); /* dependencies_used */ + + record_pst_syms (start_sym, end_sym); + + if( NULL == pst ) + warning ("No symbols in psymtab for module \"%s\" [0x%x].", mod_name_string, curr_md); + +#ifdef DUMPING + if( dumping ) { + printf( "Made new psymtab for module %s (%x to %x), sym %x to %x.\n", + mod_name_string, start_adr, end_adr, CURR_MODULE_ISYM, end_sym); + } +#endif + + /* Prepare for the next psymtab. + */ + global_syms = objfile->global_psymbols.next; + static_syms = objfile->static_psymbols.next; + free( class_entered ); + + curr_md++; + curr_fd++; + } /* psymtab for module */ + } /* psymtab for non-bogus file or module */ + } /* End of while loop over all files & modules */ + + /* There may be some routines after all files and modules -- these will get + inserted in a separate new module of their own */ + if (VALID_CURR_PROC) { + start_adr = CURR_PROC_START; + end_adr = qPD[pxdb_header_p->pd_entries -1].adrEnd; + TELL_OBJFILE; + warning ("Found functions beyond end of all files and modules [0x%x].", curr_pd); +#ifdef DUMPING + if( dumping ) { + printf( "Orphan functions at end, PD %d and beyond (%x to %x)\n", + curr_pd, start_adr, end_adr); + } +#endif + pst = hpread_start_psymtab( objfile, + section_offsets, /* ?? */ + "orphans", + start_adr, /* Low text address */ + (CURR_PROC_ISYM * sizeof (struct dntt_type_block)), + /* ldsymoff */ + global_syms, + static_syms ); + + scan_procs (&curr_pd, qPD, pxdb_header_p->pd_entries, + start_adr, end_adr, + pst, vt_bits, objfile, section_offsets); + + pst = hpread_end_psymtab (pst, + NULL, /* psymtab_include_list */ + 0, /* includes_used */ + pxdb_header_p->globals * sizeof (struct dntt_type_block), + /* byte index in LNTT of end */ + /* = capping symbol offset */ + /* = LDSYMOFF of nextfile */ + end_adr, /* text high */ + NULL, /* dependency_list */ + 0); /* dependencies_used */ + } + + +#ifdef NEVER_NEVER + /* Now build psts for non-module things (in the tail of + * the LNTT, after the last END MODULE entry). + * + * If null psts were kept on the chain, this would be + * a solution. FIXME + */ + pst = hpread_start_psymtab( objfile, + section_offsets, + "globals", + 0, + (pxdb_header_p->globals + * sizeof (struct dntt_type_block)), + objfile->global_psymbols.next, + objfile->static_psymbols.next); + hpread_end_psymtab( pst, + NULL, 0, + (max_LNTT_sym_index * sizeof (struct dntt_type_block)), + 0, + NULL, 0 ); +#endif + + clear_pst_syms (); + + return 1; + +} /* End of hpread_quick_traverse. */ + + +/* Get appropriate header, based on pxdb type. + Return value: 1 if ok, 0 if not */ +int +hpread_get_header( objfile, pxdb_header_p ) + struct objfile *objfile; + PXDB_header_ptr pxdb_header_p; +{ + asection *pinfo_section, *debug_section, *header_section; + +#ifdef DUMPING + /* Turn on for debugging information + */ + static int dumping = 0; +#endif + + header_section = bfd_get_section_by_name (objfile->obfd, "$HEADER$"); + if( !header_section ) { + /* We don't have either PINFO or DEBUG sections. But + * stuff like "libc.sl" has no debug info. There's no + * need to warn the user of this, as it may be ok. The + * caller will figure it out and issue any needed + * messages. + */ +#ifdef DUMPING + if( dumping ) + printf( "==No debug info at all for %s.\n", objfile->name ); +#endif + + return 0; + } + + /* We would like either a $DEBUG$ or $PINFO$ section. + * Once we know which, we can understand the header + * data (which we have defined to suit the more common + * $DEBUG$ case). + */ + debug_section = bfd_get_section_by_name (objfile->obfd, "$DEBUG$" ); + pinfo_section = bfd_get_section_by_name (objfile->obfd, "$PINFO$" ); + if( debug_section ) { + /* The expected case: normal pxdb header. + */ + bfd_get_section_contents( objfile->obfd, header_section, + pxdb_header_p, 0, sizeof( PXDB_header )); + + if( !pxdb_header_p->pxdbed ) { + /* This shouldn't happen if we check in "symfile.c". + */ + return 0; + } /* DEBUG section */ + } + + else if( pinfo_section ) { + /* The DOC case; we need to translate this into a + * regular header. + */ + DOC_info_PXDB_header doc_header; + +#ifdef DUMPING + if( dumping ) { + printf( "==OOps, PINFO, let's try to handle this, %s.\n", objfile->name ); + } +#endif + + bfd_get_section_contents( objfile->obfd, + header_section, + &doc_header, 0, + sizeof( DOC_info_PXDB_header )); + + if( !doc_header.pxdbed ) { + /* This shouldn't happen if we check in "symfile.c". + */ + warning ("File \"%s\" not processed by pxdb!", objfile->name); + return 0; + } + + /* Copy relevent fields to standard header passed in. + */ + pxdb_header_p->pd_entries = doc_header.pd_entries; + pxdb_header_p->fd_entries = doc_header.fd_entries; + pxdb_header_p->md_entries = doc_header.md_entries; + pxdb_header_p->pxdbed = doc_header.pxdbed; + pxdb_header_p->bighdr = doc_header.bighdr; + pxdb_header_p->sa_header = doc_header.sa_header; + pxdb_header_p->inlined = doc_header.inlined; + pxdb_header_p->globals = doc_header.globals; + pxdb_header_p->time = doc_header.time; + pxdb_header_p->pg_entries = doc_header.pg_entries; + pxdb_header_p->functions = doc_header.functions; + pxdb_header_p->files = doc_header.files; + pxdb_header_p->cd_entries = doc_header.cd_entries; + pxdb_header_p->aa_entries = doc_header.aa_entries; + pxdb_header_p->oi_entries = doc_header.oi_entries; + pxdb_header_p->version = doc_header.version; + } /* PINFO section */ + + else + { +#ifdef DUMPING + if( dumping ) + printf( "==No debug info at all for %s.\n", objfile->name ); +#endif + + return 0; + + } + + return 1; +} /* End of hpread_get_header */ +#endif /* QUICK_LOOK_UP */ + + +/* Initialization for reading native HP C debug symbols from OBJFILE. + + Its only purpose in life is to set up the symbol reader's private + per-objfile data structures, and read in the raw contents of the debug + sections (attaching pointers to the debug info into the private data + structures). + + Since BFD doesn't know how to read debug symbols in a format-independent + way (and may never do so...), we have to do it ourselves. Note we may + be called on a file without native HP C debugging symbols. + + FIXME, there should be a cleaner peephole into the BFD environment + here. +*/ +void +hpread_symfile_init (objfile) + struct objfile *objfile; +{ + asection *vt_section, *slt_section, *lntt_section, *gntt_section; + + /* Allocate struct to keep track of the symfile */ + objfile->sym_private = (PTR) + xmmalloc (objfile->md, sizeof (struct hpread_symfile_info)); + memset (objfile->sym_private, 0, sizeof (struct hpread_symfile_info)); + + /* We haven't read in any types yet. */ + TYPE_VECTOR (objfile) = 0; + + /* Read in data from the $GNTT$ subspace. */ + gntt_section = bfd_get_section_by_name (objfile->obfd, "$GNTT$"); + if (!gntt_section) + return; + + GNTT (objfile) + = obstack_alloc (&objfile->symbol_obstack, + bfd_section_size (objfile->obfd, gntt_section)); + + bfd_get_section_contents (objfile->obfd, gntt_section, GNTT (objfile), + 0, bfd_section_size (objfile->obfd, gntt_section)); + + GNTT_SYMCOUNT (objfile) + = bfd_section_size (objfile->obfd, gntt_section) + / sizeof (struct dntt_type_block); + + /* Read in data from the $LNTT$ subspace. Also keep track of the number + * of LNTT symbols. + * + * FIXME: this could be moved into the psymtab-to-symtab expansion + * code, and save startup time. At the moment this data is + * still used, though. We'd need a way to tell hp-symtab-read.c + * whether or not to load the LNTT. + */ + lntt_section = bfd_get_section_by_name (objfile->obfd, "$LNTT$"); + if (!lntt_section) + return; + + LNTT (objfile) + = obstack_alloc (&objfile->symbol_obstack, + bfd_section_size (objfile->obfd, lntt_section)); + + bfd_get_section_contents (objfile->obfd, lntt_section, LNTT (objfile), + 0, bfd_section_size (objfile->obfd, lntt_section)); + + LNTT_SYMCOUNT (objfile) + = bfd_section_size (objfile->obfd, lntt_section) + / sizeof (struct dntt_type_block); + + /* Read in data from the $SLT$ subspace. $SLT$ contains information + on source line numbers. */ + slt_section = bfd_get_section_by_name (objfile->obfd, "$SLT$"); + if (!slt_section) + return; + + SLT (objfile) = + obstack_alloc (&objfile->symbol_obstack, + bfd_section_size (objfile->obfd, slt_section)); + + bfd_get_section_contents (objfile->obfd, slt_section, SLT (objfile), + 0, bfd_section_size (objfile->obfd, slt_section)); + + /* Read in data from the $VT$ subspace. $VT$ contains things like + names and constants. Keep track of the number of symbols in the VT. */ + vt_section = bfd_get_section_by_name (objfile->obfd, "$VT$"); + if (!vt_section) + return; + + VT_SIZE (objfile) = bfd_section_size (objfile->obfd, vt_section); + + VT (objfile) = + (char *) obstack_alloc (&objfile->symbol_obstack, + VT_SIZE (objfile)); + + bfd_get_section_contents (objfile->obfd, vt_section, VT (objfile), + 0, VT_SIZE (objfile)); +} + +/* Scan and build partial symbols for a symbol file. + * + * The minimal symbol table (either SOM or HP a.out) has already been + * read in; all we need to do is setup partial symbols based on the + * native debugging information. + * + * Note that the minimal table is produced by the linker, and has + * only global routines in it; the psymtab is based on compiler- + * generated debug information and has non-global + * routines in it as well as files and class information. + * + * We assume hpread_symfile_init has been called to initialize the + * symbol reader's private data structures. + * + * SECTION_OFFSETS contains offsets relative to which the symbols in the + * various sections are (depending where the sections were actually loaded). + * MAINLINE is true if we are reading the main symbol table (as + * opposed to a shared lib or dynamically loaded file). + */ +void +hpread_build_psymtabs (objfile, section_offsets, mainline) + struct objfile *objfile; + struct section_offsets *section_offsets; + int mainline; +{ + +#ifdef DUMPING + /* Turn this on to get debugging output. + */ + static int dumping = 0; +#endif + + char *namestring; + int past_first_source_file = 0; + struct cleanup *old_chain; + + int hp_symnum, symcount, i; + int scan_start; + + union dnttentry *dn_bufp; + unsigned long valu; + char *p; + int texthigh = 0; + int have_name = 0; + + /* Current partial symtab */ + struct partial_symtab *pst; + + /* List of current psymtab's include files */ + char **psymtab_include_list; + int includes_allocated; + int includes_used; + + /* Index within current psymtab dependency list */ + struct partial_symtab **dependency_list; + int dependencies_used, dependencies_allocated; + + /* Just in case the stabs reader left turds lying around. */ + free_pending_blocks (); + make_cleanup (really_free_pendings, 0); + + pst = (struct partial_symtab *) 0; + + /* We shouldn't use alloca, instead use malloc/free. Doing so avoids + a number of problems with cross compilation and creating useless holes + in the stack when we have to allocate new entries. FIXME. */ + + includes_allocated = 30; + includes_used = 0; + psymtab_include_list = (char **) alloca (includes_allocated * + sizeof (char *)); + + dependencies_allocated = 30; + dependencies_used = 0; + dependency_list = + (struct partial_symtab **) alloca (dependencies_allocated * + sizeof (struct partial_symtab *)); + + old_chain = make_cleanup (free_objfile, objfile); + + last_source_file = 0; + +#ifdef QUICK_LOOK_UP +{ + /* Begin code for new-style loading of quick look-up tables. + */ + + /* elz: this checks whether the file has beeen processed by pxdb. + If not we would like to try to read the psymbols in + anyway, but it turns out to be not so easy. So this could + actually be commented out, but I leave it in, just in case + we decide to add support for non-pxdb-ed stuff in the future. + */ + bfd *abfd; + abfd = symfile_bfd_open (objfile->name); + if (!hpread_pxdb_needed(abfd)) + { + if ( psym_new_style ) { + PXDB_header pxdb_header; + int found_modules_in_program; + + if( hpread_get_header( objfile, &pxdb_header )) { + /* Build a minimal table. No types, no global variables, + * no include files.... + */ +#ifdef DUMPING + if( dumping ) + printf( "\nNew method for %s\n", objfile->name ); +#endif + + /* elz: quick_traverse returns true if it found + some modules in the main source file, other + than those in end.c + In C and C++, all the files have MODULES entries + in the LNTT, and the quick table traverse is all + based on finding these MODULES entries. Without + those it cannot work. + It happens that F77 programs don't have MODULES + so the quick traverse gets confused. F90 programs + have modules, and the quick method still works. + So, if modules (other than those in end.c) are + not found we give up on the quick table stuff, + and fall back on the slower method + */ + found_modules_in_program = hpread_quick_traverse( + objfile, + section_offsets, + GNTT( objfile ), + VT(objfile), + &pxdb_header ); + + discard_cleanups (old_chain); + + /* Set up to scan the global section of the LNTT. + * + * This field is not always correct: if there are + * no globals, it will point to the last record in + * the regular LNTT, which is usually an END MODULE. + * + * Since it might happen that there could be a file + * with just one global record, there's no way to + * tell other than by looking at the record, so that's + * done below. + */ + if (found_modules_in_program) + scan_start = pxdb_header.globals; + else + scan_start = 0; + } + +#ifdef DUMPING + else { + if( dumping ) + printf( "\nGoing on to old method for %s\n", objfile->name ); + } +#endif + + } /* End of new method code */ +} /* end of if pxdb exists */ +/* + * elz: if pxdb does not exists on the system, then scan the whole debug info + * Actually this will never be reached because we error out in case there + * is no pxdb on the system. It turns out that the debug info cannot be + * handled the same way as after bxdb has been run, and gdb gets very + * very confused. Ileave this in anyway, in case one day we want to + * support non pxdb-ed files. + */ + else scan_start = 0; + + bfd_close(abfd); /*close the bfd we opened to check for pxdb*/ + +} /*end of ifdef QUICK_LOOK_UP*/ +#else + scan_start = 0; /* if we don't want quick lookup tables start + from the beginning */ +#endif + + /* Make two passes, one over the GNTT symbols, the other for the + * LNTT symbols. + * + * JB comment: above isn't true--they only make one pass, over + * the LNTT. + */ + for (i = 0; i < 1; i++) + { + int within_function = 0; + + if (i) + symcount = GNTT_SYMCOUNT (objfile); + else + symcount = LNTT_SYMCOUNT (objfile); + + + for (hp_symnum = scan_start; hp_symnum < symcount; hp_symnum++) + { + QUIT; + if (i) + dn_bufp = hpread_get_gntt (hp_symnum, objfile); + else + dn_bufp = hpread_get_lntt (hp_symnum, objfile); + + if (dn_bufp->dblock.extension) + continue; + + /* Only handle things which are necessary for minimal symbols. + everything else is ignored. */ + switch (dn_bufp->dblock.kind) + { + case DNTT_TYPE_SRCFILE: + { +#ifdef QUICK_LOOK_UP + if (scan_start == hp_symnum + && symcount == hp_symnum + 1) { + /* + * If there are NO globals in an executable, + * PXDB's index to the globals will point to + * the last record in the file, which + * could be this record. (this happened for F77 libraries) + * ignore it and be done! + */ + continue; + } +#endif + + /* A source file of some kind. Note this may simply + be an included file. */ + SET_NAMESTRING (dn_bufp, &namestring, objfile); + + /* Check if this is the source file we are already working + with. */ + if (pst && !strcmp (namestring, pst->filename)) + continue; + + /* Check if this is an include file, if so check if we have + already seen it. Add it to the include list */ + p = strrchr (namestring, '.'); + if (!strcmp (p, ".h")) + { + int j, found; + + found = 0; + for (j = 0; j < includes_used; j++) + if (!strcmp (namestring, psymtab_include_list[j])) + { + found = 1; + break; + } + if (found) + continue; + + /* Add it to the list of includes seen so far and + allocate more include space if necessary. */ + psymtab_include_list[includes_used++] = namestring; + if (includes_used >= includes_allocated) + { + char **orig = psymtab_include_list; + + psymtab_include_list = (char **) + alloca ((includes_allocated *= 2) * + sizeof (char *)); + memcpy ((PTR) psymtab_include_list, (PTR) orig, + includes_used * sizeof (char *)); + } + continue; + } + + if (pst) + { + if (!have_name) + { + pst->filename = (char *) + obstack_alloc (&pst->objfile->psymbol_obstack, + strlen (namestring) + 1); + strcpy (pst->filename, namestring); + have_name = 1; + continue; + } + continue; + } + + /* This is a bonafide new source file. + End the current partial symtab and start a new one. */ + + if (pst && past_first_source_file) + { + hpread_end_psymtab (pst, psymtab_include_list, + includes_used, + (hp_symnum + * sizeof (struct dntt_type_block)), + texthigh, + dependency_list, dependencies_used); + pst = (struct partial_symtab *) 0; + includes_used = 0; + dependencies_used = 0; + } + else + past_first_source_file = 1; + + valu = hpread_get_textlow (i, hp_symnum, objfile, symcount); + valu += ANOFFSET (section_offsets, SECT_OFF_TEXT); + pst = hpread_start_psymtab (objfile, section_offsets, + namestring, valu, + (hp_symnum + * sizeof (struct dntt_type_block)), + objfile->global_psymbols.next, + objfile->static_psymbols.next); + texthigh = valu; + have_name = 1; + continue; + } + + case DNTT_TYPE_MODULE: + /* A source file. It's still unclear to me what the + real difference between a DNTT_TYPE_SRCFILE and DNTT_TYPE_MODULE + is supposed to be. */ + + /* First end the previous psymtab */ + if (pst) { + hpread_end_psymtab (pst, psymtab_include_list, includes_used, + ((hp_symnum - 1) + * sizeof (struct dntt_type_block)), + texthigh, + dependency_list, dependencies_used); + pst = (struct partial_symtab *) 0; + includes_used = 0; + dependencies_used = 0; + have_name = 0; + } + + /* Now begin a new module and a new psymtab for it */ + SET_NAMESTRING (dn_bufp, &namestring, objfile); + valu = hpread_get_textlow (i, hp_symnum, objfile, symcount); + valu += ANOFFSET (section_offsets, SECT_OFF_TEXT); + if (!pst) { + pst = hpread_start_psymtab (objfile, section_offsets, + namestring, valu, + (hp_symnum + * sizeof (struct dntt_type_block)), + objfile->global_psymbols.next, + objfile->static_psymbols.next); + texthigh = valu; + have_name = 0; + } + continue; + + case DNTT_TYPE_FUNCTION: + case DNTT_TYPE_ENTRY: + /* The beginning of a function. DNTT_TYPE_ENTRY may also denote + a secondary entry point. */ + valu = dn_bufp->dfunc.hiaddr + ANOFFSET (section_offsets, + SECT_OFF_TEXT); + if (valu > texthigh) + texthigh = valu; + valu = dn_bufp->dfunc.lowaddr + + ANOFFSET (section_offsets, SECT_OFF_TEXT); + SET_NAMESTRING (dn_bufp, &namestring, objfile); + if (dn_bufp->dfunc.global) + add_psymbol_to_list (namestring, strlen (namestring), + VAR_NAMESPACE, LOC_BLOCK, + &objfile->global_psymbols, valu, + 0, language_unknown, objfile); + else + add_psymbol_to_list (namestring, strlen (namestring), + VAR_NAMESPACE, LOC_BLOCK, + &objfile->static_psymbols, valu, + 0, language_unknown, objfile); + within_function = 1; + continue; + + case DNTT_TYPE_DOC_FUNCTION: + valu = dn_bufp->ddocfunc.hiaddr + ANOFFSET (section_offsets, + SECT_OFF_TEXT); + if (valu > texthigh) + texthigh = valu; + valu = dn_bufp->ddocfunc.lowaddr + + ANOFFSET (section_offsets, SECT_OFF_TEXT); + SET_NAMESTRING (dn_bufp, &namestring, objfile); + if (dn_bufp->ddocfunc.global) + add_psymbol_to_list (namestring, strlen (namestring), + VAR_NAMESPACE, LOC_BLOCK, + &objfile->global_psymbols, valu, + 0, language_unknown, objfile); + else + add_psymbol_to_list (namestring, strlen (namestring), + VAR_NAMESPACE, LOC_BLOCK, + &objfile->static_psymbols, valu, + 0, language_unknown, objfile); + within_function = 1; + continue; + + case DNTT_TYPE_BEGIN: + case DNTT_TYPE_END: + /* We don't check MODULE end here, because there can be + symbols beyond the module end which properly belong to the + current psymtab -- so we wait till the next MODULE start */ + + +#ifdef QUICK_LOOK_UP + if (scan_start == hp_symnum + && symcount == hp_symnum + 1) { + /* + * If there are NO globals in an executable, + * PXDB's index to the globals will point to + * the last record in the file, which is + * probably an END MODULE, i.e. this record. + * ignore it and be done! + */ + continue; + } +#endif + /* Scope block begin/end. We only care about function + and file blocks right now. */ + + if ((dn_bufp->dend.endkind == DNTT_TYPE_FUNCTION) || + (dn_bufp->dend.endkind == DNTT_TYPE_DOC_FUNCTION)) + within_function = 0; + continue; + + case DNTT_TYPE_SVAR: + case DNTT_TYPE_DVAR: + case DNTT_TYPE_TYPEDEF: + case DNTT_TYPE_TAGDEF: + { + /* Variables, typedefs an the like. */ + enum address_class storage; + namespace_enum namespace; + + /* Don't add locals to the partial symbol table. */ + if (within_function + && (dn_bufp->dblock.kind == DNTT_TYPE_SVAR + || dn_bufp->dblock.kind == DNTT_TYPE_DVAR)) + continue; + + /* TAGDEFs go into the structure namespace. */ + if (dn_bufp->dblock.kind == DNTT_TYPE_TAGDEF) + namespace = STRUCT_NAMESPACE; + else + namespace = VAR_NAMESPACE; + + /* What kind of "storage" does this use? */ + if (dn_bufp->dblock.kind == DNTT_TYPE_SVAR) + storage = LOC_STATIC; + else if (dn_bufp->dblock.kind == DNTT_TYPE_DVAR + && dn_bufp->ddvar.regvar) + storage = LOC_REGISTER; + else if (dn_bufp->dblock.kind == DNTT_TYPE_DVAR) + storage = LOC_LOCAL; + else + storage = LOC_UNDEF; + + SET_NAMESTRING (dn_bufp, &namestring, objfile); + if (!pst) { + pst = hpread_start_psymtab (objfile, section_offsets, + "globals", 0, + (hp_symnum + * sizeof (struct dntt_type_block)), + objfile->global_psymbols.next, + objfile->static_psymbols.next); + } + + /* Compute address of the data symbol */ + valu = dn_bufp->dsvar.location; + /* Relocate in case it's in a shared library */ + if (storage == LOC_STATIC) + valu += ANOFFSET (section_offsets, SECT_OFF_DATA); + + /* Luckily, dvar, svar, typedef, and tagdef all + * have their "global" bit in the same place, so it works + * (though it's bad programming practice) to reference + * "dsvar.global" even though we may be looking at + * any of the above four types. + */ + if (dn_bufp->dsvar.global) + { + add_psymbol_to_list (namestring, strlen (namestring), + namespace, storage, + &objfile->global_psymbols, + valu, + 0, language_unknown, objfile); + } + else + { + add_psymbol_to_list (namestring, strlen (namestring), + namespace, storage, + &objfile->static_psymbols, + valu, + 0, language_unknown, objfile); + } + + /* For TAGDEF's, the above code added the tagname to the + * struct namespace. This will cause tag "t" to be found + * on a reference of the form "(struct t) x". But for + * C++ classes, "t" will also be a typename, which we + * want to find on a reference of the form "ptype t". + * Therefore, we also add "t" to the var namespace. + * Do the same for enum's due to the way aCC generates + * debug info for these (see more extended comment + * in hp-symtab-read.c). + * We do the same for templates, so that "ptype t" + * where "t" is a template also works. + */ + if (dn_bufp->dblock.kind == DNTT_TYPE_TAGDEF && + dn_bufp->dtype.type.dnttp.index < LNTT_SYMCOUNT (objfile)) { + int global = dn_bufp->dtag.global; + /* Look ahead to see if it's a C++ class */ + dn_bufp = hpread_get_lntt (dn_bufp->dtype.type.dnttp.index, objfile); + if (dn_bufp->dblock.kind == DNTT_TYPE_CLASS || + dn_bufp->dblock.kind == DNTT_TYPE_ENUM || + dn_bufp->dblock.kind == DNTT_TYPE_TEMPLATE) { + if (global) { + add_psymbol_to_list (namestring, strlen (namestring), + VAR_NAMESPACE, storage, + &objfile->global_psymbols, + dn_bufp->dsvar.location, + 0, language_unknown, objfile); + } else { + add_psymbol_to_list (namestring, strlen (namestring), + VAR_NAMESPACE, storage, + &objfile->static_psymbols, + dn_bufp->dsvar.location, + 0, language_unknown, objfile); + } + } + } + } + continue; + + case DNTT_TYPE_MEMENUM: + case DNTT_TYPE_CONST: + /* Constants and members of enumerated types. */ + SET_NAMESTRING (dn_bufp, &namestring, objfile); + if (!pst) + { + pst = hpread_start_psymtab (objfile, section_offsets, + "globals", 0, + (hp_symnum + * sizeof (struct dntt_type_block)), + objfile->global_psymbols.next, + objfile->static_psymbols.next); + } + if (dn_bufp->dconst.global) + add_psymbol_to_list (namestring, strlen (namestring), + VAR_NAMESPACE, LOC_CONST, + &objfile->global_psymbols, 0, + 0, language_unknown, objfile); + else + add_psymbol_to_list (namestring, strlen (namestring), + VAR_NAMESPACE, LOC_CONST, + &objfile->static_psymbols, 0, + 0, language_unknown, objfile); + continue; + default: + continue; + } + } + } + + /* End any pending partial symbol table. + */ + if (pst) + { + hpread_end_psymtab (pst, psymtab_include_list, includes_used, + hp_symnum * sizeof (struct dntt_type_block), + 0, dependency_list, dependencies_used); + } + + discard_cleanups (old_chain); +} + +/* Perform any local cleanups required when we are done with a particular + objfile. I.E, we are in the process of discarding all symbol information + for an objfile, freeing up all memory held for it, and unlinking the + objfile struct from the global list of known objfiles. */ + +void +hpread_symfile_finish (objfile) + struct objfile *objfile; +{ + if (objfile->sym_private != NULL) + { + mfree (objfile->md, objfile->sym_private); + } +} + + +/* The remaining functions are all for internal use only. */ + +/* Various small functions to get entries in the debug symbol sections. */ + +union dnttentry * +hpread_get_lntt (index, objfile) + int index; + struct objfile *objfile; +{ + return (union dnttentry *) + &(LNTT (objfile)[(index * sizeof (struct dntt_type_block))]); +} + +static union dnttentry * +hpread_get_gntt (index, objfile) + int index; + struct objfile *objfile; +{ + return (union dnttentry *) + &(GNTT (objfile)[(index * sizeof (struct dntt_type_block))]); +} + +union sltentry * +hpread_get_slt (index, objfile) + int index; + struct objfile *objfile; +{ + return (union sltentry *)&(SLT (objfile)[index * sizeof (union sltentry)]); +} + +/* Get the low address associated with some symbol (typically the start + of a particular source file or module). Since that information is not + stored as part of the DNTT_TYPE_MODULE or DNTT_TYPE_SRCFILE symbol we must infer it from + the existance of DNTT_TYPE_FUNCTION symbols. */ + +static unsigned long +hpread_get_textlow (global, index, objfile, symcount) + int global; + int index; + struct objfile *objfile; + int symcount; +{ + union dnttentry *dn_bufp; + struct minimal_symbol *msymbol; + + /* Look for a DNTT_TYPE_FUNCTION symbol. */ + if (index < symcount) /* symcount is the number of symbols in */ + { /* the dbinfo, LNTT table*/ + do + { + if (global) + dn_bufp = hpread_get_gntt (index++, objfile); + else + dn_bufp = hpread_get_lntt (index++, objfile); + } while (dn_bufp->dblock.kind != DNTT_TYPE_FUNCTION + && dn_bufp->dblock.kind != DNTT_TYPE_DOC_FUNCTION + && dn_bufp->dblock.kind != DNTT_TYPE_END + && index < symcount); + } + + /* Avoid going past a DNTT_TYPE_END when looking for a DNTT_TYPE_FUNCTION. This + might happen when a sourcefile has no functions. */ + if (dn_bufp->dblock.kind == DNTT_TYPE_END) + return 0; + + /* Avoid going past the end of the LNTT file */ + if (index == symcount) + return 0; + + /* The minimal symbols are typically more accurate for some reason. */ + if (dn_bufp->dblock.kind == DNTT_TYPE_FUNCTION) + msymbol = lookup_minimal_symbol (dn_bufp->dfunc.name + VT (objfile), NULL, + objfile); + else /* must be a DNTT_TYPE_DOC_FUNCTION */ + msymbol = lookup_minimal_symbol (dn_bufp->ddocfunc.name + VT (objfile), NULL, + objfile); + + if (msymbol) + return SYMBOL_VALUE_ADDRESS (msymbol); + else + return dn_bufp->dfunc.lowaddr; +} + +/* Allocate and partially fill a partial symtab. It will be + completely filled at the end of the symbol list. + + SYMFILE_NAME is the name of the symbol-file we are reading from, and ADDR + is the address relative to which its symbols are (incremental) or 0 + (normal). */ + +static struct partial_symtab * +hpread_start_psymtab (objfile, section_offsets, + filename, textlow, ldsymoff, global_syms, static_syms) + struct objfile *objfile; + struct section_offsets *section_offsets; + char *filename; + CORE_ADDR textlow; + int ldsymoff; + struct partial_symbol **global_syms; + struct partial_symbol **static_syms; +{ + int offset = ANOFFSET (section_offsets, SECT_OFF_TEXT); + extern void hpread_psymtab_to_symtab(); + struct partial_symtab *result = + start_psymtab_common (objfile, section_offsets, + filename, textlow, global_syms, static_syms); + + result->textlow += offset; + result->read_symtab_private = (char *) + obstack_alloc (&objfile->psymbol_obstack, sizeof (struct symloc)); + LDSYMOFF (result) = ldsymoff; + result->read_symtab = hpread_psymtab_to_symtab; + + return result; +} + + +/* Close off the current usage of PST. + Returns PST or NULL if the partial symtab was empty and thrown away. + + capping_symbol_offset --Byte index in LNTT or GNTT of the + last symbol processed during the build + of the previous pst. + + FIXME: List variables and peculiarities of same. */ + +static struct partial_symtab * +hpread_end_psymtab (pst, include_list, num_includes, capping_symbol_offset, + capping_text, dependency_list, number_dependencies) + struct partial_symtab *pst; + char **include_list; + int num_includes; + int capping_symbol_offset; + CORE_ADDR capping_text; + struct partial_symtab **dependency_list; + int number_dependencies; +{ + int i; + struct objfile *objfile = pst -> objfile; + int offset = ANOFFSET (pst->section_offsets, SECT_OFF_TEXT); + +#ifdef DUMPING + /* Turn on to see what kind of a psymtab we've built. + */ + static int dumping = 0; +#endif + + if (capping_symbol_offset != -1) + LDSYMLEN(pst) = capping_symbol_offset - LDSYMOFF(pst); + else + LDSYMLEN(pst) = 0; + pst->texthigh = capping_text + offset; + + pst->n_global_syms = + objfile->global_psymbols.next - (objfile->global_psymbols.list + pst->globals_offset); + pst->n_static_syms = + objfile->static_psymbols.next - (objfile->static_psymbols.list + pst->statics_offset); + +#ifdef DUMPING + if( dumping ) { + printf( + "\nPst %s, LDSYMOFF %x (%x), LDSYMLEN %x (%x), globals %d, statics %d\n", + pst->filename, + LDSYMOFF( pst ), + LDSYMOFF(pst)/sizeof(struct dntt_type_block), + LDSYMLEN( pst ), + LDSYMLEN(pst)/sizeof(struct dntt_type_block), + pst->n_global_syms, pst->n_static_syms ); + } +#endif + + pst->number_of_dependencies = number_dependencies; + if (number_dependencies) + { + pst->dependencies = (struct partial_symtab **) + obstack_alloc (&objfile->psymbol_obstack, + number_dependencies * sizeof (struct partial_symtab *)); + memcpy (pst->dependencies, dependency_list, + number_dependencies * sizeof (struct partial_symtab *)); + } + else + pst->dependencies = 0; + + for (i = 0; i < num_includes; i++) + { + struct partial_symtab *subpst = + allocate_psymtab (include_list[i], objfile); + + subpst->section_offsets = pst->section_offsets; + subpst->read_symtab_private = + (char *) obstack_alloc (&objfile->psymbol_obstack, + sizeof (struct symloc)); + LDSYMOFF(subpst) = + LDSYMLEN(subpst) = + subpst->textlow = + subpst->texthigh = 0; + + /* We could save slight bits of space by only making one of these, + shared by the entire set of include files. FIXME-someday. */ + subpst->dependencies = (struct partial_symtab **) + obstack_alloc (&objfile->psymbol_obstack, + sizeof (struct partial_symtab *)); + subpst->dependencies[0] = pst; + subpst->number_of_dependencies = 1; + + subpst->globals_offset = + subpst->n_global_syms = + subpst->statics_offset = + subpst->n_static_syms = 0; + + subpst->readin = 0; + subpst->symtab = 0; + subpst->read_symtab = pst->read_symtab; + } + + sort_pst_symbols (pst); + + /* If there is already a psymtab or symtab for a file of this name, remove it. + (If there is a symtab, more drastic things also happen.) + This happens in VxWorks. */ + free_named_symtabs (pst->filename); + + if (num_includes == 0 + && number_dependencies == 0 + && pst->n_global_syms == 0 + && pst->n_static_syms == 0) + { + /* Throw away this psymtab, it's empty. We can't deallocate it, since + it is on the obstack, but we can forget to chain it on the list. */ + /* Empty psymtabs happen as a result of header files which don't have + any symbols in them. There can be a lot of them. But this check + is wrong, in that a psymtab with N_SLINE entries but nothing else + is not empty, but we don't realize that. Fixing that without slowing + things down might be tricky. + * + * It's also wrong if we're using the quick look-up tables, as + * we can get empty psymtabs from modules with no routines in + * them. + */ + + discard_psymtab (pst); + + /* Indicate that psymtab was thrown away. */ + pst = (struct partial_symtab *)NULL; + + } + return pst; +} + + +/* End of hp-psymtab-read.c */ + +/* Set indentation to 4 spaces for Emacs; this file is + mostly non-GNU-ish in its style :-( */ +#if 0 +*** Local Variables: +*** c-basic-offset: 4 +*** End: +#endif diff --git a/gdb/hp-symtab-read.c b/gdb/hp-symtab-read.c new file mode 100644 index 0000000..90d4a10 --- /dev/null +++ b/gdb/hp-symtab-read.c @@ -0,0 +1,3988 @@ +/* Read hp debug symbols and convert to internal format, for GDB. + Copyright 1993, 1996 Free Software Foundation, Inc. + + This file is part of GDB. + + 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. + + Written by the Center for Software Science at the University of Utah + and by Cygnus Support. */ + +/* Common include for hp-symtab-read.c and hp-psymtab-read.c. + * Note this has nested includes for a bunch of stuff. + */ +#include "defs.h" +#include "symtab.h" +#include "gdbtypes.h" +#include "hpread.h" +#include "demangle.h" +#include "complaints.h" + + + + +static struct complaint hpread_unhandled_end_common_complaint = +{ + "unhandled symbol in hp-symtab-read.c: DNTT_TYPE_COMMON/DNTT_TYPE_END.\n", 0, 0 +}; + +static struct complaint hpread_unhandled_type_complaint = +{ + "hpread_type_translate: unhandled type code.", 0, 0 +}; + +static struct complaint hpread_struct_complaint = +{ + "hpread_read_struct_type: expected SVAR type...", 0, 0 +}; + +static struct complaint hpread_array_complaint = +{ + "error in hpread_array_type.", 0, 0 +}; + +static struct complaint hpread_type_lookup_complaint = +{ + "error in hpread_type_lookup().", 0, 0 +}; + + +static struct complaint hpread_unexpected_end_complaint = +{ + "internal error in hp-symtab-read.c: Unexpected DNTT_TYPE_END kind.", 0, 0 +}; + +static struct complaint hpread_tagdef_complaint = +{ + "error processing class tagdef", 0, 0 +}; + +static struct complaint hpread_unhandled_common_complaint = +{ + "unhandled symbol in hp-symtab-read.c: DNTT_TYPE_COMMON.", 0, 0 +}; + +static struct complaint hpread_unhandled_blockdata_complaint = +{ + "unhandled symbol in hp-symtab-read.c: DNTT_TYPE_BLOCKDATA.", 0, 0 +}; + + +/* Forward procedure declarations */ + +static unsigned long hpread_get_scope_start + PARAMS ((sltpointer, struct objfile *)); + +static unsigned long hpread_get_line + PARAMS ((sltpointer, struct objfile *)); + +static CORE_ADDR hpread_get_location + PARAMS ((sltpointer, struct objfile *)); + +static void hpread_psymtab_to_symtab_1 + PARAMS ((struct partial_symtab *)); + +void hpread_psymtab_to_symtab + PARAMS ((struct partial_symtab *)); + +static struct symtab *hpread_expand_symtab + PARAMS ((struct objfile *, int, int, CORE_ADDR, int, + struct section_offsets *, char *)); + +static int hpread_type_translate + PARAMS ((dnttpointer)); + +static struct type **hpread_lookup_type + PARAMS ((dnttpointer, struct objfile *)); + +static struct type *hpread_alloc_type + PARAMS ((dnttpointer, struct objfile *)); + +static struct type *hpread_read_enum_type + PARAMS ((dnttpointer, union dnttentry *, struct objfile *)); + +static struct type *hpread_read_function_type + PARAMS ((dnttpointer, union dnttentry *, struct objfile *, int)); + +static struct type *hpread_read_doc_function_type + PARAMS ((dnttpointer, union dnttentry *, struct objfile *, int)); + +static struct type *hpread_read_struct_type + PARAMS ((dnttpointer, union dnttentry *, struct objfile *)); + +static struct type *hpread_get_nth_template_arg + PARAMS ((struct objfile *, int)); + +static struct type * hpread_read_templ_arg_type + PARAMS ((dnttpointer, union dnttentry *, struct objfile *, char *)); + +static struct type *hpread_read_set_type + PARAMS ((dnttpointer, union dnttentry *, struct objfile *)); + +static struct type * hpread_read_array_type + PARAMS ((dnttpointer, union dnttentry *dn_bufp, struct objfile *objfile)); + +static struct type *hpread_read_subrange_type + PARAMS ((dnttpointer, union dnttentry *, struct objfile *)); + +static struct type * hpread_type_lookup + PARAMS ((dnttpointer, struct objfile *)); + +static sltpointer hpread_record_lines + PARAMS ((struct subfile *, sltpointer, sltpointer, + struct objfile *, CORE_ADDR)); + +static void hpread_process_one_debug_symbol + PARAMS ((union dnttentry *, char *, struct section_offsets *, + struct objfile *, CORE_ADDR, int, char *, int, int * )); + +static int hpread_get_scope_depth + PARAMS ((union dnttentry *, struct objfile *, int)); + +static void fix_static_member_physnames + PARAMS ((struct type *, char *, struct objfile *)); + +static void fixup_class_method_type + PARAMS ((struct type *, struct type *, struct objfile *)); + +static void hpread_adjust_bitoffsets PARAMS ((struct type *, int)); + +static dnttpointer hpread_get_next_skip_over_anon_unions + PARAMS ((int, dnttpointer, union dnttentry **, struct objfile *)); + +/* Global to indicate presence of HP-compiled objects, + in particular, SOM executable file with SOM debug info + Defined in symtab.c, used in hppa-tdep.c. */ +extern int hp_som_som_object_present; + +/* Static used to indicate a class type that requires a + fix-up of one of its method types */ +static struct type * fixup_class = NULL; + +/* Static used to indicate the method type that is to be + used to fix-up the type for fixup_class */ +static struct type * fixup_method = NULL; + + + +/* Get the nesting depth for the source line identified by INDEX. */ + +static unsigned long +hpread_get_scope_start (index, objfile) + sltpointer index; + struct objfile *objfile; +{ + union sltentry *sl_bufp; + + sl_bufp = hpread_get_slt (index, objfile); + return sl_bufp->sspec.backptr.dnttp.index; +} + +/* Get the source line number the the line identified by INDEX. */ + +static unsigned long +hpread_get_line (index, objfile) + sltpointer index; + struct objfile *objfile; +{ + union sltentry *sl_bufp; + + sl_bufp = hpread_get_slt (index, objfile); + return sl_bufp->snorm.line; +} + +/* Find the code address associated with a given sltpointer */ + +static CORE_ADDR +hpread_get_location (index, objfile) + sltpointer index; + struct objfile *objfile; +{ + union sltentry *sl_bufp; + int i; + + /* code location of special sltentrys is determined from context */ + sl_bufp = hpread_get_slt (index, objfile); + + if (sl_bufp->snorm.sltdesc == SLT_END) + { + /* find previous normal sltentry and get address */ + for (i = 0; ((sl_bufp->snorm.sltdesc != SLT_NORMAL) && + (sl_bufp->snorm.sltdesc != SLT_NORMAL_OFFSET) && + (sl_bufp->snorm.sltdesc != SLT_EXIT)); i++) + sl_bufp = hpread_get_slt (index - i, objfile); + if (sl_bufp->snorm.sltdesc == SLT_NORMAL_OFFSET) + return sl_bufp->snormoff.address; + else + return sl_bufp->snorm.address; + } + + /* find next normal sltentry and get address */ + for (i = 0; ((sl_bufp->snorm.sltdesc != SLT_NORMAL) && + (sl_bufp->snorm.sltdesc != SLT_NORMAL_OFFSET) && + (sl_bufp->snorm.sltdesc != SLT_EXIT)); i++) + sl_bufp = hpread_get_slt (index + i, objfile); + if (sl_bufp->snorm.sltdesc == SLT_NORMAL_OFFSET) + return sl_bufp->snormoff.address; + else + return sl_bufp->snorm.address; +} + + +/* Return 1 if an HP debug symbol of type KIND has a name associated with + * it, else return 0. (This function is not currently used, but I'll + * leave it here in case it proves useful later on. - RT). + */ + +int +hpread_has_name (kind) + enum dntt_entry_type kind; +{ + switch (kind) + { + case DNTT_TYPE_SRCFILE: + case DNTT_TYPE_MODULE: + case DNTT_TYPE_FUNCTION: + case DNTT_TYPE_DOC_FUNCTION: + case DNTT_TYPE_ENTRY: + case DNTT_TYPE_IMPORT: + case DNTT_TYPE_LABEL: + case DNTT_TYPE_FPARAM: + case DNTT_TYPE_SVAR: + case DNTT_TYPE_DVAR: + case DNTT_TYPE_CONST: + case DNTT_TYPE_TYPEDEF: + case DNTT_TYPE_TAGDEF: + case DNTT_TYPE_MEMENUM: + case DNTT_TYPE_FIELD: + case DNTT_TYPE_SA: + case DNTT_TYPE_BLOCKDATA: + case DNTT_TYPE_MEMFUNC: + case DNTT_TYPE_DOC_MEMFUNC: + return 1; + + case DNTT_TYPE_BEGIN: + case DNTT_TYPE_END: + case DNTT_TYPE_POINTER: + case DNTT_TYPE_ENUM: + case DNTT_TYPE_SET: + case DNTT_TYPE_ARRAY: + case DNTT_TYPE_STRUCT: + case DNTT_TYPE_UNION: + case DNTT_TYPE_VARIANT: + case DNTT_TYPE_FILE: + case DNTT_TYPE_FUNCTYPE: + case DNTT_TYPE_SUBRANGE: + case DNTT_TYPE_WITH: + case DNTT_TYPE_COMMON: + case DNTT_TYPE_COBSTRUCT: + case DNTT_TYPE_XREF: + case DNTT_TYPE_MACRO: + case DNTT_TYPE_CLASS_SCOPE: + case DNTT_TYPE_REFERENCE: + case DNTT_TYPE_PTRMEM: + case DNTT_TYPE_PTRMEMFUNC: + case DNTT_TYPE_CLASS: + case DNTT_TYPE_GENFIELD: + case DNTT_TYPE_VFUNC: + case DNTT_TYPE_MEMACCESS: + case DNTT_TYPE_INHERITANCE: + case DNTT_TYPE_FRIEND_CLASS: + case DNTT_TYPE_FRIEND_FUNC: + case DNTT_TYPE_MODIFIER: + case DNTT_TYPE_OBJECT_ID: + case DNTT_TYPE_TEMPLATE: + case DNTT_TYPE_TEMPLATE_ARG: + case DNTT_TYPE_FUNC_TEMPLATE: + case DNTT_TYPE_LINK: + /* DNTT_TYPE_DYN_ARRAY_DESC ? */ + /* DNTT_TYPE_DESC_SUBRANGE ? */ + /* DNTT_TYPE_BEGIN_EXT ? */ + /* DNTT_TYPE_INLN ? */ + /* DNTT_TYPE_INLN_LIST ? */ + /* DNTT_TYPE_ALIAS ? */ + default: + return 0; + } +} + +/* Do the dirty work of reading in the full symbol from a partial symbol + table. */ + +static void +hpread_psymtab_to_symtab_1 (pst) + struct partial_symtab *pst; +{ + struct cleanup *old_chain; + int i; + + /* Get out quick if passed junk. */ + if (!pst) + return; + + /* Complain if we've already read in this symbol table. */ + if (pst->readin) + { + fprintf (stderr, "Psymtab for %s already read in. Shouldn't happen.\n", + pst->filename); + return; + } + + /* Read in all partial symtabs on which this one is dependent */ + for (i = 0; i < pst->number_of_dependencies; i++) + if (!pst->dependencies[i]->readin) + { + /* Inform about additional files that need to be read in. */ + if (info_verbose) + { + fputs_filtered (" ", gdb_stdout); + wrap_here (""); + fputs_filtered ("and ", gdb_stdout); + wrap_here (""); + printf_filtered ("%s...", pst->dependencies[i]->filename); + wrap_here (""); /* Flush output */ + gdb_flush (gdb_stdout); + } + hpread_psymtab_to_symtab_1 (pst->dependencies[i]); + } + + /* If it's real... */ + if (LDSYMLEN (pst)) + { + /* Init stuff necessary for reading in symbols */ + buildsym_init (); + old_chain = make_cleanup (really_free_pendings, 0); + + pst->symtab = + hpread_expand_symtab (pst->objfile, LDSYMOFF (pst), LDSYMLEN (pst), + pst->textlow, pst->texthigh - pst->textlow, + pst->section_offsets, pst->filename); + sort_symtab_syms (pst->symtab); + + do_cleanups (old_chain); + } + + pst->readin = 1; +} + +/* Read in all of the symbols for a given psymtab for real. + Be verbose about it if the user wants that. */ + +void +hpread_psymtab_to_symtab (pst) + struct partial_symtab *pst; +{ + /* Get out quick if given junk. */ + if (!pst) + return; + + /* Sanity check. */ + if (pst->readin) + { + fprintf (stderr, "Psymtab for %s already read in. Shouldn't happen.\n", + pst->filename); + return; + } + + /* elz: setting the flag to indicate that the code of the target + was compiled using an HP compiler (aCC, cc) + the processing_acc_compilation variable is declared in the + file buildsym.h, the HP_COMPILED_TARGET is defined to be equal + to 3 in the file tm_hppa.h*/ + + processing_gcc_compilation = 0; + + if (LDSYMLEN (pst) || pst->number_of_dependencies) + { + /* Print the message now, before reading the string table, + to avoid disconcerting pauses. */ + if (info_verbose) + { + printf_filtered ("Reading in symbols for %s...", pst->filename); + gdb_flush (gdb_stdout); + } + + hpread_psymtab_to_symtab_1 (pst); + + /* Match with global symbols. This only needs to be done once, + after all of the symtabs and dependencies have been read in. */ + scan_file_globals (pst->objfile); + + /* Finish up the debug error message. */ + if (info_verbose) + printf_filtered ("done.\n"); + } +} + +/* Read in a defined section of a specific object file's symbols. + + DESC is the file descriptor for the file, positioned at the + beginning of the symtab + SYM_OFFSET is the offset within the file of + the beginning of the symbols we want to read + SYM_SIZE is the size of the symbol info to read in. + TEXT_OFFSET is the beginning of the text segment we are reading symbols for + TEXT_SIZE is the size of the text segment read in. + SECTION_OFFSETS are the relocation offsets which get added to each symbol. */ + +static struct symtab * +hpread_expand_symtab (objfile, sym_offset, sym_size, text_offset, text_size, + section_offsets, filename) + struct objfile *objfile; + int sym_offset; + int sym_size; + CORE_ADDR text_offset; + int text_size; + struct section_offsets *section_offsets; + char *filename; +{ + char *namestring; + union dnttentry *dn_bufp; + unsigned max_symnum; + int at_module_boundary = 0; + /* 1 => at end, -1 => at beginning */ + + int sym_index = sym_offset / sizeof (struct dntt_type_block); + + current_objfile = objfile; + subfile_stack = 0; + + last_source_file = 0; + + /* Demangling style -- if EDG style already set, don't change it, + as HP style causes some problems with the KAI EDG compiler */ + if (current_demangling_style != edg_demangling) { + /* Otherwise, ensure that we are using HP style demangling */ + set_demangling_style (HP_DEMANGLING_STYLE_STRING); + } + + dn_bufp = hpread_get_lntt (sym_index, objfile); + if (!((dn_bufp->dblock.kind == (unsigned char) DNTT_TYPE_SRCFILE) || + (dn_bufp->dblock.kind == (unsigned char) DNTT_TYPE_MODULE))) + { + start_symtab ("globals", NULL, 0); + record_debugformat ("HP"); + } + + /* The psymtab builder (hp-psymtab-read.c) is the one that + * determined the "sym_size" argument (i.e. how many DNTT symbols + * are in this symtab), which we use to compute "max_symnum" + * (point in DNTT to which we read). + * + * Perhaps this should be changed so that + * process_one_debug_symbol() "knows" when + * to stop reading (based on reading from the MODULE to the matching + * END), and take out this reliance on a #-syms being passed in... + * (I'm worried about the reliability of this number). But I'll + * leave it as-is, for now. - RT + * + * The change above has been made. I've left the "for" loop control + * in to prepare for backing this out again. -JB + */ + max_symnum = sym_size / sizeof (struct dntt_type_block); + /* No reason to multiply on pst side and divide on sym side... FIXME */ + + /* Read in and process each debug symbol within the specified range. + */ + for (symnum = 0; + symnum < max_symnum; + symnum++) + { + QUIT; /* Allow this to be interruptable */ + dn_bufp = hpread_get_lntt (sym_index + symnum, objfile); + + if (dn_bufp->dblock.extension) + continue; + + /* Yow! We call SET_NAMESTRING on things without names! */ + SET_NAMESTRING (dn_bufp, &namestring, objfile); + + hpread_process_one_debug_symbol (dn_bufp, namestring, section_offsets, + objfile, text_offset, text_size, + filename, symnum + sym_index, + &at_module_boundary + ); + + /* OLD COMMENTS: This routine is only called for psts. All psts + * correspond to MODULES. If we ever do lazy-reading of globals + * from the LNTT, then there will be a pst which ends when the + * LNTT ends, and not at an END MODULE entry. Then we'll have + * to re-visit this break. + + if( at_end_of_module ) + break; + + */ + + /* We no longer break out of the loop when we reach the end of a + module. The reason is that with CTTI, the compiler can generate + function symbols (for template function instantiations) which are not + in any module; typically they show up beyond a module's end, and + before the next module's start. We include them in the current + module. However, we still don't trust the MAX_SYMNUM value from + the psymtab, so we break out if we enter a new module. */ + + if (at_module_boundary == -1) + break; + } + + current_objfile = NULL; + hp_som_som_object_present = 1; /* Indicate we've processed an HP SOM SOM file */ + + return end_symtab (text_offset + text_size, objfile, 0); +} + + + + +/* Convert basic types from HP debug format into GDB internal format. */ + +static int +hpread_type_translate (typep) + dnttpointer typep; +{ + if (!typep.dntti.immediate) { + error ("error in hpread_type_translate\n."); + return; + } + + switch (typep.dntti.type) + { + case HP_TYPE_BOOLEAN: + case HP_TYPE_BOOLEAN_S300_COMPAT: + case HP_TYPE_BOOLEAN_VAX_COMPAT: + return FT_BOOLEAN; + case HP_TYPE_CHAR: /* C signed char, C++ plain char */ + + case HP_TYPE_WIDE_CHAR: + return FT_CHAR; + case HP_TYPE_INT: + if (typep.dntti.bitlength <= 8) + return FT_SIGNED_CHAR; /* C++ signed char */ + if (typep.dntti.bitlength <= 16) + return FT_SHORT; + if (typep.dntti.bitlength <= 32) + return FT_INTEGER; + return FT_LONG_LONG; + case HP_TYPE_LONG: + if (typep.dntti.bitlength <= 8) + return FT_SIGNED_CHAR; /* C++ signed char. */ + return FT_LONG; + case HP_TYPE_UNSIGNED_LONG: + if (typep.dntti.bitlength <= 8) + return FT_UNSIGNED_CHAR; /* C/C++ unsigned char */ + if (typep.dntti.bitlength <= 16) + return FT_UNSIGNED_SHORT; + if (typep.dntti.bitlength <= 32) + return FT_UNSIGNED_LONG; + return FT_UNSIGNED_LONG_LONG; + case HP_TYPE_UNSIGNED_INT: + if (typep.dntti.bitlength <= 8) + return FT_UNSIGNED_CHAR; + if (typep.dntti.bitlength <= 16) + return FT_UNSIGNED_SHORT; + if (typep.dntti.bitlength <= 32) + return FT_UNSIGNED_INTEGER; + return FT_UNSIGNED_LONG_LONG; + case HP_TYPE_REAL: + case HP_TYPE_REAL_3000: + case HP_TYPE_DOUBLE: + if (typep.dntti.bitlength == 64) + return FT_DBL_PREC_FLOAT; + if (typep.dntti.bitlength == 128) + return FT_EXT_PREC_FLOAT; + return FT_FLOAT; + case HP_TYPE_COMPLEX: + case HP_TYPE_COMPLEXS3000: + if (typep.dntti.bitlength == 128) + return FT_DBL_PREC_COMPLEX; + if (typep.dntti.bitlength == 192) + return FT_EXT_PREC_COMPLEX; + return FT_COMPLEX; + case HP_TYPE_VOID: + return FT_VOID; + case HP_TYPE_STRING200: + case HP_TYPE_LONGSTRING200: + case HP_TYPE_FTN_STRING_SPEC: + case HP_TYPE_MOD_STRING_SPEC: + case HP_TYPE_MOD_STRING_3000: + case HP_TYPE_FTN_STRING_S300_COMPAT: + case HP_TYPE_FTN_STRING_VAX_COMPAT: + return FT_STRING; + case HP_TYPE_TEMPLATE_ARG: + return FT_TEMPLATE_ARG; + case HP_TYPE_TEXT: + case HP_TYPE_FLABEL: + case HP_TYPE_PACKED_DECIMAL: + case HP_TYPE_ANYPOINTER: + case HP_TYPE_GLOBAL_ANYPOINTER: + case HP_TYPE_LOCAL_ANYPOINTER: + default: + warning ("hpread_type_translate: unhandled type code.\n"); + return FT_VOID; + } +} + +/* Given a position in the DNTT, return a pointer to the + * already-built "struct type" (if any), for the type defined + * at that position. + */ + +static struct type ** +hpread_lookup_type (hp_type, objfile) + dnttpointer hp_type; + struct objfile *objfile; +{ + unsigned old_len; + int index = hp_type.dnttp.index; + int size_changed = 0; + + /* The immediate flag indicates this doesn't actually point to + * a type DNTT. + */ + if (hp_type.dntti.immediate) + return NULL; + + /* For each objfile, we maintain a "type vector". + * This an array of "struct type *"'s with one pointer per DNTT index. + * Given a DNTT index, we look in this array to see if we have + * already processed this DNTT and if it is a type definition. + * If so, then we can locate a pointer to the already-built + * "struct type", and not build it again. + * + * The need for this arises because our DNTT-walking code wanders + * around. In particular, it will encounter the same type multiple + * times (once for each object of that type). We don't want to + * built multiple "struct type"'s for the same thing. + * + * Having said this, I should point out that this type-vector is + * an expensive way to keep track of this. If most DNTT entries are + * 3 words, the type-vector will be 1/3 the size of the DNTT itself. + * Alternative solutions: + * - Keep a compressed or hashed table. Less memory, but more expensive + * to search and update. + * - (Suggested by JB): Overwrite the DNTT entry itself + * with the info. Create a new type code "ALREADY_BUILT", and modify + * the DNTT to have that type code and point to the already-built entry. + * -RT + */ + + if (index < LNTT_SYMCOUNT (objfile)) + { + if (index >= TYPE_VECTOR_LENGTH (objfile)) + { + old_len = TYPE_VECTOR_LENGTH (objfile); + + /* See if we need to allocate a type-vector. */ + if (old_len == 0) + { + TYPE_VECTOR_LENGTH(objfile) = LNTT_SYMCOUNT (objfile) + GNTT_SYMCOUNT (objfile); + TYPE_VECTOR (objfile) = (struct type **) + xmmalloc (objfile->md, TYPE_VECTOR_LENGTH (objfile) * sizeof (struct type *)); + memset (&TYPE_VECTOR (objfile)[old_len], 0, + (TYPE_VECTOR_LENGTH (objfile) - old_len) * + sizeof (struct type *)); + } + + /* See if we need to resize type-vector. With my change to + * initially allocate a correct-size type-vector, this code + * should no longer trigger. + */ + while (index >= TYPE_VECTOR_LENGTH (objfile)) { + TYPE_VECTOR_LENGTH (objfile) *= 2; + size_changed = 1; + } + if (size_changed) { + TYPE_VECTOR (objfile) = (struct type **) + xmrealloc (objfile -> md, + (char *) TYPE_VECTOR (objfile), + (TYPE_VECTOR_LENGTH (objfile) * sizeof (struct type *))); + + memset (&TYPE_VECTOR (objfile)[old_len], 0, + (TYPE_VECTOR_LENGTH (objfile) - old_len) * + sizeof (struct type *)); + } + + } + return &TYPE_VECTOR (objfile)[index]; + } + else + return NULL; +} + +/* Possibly allocate a GDB internal type so we can internalize HP_TYPE. + Note we'll just return the address of a GDB internal type if we already + have it lying around. */ + +static struct type * +hpread_alloc_type (hp_type, objfile) + dnttpointer hp_type; + struct objfile *objfile; +{ + struct type **type_addr; + + type_addr = hpread_lookup_type (hp_type, objfile); + if (*type_addr == 0) { + *type_addr = alloc_type (objfile); + + /* A hack - if we really are a C++ class symbol, then this default + * will get overriden later on. + */ + TYPE_CPLUS_SPECIFIC (*type_addr) + = (struct cplus_struct_type *) &cplus_struct_default; + } + + return *type_addr; +} + +/* Read a native enumerated type and return it in GDB internal form. */ + +static struct type * +hpread_read_enum_type (hp_type, dn_bufp, objfile) + dnttpointer hp_type; + union dnttentry *dn_bufp; + struct objfile *objfile; +{ + struct type *type; + struct pending **symlist, *osyms, *syms; + struct pending *local_list = NULL; + int o_nsyms, nsyms = 0; + dnttpointer mem; + union dnttentry *memp; + char *name; + long n; + struct symbol *sym; + + /* Allocate a GDB type. If we've already read in this enum type, + * it'll return the already built GDB type, so stop here. + * (Note: I added this check, to conform with what's done for + * struct, union, class. + * I assume this is OK. - RT) + */ + type = hpread_alloc_type (hp_type, objfile); + if (TYPE_CODE (type) == TYPE_CODE_ENUM) + return type; + + /* HP C supports "sized enums", where a specifier such as "short" or + "char" can be used to get enums of different sizes. So don't assume + an enum is always 4 bytes long. pai/1997-08-21 */ + TYPE_LENGTH (type) = dn_bufp->denum.bitlength / 8; + + symlist = &file_symbols; + osyms = *symlist; + o_nsyms = osyms ? osyms->nsyms : 0; + + /* Get a name for each member and add it to our list of members. + * The list of "mem" SOM records we are walking should all be + * SOM type DNTT_TYPE_MEMENUM (not checked). + */ + mem = dn_bufp->denum.firstmem; + while (mem.word && mem.word != DNTTNIL) + { + memp = hpread_get_lntt (mem.dnttp.index, objfile); + + name = VT (objfile) + memp->dmember.name; + sym = (struct symbol *) obstack_alloc (&objfile->symbol_obstack, + sizeof (struct symbol)); + memset (sym, 0, sizeof (struct symbol)); + SYMBOL_NAME (sym) = obsavestring (name, strlen (name), + &objfile->symbol_obstack); + SYMBOL_CLASS (sym) = LOC_CONST; + SYMBOL_NAMESPACE (sym) = VAR_NAMESPACE; + SYMBOL_VALUE (sym) = memp->dmember.value; + add_symbol_to_list (sym, symlist); + nsyms++; + mem = memp->dmember.nextmem; + } + + /* Now that we know more about the enum, fill in more info. */ + TYPE_CODE (type) = TYPE_CODE_ENUM; + TYPE_FLAGS (type) &= ~TYPE_FLAG_STUB; + TYPE_NFIELDS (type) = nsyms; + TYPE_FIELDS (type) = (struct field *) + obstack_alloc (&objfile->type_obstack, sizeof (struct field) * nsyms); + + /* Find the symbols for the members and put them into the type. + The symbols can be found in the symlist that we put them on + to cause them to be defined. osyms contains the old value + of that symlist; everything up to there was defined by us. + + Note that we preserve the order of the enum constants, so + that in something like "enum {FOO, LAST_THING=FOO}" we print + FOO, not LAST_THING. */ + for (syms = *symlist, n = 0; syms; syms = syms->next) + { + int j = 0; + if (syms == osyms) + j = o_nsyms; + for (; j < syms->nsyms; j++, n++) + { + struct symbol *xsym = syms->symbol[j]; + SYMBOL_TYPE (xsym) = type; + TYPE_FIELD_NAME (type, n) = SYMBOL_NAME (xsym); + TYPE_FIELD_BITPOS (type, n) = SYMBOL_VALUE (xsym); + TYPE_FIELD_BITSIZE (type, n) = 0; + } + if (syms == osyms) + break; + } + + return type; +} + +/* Read and internalize a native function debug symbol. */ + +static struct type * +hpread_read_function_type (hp_type, dn_bufp, objfile, newblock) + dnttpointer hp_type; + union dnttentry *dn_bufp; + struct objfile *objfile; + int newblock; +{ + struct type *type, *type1; + struct pending *syms; + struct pending *local_list = NULL; + int nsyms = 0; + dnttpointer param; + union dnttentry *paramp; + char *name; + long n; + struct symbol *sym; + int record_args = 1; + + /* See if we've already read in this type. */ + type = hpread_alloc_type (hp_type, objfile); + if (TYPE_CODE (type) == TYPE_CODE_FUNC) + { + record_args = 0; /* already read in, don't modify type */ + } + else + { + /* Nope, so read it in and store it away. */ + if (dn_bufp->dblock.kind == DNTT_TYPE_FUNCTION || + dn_bufp->dblock.kind == DNTT_TYPE_MEMFUNC) + type1 = lookup_function_type (hpread_type_lookup (dn_bufp->dfunc.retval, + objfile)); + else if (dn_bufp->dblock.kind == DNTT_TYPE_FUNCTYPE) + type1 = lookup_function_type (hpread_type_lookup (dn_bufp->dfunctype.retval, + objfile)); + else /* expect DNTT_TYPE_FUNC_TEMPLATE */ + type1 = lookup_function_type (hpread_type_lookup (dn_bufp->dfunc_template.retval, + objfile)); + memcpy ((char *) type, (char *) type1, sizeof (struct type)); + + /* Mark it -- in the middle of processing */ + TYPE_FLAGS (type) |= TYPE_FLAG_INCOMPLETE; + } + + /* Now examine each parameter noting its type, location, and a + wealth of other information. */ + if (dn_bufp->dblock.kind == DNTT_TYPE_FUNCTION || + dn_bufp->dblock.kind == DNTT_TYPE_MEMFUNC) + param = dn_bufp->dfunc.firstparam; + else if (dn_bufp->dblock.kind == DNTT_TYPE_FUNCTYPE) + param = dn_bufp->dfunctype.firstparam; + else /* expect DNTT_TYPE_FUNC_TEMPLATE */ + param = dn_bufp->dfunc_template.firstparam; + while (param.word && param.word != DNTTNIL) + { + paramp = hpread_get_lntt (param.dnttp.index, objfile); + nsyms++; + param = paramp->dfparam.nextparam; + + /* Get the name. */ + name = VT (objfile) + paramp->dfparam.name; + sym = (struct symbol *) obstack_alloc (&objfile->symbol_obstack, + sizeof (struct symbol)); + (void) memset (sym, 0, sizeof (struct symbol)); + SYMBOL_NAME (sym) = obsavestring (name, strlen (name), + &objfile->symbol_obstack); + + /* Figure out where it lives. */ + if (paramp->dfparam.regparam) + SYMBOL_CLASS (sym) = LOC_REGPARM; + else if (paramp->dfparam.indirect) + SYMBOL_CLASS (sym) = LOC_REF_ARG; + else + SYMBOL_CLASS (sym) = LOC_ARG; + SYMBOL_NAMESPACE (sym) = VAR_NAMESPACE; + if (paramp->dfparam.copyparam) + { + SYMBOL_VALUE (sym) = paramp->dfparam.location ; +#ifdef HPREAD_ADJUST_STACK_ADDRESS + SYMBOL_VALUE (sym) + += HPREAD_ADJUST_STACK_ADDRESS (CURRENT_FUNCTION_VALUE (objfile)); +#endif + /* This is likely a pass-by-invisible reference parameter, + Hack on the symbol class to make GDB happy. */ + /* ??rehrauer: This appears to be broken w/r/t to passing + C values of type float and struct. Perhaps this ought + to be highighted as a special case, but for now, just + allowing these to be LOC_ARGs seems to work fine. + */ +#if 0 + SYMBOL_CLASS (sym) = LOC_REGPARM_ADDR; +#endif + } + else + SYMBOL_VALUE (sym) = paramp->dfparam.location; + + /* Get its type. */ + SYMBOL_TYPE (sym) = hpread_type_lookup (paramp->dfparam.type, objfile); + /* Add it to the symbol list. */ + /* Note 1 (RT) At the moment, add_symbol_to_list() is also being + * called on FPARAM symbols from the process_one_debug_symbol() + * level... so parameters are getting added twice! (this shows + * up in the symbol dump you get from "maint print symbols ..."). + * Note 2 (RT) I took out the processing of FPARAM from the + * process_one_debug_symbol() level, so at the moment parameters are only + * being processed here. This seems to have no ill effect. + */ + /* Note 3 (pai/1997-08-11) I removed the add_symbol_to_list() which put + each fparam on the local_symbols list from here. Now we use the + local_list to which fparams are added below, and set the param_symbols + global to point to that at the end of this routine. */ + /* elz: I added this new list of symbols which is local to the function. + this list is the one which is actually used to build the type for the + function rather than the gloabal list pointed to by symlist. + Using a global list to keep track of the parameters is wrong, because + this function is called recursively if one parameter happend to be + a function itself with more parameters in it. Adding parameters to the + same global symbol list would not work! + Actually it did work in case of cc compiled programs where you do + not check the parameter lists of the arguments. */ + add_symbol_to_list (sym, &local_list); + + } + + /* If type was read in earlier, don't bother with modifying + the type struct */ + if (!record_args) + goto finish; + + /* Note how many parameters we found. */ + TYPE_NFIELDS (type) = nsyms; + TYPE_FIELDS (type) = (struct field *) + obstack_alloc (&objfile->type_obstack, + sizeof (struct field) * nsyms); + + /* Find the symbols for the parameters and + use them to fill parameter-type information into the function-type. + The parameter symbols can be found in the local_list that we just put them on. */ + /* Note that we preserve the order of the parameters, so + that in something like "enum {FOO, LAST_THING=FOO}" we print + FOO, not LAST_THING. */ + + /* get the parameters types from the local list not the global list + so that the type can be correctly constructed for functions which + have function as parameters */ + for (syms = local_list, n = 0; syms; syms = syms->next) + { + int j = 0; + for (j=0; j < syms->nsyms; j++, n++) + { + struct symbol *xsym = syms->symbol[j]; + TYPE_FIELD_NAME (type, n) = SYMBOL_NAME (xsym); + TYPE_FIELD_TYPE (type, n) = SYMBOL_TYPE (xsym); + TYPE_FIELD_BITPOS (type, n) = n; + TYPE_FIELD_BITSIZE (type, n) = 0; + } + } + /* Mark it as having been processed */ + TYPE_FLAGS (type) &= ~(TYPE_FLAG_INCOMPLETE); + + /* Check whether we need to fix-up a class type with this function's type */ + if (fixup_class && (fixup_method == type)) + { + fixup_class_method_type (fixup_class, fixup_method, objfile); + fixup_class = NULL; + fixup_method = NULL; + } + + /* Set the param list of this level of the context stack + to our local list. Do this only if this function was + called for creating a new block, and not if it was called + simply to get the function type. This prevents recursive + invocations from trashing param_symbols. */ +finish: + if (newblock) + param_symbols = local_list; + + return type; +} + + +/* Read and internalize a native DOC function debug symbol. */ +/* This is almost identical to hpread_read_function_type(), except + * for references to dn_bufp->ddocfunc instead of db_bufp->dfunc. + * Since debug information for DOC functions is more likely to be + * volatile, please leave it this way. + */ +static struct type * +hpread_read_doc_function_type (hp_type, dn_bufp, objfile, newblock) + dnttpointer hp_type; + union dnttentry *dn_bufp; + struct objfile *objfile; + int newblock; +{ + struct type *type, *type1; + struct pending *syms; + struct pending *local_list = NULL; + int nsyms = 0; + dnttpointer param; + union dnttentry *paramp; + char *name; + long n; + struct symbol *sym; + int record_args = 1; + + /* See if we've already read in this type. */ + type = hpread_alloc_type (hp_type, objfile); + if (TYPE_CODE (type) == TYPE_CODE_FUNC) + { + record_args = 0; /* already read in, don't modify type */ + } + else + { + /* Nope, so read it in and store it away. */ + if (dn_bufp->dblock.kind == DNTT_TYPE_DOC_FUNCTION || + dn_bufp->dblock.kind == DNTT_TYPE_DOC_MEMFUNC) + type1 = lookup_function_type (hpread_type_lookup (dn_bufp->ddocfunc.retval, + objfile)); + memcpy ((char *) type, (char *) type1, sizeof (struct type)); + + /* Mark it -- in the middle of processing */ + TYPE_FLAGS (type) |= TYPE_FLAG_INCOMPLETE; + } + + /* Now examine each parameter noting its type, location, and a + wealth of other information. */ + if (dn_bufp->dblock.kind == DNTT_TYPE_DOC_FUNCTION || + dn_bufp->dblock.kind == DNTT_TYPE_DOC_MEMFUNC) + param = dn_bufp->ddocfunc.firstparam; + while (param.word && param.word != DNTTNIL) + { + paramp = hpread_get_lntt (param.dnttp.index, objfile); + nsyms++; + param = paramp->dfparam.nextparam; + + /* Get the name. */ + name = VT (objfile) + paramp->dfparam.name; + sym = (struct symbol *) obstack_alloc (&objfile->symbol_obstack, + sizeof (struct symbol)); + (void) memset (sym, 0, sizeof (struct symbol)); + SYMBOL_NAME (sym) = name; + + /* Figure out where it lives. */ + if (paramp->dfparam.regparam) + SYMBOL_CLASS (sym) = LOC_REGPARM; + else if (paramp->dfparam.indirect) + SYMBOL_CLASS (sym) = LOC_REF_ARG; + else + SYMBOL_CLASS (sym) = LOC_ARG; + SYMBOL_NAMESPACE (sym) = VAR_NAMESPACE; + if (paramp->dfparam.copyparam) + { + SYMBOL_VALUE (sym) = paramp->dfparam.location ; +#ifdef HPREAD_ADJUST_STACK_ADDRESS + SYMBOL_VALUE (sym) + += HPREAD_ADJUST_STACK_ADDRESS (CURRENT_FUNCTION_VALUE (objfile)); +#endif + /* This is likely a pass-by-invisible reference parameter, + Hack on the symbol class to make GDB happy. */ + /* ??rehrauer: This appears to be broken w/r/t to passing + C values of type float and struct. Perhaps this ought + to be highighted as a special case, but for now, just + allowing these to be LOC_ARGs seems to work fine. + */ +#if 0 + SYMBOL_CLASS (sym) = LOC_REGPARM_ADDR; +#endif + } + else + SYMBOL_VALUE (sym) = paramp->dfparam.location; + + /* Get its type. */ + SYMBOL_TYPE (sym) = hpread_type_lookup (paramp->dfparam.type, objfile); + /* Add it to the symbol list. */ + /* Note 1 (RT) At the moment, add_symbol_to_list() is also being + * called on FPARAM symbols from the process_one_debug_symbol() + * level... so parameters are getting added twice! (this shows + * up in the symbol dump you get from "maint print symbols ..."). + * Note 2 (RT) I took out the processing of FPARAM from the + * process_one_debug_symbol() level, so at the moment parameters are only + * being processed here. This seems to have no ill effect. + */ + /* Note 3 (pai/1997-08-11) I removed the add_symbol_to_list() which put + each fparam on the local_symbols list from here. Now we use the + local_list to which fparams are added below, and set the param_symbols + global to point to that at the end of this routine. */ + + /* elz: I added this new list of symbols which is local to the function. + this list is the one which is actually used to build the type for the + function rather than the gloabal list pointed to by symlist. + Using a global list to keep track of the parameters is wrong, because + this function is called recursively if one parameter happend to be + a function itself with more parameters in it. Adding parameters to the + same global symbol list would not work! + Actually it did work in case of cc compiled programs where you do not check the + parameter lists of the arguments. */ + add_symbol_to_list (sym, &local_list); + } + + /* If type was read in earlier, don't bother with modifying + the type struct */ + if (!record_args) + goto finish; + + /* Note how many parameters we found. */ + TYPE_NFIELDS (type) = nsyms; + TYPE_FIELDS (type) = (struct field *) + obstack_alloc (&objfile->type_obstack, + sizeof (struct field) * nsyms); + + /* Find the symbols for the parameters and + use them to fill parameter-type information into the function-type. + The parameter symbols can be found in the local_list that we just put them on. */ + /* Note that we preserve the order of the parameters, so + that in something like "enum {FOO, LAST_THING=FOO}" we print + FOO, not LAST_THING. */ + + /* get the parameters types from the local list not the global list + so that the type can be correctly constructed for functions which + have function as parameters + */ + for (syms = local_list, n = 0; syms; syms = syms->next) + { + int j = 0; + for (j = 0; j < syms->nsyms; j++, n++) + { + struct symbol *xsym = syms->symbol[j]; + TYPE_FIELD_NAME (type, n) = SYMBOL_NAME (xsym); + TYPE_FIELD_TYPE (type, n) = SYMBOL_TYPE (xsym); + TYPE_FIELD_BITPOS (type, n) = n; + TYPE_FIELD_BITSIZE (type, n) = 0; + } + } + + /* Mark it as having been processed */ + TYPE_FLAGS (type) &= ~(TYPE_FLAG_INCOMPLETE); + + /* Check whether we need to fix-up a class type with this function's type */ + if (fixup_class && (fixup_method == type)) + { + fixup_class_method_type (fixup_class, fixup_method, objfile); + fixup_class = NULL; + fixup_method = NULL; + } + + /* Set the param list of this level of the context stack + to our local list. Do this only if this function was + called for creating a new block, and not if it was called + simply to get the function type. This prevents recursive + invocations from trashing param_symbols. */ +finish: + if (newblock) + param_symbols = local_list; + + return type; +} + + + +/* A file-level variable which keeps track of the current-template + * being processed. Set in hpread_read_struct_type() while processing + * a template type. Referred to in hpread_get_nth_templ_arg(). + * Yes, this is a kludge, but it arises from the kludge that already + * exists in symtab.h, namely the fact that they encode + * "template argument n" with fundamental type FT_TEMPLATE_ARG and + * bitlength n. This means that deep in processing fundamental types + * I need to ask the question "what template am I in the middle of?". + * The alternative to stuffing a global would be to pass an argument + * down the chain of calls just for this purpose. + * + * There may be problems handling nested templates... tough. + */ +static struct type * current_template = NULL; + +/* Read in and internalize a structure definition. + * This same routine is called for struct, union, and class types. + * Also called for templates, since they build a very similar + * type entry as for class types. + */ + +static struct type * +hpread_read_struct_type (hp_type, dn_bufp, objfile) + dnttpointer hp_type; + union dnttentry *dn_bufp; + struct objfile *objfile; +{ + /* The data members get linked together into a list of struct nextfield's */ + struct nextfield + { + struct nextfield *next; + struct field field; + unsigned char attributes; /* store visibility and virtuality info */ +# define ATTR_VIRTUAL 1 +# define ATTR_PRIVATE 2 +# define ATTR_PROTECT 3 + }; + + + /* The methods get linked together into a list of struct next_fn_field's */ + struct next_fn_field + { + struct next_fn_field *next; + struct fn_fieldlist field; + struct fn_field fn_field; + int num_fn_fields; + }; + + /* The template args get linked together into a list of struct next_template's */ + struct next_template + { + struct next_template *next; + struct template_arg arg; + }; + + /* The template instantiations get linked together into a list of these... */ + struct next_instantiation + { + struct next_instantiation * next; + struct type * t; + }; + + struct type *type; + struct type *baseclass; + struct type *memtype; + struct nextfield *list = 0, *tmp_list = 0; + struct next_fn_field *fn_list = 0; + struct next_fn_field *fn_p; + struct next_template *t_new, *t_list = 0; + struct nextfield *new; + struct next_fn_field *fn_new; + struct next_instantiation *i_new, *i_list = 0; + int n, nfields = 0, n_fn_fields = 0, n_fn_fields_total = 0; + int n_base_classes = 0, n_templ_args = 0; + int ninstantiations = 0; + dnttpointer field, fn_field, parent; + union dnttentry *fieldp, *fn_fieldp, *parentp; + int i; + int static_member = 0; + int const_member = 0; + int volatile_member = 0; + unsigned long vtbl_offset; + int need_bitvectors = 0; + char * method_name = NULL; + char * method_alias = NULL; + + + /* Is it something we've already dealt with? */ + type = hpread_alloc_type (hp_type, objfile); + if ((TYPE_CODE (type) == TYPE_CODE_STRUCT) || + (TYPE_CODE (type) == TYPE_CODE_UNION) || + (TYPE_CODE (type) == TYPE_CODE_CLASS) || + (TYPE_CODE (type) == TYPE_CODE_TEMPLATE)) + return type; + + /* Get the basic type correct. */ + if (dn_bufp->dblock.kind == DNTT_TYPE_STRUCT) + { + TYPE_CODE (type) = TYPE_CODE_STRUCT; + TYPE_LENGTH (type) = dn_bufp->dstruct.bitlength / 8; + } + else if (dn_bufp->dblock.kind == DNTT_TYPE_UNION) + { + TYPE_CODE (type) = TYPE_CODE_UNION; + TYPE_LENGTH (type) = dn_bufp->dunion.bitlength / 8; + } + else if (dn_bufp->dblock.kind == DNTT_TYPE_CLASS) + { + TYPE_CODE (type) = TYPE_CODE_CLASS; + TYPE_LENGTH (type) = dn_bufp->dclass.bitlength / 8; + + /* Overrides the TYPE_CPLUS_SPECIFIC(type) with allocated memory + * rather than &cplus_struct_default. + */ + allocate_cplus_struct_type(type); + + /* Fill in declared-type. + * (The C++ compiler will emit TYPE_CODE_CLASS + * for all 3 of "class", "struct" + * "union", and we have to look at the "class_decl" field if we + * want to know how it was really declared) + */ + /* (0==class, 1==union, 2==struct) */ + TYPE_DECLARED_TYPE(type) = dn_bufp->dclass.class_decl; + } + else if (dn_bufp->dblock.kind == DNTT_TYPE_TEMPLATE) + { + /* Get the basic type correct. */ + TYPE_CODE (type) = TYPE_CODE_TEMPLATE; + allocate_cplus_struct_type(type); + TYPE_DECLARED_TYPE(type) = DECLARED_TYPE_TEMPLATE; + } + else + return type; + + + TYPE_FLAGS (type) &= ~TYPE_FLAG_STUB; + + /* For classes, read the parent list. + * Question (RT): Do we need to do this for templates also? + */ + if (dn_bufp->dblock.kind == DNTT_TYPE_CLASS) { + + /* First read the parent-list (classes from which we derive fields) */ + parent = dn_bufp->dclass.parentlist; + while (parent.word && parent.word != DNTTNIL) { + parentp = hpread_get_lntt (parent.dnttp.index, objfile); + + /* "parentp" should point to a DNTT_TYPE_INHERITANCE record */ + + /* Get space to record the next field/data-member. */ + new = (struct nextfield *) alloca (sizeof (struct nextfield)); + new->next = list; + list = new; + + FIELD_BITSIZE (list->field) = 0; + + /* The "classname" field is actually a DNTT pointer to the base class */ + baseclass = hpread_type_lookup (parentp->dinheritance.classname, + objfile); + FIELD_TYPE (list->field) = baseclass; + + list->field.name = type_name_no_tag(FIELD_TYPE (list->field)); + + list->attributes = 0; + + /* Check for virtuality of base, and set the + * offset of the base subobject within the object. + * (Offset set to -1 for virtual bases (for now).) + */ + if (parentp->dinheritance.Virtual) + { + B_SET(&(list->attributes), ATTR_VIRTUAL); + parentp->dinheritance.offset = -1; + } + else + FIELD_BITPOS (list->field) = parentp->dinheritance.offset; + + /* Check visibility */ + switch (parentp->dinheritance.visibility) + { + case 1: + B_SET(&(list->attributes), ATTR_PROTECT); + break; + case 2: + B_SET(&(list->attributes), ATTR_PRIVATE); + break; + } + + n_base_classes++; + nfields++; + + parent = parentp->dinheritance.next; + } + } + + /* For templates, read the template argument list. + * This must be done before processing the member list, because + * the member list may refer back to this. E.g.: + * template <class T1, class T2> class q2 { + * public: + * T1 a; + * T2 b; + * }; + * We need to read the argument list "T1", "T2" first. + */ + if (dn_bufp->dblock.kind == DNTT_TYPE_TEMPLATE) { + /* Kludge alert: This stuffs a global "current_template" which + * is referred to by hpread_get_nth_templ_arg(). The global + * is cleared at the end of this routine. + */ + current_template = type; + + /* Read in the argument list */ + field = dn_bufp->dtemplate.arglist; + while (field.word && field.word != DNTTNIL) { + /* Get this template argument*/ + fieldp = hpread_get_lntt (field.dnttp.index, objfile); + if (fieldp->dblock.kind != DNTT_TYPE_TEMPLATE_ARG) + { + warning ("Invalid debug info: Template argument entry is of wrong kind"); + break; + } + /* Bump the count */ + n_templ_args++; + /* Allocate and fill in a struct next_template */ + t_new = (struct next_template *) alloca (sizeof (struct next_template)); + t_new->next = t_list; + t_list = t_new; + t_list->arg.name = VT (objfile) + fieldp->dtempl_arg.name; + t_list->arg.type = hpread_read_templ_arg_type(field, fieldp, + objfile, t_list->arg.name); + /* Walk to the next template argument */ + field = fieldp->dtempl_arg.nextarg; + } + } + + TYPE_NTEMPLATE_ARGS(type) = n_templ_args; + + if (n_templ_args > 0) + TYPE_TEMPLATE_ARGS(type) = (struct template_arg *) + obstack_alloc (&objfile->type_obstack, sizeof (struct template_arg) * n_templ_args); + for (n = n_templ_args; t_list; t_list = t_list->next) + { + n -= 1; + TYPE_TEMPLATE_ARG(type, n) = t_list->arg; + } + + /* Next read in and internalize all the fields/members. */ + if (dn_bufp->dblock.kind == DNTT_TYPE_STRUCT) + field = dn_bufp->dstruct.firstfield; + else if (dn_bufp->dblock.kind == DNTT_TYPE_UNION) + field = dn_bufp->dunion.firstfield; + else if (dn_bufp->dblock.kind == DNTT_TYPE_CLASS) + field = dn_bufp->dclass.memberlist; + else if (dn_bufp->dblock.kind == DNTT_TYPE_TEMPLATE) + field = dn_bufp->dtemplate.memberlist; + else + field.word = DNTTNIL; + + while (field.word && field.word != DNTTNIL) + { + fieldp = hpread_get_lntt (field.dnttp.index, objfile); + + /* At this point "fieldp" may point to either a DNTT_TYPE_FIELD + * or a DNTT_TYPE_GENFIELD record. + */ + vtbl_offset = 0; + static_member = 0; + const_member = 0; + volatile_member = 0; + + if (fieldp->dblock.kind == DNTT_TYPE_GENFIELD) { + + /* The type will be GENFIELD if the field is a method or + * a static member (or some other cases -- see below) + */ + + /* Follow a link to get to the record for the field. */ + fn_field = fieldp->dgenfield.field; + fn_fieldp = hpread_get_lntt(fn_field.dnttp.index, objfile); + + /* Virtual funcs are indicated by a VFUNC which points to the + * real entry + */ + if (fn_fieldp->dblock.kind == DNTT_TYPE_VFUNC) { + vtbl_offset = fn_fieldp->dvfunc.vtbl_offset; + fn_field = fn_fieldp->dvfunc.funcptr; + fn_fieldp = hpread_get_lntt(fn_field.dnttp.index, objfile); + } + + /* A function's entry may be preceded by a modifier which + * labels it static/constant/volatile. + */ + if (fn_fieldp->dblock.kind == DNTT_TYPE_MODIFIER) { + static_member = fn_fieldp->dmodifier.m_static; + const_member = fn_fieldp->dmodifier.m_const; + volatile_member = fn_fieldp->dmodifier.m_volatile; + fn_field = fn_fieldp->dmodifier.type; + fn_fieldp = hpread_get_lntt(fn_field.dnttp.index, objfile); + } + + /* Check whether we have a method */ + if ((fn_fieldp->dblock.kind == DNTT_TYPE_MEMFUNC) || + (fn_fieldp->dblock.kind == DNTT_TYPE_FUNCTION) || + (fn_fieldp->dblock.kind == DNTT_TYPE_DOC_MEMFUNC) || + (fn_fieldp->dblock.kind == DNTT_TYPE_DOC_FUNCTION)) { + /* Method found */ + + short ix = 0; + + /* Look up function type of method */ + memtype = hpread_type_lookup (fn_field, objfile); + + /* Methods can be seen before classes in the SOM records. + If we are processing this class because it's a parameter of a + method, at this point the method's type is actually incomplete; + we'll have to fix it up later; mark the class for this. */ + + if (TYPE_INCOMPLETE (memtype)) + { + TYPE_FLAGS (type) |= TYPE_FLAG_INCOMPLETE; + if (fixup_class) + warning ("Two classes to fix up for method?? Type information may be incorrect for some classes."); + if (fixup_method) + warning ("Two methods to be fixed up at once?? Type information may be incorrect for some classes."); + fixup_class = type; /* remember this class has to be fixed up */ + fixup_method = memtype; /* remember the method type to be used in fixup */ + } + + /* HP aCC generates operator names without the "operator" keyword, and + generates null strings as names for operators that are + user-defined type conversions to basic types (e.g. operator int ()). + So try to reconstruct name as best as possible. */ + + method_name = (char *) (VT (objfile) + fn_fieldp->dfunc.name); + method_alias = (char *) (VT (objfile) + fn_fieldp->dfunc.alias); + + if (!method_name || /* no name */ + !*method_name || /* or null name */ + cplus_mangle_opname (method_name, DMGL_ANSI)) /* or name is an operator like "<" */ + { + char * tmp_name = cplus_demangle (method_alias, DMGL_ANSI); + char * op_string = strstr (tmp_name, "operator"); + method_name = xmalloc (strlen (op_string) + 1); /* don't overwrite VT! */ + strcpy (method_name, op_string); + } + + /* First check if a method of the same name has already been seen. */ + fn_p = fn_list; + while (fn_p) + { + if (STREQ (fn_p->field.name, method_name)) + break; + fn_p = fn_p->next; + } + + /* If no such method was found, allocate a new entry in the list */ + if (!fn_p) + { + /* Get space to record this member function */ + /* Note: alloca used; this will disappear on routine exit */ + fn_new = (struct next_fn_field *) alloca (sizeof (struct next_fn_field)); + fn_new->next = fn_list; + fn_list = fn_new; + + /* Fill in the fields of the struct nextfield */ + + /* Record the (unmangled) method name */ + fn_list->field.name = method_name; + /* Initial space for overloaded methods */ + /* Note: xmalloc is used; this will persist after this routine exits */ + fn_list->field.fn_fields = (struct fn_field *) xmalloc (5 * (sizeof (struct fn_field))); + fn_list->field.length = 1; /* Init # of overloaded instances */ + fn_list->num_fn_fields = 5; /* # of entries for which space allocated */ + fn_p = fn_list; + ix = 0; /* array index for fn_field */ + /* Bump the total count of the distinctly named methods */ + n_fn_fields++; + } + else /* Another overloaded instance of an already seen method name */ + { + if (++(fn_p->field.length) > fn_p->num_fn_fields) + { + /* Increase space allocated for overloaded instances */ + fn_p->field.fn_fields + = (struct fn_field *) xrealloc (fn_p->field.fn_fields, + (fn_p->num_fn_fields + 5) * sizeof (struct fn_field)); + fn_p->num_fn_fields += 5; + } + ix = fn_p->field.length -1; /* array index for fn_field */ + } + + /* "physname" is intended to be the name of this overloaded instance. */ + if ((fn_fieldp->dfunc.language == HP_LANGUAGE_CPLUSPLUS) && + method_alias && + *method_alias) /* not a null string */ + fn_p->field.fn_fields[ix].physname = method_alias; + else + fn_p->field.fn_fields[ix].physname = method_name; + /* What's expected here is the function type */ + /* But mark it as NULL if the method was incompletely processed + We'll fix this up later when the method is fully processed */ + if (TYPE_INCOMPLETE (memtype)) + { + fn_p->field.fn_fields[ix].type = NULL; + fn_p->field.fn_fields[ix].args = NULL; + } + else + { + fn_p->field.fn_fields[ix].type = memtype; + + /* The argument list */ + fn_p->field.fn_fields[ix].type->type_specific.arg_types = + (struct type **) obstack_alloc(&objfile->type_obstack, + sizeof(struct type *) * (memtype->nfields + 1)); + for (i = 0; i < memtype->nfields; i++) + fn_p->field.fn_fields[ix].type->type_specific.arg_types[i] = memtype->fields[i].type; + /* void termination */ + fn_p->field.fn_fields[ix].type->type_specific.arg_types[memtype->nfields] = builtin_type_void; + + /* pai: It's not clear why this args field has to be set. Perhaps + * it should be eliminated entirely. */ + fn_p->field.fn_fields[ix].args = + (struct type **) obstack_alloc(&objfile->type_obstack, + sizeof(struct type *) * (memtype->nfields + 1)); + for (i = 0; i < memtype->nfields; i++) + fn_p->field.fn_fields[ix].args[i] = memtype->fields[i].type; + /* null-terminated, unlike arg_types above e*/ + fn_p->field.fn_fields[ix].args[memtype->nfields] = NULL; + } + /* For virtual functions, fill in the voffset field with the + * virtual table offset. (This is just copied over from the + * SOM record; not sure if it is what GDB expects here...). + * But if the function is a static method, set it to 1. + * + * Note that we have to add 1 because 1 indicates a static + * method, and 0 indicates a non-static, non-virtual method */ + + if (static_member) + fn_p->field.fn_fields[ix].voffset = VOFFSET_STATIC; + else + fn_p->field.fn_fields[ix].voffset = vtbl_offset ? vtbl_offset + 1 : 0; + + /* Also fill in the fcontext field with the current + * class. (The latter isn't quite right: should be the baseclass + * that defines the virtual function... Note we do have + * a variable "baseclass" that we could stuff into the fcontext + * field, but "baseclass" isn't necessarily right either, + * since the virtual function could have been defined more + * than one level up). + */ + + if (vtbl_offset != 0) + fn_p->field.fn_fields[ix].fcontext = type; + else + fn_p->field.fn_fields[ix].fcontext = NULL; + + /* Other random fields pertaining to this method */ + fn_p->field.fn_fields[ix].is_const = const_member; + fn_p->field.fn_fields[ix].is_volatile = volatile_member; /* ?? */ + switch (fieldp->dgenfield.visibility) { + case 1: + fn_p->field.fn_fields[ix].is_protected = 1; + fn_p->field.fn_fields[ix].is_private = 0; + break; + case 2: + fn_p->field.fn_fields[ix].is_protected = 0; + fn_p->field.fn_fields[ix].is_private = 1; + break; + default: /* public */ + fn_p->field.fn_fields[ix].is_protected = 0; + fn_p->field.fn_fields[ix].is_private = 0; + } + fn_p->field.fn_fields[ix].is_stub = 0; + + /* HP aCC emits both MEMFUNC and FUNCTION entries for a method; + if the class points to the FUNCTION, there is usually separate + code for the method; but if we have a MEMFUNC, the method has + been inlined (and there is usually no FUNCTION entry) + FIXME Not sure if this test is accurate. pai/1997-08-22 */ + if ((fn_fieldp->dblock.kind == DNTT_TYPE_MEMFUNC) || + (fn_fieldp->dblock.kind == DNTT_TYPE_DOC_MEMFUNC)) + fn_p->field.fn_fields[ix].is_inlined = 1; + else + fn_p->field.fn_fields[ix].is_inlined = 0; + + fn_p->field.fn_fields[ix].dummy = 0; + + /* Bump the total count of the member functions */ + n_fn_fields_total++; + + } else if (fn_fieldp->dblock.kind == DNTT_TYPE_SVAR) { + /* This case is for static data members of classes */ + + /* pai:: FIXME -- check that "staticmem" bit is set */ + + /* Get space to record this static member */ + new = (struct nextfield *) alloca (sizeof (struct nextfield)); + new->next = list; + list = new; + + list->field.name = VT (objfile) + fn_fieldp->dsvar.name; + FIELD_BITSIZE (list->field) = -1; /* indicates static member */ + SET_FIELD_PHYSNAME (list->field, 0); /* initialize to empty */ + memtype = hpread_type_lookup (fn_fieldp->dsvar.type, objfile); + + FIELD_TYPE (list->field) = memtype; + list->attributes = 0; + switch (fieldp->dgenfield.visibility) { + case 1: + B_SET(&(list->attributes), ATTR_PROTECT); + break; + case 2: + B_SET(&(list->attributes), ATTR_PRIVATE); + break; + } + nfields++; + } + + else if (fn_fieldp->dblock.kind == DNTT_TYPE_FIELD) + { + /* FIELDs follow GENFIELDs for fields of anonymous unions. + Code below is replicated from the case for FIELDs further + below, except that fieldp is replaced by fn_fieldp */ + if (!fn_fieldp->dfield.a_union) + warning ("Debug info inconsistent: FIELD of anonymous union doesn't have a_union bit set"); + /* Get space to record the next field/data-member. */ + new = (struct nextfield *) alloca (sizeof (struct nextfield)); + new->next = list; + list = new; + + list->field.name = VT (objfile) + fn_fieldp->dfield.name; + FIELD_BITPOS (list->field) = fn_fieldp->dfield.bitoffset; + if (fn_fieldp->dfield.bitlength % 8) + list->field.bitsize = fn_fieldp->dfield.bitlength; + else + list->field.bitsize = 0; + + memtype = hpread_type_lookup (fn_fieldp->dfield.type, objfile); + list->field.type = memtype; + list->attributes = 0; + switch (fn_fieldp->dfield.visibility) { + case 1: + B_SET(&(list->attributes), ATTR_PROTECT); + break; + case 2: + B_SET(&(list->attributes), ATTR_PRIVATE); + break; + } + nfields++; + } + else if (fn_fieldp->dblock.kind == DNTT_TYPE_SVAR) + { + /* Field of anonymous union; union is not inside a class */ + if (!fn_fieldp->dsvar.a_union) + warning ("Debug info inconsistent: SVAR field in anonymous union doesn't have a_union bit set"); + /* Get space to record the next field/data-member. */ + new = (struct nextfield *) alloca (sizeof (struct nextfield)); + new->next = list; + list = new; + + list->field.name = VT (objfile) + fn_fieldp->dsvar.name; + FIELD_BITPOS (list->field) = 0; /* FIXME is this always true? */ + FIELD_BITSIZE (list->field) = 0; /* use length from type */ + memtype = hpread_type_lookup (fn_fieldp->dsvar.type, objfile); + list->field.type = memtype; + list->attributes = 0; + /* No info to set visibility -- always public */ + nfields++; + } + else if (fn_fieldp->dblock.kind == DNTT_TYPE_DVAR) + { + /* Field of anonymous union; union is not inside a class */ + if (!fn_fieldp->ddvar.a_union) + warning ("Debug info inconsistent: DVAR field in anonymous union doesn't have a_union bit set"); + /* Get space to record the next field/data-member. */ + new = (struct nextfield *) alloca (sizeof (struct nextfield)); + new->next = list; + list = new; + + list->field.name = VT (objfile) + fn_fieldp->ddvar.name; + FIELD_BITPOS (list->field) = 0; /* FIXME is this always true? */ + FIELD_BITSIZE (list->field) = 0; /* use length from type */ + memtype = hpread_type_lookup (fn_fieldp->ddvar.type, objfile); + list->field.type = memtype; + list->attributes = 0; + /* No info to set visibility -- always public */ + nfields++; + } + else { /* Not a method, nor a static data member, nor an anon union field */ + + /* This case is for miscellaneous type entries (local enums, + local function templates, etc.) that can be present + inside a class. */ + + /* Enums -- will be handled by other code that takes care + of DNTT_TYPE_ENUM; here we see only DNTT_TYPE_MEMENUM so + it's not clear we could have handled them here at all. */ + /* FUNC_TEMPLATE: is handled by other code (??). */ + /* MEMACCESS: modified access for inherited member. Not + sure what to do with this, ignoriing it at present. */ + + /* What other entries can appear following a GENFIELD which + we do not handle above? (MODIFIER, VFUNC handled above.) */ + + if ((fn_fieldp->dblock.kind != DNTT_TYPE_MEMACCESS) && + (fn_fieldp->dblock.kind != DNTT_TYPE_MEMENUM) && + (fn_fieldp->dblock.kind != DNTT_TYPE_FUNC_TEMPLATE)) + warning ("Internal error: Unexpected debug record kind %d found following DNTT_GENFIELD", + fn_fieldp->dblock.kind); + } + /* walk to the next FIELD or GENFIELD */ + field = fieldp->dgenfield.nextfield; + + } + else if (fieldp->dblock.kind == DNTT_TYPE_FIELD) { + + /* Ordinary structure/union/class field */ + struct type * anon_union_type; + + /* Get space to record the next field/data-member. */ + new = (struct nextfield *) alloca (sizeof (struct nextfield)); + new->next = list; + list = new; + + list->field.name = VT (objfile) + fieldp->dfield.name; + + + /* A FIELD by itself (without a GENFIELD) can also be a static member */ + if (fieldp->dfield.staticmem) + { + FIELD_BITPOS (list->field) = -1; + FIELD_BITSIZE (list->field) = 0; + } + else /* Non-static data member */ + { + FIELD_BITPOS (list->field) = fieldp->dfield.bitoffset; + if (fieldp->dfield.bitlength % 8) + FIELD_BITSIZE (list->field) = fieldp->dfield.bitlength; + else + FIELD_BITSIZE (list->field) = 0; + } + + memtype = hpread_type_lookup (fieldp->dfield.type, objfile); + FIELD_TYPE (list->field) = memtype; + list->attributes = 0; + switch (fieldp->dfield.visibility) { + case 1: + B_SET(&(list->attributes), ATTR_PROTECT); + break; + case 2: + B_SET(&(list->attributes), ATTR_PRIVATE); + break; + } + nfields++; + + + /* Note 1: First, we have to check if the current field is an anonymous + union. If it is, then *its* fields are threaded along in the + nextfield chain. :-( This was supposed to help debuggers, but is + really just a nuisance since we deal with anonymous unions anyway by + checking that the name is null. So anyway, we skip over the fields + of the anonymous union. pai/1997-08-22 */ + /* Note 2: In addition, the bitoffsets for the fields of the anon union + are relative to the enclosing struct, *NOT* relative to the anon + union! This is an even bigger nuisance -- we have to go in and munge + the anon union's type information appropriately. pai/1997-08-22 */ + + /* Both tasks noted above are done by a separate function. This takes us + to the next FIELD or GENFIELD, skipping anon unions, and recursively + processing intermediate types. */ + field = hpread_get_next_skip_over_anon_unions (1, field, &fieldp, objfile); + + } else { + /* neither field nor genfield ?? is this possible?? */ + /* pai:: FIXME walk to the next -- how? */ + warning ("Internal error: unexpected DNTT kind %d encountered as field of struct"); + warning ("Skipping remaining fields of struct"); + break; /* get out of loop of fields */ + } + } + + /* If it's a template, read in the instantiation list */ + if (dn_bufp->dblock.kind == DNTT_TYPE_TEMPLATE) { + ninstantiations = 0; + field = dn_bufp->dtemplate.expansions; + while (field.word && field.word != DNTTNIL) { + fieldp = hpread_get_lntt (field.dnttp.index, objfile); + + /* The expansions or nextexp should point to a tagdef */ + if (fieldp->dblock.kind != DNTT_TYPE_TAGDEF) + break; + + i_new = (struct next_instantiation *) alloca (sizeof (struct next_instantiation)); + i_new->next = i_list; + i_list = i_new; + i_list->t = hpread_type_lookup (field, objfile); + ninstantiations++; + + /* And the "type" field of that should point to a class */ + field = fieldp->dtag.type; + fieldp = hpread_get_lntt (field.dnttp.index, objfile); + if (fieldp->dblock.kind != DNTT_TYPE_CLASS) + break; + + /* Get the next expansion */ + field = fieldp->dclass.nextexp; + } + } + TYPE_NINSTANTIATIONS(type) = ninstantiations; + if (ninstantiations > 0) + TYPE_INSTANTIATIONS(type) = (struct type **) + obstack_alloc (&objfile->type_obstack, sizeof (struct type *) * ninstantiations); + for (n = ninstantiations; i_list; i_list = i_list->next) + { + n -= 1; + TYPE_INSTANTIATION(type, n) = i_list->t; + } + + + /* Copy the field-list to GDB's symbol table */ + TYPE_NFIELDS (type) = nfields; + TYPE_N_BASECLASSES (type) = n_base_classes; + TYPE_FIELDS (type) = (struct field *) + obstack_alloc (&objfile->type_obstack, sizeof (struct field) * nfields); + /* Copy the saved-up fields into the field vector. */ + for (n = nfields, tmp_list = list; tmp_list; tmp_list = tmp_list->next) + { + n -= 1; + TYPE_FIELD (type, n) = tmp_list->field; + } + + /* Copy the "function-field-list" (i.e., the list of member + * functions in the class) to GDB's symbol table + */ + TYPE_NFN_FIELDS (type) = n_fn_fields; + TYPE_NFN_FIELDS_TOTAL (type) = n_fn_fields_total; + TYPE_FN_FIELDLISTS(type) = (struct fn_fieldlist *) + obstack_alloc (&objfile->type_obstack, sizeof (struct fn_fieldlist) * n_fn_fields); + for (n = n_fn_fields; fn_list; fn_list = fn_list->next) + { + n -= 1; + TYPE_FN_FIELDLIST(type, n) = fn_list->field; + } + + /* pai:: FIXME -- perhaps each bitvector should be created individually */ + for (n = nfields, tmp_list = list; tmp_list; tmp_list = tmp_list->next) + { + n -= 1; + if (tmp_list->attributes) + { + need_bitvectors = 1; + break; + } + } + + if (need_bitvectors) + { + /* pai:: this step probably redundant */ + ALLOCATE_CPLUS_STRUCT_TYPE (type); + + TYPE_FIELD_VIRTUAL_BITS (type) = + (B_TYPE *) TYPE_ALLOC (type, B_BYTES (nfields)); + B_CLRALL (TYPE_FIELD_VIRTUAL_BITS (type), nfields); + + TYPE_FIELD_PRIVATE_BITS (type) = + (B_TYPE *) TYPE_ALLOC (type, B_BYTES (nfields)); + B_CLRALL (TYPE_FIELD_PRIVATE_BITS (type), nfields); + + TYPE_FIELD_PROTECTED_BITS (type) = + (B_TYPE *) TYPE_ALLOC (type, B_BYTES (nfields)); + B_CLRALL (TYPE_FIELD_PROTECTED_BITS (type), nfields); + + /* this field vector isn't actually used with HP aCC */ + TYPE_FIELD_IGNORE_BITS (type) = + (B_TYPE *) TYPE_ALLOC (type, B_BYTES (nfields)); + B_CLRALL (TYPE_FIELD_IGNORE_BITS (type), nfields); + + while (nfields-- > 0) + { + if (B_TST(&(list->attributes),ATTR_VIRTUAL)) + SET_TYPE_FIELD_VIRTUAL (type, nfields); + if (B_TST(&(list->attributes),ATTR_PRIVATE)) + SET_TYPE_FIELD_PRIVATE (type, nfields); + if (B_TST(&(list->attributes),ATTR_PROTECT)) + SET_TYPE_FIELD_PROTECTED (type, nfields); + + list = list->next; + } + } + else + { + TYPE_FIELD_VIRTUAL_BITS(type) = NULL; + TYPE_FIELD_PROTECTED_BITS(type) = NULL; + TYPE_FIELD_PRIVATE_BITS(type) = NULL; + } + + if (has_vtable(type)) + { + /* Allocate space for class runtime information */ + TYPE_RUNTIME_PTR(type) = (struct runtime_info *) xmalloc (sizeof(struct runtime_info)); + /* Set flag for vtable */ + TYPE_VTABLE(type) = 1; + /* The first non-virtual base class with a vtable. */ + TYPE_PRIMARY_BASE(type) = primary_base_class(type); + /* The virtual base list. */ + TYPE_VIRTUAL_BASE_LIST(type) = virtual_base_list(type); + } + else + TYPE_RUNTIME_PTR(type) = NULL; + + /* If this is a local type (C++ - declared inside a function), record file name & line # */ + if (hpread_get_scope_depth (dn_bufp, objfile, 1 /* no need for real depth */)) + { + TYPE_LOCALTYPE_PTR (type) = (struct local_type_info *) xmalloc (sizeof (struct local_type_info)); + TYPE_LOCALTYPE_FILE (type) = (char *) xmalloc (strlen (current_subfile->name) + 1); + strcpy (TYPE_LOCALTYPE_FILE (type), current_subfile->name); + if (current_subfile->line_vector && (current_subfile->line_vector->nitems > 0)) + TYPE_LOCALTYPE_LINE (type) = current_subfile->line_vector->item[current_subfile->line_vector->nitems - 1].line; + else + TYPE_LOCALTYPE_LINE (type) = 0; + } + else + TYPE_LOCALTYPE_PTR (type) = NULL; + + /* Clear the global saying what template we are in the middle of processing */ + current_template = NULL; + + return type; +} + +/* Adjust the physnames for each static member of a struct + or class type to be something like "A::x"; then various + other pieces of code that do a lookup_symbol on the phyname + work correctly. + TYPE is a pointer to the struct/class type + NAME is a char * (string) which is the class/struct name + Void return */ + +static void +fix_static_member_physnames (type, class_name, objfile) + struct type * type; + char * class_name; + struct objfile * objfile; +{ + int i; + + /* We fix the member names only for classes or structs */ + if (TYPE_CODE (type) != TYPE_CODE_STRUCT) + return; + + for (i=0; i < TYPE_NFIELDS (type); i++) + if (TYPE_FIELD_STATIC (type, i)) + { + if (TYPE_FIELD_STATIC_PHYSNAME (type, i)) + return; /* physnames are already set */ + + SET_FIELD_PHYSNAME (type->fields[i], + obstack_alloc (&objfile->type_obstack, + strlen (class_name) + strlen (TYPE_FIELD_NAME (type, i)) + 3)); + strcpy (TYPE_FIELD_STATIC_PHYSNAME (type, i), class_name); + strcat (TYPE_FIELD_STATIC_PHYSNAME (type, i), "::"); + strcat (TYPE_FIELD_STATIC_PHYSNAME (type, i), TYPE_FIELD_NAME (type, i)); + } +} + +/* Fix-up the type structure for a CLASS so that the type entry + * for a method (previously marked with a null type in hpread_read_struct_type() + * is set correctly to METHOD. + * OBJFILE is as for other such functions. + * Void return. */ + +static void +fixup_class_method_type (class, method, objfile) + struct type * class; + struct type * method; + struct objfile * objfile; +{ + int i, j, k; + + if (!class || !method || !objfile) + return; + + /* Only for types that have methods */ + if ((TYPE_CODE (class) != TYPE_CODE_CLASS) && + (TYPE_CODE (class) != TYPE_CODE_UNION)) + return; + + /* Loop over all methods and find the one marked with a NULL type */ + for (i = 0; i < TYPE_NFN_FIELDS (class); i++) + for (j = 0; j < TYPE_FN_FIELDLIST_LENGTH (class, i); j++) + if (TYPE_FN_FIELD_TYPE (TYPE_FN_FIELDLIST1 (class, i), j) == NULL) + { + /* Set the method type */ + TYPE_FN_FIELD_TYPE (TYPE_FN_FIELDLIST1 (class, i), j) = method; + /* The argument list */ + (TYPE_FN_FIELD_TYPE (TYPE_FN_FIELDLIST1 (class, i), j))->type_specific.arg_types + = (struct type **) obstack_alloc(&objfile->type_obstack, + sizeof(struct type *) * (method->nfields + 1)); + for (k = 0; k < method->nfields; k++) + (TYPE_FN_FIELD_TYPE (TYPE_FN_FIELDLIST1 (class, i), j))->type_specific.arg_types[k] = method->fields[k].type; + /* void termination */ + (TYPE_FN_FIELD_TYPE (TYPE_FN_FIELDLIST1 (class, i), j))->type_specific.arg_types[method->nfields] = builtin_type_void; + + /* pai: It's not clear why this args field has to be set. Perhaps + * it should be eliminated entirely. */ + (TYPE_FN_FIELD (TYPE_FN_FIELDLIST1 (class, i), j)).args + = (struct type **) obstack_alloc(&objfile->type_obstack, + sizeof(struct type *) * (method->nfields + 1)); + for (k = 0; k < method->nfields; k++) + (TYPE_FN_FIELD (TYPE_FN_FIELDLIST1 (class, i), j)).args[k] = method->fields[k].type; + /* null-terminated, unlike arg_types above */ + (TYPE_FN_FIELD (TYPE_FN_FIELDLIST1 (class, i), j)).args[method->nfields] = NULL; + + /* Break out of both loops -- only one method to fix up in a class */ + goto finish; + } + +finish: + TYPE_FLAGS (class) &= ~TYPE_FLAG_INCOMPLETE; +} + + +/* If we're in the middle of processing a template, get a pointer + * to the Nth template argument. + * An example may make this clearer: + * template <class T1, class T2> class q2 { + * public: + * T1 a; + * T2 b; + * }; + * The type for "a" will be "first template arg" and + * the type for "b" will be "second template arg". + * We need to look these up in order to fill in "a" and "b"'s type. + * This is called from hpread_type_lookup(). + */ +static struct type * +hpread_get_nth_template_arg(objfile, n) + struct objfile *objfile; + int n; +{ + if (current_template != NULL) + return TYPE_TEMPLATE_ARG(current_template, n).type; + else + return lookup_fundamental_type (objfile, FT_TEMPLATE_ARG); +} + +/* Read in and internalize a TEMPL_ARG (template arg) symbol. */ + +static struct type * +hpread_read_templ_arg_type (hp_type, dn_bufp, objfile, name) + dnttpointer hp_type; + union dnttentry *dn_bufp; + struct objfile *objfile; + char * name; +{ + struct type *type; + + /* See if it's something we've already deal with. */ + type = hpread_alloc_type (hp_type, objfile); + if (TYPE_CODE (type) == TYPE_CODE_TEMPLATE_ARG) + return type; + + /* Nope. Fill in the appropriate fields. */ + TYPE_CODE (type) = TYPE_CODE_TEMPLATE_ARG; + TYPE_LENGTH (type) = 0; + TYPE_NFIELDS (type) = 0; + TYPE_NAME (type) = name; + return type; +} + +/* Read in and internalize a set debug symbol. */ + +static struct type * +hpread_read_set_type (hp_type, dn_bufp, objfile) + dnttpointer hp_type; + union dnttentry *dn_bufp; + struct objfile *objfile; +{ + struct type *type; + + /* See if it's something we've already deal with. */ + type = hpread_alloc_type (hp_type, objfile); + if (TYPE_CODE (type) == TYPE_CODE_SET) + return type; + + /* Nope. Fill in the appropriate fields. */ + TYPE_CODE (type) = TYPE_CODE_SET; + TYPE_LENGTH (type) = dn_bufp->dset.bitlength / 8; + TYPE_NFIELDS (type) = 0; + TYPE_TARGET_TYPE (type) = hpread_type_lookup (dn_bufp->dset.subtype, + objfile); + return type; +} + +/* Read in and internalize an array debug symbol. */ + +static struct type * +hpread_read_array_type (hp_type, dn_bufp, objfile) + dnttpointer hp_type; + union dnttentry *dn_bufp; + struct objfile *objfile; +{ + struct type *type; + + /* Allocate an array type symbol. + * Why no check for already-read here, like in the other + * hpread_read_xxx_type routines? Because it kept us + * from properly determining the size of the array! + */ + type = hpread_alloc_type (hp_type, objfile); + + TYPE_CODE (type) = TYPE_CODE_ARRAY; + + /* Although the hp-symtab.h does not *require* this to be the case, + * GDB is assuming that "arrayisbytes" and "elemisbytes" be consistent. + * I.e., express both array-length and element-length in bits, + * or express both array-length and element-length in bytes. + */ + if (!((dn_bufp->darray.arrayisbytes && dn_bufp->darray.elemisbytes) || + (!dn_bufp->darray.arrayisbytes && !dn_bufp->darray.elemisbytes))) { + warning ("error in hpread_array_type.\n"); + return; + } else if (dn_bufp->darray.arraylength == 0x7fffffff) { + /* The HP debug format represents char foo[]; as an array with + * length 0x7fffffff. Internally GDB wants to represent this + * as an array of length zero. + */ + TYPE_LENGTH (type) = 0; + } else if (dn_bufp->darray.arrayisbytes) + TYPE_LENGTH (type) = dn_bufp->darray.arraylength; + else /* arraylength is in bits */ + TYPE_LENGTH (type) = dn_bufp->darray.arraylength / 8; + + TYPE_TARGET_TYPE (type) = hpread_type_lookup (dn_bufp->darray.elemtype, + objfile); + + /* The one "field" is used to store the subscript type */ + /* Since C and C++ multi-dimensional arrays are simply represented + * as: array of array of ..., we only need one subscript-type + * per array. This subscript type is typically a subrange of integer. + * If this gets extended to support languages like Pascal, then + * we need to fix this to represent multi-dimensional arrays properly. + */ + TYPE_NFIELDS (type) = 1; + TYPE_FIELDS (type) = (struct field *) + obstack_alloc (&objfile->type_obstack, sizeof (struct field)); + TYPE_FIELD_TYPE (type, 0) = hpread_type_lookup (dn_bufp->darray.indextype, + objfile); + return type; +} + +/* Read in and internalize a subrange debug symbol. */ +static struct type * +hpread_read_subrange_type (hp_type, dn_bufp, objfile) + dnttpointer hp_type; + union dnttentry *dn_bufp; + struct objfile *objfile; +{ + struct type *type; + + /* Is it something we've already dealt with. */ + type = hpread_alloc_type (hp_type, objfile); + if (TYPE_CODE (type) == TYPE_CODE_RANGE) + return type; + + /* Nope, internalize it. */ + TYPE_CODE (type) = TYPE_CODE_RANGE; + TYPE_LENGTH (type) = dn_bufp->dsubr.bitlength / 8; + TYPE_NFIELDS (type) = 2; + TYPE_FIELDS (type) + = (struct field *) obstack_alloc (&objfile->type_obstack, + 2 * sizeof (struct field)); + + if (dn_bufp->dsubr.dyn_low) + TYPE_FIELD_BITPOS (type, 0) = 0; + else + TYPE_FIELD_BITPOS (type, 0) = dn_bufp->dsubr.lowbound; + + if (dn_bufp->dsubr.dyn_high) + TYPE_FIELD_BITPOS (type, 1) = -1; + else + TYPE_FIELD_BITPOS (type, 1) = dn_bufp->dsubr.highbound; + TYPE_TARGET_TYPE (type) = hpread_type_lookup (dn_bufp->dsubr.subtype, + objfile); + return type; +} + +/* struct type * hpread_type_lookup(hp_type, objfile) + * Arguments: + * hp_type: A pointer into the DNTT specifying what type we + * are about to "look up"., or else [for fundamental types + * like int, float, ...] an "immediate" structure describing + * the type. + * objfile: ? + * Return value: A pointer to a "struct type" (representation of a + * type in GDB's internal symbol table - see gdbtypes.h) + * Routine description: + * There are a variety of places when scanning the DNTT when we + * need to interpret a "type" field. The simplest and most basic + * example is when we're processing the symbol table record + * for a data symbol (a SVAR or DVAR record). That has + * a "type" field specifying the type of the data symbol. That + * "type" field is either an "immediate" type specification (for the + * fundamental types) or a DNTT pointer (for more complicated types). + * For the more complicated types, we may or may not have already + * processed the pointed-to type. (Multiple data symbols can of course + * share the same type). + * The job of hpread_type_lookup() is to process this "type" field. + * Most of the real work is done in subroutines. Here we interpret + * the immediate flag. If not immediate, chase the DNTT pointer to + * find our way to the SOM record describing the type, switch on + * the SOM kind, and then call an appropriate subroutine depending + * on what kind of type we are constructing. (e.g., an array type, + * a struct/class type, etc). + */ +static struct type * +hpread_type_lookup (hp_type, objfile) + dnttpointer hp_type; + struct objfile *objfile; +{ + union dnttentry *dn_bufp; + struct type * tmp_type; + + /* First see if it's a simple builtin type. */ + if (hp_type.dntti.immediate) + /* If this is a template argument, the argument number is + * encoded in the bitlength. All other cases, just return + * GDB's representation of this fundamental type. + */ + if (hp_type.dntti.type == HP_TYPE_TEMPLATE_ARG) + return hpread_get_nth_template_arg(objfile, hp_type.dntti.bitlength); + else + return lookup_fundamental_type (objfile, hpread_type_translate (hp_type)); + + /* Not a builtin type. We'll have to read it in. */ + if (hp_type.dnttp.index < LNTT_SYMCOUNT (objfile)) + dn_bufp = hpread_get_lntt (hp_type.dnttp.index, objfile); + else + /* This is a fancy way of returning NULL */ + return lookup_fundamental_type (objfile, FT_VOID); + + switch (dn_bufp->dblock.kind) + { + case DNTT_TYPE_SRCFILE: + case DNTT_TYPE_MODULE: + case DNTT_TYPE_ENTRY: + case DNTT_TYPE_BEGIN: + case DNTT_TYPE_END: + case DNTT_TYPE_IMPORT: + case DNTT_TYPE_LABEL: + case DNTT_TYPE_FPARAM: + case DNTT_TYPE_SVAR: + case DNTT_TYPE_DVAR: + case DNTT_TYPE_CONST: + case DNTT_TYPE_MEMENUM: + case DNTT_TYPE_VARIANT: + case DNTT_TYPE_FILE: + case DNTT_TYPE_WITH: + case DNTT_TYPE_COMMON: + case DNTT_TYPE_COBSTRUCT: + case DNTT_TYPE_XREF: + case DNTT_TYPE_SA: + case DNTT_TYPE_MACRO: + case DNTT_TYPE_BLOCKDATA: + case DNTT_TYPE_CLASS_SCOPE: + case DNTT_TYPE_MEMACCESS: + case DNTT_TYPE_INHERITANCE: + case DNTT_TYPE_OBJECT_ID: + case DNTT_TYPE_FRIEND_CLASS: + case DNTT_TYPE_FRIEND_FUNC: + /* These are not types - something went wrong. */ + /* This is a fancy way of returning NULL */ + return lookup_fundamental_type (objfile, FT_VOID); + + case DNTT_TYPE_FUNCTION: + /* We wind up here when dealing with class member functions + * (called from hpread_read_struct_type(), i.e. when processing + * the class definition itself). + */ + return hpread_read_function_type (hp_type, dn_bufp, objfile, 0); + + case DNTT_TYPE_DOC_FUNCTION: + return hpread_read_doc_function_type (hp_type, dn_bufp, objfile, 0); + + case DNTT_TYPE_TYPEDEF: + { + /* A typedef - chase it down by making a recursive call */ + struct type *structtype = hpread_type_lookup (dn_bufp->dtype.type, + objfile); + + /* The following came from the base hpread.c that we inherited. + * It is WRONG so I have commented it out. - RT + *... + + char *suffix; + suffix = VT (objfile) + dn_bufp->dtype.name; + TYPE_NAME (structtype) = suffix; + + * ... further explanation .... + * + * What we have here is a typedef pointing to a typedef. + * E.g., + * typedef int foo; + * typedef foo fum; + * + * What we desire to build is (these are pictures + * of "struct type"'s): + * + * +---------+ +----------+ +------------+ + * | typedef | | typedef | | fund. type | + * | type| -> | type| -> | | + * | "fum" | | "foo" | | "int" | + * +---------+ +----------+ +------------+ + * + * What this commented-out code is doing is smashing the + * name of pointed-to-type to be the same as the pointed-from + * type. So we wind up with something like: + * + * +---------+ +----------+ +------------+ + * | typedef | | typedef | | fund. type | + * | type| -> | type| -> | | + * | "fum" | | "fum" | | "fum" | + * +---------+ +----------+ +------------+ + * + */ + + return structtype; + } + + case DNTT_TYPE_TAGDEF: + { + /* Just a little different from above. We have to tack on + * an identifier of some kind (struct, union, enum, class, etc). + */ + struct type *structtype = hpread_type_lookup (dn_bufp->dtype.type, + objfile); + char *prefix, *suffix; + suffix = VT (objfile) + dn_bufp->dtype.name; + + /* Lookup the next type in the list. It should be a structure, + * union, class, enum, or template type. + * We will need to attach that to our name. + */ + if (dn_bufp->dtype.type.dnttp.index < LNTT_SYMCOUNT (objfile)) + dn_bufp = hpread_get_lntt (dn_bufp->dtype.type.dnttp.index, objfile); + else { + complain (&hpread_type_lookup_complaint); + return; + } + + if (dn_bufp->dblock.kind == DNTT_TYPE_STRUCT) { + prefix = "struct "; + } else if (dn_bufp->dblock.kind == DNTT_TYPE_UNION) { + prefix = "union "; + } else if (dn_bufp->dblock.kind == DNTT_TYPE_CLASS) { + /* Further field for CLASS saying how it was really declared */ + /* 0==class, 1==union, 2==struct */ + if (dn_bufp->dclass.class_decl == 0) + prefix = "class "; + else if (dn_bufp->dclass.class_decl == 1) + prefix = "union "; + else if (dn_bufp->dclass.class_decl == 2) + prefix = "struct "; + else + prefix = ""; + } else if (dn_bufp->dblock.kind == DNTT_TYPE_ENUM) { + prefix = "enum "; + } else if (dn_bufp->dblock.kind == DNTT_TYPE_TEMPLATE) { + prefix = "template "; + } else { + prefix = ""; + } + + /* Build the correct name. */ + structtype->name + = (char *) obstack_alloc (&objfile->type_obstack, + strlen (prefix) + strlen (suffix) + 1); + TYPE_NAME (structtype) = strcpy (TYPE_NAME (structtype), prefix); + TYPE_NAME (structtype) = strcat (TYPE_NAME (structtype), suffix); + TYPE_TAG_NAME (structtype) = suffix; + + /* For classes/structs, we have to set the static member "physnames" + to point to strings like "Class::Member" */ + if (TYPE_CODE (structtype) == TYPE_CODE_STRUCT) + fix_static_member_physnames (structtype, suffix, objfile); + + return structtype; + } + + case DNTT_TYPE_POINTER: + /* Pointer type - call a routine in gdbtypes.c that constructs + * the appropriate GDB type. + */ + return make_pointer_type ( + hpread_type_lookup (dn_bufp->dptr.pointsto, + objfile), + NULL); + + case DNTT_TYPE_REFERENCE: + /* C++ reference type - call a routine in gdbtypes.c that constructs + * the appropriate GDB type. + */ + return make_reference_type ( + hpread_type_lookup (dn_bufp->dreference.pointsto, + objfile), + NULL); + + case DNTT_TYPE_ENUM: + return hpread_read_enum_type (hp_type, dn_bufp, objfile); + case DNTT_TYPE_SET: + return hpread_read_set_type (hp_type, dn_bufp, objfile); + case DNTT_TYPE_SUBRANGE: + return hpread_read_subrange_type (hp_type, dn_bufp, objfile); + case DNTT_TYPE_ARRAY: + return hpread_read_array_type (hp_type, dn_bufp, objfile); + case DNTT_TYPE_STRUCT: + case DNTT_TYPE_UNION: + return hpread_read_struct_type (hp_type, dn_bufp, objfile); + case DNTT_TYPE_FIELD: + return hpread_type_lookup (dn_bufp->dfield.type, objfile); + + case DNTT_TYPE_FUNCTYPE: + /* Here we want to read the function SOMs and return a + * type for it. We get here, for instance, when processing + * pointer-to-function type. + */ + return hpread_read_function_type (hp_type, dn_bufp, objfile, 0); + + case DNTT_TYPE_PTRMEM: + /* Declares a C++ pointer-to-data-member type. + * The "pointsto" field defines the class, + * while the "memtype" field defines the pointed-to-type. + */ + { + struct type * ptrmemtype; + struct type * class_type; + struct type * memtype; + memtype = hpread_type_lookup (dn_bufp->dptrmem.memtype, + objfile), + class_type = hpread_type_lookup (dn_bufp->dptrmem.pointsto, + objfile), + ptrmemtype = alloc_type(objfile); + smash_to_member_type(ptrmemtype, class_type, memtype); + return make_pointer_type(ptrmemtype, NULL); + } + break; + + case DNTT_TYPE_PTRMEMFUNC: + /* Defines a C++ pointer-to-function-member type. + * The "pointsto" field defines the class, + * while the "memtype" field defines the pointed-to-type. + */ + { + struct type * ptrmemtype; + struct type * class_type; + struct type * functype; + struct type * retvaltype; + int nargs; + int i; + struct type ** args_type; + class_type = hpread_type_lookup (dn_bufp->dptrmem.pointsto, + objfile); + functype = hpread_type_lookup (dn_bufp->dptrmem.memtype, + objfile); + retvaltype = TYPE_TARGET_TYPE (functype); + nargs = TYPE_NFIELDS (functype); + args_type = (struct type **) xmalloc ((nargs+1) * sizeof (struct type *)); + for (i = 0; i < nargs; i++) { + args_type[i] = TYPE_FIELD_TYPE (functype, i); + } + args_type[nargs] = NULL; + ptrmemtype = alloc_type(objfile); + smash_to_method_type(ptrmemtype, class_type, retvaltype, args_type); + return make_pointer_type(ptrmemtype, NULL); + } + break; + + case DNTT_TYPE_CLASS: + return hpread_read_struct_type (hp_type, dn_bufp, objfile); + + case DNTT_TYPE_GENFIELD: + /* Chase pointer from GENFIELD to FIELD, and make recursive + * call on that. + */ + return hpread_type_lookup (dn_bufp->dgenfield.field, objfile); + + case DNTT_TYPE_VFUNC: + /* C++ virtual function. + * We get here in the course of processing a class type which + * contains virtual functions. Just go through another level + * of indirection to get to the pointed-to function SOM. + */ + return hpread_type_lookup (dn_bufp->dvfunc.funcptr, objfile); + + case DNTT_TYPE_MODIFIER: + /* Check the modifiers and then just make a recursive call on + * the "type" pointed to by the modifier DNTT. + * + * pai:: FIXME -- do we ever want to handle "m_duplicate" and + * "m_void" modifiers? Is static_flag really needed here? + * (m_static used for methods of classes, elsewhere). + */ + tmp_type = make_cv_type (dn_bufp->dmodifier.m_const, + dn_bufp->dmodifier.m_volatile, + hpread_type_lookup (dn_bufp->dmodifier.type, objfile), + 0); + return tmp_type; + + + case DNTT_TYPE_MEMFUNC: + /* Member function. Treat like a function. + * I think we get here in the course of processing a + * pointer-to-member-function type... + */ + return hpread_read_function_type (hp_type, dn_bufp, objfile, 0); + + case DNTT_TYPE_DOC_MEMFUNC: + return hpread_read_doc_function_type (hp_type, dn_bufp, objfile, 0); + + case DNTT_TYPE_TEMPLATE: + /* Template - sort of the header for a template definition, + * which like a class, points to a member list and also points + * to a TEMPLATE_ARG list of type-arguments. + */ + return hpread_read_struct_type (hp_type, dn_bufp, objfile); + + case DNTT_TYPE_TEMPLATE_ARG: + { + char * name; + /* The TEMPLATE record points to an argument list of + * TEMPLATE_ARG records, each of which describes one + * of the type-arguments. + */ + name = VT (objfile) + dn_bufp->dtempl_arg.name; + return hpread_read_templ_arg_type (hp_type, dn_bufp, objfile, name); + } + + case DNTT_TYPE_FUNC_TEMPLATE: + /* We wind up here when processing a TEMPLATE type, + * if the template has member function(s). + * Treat it like a FUNCTION. + */ + return hpread_read_function_type (hp_type, dn_bufp, objfile, 0); + + case DNTT_TYPE_LINK: + /* The LINK record is used to link up templates with instantiations. + * There is no type associated with the LINK record per se. + */ + return lookup_fundamental_type (objfile, FT_VOID); + + /* Also not yet handled... */ + /* case DNTT_TYPE_DYN_ARRAY_DESC: */ + /* case DNTT_TYPE_DESC_SUBRANGE: */ + /* case DNTT_TYPE_BEGIN_EXT: */ + /* case DNTT_TYPE_INLN: */ + /* case DNTT_TYPE_INLN_LIST: */ + /* case DNTT_TYPE_ALIAS: */ + default: + /* A fancy way of returning NULL */ + return lookup_fundamental_type (objfile, FT_VOID); + } +} + +static sltpointer +hpread_record_lines (subfile, s_idx, e_idx, objfile, offset) + struct subfile *subfile; + sltpointer s_idx, e_idx; + struct objfile *objfile; + CORE_ADDR offset; +{ + union sltentry *sl_bufp; + + while (s_idx <= e_idx) + { + sl_bufp = hpread_get_slt (s_idx, objfile); + /* Only record "normal" entries in the SLT. */ + if (sl_bufp->snorm.sltdesc == SLT_NORMAL + || sl_bufp->snorm.sltdesc == SLT_EXIT) + record_line (subfile, sl_bufp->snorm.line, + sl_bufp->snorm.address + offset); + else if (sl_bufp->snorm.sltdesc == SLT_NORMAL_OFFSET) + record_line (subfile, sl_bufp->snormoff.line, + sl_bufp->snormoff.address + offset); + s_idx++; + } + return e_idx; +} + +/* Given a function "f" which is a member of a class, find + * the classname that it is a member of. Used to construct + * the name (e.g., "c::f") which GDB will put in the + * "demangled name" field of the function's symbol. + * Called from hpread_process_one_debug_symbol() + * If "f" is not a member function, return NULL. + */ +char * class_of (functype) +struct type * functype; +{ + struct type * first_param_type; + char * first_param_name; + struct type * pointed_to_type; + char * class_name; + + /* Check that the function has a first argument "this", + * and that "this" is a pointer to a class. If not, + * functype is not a member function, so return NULL. + */ + if (TYPE_NFIELDS(functype) == 0) + return NULL; + first_param_name = TYPE_FIELD_NAME (functype, 0); + if (first_param_name == NULL) + return NULL; /* paranoia */ + if (strcmp(first_param_name, "this")) + return NULL; + first_param_type = TYPE_FIELD_TYPE (functype, 0); + if (first_param_type == NULL) + return NULL; /* paranoia */ + if (TYPE_CODE(first_param_type) != TYPE_CODE_PTR) + return NULL; + + /* Get the thing that "this" points to, check that + * it's a class, and get its class name. + */ + pointed_to_type = TYPE_TARGET_TYPE(first_param_type); + if (pointed_to_type == NULL) + return NULL; /* paranoia */ + if (TYPE_CODE(pointed_to_type) != TYPE_CODE_CLASS) + return NULL; + class_name = TYPE_NAME(pointed_to_type); + if (class_name == NULL) + return NULL; /* paranoia */ + + /* The class name may be of the form "class c", in which case + * we want to strip off the leading "class ". + */ + if (strncmp(class_name, "class ", 6) == 0) + class_name += 6; + + return class_name; +} + +/* Internalize one native debug symbol. + * Called in a loop from hpread_expand_symtab(). + * Arguments: + * dn_bufp: + * name: + * section_offsets: + * objfile: + * text_offset: + * text_size: + * filename: + * index: Index of this symbol + * at_module_boundary_p Pointer to boolean flag to control caller's loop. + */ + +static void +hpread_process_one_debug_symbol (dn_bufp, name, section_offsets, objfile, + text_offset, text_size, filename, + index, at_module_boundary_p + ) + union dnttentry *dn_bufp; + char *name; + struct section_offsets *section_offsets; + struct objfile *objfile; + CORE_ADDR text_offset; + int text_size; + char *filename; + int index; + int *at_module_boundary_p; +{ + unsigned long desc; + int type; + CORE_ADDR valu; + int offset = ANOFFSET (section_offsets, SECT_OFF_TEXT); + int data_offset = ANOFFSET (section_offsets, SECT_OFF_DATA); + union dnttentry *dn_temp; + dnttpointer hp_type; + struct symbol *sym; + struct context_stack *new; + char * class_scope_name; + extern int is_in_import_list (); /* in somread.c */ + + /* Allocate one GDB debug symbol and fill in some default values. */ + sym = (struct symbol *) obstack_alloc (&objfile->symbol_obstack, + sizeof (struct symbol)); + memset (sym, 0, sizeof (struct symbol)); + SYMBOL_NAME (sym) = obsavestring (name, strlen (name), &objfile->symbol_obstack); + SYMBOL_LANGUAGE (sym) = language_auto; + SYMBOL_NAMESPACE (sym) = VAR_NAMESPACE; + SYMBOL_LINE (sym) = 0; + SYMBOL_VALUE (sym) = 0; + SYMBOL_CLASS (sym) = LOC_TYPEDEF; + + /* Just a trick in case the SOM debug symbol is a type definition. + * There are routines that are set up to build a GDB type symbol, given + * a SOM dnttpointer. So we set up a dummy SOM dnttpointer "hp_type". + * This allows us to call those same routines. + */ + hp_type.dnttp.extension = 1; + hp_type.dnttp.immediate = 0; + hp_type.dnttp.global = 0; + hp_type.dnttp.index = index; + + /* This "type" is the type of SOM record. + * Switch on SOM type. + */ + type = dn_bufp->dblock.kind; + switch (type) + { + case DNTT_TYPE_SRCFILE: + /* This type of symbol indicates from which source file or + * include file any following data comes. It may indicate: + * + * o The start of an entirely new source file (and thus + * a new module) + * + * o The start of a different source file due to #include + * + * o The end of an include file and the return to the original + * file. Thus if "foo.c" includes "bar.h", we see first + * a SRCFILE for foo.c, then one for bar.h, and then one for + * foo.c again. + * + * If it indicates the start of a new module then we must + * finish the symbol table of the previous module + * (if any) and start accumulating a new symbol table. + */ + + valu = text_offset; + if (!last_source_file ) { + /* + * A note on "last_source_file": this is a char* pointing + * to the actual file name. "start_symtab" sets it, + * "end_symtab" clears it. + * + * So if "last_source_file" is NULL, then either this is + * the first record we are looking at, or a previous call + * to "end_symtab()" was made to close out the previous + * module. Since we're now quitting the scan loop when we + * see a MODULE END record, we should never get here, except + * in the case that we're not using the quick look-up tables + * and have to use the old system as a fall-back. + */ + start_symtab (name, NULL, valu); + record_debugformat ("HP"); + SL_INDEX (objfile) = dn_bufp->dsfile.address; + } + + else { + /* Either a new include file, or a SRCFILE record + * saying we are back in the main source (or out of + * a nested include file) again. + */ + SL_INDEX (objfile) = hpread_record_lines (current_subfile, + SL_INDEX (objfile), + dn_bufp->dsfile.address, + objfile, offset); + } + + /* A note on "start_subfile". This routine will check + * the name we pass it and look for an existing subfile + * of that name. There's thus only one sub-file for the + * actual source (e.g. for "foo.c" in foo.c), despite the + * fact that we'll see lots of SRCFILE entries for foo.c + * inside foo.c. + */ + start_subfile (name, NULL); + break; + + case DNTT_TYPE_MODULE: + /* + * We no longer ignore DNTT_TYPE_MODULE symbols. The module + * represents the meaningful semantic structure of a compilation + * unit. We expect to start the psymtab-to-symtab expansion + * looking at a MODULE entry, and to end it at the corresponding + * END MODULE entry. + * + *--Begin outdated comments + * + * This record signifies the start of a new source module + * In C/C++ there is no explicit "module" construct in the language, + * but each compilation unit is implicitly a module and they + * do emit the DNTT_TYPE_MODULE records. + * The end of the module is marked by a matching DNTT_TYPE_END record. + * + * The reason GDB gets away with ignoring the DNTT_TYPE_MODULE record + * is it notices the DNTT_TYPE_END record for the previous + * module (see comments under DNTT_TYPE_END case), and then treats + * the next DNTT_TYPE_SRCFILE record as if it were the module-start record. + * (i.e., it makes a start_symtab() call). + * This scheme seems a little convoluted, but I'll leave it + * alone on the principle "if it ain't broke don't fix + * it". (RT). + * + *-- End outdated comments + */ + + valu = text_offset; + if (!last_source_file ) + { + /* Start of a new module. We know this because "last_source_file" + * is NULL, which can only happen the first time or if we just + * made a call to end_symtab() to close out the previous module. + */ + start_symtab (name, NULL, valu); + SL_INDEX (objfile) = dn_bufp->dmodule.address; + } + else + { + /* This really shouldn't happen if we're using the quick + * look-up tables, as it would mean we'd scanned past an + * END MODULE entry. But if we're not using the tables, + * we started the module on the SRCFILE entry, so it's ok. + * For now, accept this. + */ + /* warning( "Error expanding psymtab, missed module end, found entry for %s", + * name ); + */ + *at_module_boundary_p = -1; + } + + start_subfile (name, NULL); + break; + + case DNTT_TYPE_FUNCTION: + case DNTT_TYPE_ENTRY: + /* A function or secondary entry point. */ + valu = dn_bufp->dfunc.lowaddr + offset; + + /* Record lines up to this point. */ + SL_INDEX (objfile) = hpread_record_lines (current_subfile, + SL_INDEX (objfile), + dn_bufp->dfunc.address, + objfile, offset); + + WITHIN_FUNCTION (objfile) = 1; + CURRENT_FUNCTION_VALUE (objfile) = valu; + + /* Stack must be empty now. */ + if (context_stack_depth != 0) + complain (&lbrac_unmatched_complaint, (char *) symnum); + new = push_context (0, valu); + + /* Built a type for the function. This includes processing + * the symbol records for the function parameters. + */ + SYMBOL_CLASS (sym) = LOC_BLOCK; + SYMBOL_TYPE (sym) = hpread_read_function_type (hp_type, dn_bufp, objfile, 1); + + /* The "SYMBOL_NAME" field is expected to be the mangled name + * (if any), which we get from the "alias" field of the SOM record + * if that exists. + */ + if ((dn_bufp->dfunc.language == HP_LANGUAGE_CPLUSPLUS) && + dn_bufp->dfunc.alias && /* has an alias */ + *(char *)(VT (objfile) + dn_bufp->dfunc.alias)) /* not a null string */ + SYMBOL_NAME (sym) = VT (objfile) + dn_bufp->dfunc.alias; + else + SYMBOL_NAME (sym) = VT (objfile) + dn_bufp->dfunc.name; + + /* Special hack to get around HP compilers' insistence on + * reporting "main" as "_MAIN_" for C/C++ */ + if ((strcmp (SYMBOL_NAME (sym), "_MAIN_") == 0) && + (strcmp (VT (objfile) + dn_bufp->dfunc.name, "main") == 0)) + SYMBOL_NAME (sym) = VT (objfile) + dn_bufp->dfunc.name; + + /* The SYMBOL_CPLUS_DEMANGLED_NAME field is expected to + * be the demangled name. + */ + if (dn_bufp->dfunc.language == HP_LANGUAGE_CPLUSPLUS) + { + /* SYMBOL_INIT_DEMANGLED_NAME is a macro which winds up + * calling the demangler in libiberty (cplus_demangle()) to + * do the job. This generally does the job, even though + * it's intended for the GNU compiler and not the aCC compiler + * Note that SYMBOL_INIT_DEMANGLED_NAME calls the + * demangler with arguments DMGL_PARAMS | DMGL_ANSI. + * Generally, we don't want params when we display + * a demangled name, but when I took out the DMGL_PARAMS, + * some things broke, so I'm leaving it in here, and + * working around the issue in stack.c. - RT + */ + SYMBOL_INIT_DEMANGLED_NAME (sym, &objfile->symbol_obstack); + if ((SYMBOL_NAME (sym) == VT (objfile) + dn_bufp->dfunc.alias) && + (!SYMBOL_CPLUS_DEMANGLED_NAME(sym))) { + + /* Well, the symbol name is mangled, but the + * demangler in libiberty failed so the demangled + * field is still NULL. Try to + * do the job ourselves based on the "name" field + * in the SOM record. A complication here is that + * the name field contains only the function name + * (like "f"), whereas we want the class qualification + * (as in "c::f"). Try to reconstruct that. + */ + char * basename; + char * classname; + char * dem_name; + basename = VT (objfile) + dn_bufp->dfunc.name; + classname = class_of(SYMBOL_TYPE(sym)); + if (classname) { + dem_name = xmalloc(strlen(basename)+strlen(classname)+3); + strcpy(dem_name, classname); + strcat(dem_name, "::"); + strcat(dem_name, basename); + SYMBOL_CPLUS_DEMANGLED_NAME(sym) = dem_name; + SYMBOL_LANGUAGE (sym) = language_cplus; + } + } + } + + /* Add the function symbol to the list of symbols in this blockvector */ + if (dn_bufp->dfunc.global) + add_symbol_to_list (sym, &global_symbols); + else + add_symbol_to_list (sym, &file_symbols); + new->name = sym; + + /* Search forward to the next BEGIN and also read + * in the line info up to that point. + * Not sure why this is needed. + * In HP FORTRAN this code is harmful since there + * may not be a BEGIN after the FUNCTION. + * So I made it C/C++ specific. - RT + */ + if (dn_bufp->dfunc.language == HP_LANGUAGE_C || + dn_bufp->dfunc.language == HP_LANGUAGE_CPLUSPLUS) { + while (dn_bufp->dblock.kind != DNTT_TYPE_BEGIN) + { + dn_bufp = hpread_get_lntt (++index, objfile); + if (dn_bufp->dblock.extension) + continue; + } + SL_INDEX (objfile) = hpread_record_lines (current_subfile, + SL_INDEX (objfile), + dn_bufp->dbegin.address, + objfile, offset); + SYMBOL_LINE (sym) = hpread_get_line (dn_bufp->dbegin.address, objfile); + } + record_line (current_subfile, SYMBOL_LINE (sym), valu); + break; + + case DNTT_TYPE_DOC_FUNCTION: + valu = dn_bufp->ddocfunc.lowaddr + offset; + + /* Record lines up to this point. */ + SL_INDEX (objfile) = hpread_record_lines (current_subfile, + SL_INDEX (objfile), + dn_bufp->ddocfunc.address, + objfile, offset); + + WITHIN_FUNCTION (objfile) = 1; + CURRENT_FUNCTION_VALUE (objfile) = valu; + /* Stack must be empty now. */ + if (context_stack_depth != 0) + complain (&lbrac_unmatched_complaint, (char *) symnum); + new = push_context (0, valu); + + /* Built a type for the function. This includes processing + * the symbol records for the function parameters. + */ + SYMBOL_CLASS (sym) = LOC_BLOCK; + SYMBOL_TYPE (sym) = hpread_read_doc_function_type (hp_type, dn_bufp, objfile, 1); + + /* The "SYMBOL_NAME" field is expected to be the mangled name + * (if any), which we get from the "alias" field of the SOM record + * if that exists. + */ + if ((dn_bufp->ddocfunc.language == HP_LANGUAGE_CPLUSPLUS) && + dn_bufp->ddocfunc.alias && /* has an alias */ + *(char *)(VT (objfile) + dn_bufp->ddocfunc.alias)) /* not a null string */ + SYMBOL_NAME (sym) = VT (objfile) + dn_bufp->ddocfunc.alias; + else + SYMBOL_NAME (sym) = VT (objfile) + dn_bufp->ddocfunc.name; + + /* Special hack to get around HP compilers' insistence on + * reporting "main" as "_MAIN_" for C/C++ */ + if ((strcmp (SYMBOL_NAME (sym), "_MAIN_") == 0) && + (strcmp (VT (objfile) + dn_bufp->ddocfunc.name, "main") == 0)) + SYMBOL_NAME (sym) = VT (objfile) + dn_bufp->ddocfunc.name; + + if (dn_bufp->ddocfunc.language == HP_LANGUAGE_CPLUSPLUS) { + + /* SYMBOL_INIT_DEMANGLED_NAME is a macro which winds up + * calling the demangler in libiberty (cplus_demangle()) to + * do the job. This generally does the job, even though + * it's intended for the GNU compiler and not the aCC compiler + * Note that SYMBOL_INIT_DEMANGLED_NAME calls the + * demangler with arguments DMGL_PARAMS | DMGL_ANSI. + * Generally, we don't want params when we display + * a demangled name, but when I took out the DMGL_PARAMS, + * some things broke, so I'm leaving it in here, and + * working around the issue in stack.c. - RT + */ + SYMBOL_INIT_DEMANGLED_NAME (sym, &objfile->symbol_obstack); + + if ((SYMBOL_NAME (sym) == VT (objfile) + dn_bufp->ddocfunc.alias) && + (!SYMBOL_CPLUS_DEMANGLED_NAME(sym))) { + + /* Well, the symbol name is mangled, but the + * demangler in libiberty failed so the demangled + * field is still NULL. Try to + * do the job ourselves based on the "name" field + * in the SOM record. A complication here is that + * the name field contains only the function name + * (like "f"), whereas we want the class qualification + * (as in "c::f"). Try to reconstruct that. + */ + char * basename; + char * classname; + char * dem_name; + basename = VT (objfile) + dn_bufp->ddocfunc.name; + classname = class_of(SYMBOL_TYPE(sym)); + if (classname) { + dem_name = xmalloc(strlen(basename)+strlen(classname)+3); + strcpy(dem_name, classname); + strcat(dem_name, "::"); + strcat(dem_name, basename); + SYMBOL_CPLUS_DEMANGLED_NAME(sym) = dem_name; + SYMBOL_LANGUAGE (sym) = language_cplus; + } + } + } + + /* Add the function symbol to the list of symbols in this blockvector */ + if (dn_bufp->ddocfunc.global) + add_symbol_to_list (sym, &global_symbols); + else + add_symbol_to_list (sym, &file_symbols); + new->name = sym; + + /* Search forward to the next BEGIN and also read + * in the line info up to that point. + * Not sure why this is needed. + * In HP FORTRAN this code is harmful since there + * may not be a BEGIN after the FUNCTION. + * So I made it C/C++ specific. - RT + */ + if (dn_bufp->ddocfunc.language == HP_LANGUAGE_C || + dn_bufp->ddocfunc.language == HP_LANGUAGE_CPLUSPLUS) { + while (dn_bufp->dblock.kind != DNTT_TYPE_BEGIN) + { + dn_bufp = hpread_get_lntt (++index, objfile); + if (dn_bufp->dblock.extension) + continue; + } + SL_INDEX (objfile) = hpread_record_lines (current_subfile, + SL_INDEX (objfile), + dn_bufp->dbegin.address, + objfile, offset); + SYMBOL_LINE (sym) = hpread_get_line (dn_bufp->dbegin.address, objfile); + } + record_line (current_subfile, SYMBOL_LINE (sym), valu); + break; + + case DNTT_TYPE_BEGIN: + /* Begin a new scope. */ + if (context_stack_depth == 1 /* this means we're at function level */ && + context_stack[0].name != NULL /* this means it's a function */ && + context_stack[0].depth == 0 /* this means it's the first BEGIN + we've seen after the FUNCTION */ + ) + { + /* This is the first BEGIN after a FUNCTION. + * We ignore this one, since HP compilers always insert + * at least one BEGIN, i.e. it's: + * + * FUNCTION + * argument symbols + * BEGIN + * local symbols + * (possibly nested BEGIN ... END's if there are inner { } blocks) + * END + * END + * + * By ignoring this first BEGIN, the local symbols get treated + * as belonging to the function scope, and "print func::local_sym" + * works (which is what we want). + */ + + /* All we do here is increase the depth count associated with + * the FUNCTION entry in the context stack. This ensures that + * the next BEGIN we see (if any), representing a real nested { } + * block, will get processed. + */ + + context_stack[0].depth++; + + } else { + + /* Record lines up to this SLT pointer. */ + SL_INDEX (objfile) = hpread_record_lines (current_subfile, + SL_INDEX (objfile), + dn_bufp->dbegin.address, + objfile, offset); + /* Calculate start address of new scope */ + valu = hpread_get_location (dn_bufp->dbegin.address, objfile); + valu += offset; /* Relocate for dynamic loading */ + /* We use the scope start DNTT index as nesting depth identifier! */ + desc = hpread_get_scope_start (dn_bufp->dbegin.address, objfile); + new = push_context (desc, valu); + } + break; + + case DNTT_TYPE_END: + /* End a scope. */ + + /* Valid end kinds are: + * MODULE + * FUNCTION + * WITH + * COMMON + * BEGIN + * CLASS_SCOPE + */ + + SL_INDEX (objfile) = hpread_record_lines (current_subfile, + SL_INDEX (objfile), + dn_bufp->dend.address, + objfile, offset); + switch (dn_bufp->dend.endkind) + { + case DNTT_TYPE_MODULE: + /* Ending a module ends the symbol table for that module. + * Calling end_symtab() has the side effect of clearing the + * last_source_file pointer, which in turn signals + * process_one_debug_symbol() to treat the next DNTT_TYPE_SRCFILE + * record as a module-begin. + */ + valu = text_offset + text_size + offset; + + /* Tell our caller that we're done with expanding the + * debug information for a module. + */ + *at_module_boundary_p = 1; + + /* Don't do this, as our caller will do it! + * + * (void) end_symtab (valu, objfile, 0); + */ + break; + + case DNTT_TYPE_FUNCTION: + /* Ending a function, well, ends the function's scope. */ + dn_temp = hpread_get_lntt (dn_bufp->dend.beginscope.dnttp.index, + objfile); + valu = dn_temp->dfunc.hiaddr + offset; + /* Insert func params into local list */ + merge_symbol_lists (¶m_symbols, &local_symbols); + new = pop_context (); + /* Make a block for the local symbols within. */ + finish_block (new->name, &local_symbols, new->old_blocks, + new->start_addr, valu, objfile); + WITHIN_FUNCTION (objfile) = 0; /* This may have to change for Pascal */ + local_symbols = new->locals; + param_symbols = new->params; + break; + + case DNTT_TYPE_BEGIN: + if (context_stack_depth == 1 && + context_stack[0].name != NULL && + context_stack[0].depth == 1) + { + /* This is the END corresponding to the + * BEGIN which we ignored - see DNTT_TYPE_BEGIN case above. + */ + context_stack[0].depth--; + } else { + /* Ending a local scope. */ + valu = hpread_get_location (dn_bufp->dend.address, objfile); + /* Why in the hell is this needed? */ + valu += offset + 9; /* Relocate for dynamic loading */ + new = pop_context (); + desc = dn_bufp->dend.beginscope.dnttp.index; + if (desc != new->depth) + complain (&lbrac_mismatch_complaint, (char *) symnum); + + /* Make a block for the local symbols within. */ + finish_block (new->name, &local_symbols, new->old_blocks, + new->start_addr, valu, objfile); + local_symbols = new->locals; + param_symbols = new->params; + } + break; + + case DNTT_TYPE_WITH: + /* Since we ignore the DNTT_TYPE_WITH that starts the scope, + * we can ignore the DNTT_TYPE_END that ends it. + */ + break; + + case DNTT_TYPE_COMMON: + /* End a FORTRAN common block. We don't currently handle these */ + complain (&hpread_unhandled_end_common_complaint); + break; + + case DNTT_TYPE_CLASS_SCOPE: + + /* pai: FIXME Not handling nested classes for now -- must + * maintain a stack */ + class_scope_name = NULL; + +#if 0 + /* End a class scope */ + valu = hpread_get_location (dn_bufp->dend.address, objfile); + /* Why in the hell is this needed? */ + valu += offset + 9; /* Relocate for dynamic loading */ + new = pop_context (); + desc = dn_bufp->dend.beginscope.dnttp.index; + if (desc != new->depth) + complain (&lbrac_mismatch_complaint, (char *) symnum); + /* Make a block for the local symbols within. */ + finish_block (new->name, &local_symbols, new->old_blocks, + new->start_addr, valu, objfile); + local_symbols = new->locals; + param_symbols = new->params; +#endif + break; + + default: + complain (&hpread_unexpected_end_complaint); + break; + } + break; + + /* DNTT_TYPE_IMPORT is not handled */ + + case DNTT_TYPE_LABEL: + SYMBOL_NAMESPACE (sym) = LABEL_NAMESPACE; + break; + + case DNTT_TYPE_FPARAM: + /* Function parameters. */ + /* Note 1: This code was present in the 4.16 sources, and then + removed, because fparams are handled in + hpread_read_function_type(). However, while fparam symbols + are indeed handled twice, this code here cannot be removed + because then they don't get added to the local symbol list of + the function's code block, which leads to a failure to look + up locals, "this"-relative member names, etc. So I've put + this code back in. pai/1997-07-21 */ + /* Note 2: To fix a defect, we stopped adding FPARAMS to local_symbols + in hpread_read_function_type(), so FPARAMS had to be handled + here. I changed the location to be the appropriate argument + kinds rather than LOC_LOCAL. pai/1997-08-08 */ + /* Note 3: Well, the fix in Note 2 above broke argument printing + in traceback frames, and further it makes assumptions about the + order of the FPARAM entries from HP compilers (cc and aCC in particular + generate them in reverse orders -- fixing one breaks for the other). + So I've added code in hpread_read_function_type() to add fparams + to a param_symbols list for the current context level. These are + then merged into local_symbols when a function end is reached. + pai/1997-08-11 */ + + break; /* do nothing; handled in hpread_read_function_type() */ + +#if 0 /* Old code */ + if (dn_bufp->dfparam.regparam) + SYMBOL_CLASS (sym) = LOC_REGISTER; + else if (dn_bufp->dfparam.indirect) + SYMBOL_CLASS (sym) = LOC_REF_ARG; + else + SYMBOL_CLASS (sym) = LOC_ARG; + SYMBOL_NAMESPACE (sym) = VAR_NAMESPACE; + if (dn_bufp->dfparam.copyparam) + { + SYMBOL_VALUE (sym) = dn_bufp->dfparam.location; +#ifdef HPREAD_ADJUST_STACK_ADDRESS + SYMBOL_VALUE (sym) + += HPREAD_ADJUST_STACK_ADDRESS (CURRENT_FUNCTION_VALUE (objfile)); +#endif + } + else + SYMBOL_VALUE (sym) = dn_bufp->dfparam.location; + SYMBOL_TYPE (sym) = hpread_type_lookup (dn_bufp->dfparam.type, objfile); + add_symbol_to_list (sym, &fparam_symbols); + break; +#endif + + case DNTT_TYPE_SVAR: + /* Static variables. */ + SYMBOL_CLASS (sym) = LOC_STATIC; + + /* Note: There is a case that arises with globals in shared + * libraries where we need to set the address to LOC_INDIRECT. + * This case is if you have a global "g" in one library, and + * it is referenced "extern <type> g;" in another library. + * If we're processing the symbols for the referencing library, + * we'll see a global "g", but in this case the address given + * in the symbol table contains a pointer to the real "g". + * We use the storage class LOC_INDIRECT to indicate this. RT + */ + if (is_in_import_list (SYMBOL_NAME(sym), objfile)) + SYMBOL_CLASS (sym) = LOC_INDIRECT; + + SYMBOL_VALUE_ADDRESS (sym) = dn_bufp->dsvar.location + data_offset; + SYMBOL_TYPE (sym) = hpread_type_lookup (dn_bufp->dsvar.type, objfile); + + if (dn_bufp->dsvar.global) + add_symbol_to_list (sym, &global_symbols); + + else if (WITHIN_FUNCTION (objfile)) + add_symbol_to_list (sym, &local_symbols); + + else + add_symbol_to_list (sym, &file_symbols); + + if (dn_bufp->dsvar.thread_specific) + { + /* Thread-local variable. + */ + SYMBOL_CLASS (sym) = LOC_THREAD_LOCAL_STATIC; + SYMBOL_BASEREG (sym) = CR27_REGNUM; + + if( objfile->flags & OBJF_SHARED ) { + /* + * This variable is not only thread local but + * in a shared library. + * + * Alas, the shared lib structures are private + * to "somsolib.c". But C lets us point to one. + */ + struct so_list *so; + + if( objfile->obj_private == NULL ) + error( "Internal error in reading shared library information." ); + + so = ((obj_private_data_t *)(objfile->obj_private))->so_info; + if( so == NULL ) + error( "Internal error in reading shared library information." ); + + /* Thread-locals in shared libraries do NOT have the + * standard offset ("data_offset"), so we re-calculate + * where to look for this variable, using a call-back + * to interpret the private shared-library data. + */ + SYMBOL_VALUE_ADDRESS(sym) = dn_bufp->dsvar.location + + so_lib_thread_start_addr( so ); + } + } + break; + + case DNTT_TYPE_DVAR: + /* Dynamic variables. */ + if (dn_bufp->ddvar.regvar) + SYMBOL_CLASS (sym) = LOC_REGISTER; + else + SYMBOL_CLASS (sym) = LOC_LOCAL; + + SYMBOL_VALUE (sym) = dn_bufp->ddvar.location; +#ifdef HPREAD_ADJUST_STACK_ADDRESS + SYMBOL_VALUE (sym) + += HPREAD_ADJUST_STACK_ADDRESS (CURRENT_FUNCTION_VALUE (objfile)); +#endif + SYMBOL_TYPE (sym) = hpread_type_lookup (dn_bufp->ddvar.type, objfile); + if (dn_bufp->ddvar.global) + add_symbol_to_list (sym, &global_symbols); + else if (WITHIN_FUNCTION (objfile)) + add_symbol_to_list (sym, &local_symbols); + else + add_symbol_to_list (sym, &file_symbols); + break; + + case DNTT_TYPE_CONST: + /* A constant (pascal?). */ + SYMBOL_CLASS (sym) = LOC_CONST; + SYMBOL_VALUE (sym) = dn_bufp->dconst.location; + SYMBOL_TYPE (sym) = hpread_type_lookup (dn_bufp->dconst.type, objfile); + if (dn_bufp->dconst.global) + add_symbol_to_list (sym, &global_symbols); + else if (WITHIN_FUNCTION (objfile)) + add_symbol_to_list (sym, &local_symbols); + else + add_symbol_to_list (sym, &file_symbols); + break; + + case DNTT_TYPE_TYPEDEF: + /* A typedef. We do want to process these, since a name is + * added to the namespace for the typedef'ed name. + */ + SYMBOL_NAMESPACE (sym) = VAR_NAMESPACE; + SYMBOL_TYPE (sym) = hpread_type_lookup (dn_bufp->dtype.type, objfile); + if (dn_bufp->dtype.global) + add_symbol_to_list (sym, &global_symbols); + else if (WITHIN_FUNCTION (objfile)) + add_symbol_to_list (sym, &local_symbols); + else + add_symbol_to_list (sym, &file_symbols); + break; + + case DNTT_TYPE_TAGDEF: + { + int global = dn_bufp->dtag.global; + /* Structure, union, enum, template, or class tag definition */ + /* We do want to process these, since a name is + * added to the namespace for the tag name (and if C++ class, + * for the typename also). + */ + SYMBOL_NAMESPACE (sym) = STRUCT_NAMESPACE; + + /* The tag contains in its "type" field a pointer to the + * DNTT_TYPE_STRUCT, DNTT_TYPE_UNION, DNTT_TYPE_ENUM, + * DNTT_TYPE_CLASS or DNTT_TYPE_TEMPLATE + * record that actually defines the type. + */ + SYMBOL_TYPE (sym) = hpread_type_lookup (dn_bufp->dtype.type, objfile); + TYPE_NAME (sym->type) = SYMBOL_NAME (sym); + TYPE_TAG_NAME (sym->type) = SYMBOL_NAME (sym); + if (dn_bufp->dtag.global) + add_symbol_to_list (sym, &global_symbols); + else if (WITHIN_FUNCTION (objfile)) + add_symbol_to_list (sym, &local_symbols); + else + add_symbol_to_list (sym, &file_symbols); + + /* If this is a C++ class, then we additionally + * need to define a typedef for the + * class type. E.g., so that the name "c" becomes visible as + * a type name when the user says "class c { ... }". + * In order to figure this out, we need to chase down the "type" + * field to get to the DNTT_TYPE_CLASS record. + * + * We also add the typename for ENUM. Though this isn't + * strictly correct, it is necessary because of the debug info + * generated by the aCC compiler, in which we cannot + * distinguish between: + * enum e { ... }; + * and + * typedef enum { ... } e; + * I.e., the compiler emits the same debug info for the above + * two cases, in both cases "e" appearing as a tagdef. + * Therefore go ahead and generate the typename so that + * "ptype e" will work in the above cases. + * + * We also add the typename for TEMPLATE, so as to allow "ptype t" + * when "t" is a template name. + */ + if (dn_bufp->dtype.type.dnttp.index < LNTT_SYMCOUNT (objfile)) + dn_bufp = hpread_get_lntt (dn_bufp->dtag.type.dnttp.index, objfile); + else { + complain (&hpread_tagdef_complaint); + return; + } + if (dn_bufp->dblock.kind == DNTT_TYPE_CLASS || + dn_bufp->dblock.kind == DNTT_TYPE_ENUM || + dn_bufp->dblock.kind == DNTT_TYPE_TEMPLATE) { + struct symbol *newsym; + + newsym = (struct symbol *) obstack_alloc (&objfile->symbol_obstack, + sizeof (struct symbol)); + memset (newsym, 0, sizeof (struct symbol)); + SYMBOL_NAME (newsym) = name; + SYMBOL_LANGUAGE (newsym) = language_auto; + SYMBOL_NAMESPACE (newsym) = VAR_NAMESPACE; + SYMBOL_LINE (newsym) = 0; + SYMBOL_VALUE (newsym) = 0; + SYMBOL_CLASS (newsym) = LOC_TYPEDEF; + SYMBOL_TYPE (newsym) = sym->type; + if (global) + add_symbol_to_list (newsym, &global_symbols); + else if (WITHIN_FUNCTION (objfile)) + add_symbol_to_list (newsym, &local_symbols); + else + add_symbol_to_list (newsym, &file_symbols); + } + } + break; + + case DNTT_TYPE_POINTER: + /* Declares a pointer type. Should not be necessary to do anything + * with the type at this level; these are processed + * at the hpread_type_lookup() level. + */ + break; + + case DNTT_TYPE_ENUM: + /* Declares an enum type. Should not be necessary to do anything + * with the type at this level; these are processed + * at the hpread_type_lookup() level. + */ + break; + + case DNTT_TYPE_MEMENUM: + /* Member of enum */ + /* Ignored at this level, but hpread_read_enum_type() will take + * care of walking the list of enumeration members. + */ + break; + + case DNTT_TYPE_SET: + /* Declares a set type. Should not be necessary to do anything + * with the type at this level; these are processed + * at the hpread_type_lookup() level. + */ + break; + + case DNTT_TYPE_SUBRANGE: + /* Declares a subrange type. Should not be necessary to do anything + * with the type at this level; these are processed + * at the hpread_type_lookup() level. + */ + break; + + case DNTT_TYPE_ARRAY: + /* Declares an array type. Should not be necessary to do anything + * with the type at this level; these are processed + * at the hpread_type_lookup() level. + */ + break; + + case DNTT_TYPE_STRUCT: + case DNTT_TYPE_UNION: + /* Declares an struct/union type. + * Should not be necessary to do anything + * with the type at this level; these are processed + * at the hpread_type_lookup() level. + */ + break; + + case DNTT_TYPE_FIELD: + /* Structure/union/class field */ + /* Ignored at this level, but hpread_read_struct_type() will take + * care of walking the list of structure/union/class members. + */ + break; + + /* DNTT_TYPE_VARIANT is not handled by GDB */ + + /* DNTT_TYPE_FILE is not handled by GDB */ + + case DNTT_TYPE_FUNCTYPE: + /* Function type */ + /* Ignored at this level, handled within hpread_type_lookup() */ + break; + + case DNTT_TYPE_WITH: + /* This is emitted within methods to indicate "with <class>" + * scoping rules (i.e., indicate that the class data members + * are directly visible). + * However, since GDB already infers this by looking at the + * "this" argument, interpreting the DNTT_TYPE_WITH + * symbol record is unnecessary. + */ + break; + + case DNTT_TYPE_COMMON: + /* FORTRAN common. Not yet handled. */ + complain (&hpread_unhandled_common_complaint); + break; + + /* DNTT_TYPE_COBSTRUCT is not handled by GDB. */ + /* DNTT_TYPE_XREF is not handled by GDB. */ + /* DNTT_TYPE_SA is not handled by GDB. */ + /* DNTT_TYPE_MACRO is not handled by GDB */ + + case DNTT_TYPE_BLOCKDATA: + /* Not sure what this is - part of FORTRAN support maybe? + * Anyway, not yet handled. + */ + complain (&hpread_unhandled_blockdata_complaint); + break; + + case DNTT_TYPE_CLASS_SCOPE: + + + + /* The compiler brackets member functions with a CLASS_SCOPE/END + * pair of records, presumably to put them in a different scope + * from the module scope where they are normally defined. + * E.g., in the situation: + * void f() { ... } + * void c::f() { ...} + * The member function "c::f" will be bracketed by a CLASS_SCOPE/END. + * This causes "break f" at the module level to pick the + * the file-level function f(), not the member function + * (which needs to be referenced via "break c::f"). + * + * Here we record the class name to generate the demangled names of + * member functions later. + * + * FIXME Not being used now for anything -- cplus_demangle seems + * enough for getting the class-qualified names of functions. We + * may need this for handling nested classes and types. */ + + /* pai: FIXME Not handling nested classes for now -- need to + * maintain a stack */ + + dn_temp = hpread_get_lntt (dn_bufp->dclass_scope.type.dnttp.index, objfile); + if (dn_temp->dblock.kind == DNTT_TYPE_TAGDEF) + class_scope_name = VT (objfile) + dn_temp->dtag.name; + else + class_scope_name = NULL; + +#if 0 + + /* Begin a new scope. */ + SL_INDEX (objfile) = hpread_record_lines (current_subfile, + SL_INDEX (objfile), + dn_bufp->dclass_scope.address, + objfile, offset); + valu = hpread_get_location (dn_bufp->dclass_scope.address, objfile); + valu += offset; /* Relocate for dynamic loading */ + desc = hpread_get_scope_start (dn_bufp->dclass_scope.address, objfile); + /* We use the scope start DNTT index as the nesting depth identifier! */ + new = push_context (desc, valu); +#endif + break; + + case DNTT_TYPE_REFERENCE: + /* Declares a C++ reference type. Should not be necessary to do anything + * with the type at this level; these are processed + * at the hpread_type_lookup() level. + */ + break; + + case DNTT_TYPE_PTRMEM: + /* Declares a C++ pointer-to-data-member type. This does not + * need to be handled at this level; being a type description it + * is instead handled at the hpread_type_lookup() level. + */ + break; + + case DNTT_TYPE_PTRMEMFUNC: + /* Declares a C++ pointer-to-function-member type. This does not + * need to be handled at this level; being a type description it + * is instead handled at the hpread_type_lookup() level. + */ + break; + + case DNTT_TYPE_CLASS: + /* Declares a class type. + * Should not be necessary to do anything + * with the type at this level; these are processed + * at the hpread_type_lookup() level. + */ + break; + + case DNTT_TYPE_GENFIELD: + /* I believe this is used for class member functions */ + /* Ignored at this level, but hpread_read_struct_type() will take + * care of walking the list of class members. + */ + break; + + case DNTT_TYPE_VFUNC: + /* Virtual function */ + /* This does not have to be handled at this level; handled in + * the course of processing class symbols. + */ + break; + + case DNTT_TYPE_MEMACCESS: + /* DDE ignores this symbol table record. + * It has something to do with "modified access" to class members. + * I'll assume we can safely ignore it too. + */ + break; + + case DNTT_TYPE_INHERITANCE: + /* These don't have to be handled here, since they are handled + * within hpread_read_struct_type() in the process of constructing + * a class type. + */ + break; + + case DNTT_TYPE_FRIEND_CLASS: + case DNTT_TYPE_FRIEND_FUNC: + /* These can safely be ignored, as GDB doesn't need this + * info. DDE only uses it in "describe". We may later want + * to extend GDB's "ptype" to give this info, but for now + * it seems safe enough to ignore it. + */ + break; + + case DNTT_TYPE_MODIFIER: + /* Intended to supply "modified access" to a type */ + /* From the way DDE handles this, it looks like it always + * modifies a type. Therefore it is safe to ignore it at this + * level, and handle it in hpread_type_lookup(). + */ + break; + + case DNTT_TYPE_OBJECT_ID: + /* Just ignore this - that's all DDE does */ + break; + + case DNTT_TYPE_MEMFUNC: + /* Member function */ + /* This does not have to be handled at this level; handled in + * the course of processing class symbols. + */ + break; + + case DNTT_TYPE_DOC_MEMFUNC: + /* Member function */ + /* This does not have to be handled at this level; handled in + * the course of processing class symbols. + */ + break; + + case DNTT_TYPE_TEMPLATE: + /* Template - sort of the header for a template definition, + * which like a class, points to a member list and also points + * to a TEMPLATE_ARG list of type-arguments. + * We do not need to process TEMPLATE records at this level though. + */ + break; + + case DNTT_TYPE_TEMPLATE_ARG: + /* The TEMPLATE record points to an argument list of + * TEMPLATE_ARG records, each of which describes one + * of the type-arguments. + * We do not need to process TEMPLATE_ARG records at this level though. + */ + break; + + case DNTT_TYPE_FUNC_TEMPLATE: + /* This will get emitted for member functions of templates. + * But we don't need to process this record at this level though, + * we will process it in the course of processing a TEMPLATE + * record. + */ + break; + + case DNTT_TYPE_LINK: + /* The LINK record is used to link up templates with instantiations. */ + /* It is not clear why this is needed, and furthermore aCC does + * not appear to generate this, so I think we can safely ignore it. - RT + */ + break; + + /* DNTT_TYPE_DYN_ARRAY_DESC is not handled by GDB */ + /* DNTT_TYPE_DESC_SUBRANGE is not handled by GDB */ + /* DNTT_TYPE_BEGIN_EXT is not handled by GDB */ + /* DNTT_TYPE_INLN is not handled by GDB */ + /* DNTT_TYPE_INLN_LIST is not handled by GDB */ + /* DNTT_TYPE_ALIAS is not handled by GDB */ + + default: + break; + } +} + +/* Get nesting depth for a DNTT entry. + * DN_BUFP points to a DNTT entry. + * OBJFILE is the object file. + * REPORT_NESTED is a flag; if 0, real nesting depth is + * reported, if it is 1, the function simply returns a + * non-zero value if the nesting depth is anything > 0. + * + * Return value is an integer. 0 => not a local type / name + * positive return => type or name is local to some + * block or function. + */ + + +/* elz: ATTENTION: FIXME: NOTE: WARNING!!!! + this function now returns 0 right away. It was taking too much time + at start up. Now, though, the local types are not handled correctly. +*/ + + +static int +hpread_get_scope_depth (dn_bufp, objfile, report_nested) + union dnttentry * dn_bufp; + struct objfile * objfile; + int report_nested; +{ + register int index; + register union dnttentry * dn_tmp; + register short depth = 0; +/****************************/ + return 0; +/****************************/ + + index = (((char *) dn_bufp) - LNTT (objfile)) / (sizeof (struct dntt_type_block)); + + while (--index >= 0) + { + dn_tmp = hpread_get_lntt (index, objfile); + switch (dn_tmp->dblock.kind) + { + case DNTT_TYPE_MODULE: + return depth; + case DNTT_TYPE_END: + /* index is signed int; dnttp.index is 29-bit unsigned int! */ + index = (int) dn_tmp->dend.beginscope.dnttp.index; + break; + case DNTT_TYPE_BEGIN: + case DNTT_TYPE_FUNCTION: + case DNTT_TYPE_DOC_FUNCTION: + case DNTT_TYPE_WITH: + case DNTT_TYPE_COMMON: + case DNTT_TYPE_CLASS_SCOPE: + depth++; + if (report_nested) + return 1; + break; + default: + break; + } + } + return depth; +} + +/* Adjust the bitoffsets for all fields of an anonymous union of + type TYPE by negative BITS. This handles HP aCC's hideous habit + of giving members of anonymous unions bit offsets relative to the + enclosing structure instead of relative to the union itself. */ + +static void +hpread_adjust_bitoffsets (type, bits) + struct type * type; + int bits; +{ + register int i; + + /* This is done only for unions; caller had better check that + it is an anonymous one. */ + if (TYPE_CODE (type) != TYPE_CODE_UNION) + return; + + /* Adjust each field; since this is a union, there are no base + classes. Also no static membes. Also, no need for recursion as + the members of this union if themeselves structs or unions, have + the correct bitoffsets; if an anonymous union is a member of this + anonymous union, the code in hpread_read_struct_type() will + adjust for that. */ + + for (i = 0; i < TYPE_NFIELDS (type); i++) + TYPE_FIELD_BITPOS (type, i) -= bits; +} + +/* Because of quirks in HP compilers' treatment of anonymous unions inside + classes, we have to chase through a chain of threaded FIELD entries. + If we encounter an anonymous union in the chain, we must recursively skip over + that too. + + This function does a "next" in the chain of FIELD entries, but transparently + skips over anonymous unions' fields (recursively). + + Inputs are the number of times to do "next" at the top level, the dnttpointer + (FIELD) and entry pointer (FIELDP) for the dntt record corresponding to it, + and the ubiquitous objfile parameter. (Note: FIELDP is a **.) Return value + is a dnttpointer for the new field after all the skipped ones */ + +static dnttpointer +hpread_get_next_skip_over_anon_unions (skip_fields, field, fieldp, objfile) + int skip_fields; + dnttpointer field; + union dnttentry ** fieldp; + struct objfile * objfile; +{ + struct type * anon_type; + register int i; + int bitoffset; + char * name; + + for (i=0; i < skip_fields; i++) + { + /* Get type of item we're looking at now; recursively processes the types + of these intermediate items we skip over, so they aren't lost. */ + anon_type = hpread_type_lookup ((*fieldp)->dfield.type, objfile); + anon_type = CHECK_TYPEDEF (anon_type); + bitoffset = (*fieldp)->dfield.bitoffset; + name = VT (objfile) + (*fieldp)->dfield.name; + /* First skip over one item to avoid stack death on recursion */ + field = (*fieldp)->dfield.nextfield; + *fieldp = hpread_get_lntt (field.dnttp.index, objfile); + /* Do we have another anonymous union? If so, adjust the bitoffsets + of its members and skip over its members. */ + if ((TYPE_CODE (anon_type) == TYPE_CODE_UNION) && + (!name || STREQ (name, ""))) + { + hpread_adjust_bitoffsets (anon_type, bitoffset); + field = hpread_get_next_skip_over_anon_unions (TYPE_NFIELDS (anon_type), field, fieldp, objfile); + } + } + return field; +} + + diff --git a/gdb/hppa-tdep.c b/gdb/hppa-tdep.c index d0d5bf9..f784c21 100644 --- a/gdb/hppa-tdep.c +++ b/gdb/hppa-tdep.c @@ -23,6 +23,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "defs.h" #include "frame.h" +#include "bfd.h" #include "inferior.h" #include "value.h" @@ -33,9 +34,13 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include <sys/types.h> #endif +#include <dl.h> #include <sys/param.h> #include <signal.h> +#include <sys/ptrace.h> +#include <machine/save_state.h> + #ifdef COFF_ENCAPSULATE #include "a.out.encap.h" #else @@ -52,6 +57,14 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "symfile.h" #include "objfiles.h" +/* To support asking "What CPU is this?" */ +#include <unistd.h> + +/* To support detection of the pseudo-initial frame + that threads have. */ +#define THREAD_INITIAL_FRAME_SYMBOL "__pthread_exit" +#define THREAD_INITIAL_FRAME_SYM_LEN sizeof(THREAD_INITIAL_FRAME_SYMBOL) + static int extract_5_load PARAMS ((unsigned int)); static unsigned extract_5R_store PARAMS ((unsigned int)); @@ -87,6 +100,9 @@ static int restore_pc_queue PARAMS ((struct frame_saved_regs *)); static int hppa_alignof PARAMS ((struct type *)); +/* To support multi-threading and stepping. */ +int hppa_prepare_to_proceed PARAMS (()); + static int prologue_inst_adjust_sp PARAMS ((unsigned long)); static int is_branch PARAMS ((unsigned long)); @@ -108,8 +124,29 @@ static void internalize_unwinds PARAMS ((struct objfile *, asection *, unsigned int, unsigned int, CORE_ADDR)); static void pa_print_registers PARAMS ((char *, int, int)); +static void pa_strcat_registers PARAMS ((char *, int, int, GDB_FILE *)); +static void pa_register_look_aside PARAMS ((char *, int, long *)); static void pa_print_fp_reg PARAMS ((int)); +static void pa_strcat_fp_reg PARAMS ((int, GDB_FILE *, enum precision_type)); + +typedef struct { + struct minimal_symbol * msym; + CORE_ADDR solib_handle; +} args_for_find_stub; + +static CORE_ADDR cover_find_stub_with_shl_get PARAMS ((args_for_find_stub *)); + +static int is_pa_2 = 0; /* False */ + +/* This is declared in symtab.c; set to 1 in hp-symtab-read.c */ +extern int hp_som_som_object_present; + +/* In breakpoint.c */ +extern int exception_catchpoints_are_fragile; +/* This is defined in valops.c. */ +extern value_ptr +find_function_in_inferior PARAMS((char *)); /* Should call_function allocate stack space for a struct return? */ int @@ -376,7 +413,7 @@ internalize_unwinds (objfile, table, section, entries, size, text_offset) for (i = 0; i < entries; i++) { table[i].region_start = bfd_get_32 (objfile->obfd, - (bfd_byte *)buf); + (bfd_byte *)buf); table[i].region_start += text_offset; buf += 4; table[i].region_end = bfd_get_32 (objfile->obfd, (bfd_byte *)buf); @@ -395,11 +432,14 @@ internalize_unwinds (objfile, table, section, entries, size, text_offset) table[i].Args_stored = (tmp >> 15) & 0x1; table[i].Variable_Frame = (tmp >> 14) & 0x1; table[i].Separate_Package_Body = (tmp >> 13) & 0x1; - table[i].Frame_Extension_Millicode = (tmp >> 12 ) & 0x1; + table[i].Frame_Extension_Millicode = (tmp >> 12) & 0x1; table[i].Stack_Overflow_Check = (tmp >> 11) & 0x1; table[i].Two_Instruction_SP_Increment = (tmp >> 10) & 0x1; table[i].Ada_Region = (tmp >> 9) & 0x1; - table[i].reserved2 = (tmp >> 5) & 0xf; + table[i].cxx_info = (tmp >> 8) & 0x1; + table[i].cxx_try_catch = (tmp >> 7) & 0x1; + table[i].sched_entry_seq = (tmp >> 6) & 0x1; + table[i].reserved2 = (tmp >> 5) & 0x1; table[i].Save_SP = (tmp >> 4) & 0x1; table[i].Save_RP = (tmp >> 3) & 0x1; table[i].Save_MRP_in_frame = (tmp >> 2) & 0x1; @@ -410,8 +450,13 @@ internalize_unwinds (objfile, table, section, entries, size, text_offset) table[i].MPE_XL_interrupt_marker = (tmp >> 31) & 0x1; table[i].HP_UX_interrupt_marker = (tmp >> 30) & 0x1; table[i].Large_frame = (tmp >> 29) & 0x1; - table[i].reserved4 = (tmp >> 27) & 0x3; + table[i].Pseudo_SP_Set = (tmp >> 28) & 0x1; + table[i].reserved4 = (tmp >> 27) & 0x1; table[i].Total_frame_size = tmp & 0x7ffffff; + + /* Stub unwinds are handled elsewhere. */ + table[i].stub_unwind.stub_type = 0; + table[i].stub_unwind.padding = 0; } } } @@ -432,6 +477,7 @@ read_unwind_info (objfile) unsigned stub_entries, total_entries; CORE_ADDR text_offset; struct obj_unwind_info *ui; + obj_private_data_t *obj_private; text_offset = ANOFFSET (objfile->section_offsets, 0); ui = (struct obj_unwind_info *)obstack_alloc (&objfile->psymbol_obstack, @@ -461,8 +507,8 @@ read_unwind_info (objfile) if (elf_unwind_sec) { - elf_unwind_size = bfd_section_size (objfile->obfd, elf_unwind_sec); - elf_unwind_entries = elf_unwind_size / UNWIND_ENTRY_SIZE; + elf_unwind_size = bfd_section_size (objfile->obfd, elf_unwind_sec); /* purecov: deadcode */ + elf_unwind_entries = elf_unwind_size / UNWIND_ENTRY_SIZE; /* purecov: deadcode */ } else { @@ -486,8 +532,9 @@ read_unwind_info (objfile) total_size = total_entries * sizeof (struct unwind_table_entry); /* Allocate memory for the unwind table. */ - ui->table = obstack_alloc (&objfile->psymbol_obstack, total_size); - ui->last = total_entries - 1; + ui->table = (struct unwind_table_entry *) + obstack_alloc (&objfile->psymbol_obstack, total_size); + ui->last = total_entries - 1; /* Internalize the standard unwind entries. */ index = 0; @@ -520,8 +567,8 @@ read_unwind_info (objfile) (bfd_byte *) buf); ui->table[index].region_start += text_offset; buf += 4; - ui->table[index].stub_type = bfd_get_8 (objfile->obfd, - (bfd_byte *) buf); + ui->table[index].stub_unwind.stub_type = bfd_get_8 (objfile->obfd, + (bfd_byte *) buf); buf += 2; ui->table[index].region_end = ui->table[index].region_start + 4 * @@ -536,7 +583,18 @@ read_unwind_info (objfile) compare_unwind_entries); /* Keep a pointer to the unwind information. */ - objfile->obj_private = (PTR) ui; + if(objfile->obj_private == NULL) + { + obj_private = (obj_private_data_t *) + obstack_alloc(&objfile->psymbol_obstack, + sizeof(obj_private_data_t)); + obj_private->unwind_info = NULL; + obj_private->so_info = NULL; + + objfile->obj_private = (PTR) obj_private; + } + obj_private = (obj_private_data_t *)objfile->obj_private; + obj_private->unwind_info = ui; } /* Lookup the unwind (stack backtrace) info for the given PC. We search all @@ -551,16 +609,23 @@ find_unwind_entry(pc) int first, middle, last; struct objfile *objfile; + /* A function at address 0? Not in HP-UX! */ + if (pc == (CORE_ADDR) 0) + return NULL; + ALL_OBJFILES (objfile) { struct obj_unwind_info *ui; - - ui = OBJ_UNWIND_INFO (objfile); + ui = NULL; + if (objfile->obj_private) + ui = ((obj_private_data_t *)(objfile->obj_private))->unwind_info; if (!ui) { read_unwind_info (objfile); - ui = OBJ_UNWIND_INFO (objfile); + if (objfile->obj_private == NULL) + error ("Internal error reading unwind information."); /* purecov: deadcode */ + ui = ((obj_private_data_t *)(objfile->obj_private))->unwind_info; } /* First, check the cache */ @@ -657,8 +722,7 @@ pc_in_linker_stub (pc) ldsid (rp),r1 ; Get space associated with RP into r1 mtsp r1,sp ; Move it into space register 0 - be,n 0(sr0),rp) ; back to your regularly scheduled program - */ + be,n 0(sr0),rp) ; back to your regularly scheduled program */ /* Maximum known linker stub size is 4 instructions. Search forward from the given PC, then backward. */ @@ -729,6 +793,10 @@ find_proc_framesize (pc) struct unwind_table_entry *u; struct minimal_symbol *msym_us; + /* This may indicate a bug in our callers... */ + if (pc == (CORE_ADDR)0) + return -1; + u = find_unwind_entry (pc); if (!u) @@ -760,6 +828,10 @@ rp_saved (pc) { struct unwind_table_entry *u; + /* A function at, and thus a return PC from, address 0? Not in HP-UX! */ + if (pc == (CORE_ADDR) 0) + return 0; + u = find_unwind_entry (pc); if (!u) @@ -773,9 +845,9 @@ rp_saved (pc) if (u->Save_RP) return -20; - else if (u->stub_type != 0) + else if (u->stub_unwind.stub_type != 0) { - switch (u->stub_type) + switch (u->stub_unwind.stub_type) { case EXPORT: case IMPORT: @@ -801,7 +873,7 @@ frameless_function_invocation (frame) if (u == 0) return 0; - return (u->Total_frame_size == 0 && u->stub_type == 0); + return (u->Total_frame_size == 0 && u->stub_unwind.stub_type == 0); } CORE_ADDR @@ -818,7 +890,7 @@ saved_pc_after_call (frame) /* If PC is in a linker stub, then we need to dig the address the stub will return to out of the stack. */ u = find_unwind_entry (pc); - if (u && u->stub_type != 0) + if (u && u->stub_unwind.stub_type != 0) return FRAME_SAVED_PC (frame); else return pc; @@ -830,6 +902,9 @@ hppa_frame_saved_pc (frame) { CORE_ADDR pc = get_frame_pc (frame); struct unwind_table_entry *u; + CORE_ADDR old_pc; + int spun_around_loop = 0; + int rp_offset = 0; /* BSD, HPUX & OSF1 all lay out the hardware state in the same manner at the base of the frame in an interrupt handler. Registers within @@ -884,10 +959,12 @@ hppa_frame_saved_pc (frame) } else { - int rp_offset; + spun_around_loop = 0; + old_pc = pc; restart: rp_offset = rp_saved (pc); + /* Similar to code in frameless function case. If the next frame is a signal or interrupt handler, then dig the right information out of the saved register info. */ @@ -914,9 +991,15 @@ restart: pc = read_memory_integer (saved_regs.regs[RP_REGNUM], 4) & ~0x3; } else if (rp_offset == 0) - pc = read_register (RP_REGNUM) & ~0x3; + { + old_pc = pc; + pc = read_register (RP_REGNUM) & ~0x3; + } else - pc = read_memory_integer (frame->frame + rp_offset, 4) & ~0x3; + { + old_pc = pc; + pc = read_memory_integer (frame->frame + rp_offset, 4) & ~0x3; + } } /* If PC is inside a linker stub, then dig out the address the stub @@ -925,8 +1008,8 @@ restart: Don't do this for long branch stubs. Why? For some unknown reason _start is marked as a long branch stub in hpux10. */ u = find_unwind_entry (pc); - if (u && u->stub_type != 0 - && u->stub_type != LONG_BRANCH) + if (u && u->stub_unwind.stub_type != 0 + && u->stub_unwind.stub_type != LONG_BRANCH) { unsigned int insn; @@ -941,7 +1024,19 @@ restart: if ((insn & 0xfc00e000) == 0xe8000000) return (pc + extract_17 (insn) + 8) & ~0x3; else - goto restart; + { + if (old_pc == pc) + spun_around_loop++; + + if (spun_around_loop > 1) + { + /* We're just about to go around the loop again with + no more hope of success. Die. */ + error("Unable to find return pc for this frame"); + } + else + goto restart; + } } return pc; @@ -975,7 +1070,7 @@ init_extra_frame_info (fromleaf, frame) frame. (we always want frame->frame to point at the lowest address in the frame). */ if (framesize == -1) - frame->frame = read_register (FP_REGNUM); + frame->frame = TARGET_READ_FP (); else frame->frame -= framesize; return; @@ -996,7 +1091,7 @@ init_extra_frame_info (fromleaf, frame) sorts, and its base is the high address in its parent's frame. */ framesize = find_proc_framesize(frame->pc); if (framesize == -1) - frame->frame = read_register (FP_REGNUM); + frame->frame = TARGET_READ_FP (); else frame->frame = read_register (SP_REGNUM) - framesize; } @@ -1018,6 +1113,39 @@ frame_chain (frame) CORE_ADDR frame_base; struct frame_info *tmp_frame; + CORE_ADDR caller_pc; + + struct minimal_symbol *min_frame_symbol; + struct symbol *frame_symbol; + char *frame_symbol_name; + + /* If this is a threaded application, and we see the + routine "__pthread_exit", treat it as the stack root + for this thread. */ + min_frame_symbol = lookup_minimal_symbol_by_pc (frame->pc); + frame_symbol = find_pc_function(frame->pc); + + if ((min_frame_symbol != 0) /* && (frame_symbol == 0) */) + { + /* The test above for "no user function name" would defend + against the slim likelihood that a user might define a + routine named "__pthread_exit" and then try to debug it. + + If it weren't commented out, and you tried to debug the + pthread library itself, you'd get errors. + + So for today, we don't make that check. */ + frame_symbol_name = SYMBOL_NAME(min_frame_symbol); + if (frame_symbol_name != 0) { + if (0 == strncmp(frame_symbol_name, + THREAD_INITIAL_FRAME_SYMBOL, + THREAD_INITIAL_FRAME_SYM_LEN)) { + /* Pretend we've reached the bottom of the stack. */ + return (CORE_ADDR) 0; + } + } + } /* End of hacky code for threads. */ + /* Handle HPUX, BSD, and OSF1 style interrupt frames first. These are easy; at *sp we have a full save state strucutre which we can pull the old stack pointer from. Also see frame_saved_pc for @@ -1036,19 +1164,29 @@ frame_chain (frame) /* Get frame sizes for the current frame and the frame of the caller. */ my_framesize = find_proc_framesize (frame->pc); + caller_pc = FRAME_SAVED_PC(frame); + + /* If we can't determine the caller's PC, then it's not likely we can + really determine anything meaningful about its frame. We'll consider + this to be stack bottom. */ + if (caller_pc == (CORE_ADDR) 0) + return (CORE_ADDR) 0; + caller_framesize = find_proc_framesize (FRAME_SAVED_PC(frame)); /* If caller does not have a frame pointer, then its frame can be found at current_frame - caller_framesize. */ if (caller_framesize != -1) - return frame_base - caller_framesize; - + { + return frame_base - caller_framesize; + } /* Both caller and callee have frame pointers and are GCC compiled (SAVE_SP bit in unwind descriptor is on for both functions. The previous frame pointer is found at the top of the current frame. */ if (caller_framesize == -1 && my_framesize == -1) - return read_memory_integer (frame_base, 4); - + { + return read_memory_integer (frame_base, 4); + } /* Caller has a frame pointer, but callee does not. This is a little more difficult as GCC and HP C lay out locals and callee register save areas very differently. @@ -1076,8 +1214,14 @@ frame_chain (frame) think anyone has actually written any tools (not even "strip") which leave them out of an executable, so maybe this is a moot point. */ + /* ??rehrauer: Actually, it's quite possible to stepi your way into + code that doesn't have unwind entries. For example, stepping into + the dynamic linker will give you a PC that has none. Thus, I've + disabled this warning. */ +#if 0 warning ("Unable to find unwind for PC 0x%x -- Help!", tmp_frame->pc); - return 0; +#endif + return (CORE_ADDR) 0; } /* Entry_GR specifies the number of callee-saved general registers @@ -1097,7 +1241,9 @@ frame_chain (frame) if (u->Save_SP && !tmp_frame->signal_handler_caller && !pc_in_interrupt_handler (tmp_frame->pc)) - return read_memory_integer (tmp_frame->frame, 4); + { + return read_memory_integer (tmp_frame->frame, 4); + } /* %r3 was saved somewhere in the stack. Dig it out. */ else { @@ -1141,9 +1287,13 @@ frame_chain (frame) { u = find_unwind_entry (FRAME_SAVED_PC (frame)); if (!u) - return read_memory_integer (saved_regs.regs[FP_REGNUM], 4); + { + return read_memory_integer (saved_regs.regs[FP_REGNUM], 4); + } else - return frame_base - (u->Total_frame_size << 3); + { + return frame_base - (u->Total_frame_size << 3); + } } return read_memory_integer (saved_regs.regs[FP_REGNUM], 4); @@ -1169,14 +1319,18 @@ frame_chain (frame) { u = find_unwind_entry (FRAME_SAVED_PC (frame)); if (!u) - return read_memory_integer (saved_regs.regs[FP_REGNUM], 4); + { + return read_memory_integer (saved_regs.regs[FP_REGNUM], 4); + } else - return frame_base - (u->Total_frame_size << 3); + { + return frame_base - (u->Total_frame_size << 3); + } } /* The value in %r3 was never saved into the stack (thus %r3 still holds the value of the previous frame pointer). */ - return read_register (FP_REGNUM); + return TARGET_READ_FP (); } } @@ -1230,7 +1384,7 @@ hppa_frame_chain_valid (chain, thisframe) /* If this frame does not save SP, has no stack, isn't a stub, and doesn't "call" an interrupt routine or signal handler caller, then its not valid. */ - if (u->Save_SP || u->Total_frame_size || u->stub_type != 0 + if (u->Save_SP || u->Total_frame_size || u->stub_unwind.stub_type != 0 || (thisframe->next && thisframe->next->signal_handler_caller) || (next_u && next_u->HP_UX_interrupt_marker)) return 1; @@ -1242,11 +1396,10 @@ hppa_frame_chain_valid (chain, thisframe) } /* - * These functions deal with saving and restoring register state - * around a function call in the inferior. They keep the stack - * double-word aligned; eventually, on an hp700, the stack will have - * to be aligned to a 64-byte boundary. - */ + These functions deal with saving and restoring register state + around a function call in the inferior. They keep the stack + double-word aligned; eventually, on an hp700, the stack will have + to be aligned to a 64-byte boundary. */ void push_dummy_frame (inf_status) @@ -1295,7 +1448,7 @@ push_dummy_frame (inf_status) int_buffer = read_register (RP_REGNUM) | 0x3; write_memory (sp - 20, (char *)&int_buffer, 4); - int_buffer = read_register (FP_REGNUM); + int_buffer = TARGET_READ_FP (); write_memory (sp, (char *)&int_buffer, 4); write_register (FP_REGNUM, sp); @@ -1446,9 +1599,8 @@ hppa_pop_frame () flush_cached_frames (); } -/* - * After returning to a dummy on the stack, restore the instruction - * queue space registers. */ +/* After returning to a dummy on the stack, restore the instruction + queue space registers. */ static int restore_pc_queue (fsr) @@ -1463,17 +1615,15 @@ restore_pc_queue (fsr) write_register (PCOQ_HEAD_REGNUM, pc + 4); write_register (PCOQ_TAIL_REGNUM, pc + 8); - /* - * HPUX doesn't let us set the space registers or the space - * registers of the PC queue through ptrace. Boo, hiss. - * Conveniently, the call dummy has this sequence of instructions - * after the break: - * mtsp r21, sr0 - * ble,n 0(sr0, r22) - * - * So, load up the registers and single step until we are in the - * right place. - */ + /* HPUX doesn't let us set the space registers or the space + registers of the PC queue through ptrace. Boo, hiss. + Conveniently, the call dummy has this sequence of instructions + after the break: + mtsp r21, sr0 + ble,n 0(sr0, r22) + + So, load up the registers and single step until we are in the + right place. */ write_register (21, read_memory_integer (fsr->regs[PCSQ_HEAD_REGNUM], 4)); write_register (22, new_pc); @@ -1504,6 +1654,7 @@ restore_pc_queue (fsr) return 1; } +#if 0 CORE_ADDR hppa_push_arguments (nargs, args, sp, struct_return, struct_addr) int nargs; @@ -1519,14 +1670,19 @@ hppa_push_arguments (nargs, args, sp, struct_return, struct_addr) for (i = 0; i < nargs; i++) { + int x = 0; + /* cum is the sum of the lengths in bytes of + the arguments seen so far */ cum += TYPE_LENGTH (VALUE_TYPE (args[i])); /* value must go at proper alignment. Assume alignment is a - power of two.*/ + power of two. */ alignment = hppa_alignof (VALUE_TYPE (args[i])); + if (cum % alignment) cum = (cum + alignment) & -alignment; offset[i] = -cum; + } sp += max ((cum + 7) & -8, 16); @@ -1538,16 +1694,240 @@ hppa_push_arguments (nargs, args, sp, struct_return, struct_addr) write_register (28, struct_addr); return sp + 32; } +#endif -/* - * Insert the specified number of args and function address - * into a call sequence of the above form stored at DUMMYNAME. - * - * On the hppa we need to call the stack dummy through $$dyncall. - * Therefore our version of FIX_CALL_DUMMY takes an extra argument, - * real_pc, which is the location where gdb should start up the - * inferior to do the function call. - */ +/* elz: I am rewriting this function, because the one above is a very + obscure piece of code. + This function pushes the arguments on the stack. The stack grows up + on the PA. + Each argument goes in one (or more) word (4 bytes) on the stack. + The first four words for the args must be allocated, even if they + are not used. + The 'topmost' arg is arg0, the 'bottom-most' is arg3. (if you think of + them as 1 word long). + Below these there can be any number of arguments, as needed by the function. + If an arg is bigger than one word, it will be written on the stack + occupying as many words as needed. Args that are bigger than 64bits + are not copied on the stack, a pointer is passed instead. + + On top of the arg0 word there are other 8 words (32bytes) which are used + for other purposes */ + +CORE_ADDR +hppa_push_arguments (nargs, args, sp, struct_return, struct_addr) + int nargs; + value_ptr *args; + CORE_ADDR sp; + int struct_return; + CORE_ADDR struct_addr; +{ + /* array of arguments' offsets */ + int *offset = (int *)alloca(nargs * sizeof (int)); + /* array of arguments' lengths: real lengths in bytes, not aligned to word size */ + int *lengths = (int *)alloca(nargs * sizeof (int)); + + int bytes_reserved; /* this is the number of bytes on the stack occupied by an + argument. This will be always a multiple of 4 */ + + int cum_bytes_reserved = 0; /* this is the total number of bytes reserved by the args + seen so far. It is a multiple of 4 always */ + int cum_bytes_aligned = 0; /* same as above, but aligned on 8 bytes */ + int i; + + /* When an arg does not occupy a whole word, for instance in bitfields: + if the arg is x bits (0<x<32), it must be written + starting from the (x-1)-th position down until the 0-th position. + It is enough to align it to the word. */ + /* if an arg occupies 8 bytes, it must be aligned on the 64-bits + high order word in odd arg word. */ + /* if an arg is larger than 64 bits, we need to pass a pointer to it, and + copy the actual value on the stack, so that the callee can play with it. + This is taken care of in valops.c in the call_function_by_hand function. + The argument that is received in this function here has already be converted + to a pointer to whatever is needed, so that it just can be pushed + as a word argument */ + + for (i = 0; i < nargs; i++) + { + + lengths[i] = TYPE_LENGTH (VALUE_TYPE (args[i])); + + if (lengths[i] % 4) + bytes_reserved = (lengths[i] / 4) * 4 + 4; + else + bytes_reserved = lengths[i]; + + offset[i] = cum_bytes_reserved + lengths[i]; + + if ((bytes_reserved == 8) && (offset[i] % 8)) /* if 64-bit arg is not 64 bit aligned */ + { + int new_offset=0; + /* bytes_reserved is already aligned to the word, so we put it at one word + more down the stack. This will leave one empty word on the + stack, and one unused register. This is OK, see the calling + convention doc */ + /* the offset may have to be moved to the corresponding position + one word down the stack, to maintain + alignment. */ + new_offset = (offset[i] / 8) * 8 + 8; + if ((new_offset - offset[i]) >=4) + { + bytes_reserved += 4; + offset[i] += 4; + } + } + + cum_bytes_reserved += bytes_reserved; + + } + + /* now move up the sp to reserve at least 4 words required for the args, + or more than this if needed */ + /* wee also need to keep the sp aligned to 8 bytes */ + cum_bytes_aligned = STACK_ALIGN (cum_bytes_reserved); + sp += max (cum_bytes_aligned, 16); + + /* now write each of the args at the proper offset down the stack */ + for (i = 0; i < nargs; i++) + write_memory (sp - offset[i], VALUE_CONTENTS (args[i]), lengths[i]); + + + /* if a structure has to be returned, set up register 28 to hold its address */ + if (struct_return) + write_register (28, struct_addr); + + /* the stack will have other 8 words on top of the args */ + return sp + 32; +} + + +/* elz: this function returns a value which is built looking at the given address. + It is called from call_function_by_hand, in case we need to return a + value which is larger than 64 bits, and it is stored in the stack rather than + in the registers r28 and r29 or fr4. + This function does the same stuff as value_being_returned in values.c, but + gets the value from the stack rather than from the buffer where all the + registers were saved when the function called completed. */ +value_ptr +hppa_value_returned_from_stack (valtype , addr) + register struct type *valtype; + CORE_ADDR addr; +{ + register value_ptr val; + + val = allocate_value (valtype); + CHECK_TYPEDEF (valtype); + target_read_memory(addr, VALUE_CONTENTS_RAW (val), TYPE_LENGTH (valtype)); + + return val; +} + + + +/* elz: Used to lookup a symbol in the shared libraries. + This function calls shl_findsym, indirectly through a + call to __d_shl_get. __d_shl_get is in end.c, which is always + linked in by the hp compilers/linkers. + The call to shl_findsym cannot be made directly because it needs + to be active in target address space. + inputs: - minimal symbol pointer for the function we want to look up + - address in target space of the descriptor for the library + where we want to look the symbol up. + This address is retrieved using the + som_solib_get_solib_by_pc function (somsolib.c). + output: - real address in the library of the function. + note: the handle can be null, in which case shl_findsym will look for + the symbol in all the loaded shared libraries. + files to look at if you need reference on this stuff: + dld.c, dld_shl_findsym.c + end.c + man entry for shl_findsym */ + +CORE_ADDR +find_stub_with_shl_get(function, handle) + struct minimal_symbol *function; + CORE_ADDR handle; +{ + struct symbol *get_sym, *symbol2; + struct minimal_symbol *buff_minsym, *msymbol; + struct type *ftype; + value_ptr *args; + value_ptr funcval, val; + + int x, namelen, err_value, tmp = -1; + CORE_ADDR endo_buff_addr, value_return_addr, errno_return_addr; + CORE_ADDR stub_addr; + + + args = (value_ptr *) alloca (sizeof (value_ptr) * 8); /* 6 for the arguments and one null one??? */ + funcval = find_function_in_inferior("__d_shl_get"); + get_sym = lookup_symbol("__d_shl_get", NULL, VAR_NAMESPACE, NULL, NULL); + buff_minsym = lookup_minimal_symbol("__buffer", NULL, NULL); + msymbol = lookup_minimal_symbol ("__shldp", NULL, NULL); + symbol2 = lookup_symbol("__shldp", NULL, VAR_NAMESPACE, NULL, NULL); + endo_buff_addr = SYMBOL_VALUE_ADDRESS (buff_minsym); + namelen = strlen(SYMBOL_NAME(function)); + value_return_addr = endo_buff_addr + namelen; + ftype = check_typedef(SYMBOL_TYPE(get_sym)); + + /* do alignment */ + if ((x=value_return_addr % 64) !=0) + value_return_addr = value_return_addr + 64 - x; + + errno_return_addr = value_return_addr + 64; + + + /* set up stuff needed by __d_shl_get in buffer in end.o */ + + target_write_memory(endo_buff_addr, SYMBOL_NAME(function), namelen); + + target_write_memory(value_return_addr, (char *) &tmp, 4); + + target_write_memory(errno_return_addr, (char *) &tmp, 4); + + target_write_memory(SYMBOL_VALUE_ADDRESS(msymbol), + (char *)&handle, 4); + + /* now prepare the arguments for the call */ + + args[0] = value_from_longest (TYPE_FIELD_TYPE(ftype, 0), 12); + args[1] = value_from_longest (TYPE_FIELD_TYPE(ftype, 1), SYMBOL_VALUE_ADDRESS(msymbol)); + args[2] = value_from_longest (TYPE_FIELD_TYPE(ftype, 2), endo_buff_addr); + args[3] = value_from_longest (TYPE_FIELD_TYPE(ftype, 3), TYPE_PROCEDURE); + args[4] = value_from_longest (TYPE_FIELD_TYPE(ftype, 4), value_return_addr); + args[5] = value_from_longest (TYPE_FIELD_TYPE(ftype, 5), errno_return_addr); + + /* now call the function */ + + val = call_function_by_hand(funcval, 6, args); + + /* now get the results */ + + target_read_memory(errno_return_addr, (char *) &err_value, sizeof(err_value)); + + target_read_memory(value_return_addr, (char *) &stub_addr, sizeof(stub_addr)); + if (stub_addr <= 0) + error("call to __d_shl_get failed, error code is %d", err_value); /* purecov: deadcode */ + + return(stub_addr); +} + +/* Cover routine for find_stub_with_shl_get to pass to catch_errors */ +static CORE_ADDR +cover_find_stub_with_shl_get (args) + args_for_find_stub * args; +{ + return find_stub_with_shl_get (args->msym, args->solib_handle); +} + + +/* Insert the specified number of args and function address + into a call sequence of the above form stored at DUMMYNAME. + + On the hppa we need to call the stack dummy through $$dyncall. + Therefore our version of FIX_CALL_DUMMY takes an extra argument, + real_pc, which is the location where gdb should start up the + inferior to do the function call. */ CORE_ADDR hppa_fix_call_dummy (dummy, pc, fun, nargs, args, type, gcc_p) @@ -1564,11 +1944,13 @@ hppa_fix_call_dummy (dummy, pc, fun, nargs, args, type, gcc_p) struct minimal_symbol *trampoline; int flags = read_register (FLAGS_REGNUM); struct unwind_table_entry *u; + CORE_ADDR new_stub=0; + CORE_ADDR solib_handle=0; trampoline = NULL; msymbol = lookup_minimal_symbol ("$$dyncall", NULL, NULL); if (msymbol == NULL) - error ("Can't find an address for $$dyncall trampoline"); + error ("Can't find an address for $$dyncall trampoline"); /* purecov: deadcode */ dyncall_addr = SYMBOL_VALUE_ADDRESS (msymbol); @@ -1593,7 +1975,16 @@ hppa_fix_call_dummy (dummy, pc, fun, nargs, args, type, gcc_p) function in a shared library. We must call an import stub rather than the export stub or real function for lazy binding to work correctly. */ - if (som_solib_get_got_by_pc (fun)) + + /* elz: let's see if fun is in a shared library */ + solib_handle = som_solib_get_solib_by_pc(fun); + + /* elz: for 10.30 and 11.00 the calls via __d_plt_call cannot be made + via import stubs, only via plables, so this code here becomes useless. + On 10.20, the plables mechanism works too, so we just ignore this import + stub stuff */ +#if 0 + if (solib_handle) { struct objfile *objfile; struct minimal_symbol *funsymbol, *stub_symbol; @@ -1619,7 +2010,7 @@ hppa_fix_call_dummy (dummy, pc, fun, nargs, args, type, gcc_p) /* It must also be an import stub. */ u = find_unwind_entry (SYMBOL_VALUE (stub_symbol)); - if (!u || u->stub_type != IMPORT) + if (!u || u->stub_unwind.stub_type != IMPORT) continue; /* OK. Looks like the correct import stub. */ @@ -1630,6 +2021,7 @@ hppa_fix_call_dummy (dummy, pc, fun, nargs, args, type, gcc_p) if (newfun == 0) write_register (19, som_solib_get_got_by_pc (fun)); } +#endif /* end of if 0 */ #endif } @@ -1639,8 +2031,28 @@ hppa_fix_call_dummy (dummy, pc, fun, nargs, args, type, gcc_p) the value in sp-24 will get fried and you end up returning to the wrong location. You can't call the import stub directly as the code to bind the PLT entry to a function can't return to a stack address.) */ + + /* elz: + There does not have to be an import stub to call a routine in a + different load module (note: a "load module" is an a.out or a shared + library). If you call a routine indirectly, going through $$dyncall (or + $$dyncall_external), you won't go through an import stub. Import stubs + are only used for direct calls to an imported routine. + + What you (wdb) need is to go through $$dyncall with a proper plabel for + the imported routine. shl_findsym() returns you the address of a plabel + suitable for use in making an indirect call through, e.g., through + $$dyncall. + This is taken care below with the call to find_stub_.... */ +#if 0 + /* elz: this check here is not necessary if we are going to call stuff through + plabels only, we just now check whether the function we call is in a shlib */ u = find_unwind_entry (fun); - if (u && u->stub_type == IMPORT) + + if (u && u->stub_unwind.stub_type == IMPORT || + (!(u && u->stub_unwind.stub_type == IMPORT) && solib_handle)) +#endif /* 0 */ + if (solib_handle) { CORE_ADDR new_fun; @@ -1651,25 +2063,47 @@ hppa_fix_call_dummy (dummy, pc, fun, nargs, args, type, gcc_p) trampoline = lookup_minimal_symbol ("__d_plt_call", NULL, NULL); if (trampoline == NULL) - error ("Can't find an address for __d_plt_call or __gcc_plt_call trampoline"); - + { + error ("Can't find an address for __d_plt_call or __gcc_plt_call trampoline\nSuggest linking executable with -g (links in /opt/langtools/lib/end.o)"); + } /* This is where sr4export will jump to. */ new_fun = SYMBOL_VALUE_ADDRESS (trampoline); if (strcmp (SYMBOL_NAME (trampoline), "__d_plt_call") == 0) { + /* if the function is in a shared library, but we have no import sub for + it, we need to get the plabel from a call to __d_shl_get, which is a + function in end.o. To call this function we need to set up various things */ + + /* actually now we just use the plabel any time we make the call, + because on 10.30 and 11.00 this is the only acceptable way. This also + works fine for 10.20 */ + /* if (!(u && u->stub_unwind.stub_type == IMPORT) && solib_handle) */ + { + struct minimal_symbol *fmsymbol = lookup_minimal_symbol_by_pc(fun); + + new_stub = find_stub_with_shl_get(fmsymbol, solib_handle); + + if (new_stub == NULL) + error("Can't find an import stub for %s", SYMBOL_NAME(fmsymbol)); /* purecov: deadcode */ + } + /* We have to store the address of the stub in __shlib_funcptr. */ - msymbol = lookup_minimal_symbol ("__shlib_funcptr", NULL, - (struct objfile *)NULL); - if (msymbol == NULL) - error ("Can't find an address for __shlib_funcptr"); + msymbol = lookup_minimal_symbol ("__shlib_funcptr", NULL, + (struct objfile *)NULL); + if (msymbol == NULL) + error ("Can't find an address for __shlib_funcptr"); /* purecov: deadcode */ - target_write_memory (SYMBOL_VALUE_ADDRESS (msymbol), (char *)&fun, 4); + /* if (new_stub != NULL) */ + target_write_memory (SYMBOL_VALUE_ADDRESS (msymbol), (char *)&new_stub, 4); + /* this is no longer used */ + /* else + target_write_memory (SYMBOL_VALUE_ADDRESS (msymbol), (char *)&fun, 4); */ /* We want sr4export to call __d_plt_call, so we claim it is the final target. Clear trampoline. */ - fun = new_fun; - trampoline = NULL; + fun = new_fun; + trampoline = NULL; } } @@ -1701,7 +2135,7 @@ hppa_fix_call_dummy (dummy, pc, fun, nargs, args, type, gcc_p) { msymbol = lookup_minimal_symbol ("_sr4export", NULL, NULL); if (msymbol == NULL) - error ("Can't find an address for _sr4export trampoline"); + error ("Can't find an address for _sr4export trampoline"); /* purecov: deadcode */ trampoline_addr = SYMBOL_VALUE_ADDRESS (msymbol); } @@ -1749,6 +2183,28 @@ hppa_fix_call_dummy (dummy, pc, fun, nargs, args, type, gcc_p) } + + + +/* If the pid is in a syscall, then the FP register is not readable. + We'll return zero in that case, rather than attempting to read it + and cause a warning. */ +CORE_ADDR +target_read_fp (pid) + int pid; +{ + int flags = read_register (FLAGS_REGNUM); + + if (flags & 2) { + return (CORE_ADDR) 0; + } + + /* This is the only site that may directly read_register () the FP + register. All others must use TARGET_READ_FP (). */ + return read_register (FP_REGNUM); +} + + /* Get the PC from %r31 if currently in a syscall. Also mask out privilege bits. */ @@ -1812,7 +2268,8 @@ hppa_alignof (type) for (i = 0; i < TYPE_NFIELDS (type); i++) { /* Bit fields have no real alignment. */ - if (!TYPE_FIELD_BITPOS (type, i)) + /* if (!TYPE_FIELD_BITPOS (type, i)) */ + if (!TYPE_FIELD_BITSIZE (type, i)) /* elz: this should be bitsize */ { align = hppa_alignof (TYPE_FIELD_TYPE (type, i)); max_align = max (max_align, align); @@ -1833,18 +2290,177 @@ pa_do_registers_info (regnum, fpregs) { char raw_regs [REGISTER_BYTES]; int i; - + + /* Make a copy of gdb's save area (may cause actual + reads from the target). */ for (i = 0; i < NUM_REGS; i++) read_relative_register_raw_bytes (i, raw_regs + REGISTER_BYTE (i)); + if (regnum == -1) pa_print_registers (raw_regs, regnum, fpregs); - else if (regnum < FP0_REGNUM) - printf_unfiltered ("%s %x\n", REGISTER_NAME (regnum), *(long *)(raw_regs + - REGISTER_BYTE (regnum))); + else if (regnum < FP4_REGNUM) { + long reg_val[2]; + + /* Why is the value not passed through "extract_signed_integer" + as in "pa_print_registers" below? */ + pa_register_look_aside(raw_regs, regnum, ®_val[0]); + + if(!is_pa_2) { + printf_unfiltered ("%s %x\n", REGISTER_NAME (regnum), reg_val[1]); + } + else { + /* Fancy % formats to prevent leading zeros. */ + if(reg_val[0] == 0) + printf_unfiltered("%s %x\n", REGISTER_NAME (regnum), reg_val[1]); + else + printf_unfiltered("%s %x%8.8x\n", REGISTER_NAME (regnum), + reg_val[0], reg_val[1]); + } + } else + /* Note that real floating point values only start at + FP4_REGNUM. FP0 and up are just status and error + registers, which have integral (bit) values. */ pa_print_fp_reg (regnum); } +/********** new function ********************/ +void +pa_do_strcat_registers_info (regnum, fpregs, stream, precision) + int regnum; + int fpregs; + GDB_FILE *stream; + enum precision_type precision; +{ + char raw_regs [REGISTER_BYTES]; + int i; + + /* Make a copy of gdb's save area (may cause actual + reads from the target). */ + for (i = 0; i < NUM_REGS; i++) + read_relative_register_raw_bytes (i, raw_regs + REGISTER_BYTE (i)); + + if (regnum == -1) + pa_strcat_registers (raw_regs, regnum, fpregs, stream); + + else if (regnum < FP4_REGNUM) { + long reg_val[2]; + + /* Why is the value not passed through "extract_signed_integer" + as in "pa_print_registers" below? */ + pa_register_look_aside(raw_regs, regnum, ®_val[0]); + + if(!is_pa_2) { + fprintf_unfiltered (stream, "%s %x", REGISTER_NAME (regnum), reg_val[1]); + } + else { + /* Fancy % formats to prevent leading zeros. */ + if(reg_val[0] == 0) + fprintf_unfiltered(stream, "%s %x", REGISTER_NAME (regnum), + reg_val[1]); + else + fprintf_unfiltered(stream, "%s %x%8.8x", REGISTER_NAME (regnum), + reg_val[0], reg_val[1]); + } + } + else + /* Note that real floating point values only start at + FP4_REGNUM. FP0 and up are just status and error + registers, which have integral (bit) values. */ + pa_strcat_fp_reg (regnum, stream, precision); +} + +/* If this is a PA2.0 machine, fetch the real 64-bit register + value. Otherwise use the info from gdb's saved register area. + + Note that reg_val is really expected to be an array of longs, + with two elements. */ +static void +pa_register_look_aside(raw_regs, regnum, raw_val) + char *raw_regs; + int regnum; + long *raw_val; +{ + static int know_which = 0; /* False */ + + int regaddr; + unsigned int offset; + register int i; + int start; + + + char buf[MAX_REGISTER_RAW_SIZE]; + long long reg_val; + + if(!know_which) { + if(CPU_PA_RISC2_0 == sysconf(_SC_CPU_VERSION)) { + is_pa_2 = (1==1); + } + + know_which = 1; /* True */ + } + + raw_val[0] = 0; + raw_val[1] = 0; + + if(!is_pa_2) { + raw_val[1] = *(long *)(raw_regs + REGISTER_BYTE(regnum)); + return; + } + + /* Code below copied from hppah-nat.c, with fixes for wide + registers, using different area of save_state, etc. */ + if(regnum == FLAGS_REGNUM || regnum >= FP0_REGNUM) { + /* Use narrow regs area of save_state and default macro. */ + offset = U_REGS_OFFSET; + regaddr = register_addr(regnum, offset); + start = 1; + } + else { + /* Use wide regs area, and calculate registers as 8 bytes wide. + + We'd like to do this, but current version of "C" doesn't + permit "offsetof": + + offset = offsetof(save_state_t, ss_wide); + + Note that to avoid "C" doing typed pointer arithmetic, we + have to cast away the type in our offset calculation: + otherwise we get an offset of 1! */ + save_state_t temp; + offset = ((int) &temp.ss_wide) - ((int) &temp); + regaddr = offset + regnum * 8; + start = 0; + } + + for(i = start; i < 2; i++) + { + errno = 0; + raw_val[i] = call_ptrace (PT_RUREGS, inferior_pid, + (PTRACE_ARG3_TYPE) regaddr, 0); + if (errno != 0) + { + /* Warning, not error, in case we are attached; sometimes the + kernel doesn't let us at the registers. */ + char *err = safe_strerror (errno); + char *msg = alloca (strlen (err) + 128); + sprintf (msg, "reading register %s: %s", REGISTER_NAME (regnum), err); + warning (msg); + goto error_exit; + } + + regaddr += sizeof (long); + } + + if (regnum == PCOQ_HEAD_REGNUM || regnum == PCOQ_TAIL_REGNUM) + raw_val[1] &= ~0x3; /* I think we're masking out space bits */ + +error_exit: + ; +} + +/* "Info all-reg" command */ + static void pa_print_registers (raw_regs, regnum, fpregs) char *raw_regs; @@ -1852,30 +2468,101 @@ pa_print_registers (raw_regs, regnum, fpregs) int fpregs; { int i,j; - long val; + long raw_val[2]; /* Alas, we are compiled so that "long long" is 32 bits */ + long long_val; for (i = 0; i < 18; i++) { for (j = 0; j < 4; j++) { - val = - extract_signed_integer (raw_regs + REGISTER_BYTE (i+(j*18)), 4); - printf_unfiltered ("%8.8s: %8x ", REGISTER_NAME (i+(j*18)), val); + /* Q: Why is the value passed through "extract_signed_integer", + while above, in "pa_do_registers_info" it isn't? + A: ? */ + pa_register_look_aside(raw_regs, i+(j*18), &raw_val[0]); + + /* Even fancier % formats to prevent leading zeros + and still maintain the output in columns. */ + if(!is_pa_2) { + /* Being big-endian, on this machine the low bits + (the ones we want to look at) are in the second longword. */ + long_val = extract_signed_integer (&raw_val[1], 4); + printf_filtered ("%8.8s: %8x ", + REGISTER_NAME (i+(j*18)), long_val); + } + else { + /* raw_val = extract_signed_integer(&raw_val, 8); */ + if(raw_val[0] == 0) + printf_filtered("%8.8s: %8x ", + REGISTER_NAME (i+(j*18)), raw_val[1]); + else + printf_filtered("%8.8s: %8x%8.8x ", REGISTER_NAME (i+(j*18)), + raw_val[0], raw_val[1]); + } } printf_unfiltered ("\n"); } if (fpregs) - for (i = 72; i < NUM_REGS; i++) + for (i = FP4_REGNUM; i < NUM_REGS; i++) /* FP4_REGNUM == 72 */ pa_print_fp_reg (i); } +/************* new function ******************/ +static void +pa_strcat_registers (raw_regs, regnum, fpregs, stream) + char *raw_regs; + int regnum; + int fpregs; + GDB_FILE *stream; +{ + int i,j; + long raw_val[2]; /* Alas, we are compiled so that "long long" is 32 bits */ + long long_val; + enum precision_type precision; + + precision = unspecified_precision; + + for (i = 0; i < 18; i++) + { + for (j = 0; j < 4; j++) + { + /* Q: Why is the value passed through "extract_signed_integer", + while above, in "pa_do_registers_info" it isn't? + A: ? */ + pa_register_look_aside(raw_regs, i+(j*18), &raw_val[0]); + + /* Even fancier % formats to prevent leading zeros + and still maintain the output in columns. */ + if(!is_pa_2) { + /* Being big-endian, on this machine the low bits + (the ones we want to look at) are in the second longword. */ + long_val = extract_signed_integer(&raw_val[1], 4); + fprintf_filtered (stream, "%8.8s: %8x ", REGISTER_NAME (i+(j*18)), long_val); + } + else { + /* raw_val = extract_signed_integer(&raw_val, 8); */ + if(raw_val[0] == 0) + fprintf_filtered(stream, "%8.8s: %8x ", REGISTER_NAME (i+(j*18)), + raw_val[1]); + else + fprintf_filtered(stream, "%8.8s: %8x%8.8x ", REGISTER_NAME (i+(j*18)), + raw_val[0], raw_val[1]); + } + } + fprintf_unfiltered (stream, "\n"); + } + + if (fpregs) + for (i = FP4_REGNUM; i < NUM_REGS; i++) /* FP4_REGNUM == 72 */ + pa_strcat_fp_reg (i, stream, precision); +} + static void pa_print_fp_reg (i) int i; { - unsigned char raw_buffer[MAX_REGISTER_RAW_SIZE]; - unsigned char virtual_buffer[MAX_REGISTER_VIRTUAL_SIZE]; + char raw_buffer[MAX_REGISTER_RAW_SIZE]; + char virtual_buffer[MAX_REGISTER_VIRTUAL_SIZE]; /* Get 32bits of data. */ read_relative_register_raw_bytes (i, raw_buffer); @@ -1913,6 +2600,47 @@ pa_print_fp_reg (i) } } +/*************** new function ***********************/ +static void +pa_strcat_fp_reg (i, stream, precision) + int i; + GDB_FILE *stream; + enum precision_type precision; +{ + char raw_buffer[MAX_REGISTER_RAW_SIZE]; + char virtual_buffer[MAX_REGISTER_VIRTUAL_SIZE]; + + fputs_filtered (REGISTER_NAME (i), stream); + print_spaces_filtered (8 - strlen (REGISTER_NAME (i)), stream); + + /* Get 32bits of data. */ + read_relative_register_raw_bytes (i, raw_buffer); + + /* Put it in the buffer. No conversions are ever necessary. */ + memcpy (virtual_buffer, raw_buffer, REGISTER_RAW_SIZE (i)); + + if (precision == double_precision && (i % 2) == 0) + { + + char raw_buf[MAX_REGISTER_RAW_SIZE]; + + /* Get the data in raw format for the 2nd half. */ + read_relative_register_raw_bytes (i + 1, raw_buf); + + /* Copy it into the appropriate part of the virtual buffer. */ + memcpy (virtual_buffer + REGISTER_RAW_SIZE(i), raw_buf, REGISTER_RAW_SIZE (i)); + + val_print (builtin_type_double, virtual_buffer, 0, 0 , stream, 0, + 1, 0, Val_pretty_default); + + } + else { + val_print (REGISTER_VIRTUAL_TYPE (i), virtual_buffer, 0, 0, stream, 0, + 1, 0, Val_pretty_default); + } + +} + /* Return one if PC is in the call path of a trampoline, else return zero. Note we return one for *any* call trampoline (long-call, arg-reloc), not @@ -1960,23 +2688,23 @@ in_solib_call_trampoline (pc, name) return 0; /* If this isn't a linker stub, then return now. */ - if (u->stub_type == 0) + if (u->stub_unwind.stub_type == 0) return 0; /* By definition a long-branch stub is a call stub. */ - if (u->stub_type == LONG_BRANCH) + if (u->stub_unwind.stub_type == LONG_BRANCH) return 1; /* The call and return path execute the same instructions within an IMPORT stub! So an IMPORT stub is both a call and return trampoline. */ - if (u->stub_type == IMPORT) + if (u->stub_unwind.stub_type == IMPORT) return 1; /* Parameter relocation stubs always have a call path and may have a return path. */ - if (u->stub_type == PARAMETER_RELOCATION - || u->stub_type == EXPORT) + if (u->stub_unwind.stub_type == PARAMETER_RELOCATION + || u->stub_unwind.stub_type == EXPORT) { CORE_ADDR addr; @@ -1998,12 +2726,12 @@ in_solib_call_trampoline (pc, name) } /* Should never happen. */ - warning ("Unable to find branch in parameter relocation stub.\n"); - return 0; + warning ("Unable to find branch in parameter relocation stub.\n"); /* purecov: deadcode */ + return 0; /* purecov: deadcode */ } /* Unknown stub type. For now, just return zero. */ - return 0; + return 0; /* purecov: deadcode */ } /* Return one if PC is in the return path of a trampoline, else return zero. @@ -2026,19 +2754,19 @@ in_solib_return_trampoline (pc, name) /* If this isn't a linker stub or it's just a long branch stub, then return zero. */ - if (u->stub_type == 0 || u->stub_type == LONG_BRANCH) + if (u->stub_unwind.stub_type == 0 || u->stub_unwind.stub_type == LONG_BRANCH) return 0; /* The call and return path execute the same instructions within an IMPORT stub! So an IMPORT stub is both a call and return trampoline. */ - if (u->stub_type == IMPORT) + if (u->stub_unwind.stub_type == IMPORT) return 1; /* Parameter relocation stubs always have a call path and may have a return path. */ - if (u->stub_type == PARAMETER_RELOCATION - || u->stub_type == EXPORT) + if (u->stub_unwind.stub_type == PARAMETER_RELOCATION + || u->stub_unwind.stub_type == EXPORT) { CORE_ADDR addr; @@ -2060,12 +2788,12 @@ in_solib_return_trampoline (pc, name) } /* Should never happen. */ - warning ("Unable to find branch in parameter relocation stub.\n"); - return 0; + warning ("Unable to find branch in parameter relocation stub.\n"); /* purecov: deadcode */ + return 0; /* purecov: deadcode */ } /* Unknown stub type. For now, just return zero. */ - return 0; + return 0; /* purecov: deadcode */ } @@ -2086,6 +2814,17 @@ in_solib_return_trampoline (pc, name) calling an argument relocation stub. It even handles some stubs used in dynamic executables. */ +# if 0 +CORE_ADDR +skip_trampoline_code (pc, name) + CORE_ADDR pc; + char *name; +{ + return find_solib_trampoline_target(pc); +} + +#endif + CORE_ADDR skip_trampoline_code (pc, name) CORE_ADDR pc; @@ -2094,10 +2833,12 @@ skip_trampoline_code (pc, name) long orig_pc = pc; long prev_inst, curr_inst, loc; static CORE_ADDR dyncall = 0; + static CORE_ADDR dyncall_external = 0; static CORE_ADDR sr4export = 0; struct minimal_symbol *msym; struct unwind_table_entry *u; + /* FIXME XXX - dyncall and sr4export must be initialized whenever we get a new exec file */ @@ -2110,6 +2851,15 @@ skip_trampoline_code (pc, name) dyncall = -1; } + if (!dyncall_external) + { + msym = lookup_minimal_symbol ("$$dyncall_external", NULL, NULL); + if (msym) + dyncall_external = SYMBOL_VALUE_ADDRESS (msym); + else + dyncall_external = -1; + } + if (!sr4export) { msym = lookup_minimal_symbol ("_sr4export", NULL, NULL); @@ -2131,6 +2881,11 @@ skip_trampoline_code (pc, name) if (pc & 0x2) pc = (CORE_ADDR) read_memory_integer (pc & ~0x3, 4); } + if (pc == dyncall_external) + { + pc = (CORE_ADDR) read_register (22); + pc = (CORE_ADDR) read_memory_integer (pc & ~0x3, 4); + } else if (pc == sr4export) pc = (CORE_ADDR) (read_register (22)); @@ -2141,13 +2896,77 @@ skip_trampoline_code (pc, name) return 0; /* If this isn't a linker stub, then return now. */ - if (u->stub_type == 0) - return orig_pc == pc ? 0 : pc & ~0x3; + /* elz: attention here! (FIXME) because of a compiler/linker + error, some stubs which should have a non zero stub_unwind.stub_type + have unfortunately a value of zero. So this function would return here + as if we were not in a trampoline. To fix this, we go look at the partial + symbol information, which reports this guy as a stub. + (FIXME): Unfortunately, we are not that lucky: it turns out that the + partial symbol information is also wrong sometimes. This is because + when it is entered (somread.c::som_symtab_read()) it can happen that + if the type of the symbol (from the som) is Entry, and the symbol is + in a shared library, then it can also be a trampoline. This would + be OK, except that I believe the way they decide if we are ina shared library + does not work. SOOOO..., even if we have a regular function w/o trampolines + its minimal symbol can be assigned type mst_solib_trampoline. + Also, if we find that the symbol is a real stub, then we fix the unwind + descriptor, and define the stub type to be EXPORT. + Hopefully this is correct most of the times. */ + if (u->stub_unwind.stub_type == 0) + { + +/* elz: NOTE (FIXME!) once the problem with the unwind information is fixed + we can delete all the code which appears between the lines */ +/*--------------------------------------------------------------------------*/ + msym = lookup_minimal_symbol_by_pc (pc); + + if (msym == NULL || MSYMBOL_TYPE (msym) != mst_solib_trampoline) + return orig_pc == pc ? 0 : pc & ~0x3; + + else if (msym != NULL && MSYMBOL_TYPE (msym) == mst_solib_trampoline) + { + struct objfile *objfile; + struct minimal_symbol *msymbol; + int function_found = 0; + + /* go look if there is another minimal symbol with the same name as + this one, but with type mst_text. This would happen if the msym + is an actual trampoline, in which case there would be another + symbol with the same name corresponding to the real function */ + + ALL_MSYMBOLS (objfile, msymbol) + { + if (MSYMBOL_TYPE (msymbol) == mst_text + && STREQ (SYMBOL_NAME (msymbol) , SYMBOL_NAME (msym))) + { + function_found = 1; + break; + } + } + + if (function_found) + /* the type of msym is correct (mst_solib_trampoline), but + the unwind info is wrong, so set it to the correct value */ + u->stub_unwind.stub_type = EXPORT; + else + /* the stub type info in the unwind is correct (this is not a + trampoline), but the msym type information is wrong, it + should be mst_text. So we need to fix the msym, and also + get out of this function */ + { + MSYMBOL_TYPE (msym) = mst_text; + return orig_pc == pc ? 0 : pc & ~0x3; + } + } + +/*--------------------------------------------------------------------------*/ + } /* It's a stub. Search for a branch and figure out where it goes. Note we have to handle multi insn branch sequences like ldil;ble. Most (all?) other branches can be determined by examining the contents of certain registers and the stack. */ + loc = pc; curr_inst = 0; prev_inst = 0; @@ -2178,7 +2997,11 @@ skip_trampoline_code (pc, name) } } - /* Does it look like a be 0(sr0,%r21)? That's the branch from an + /* Does it look like a be 0(sr0,%r21)? OR + Does it look like a be, n 0(sr0,%r21)? OR + Does it look like a bve (r21)? (this is on PA2.0) + Does it look like a bve, n(r21)? (this is also on PA2.0) + That's the branch from an import stub to an export stub. It is impossible to determine the target of the branch via @@ -2195,7 +3018,10 @@ skip_trampoline_code (pc, name) Then lookup a minimal symbol with the same name; we should get the minimal symbol for the target routine in the shared library as those take precedence of import/export stubs. */ - if (curr_inst == 0xe2a00000) + if ((curr_inst == 0xe2a00000) || + (curr_inst == 0xe2a00002) || + (curr_inst == 0xeaa0d000) || + (curr_inst == 0xeaa0d002)) { struct minimal_symbol *stubsym, *libsym; @@ -2219,15 +3045,18 @@ skip_trampoline_code (pc, name) /* Does it look like bl X,%rp or bl X,%r0? Another way to do a branch from the stub to the actual function. */ + /*elz*/ else if ((curr_inst & 0xffe0e000) == 0xe8400000 - || (curr_inst & 0xffe0e000) == 0xe8000000) + || (curr_inst & 0xffe0e000) == 0xe8000000 + || (curr_inst & 0xffe0e000) == 0xe800A000) return (loc + extract_17 (curr_inst) + 8) & ~0x3; /* Does it look like bv (rp)? Note this depends on the current stack pointer being the same as the stack pointer in the stub itself! This is a branch on from the stub back to the original caller. */ - else if ((curr_inst & 0xffe0e000) == 0xe840c000) + /*else if ((curr_inst & 0xffe0e000) == 0xe840c000)*/ + else if ((curr_inst & 0xffe0f000) == 0xe840c000) { /* Yup. See if the previous instruction loaded rp from sp - 8. */ @@ -2241,6 +3070,15 @@ skip_trampoline_code (pc, name) } } + /* elz: added this case to capture the new instruction + at the end of the return part of an export stub used by + the PA2.0: BVE, n (rp) */ + else if ((curr_inst & 0xffe0f000) == 0xe840d000) + { + return (read_memory_integer + (read_register (SP_REGNUM) - 24, 4)) & ~0x3; + } + /* What about be,n 0(sr0,%rp)? It's just another way we return to the original caller from the stub. Used in dynamic executables. */ else if (curr_inst == 0xe0400002) @@ -2259,6 +3097,7 @@ skip_trampoline_code (pc, name) } } + /* For the given instruction (INST), return any adjustment it makes to the stack pointer or zero for no adjustment. @@ -2363,8 +3202,12 @@ static int inst_saves_fr (inst) unsigned long inst; { + /* is this an FSTDS ?*/ if ((inst & 0xfc00dfc0) == 0x2c001200) return extract_5r_store (inst); + /* is this an FSTWS ?*/ + if ((inst & 0xfc00df80) == 0x24001200) + return extract_5r_store (inst); return 0; } @@ -2374,8 +3217,9 @@ inst_saves_fr (inst) Use information in the unwind table to determine what exactly should be in the prologue. */ + CORE_ADDR -skip_prologue (pc) +skip_prologue_hard_way (pc) CORE_ADDR pc; { char buf[4]; @@ -2392,7 +3236,7 @@ restart: if (!u) return pc; - /* If we are not at the beginning of a function, then return now. */ + /* If we are not at the beginning of a function, then return now. */ if ((pc & ~0x3) != u->region_start) return pc; @@ -2592,6 +3436,162 @@ restart: return pc; } + + + + +/* return 0 if we cannot determine the end of the prologue, + return the new pc value if we know where the prologue ends */ + +static CORE_ADDR +after_prologue (pc) + CORE_ADDR pc; +{ + struct symtab_and_line sal; + CORE_ADDR func_addr, func_end; + struct symbol *f; + + if (!find_pc_partial_function (pc, NULL, &func_addr, &func_end)) + return 0; /* Unknown */ + + f = find_pc_function (pc); + if (!f) + return 0; /* no debug info, do it the hard way! */ + + sal = find_pc_line (func_addr, 0); + + if (sal.end < func_end) + { + /* this happens when the function has no prologue, because the way + find_pc_line works: elz. Note: this may not be a very good + way to decide whether a function has a prologue or not, but + it is the best I can do with the info available + Also, this will work for functions like: int f() + { + return 2; + } + I.e. the bp will be inserted at the first open brace. + For functions where the body is only one line written like this: + int f() + { return 2; } + this will make the breakpoint to be at the last brace, after the body + has been executed already. What's the point of stepping through a function + without any variables anyway?? */ + + if ((SYMBOL_LINE(f) > 0) && (SYMBOL_LINE(f) < sal.line)) + return pc; /*no adjusment will be made*/ + else + return sal.end; /* this is the end of the prologue */ + } + /* The line after the prologue is after the end of the function. In this + case, put the end of the prologue is the beginning of the function. */ + /* This should happen only when the function is prologueless and has no + code in it. For instance void dumb(){} Note: this kind of function + is used quite a lot in the test system */ + + else return pc; /* no adjustment will be made */ +} + +/* To skip prologues, I use this predicate. Returns either PC itself + if the code at PC does not look like a function prologue; otherwise + returns an address that (if we're lucky) follows the prologue. If + LENIENT, then we must skip everything which is involved in setting + up the frame (it's OK to skip more, just so long as we don't skip + anything which might clobber the registers which are being saved. + Currently we must not skip more on the alpha, but we might the lenient + stuff some day. */ + +CORE_ADDR +skip_prologue (pc) + CORE_ADDR pc; +{ + unsigned long inst; + int offset; + CORE_ADDR post_prologue_pc; + char buf[4]; + +#ifdef GDB_TARGET_HAS_SHARED_LIBS + /* Silently return the unaltered pc upon memory errors. + This could happen on OSF/1 if decode_line_1 tries to skip the + prologue for quickstarted shared library functions when the + shared library is not yet mapped in. + Reading target memory is slow over serial lines, so we perform + this check only if the target has shared libraries. */ + if (target_read_memory (pc, buf, 4)) + return pc; +#endif + + /* See if we can determine the end of the prologue via the symbol table. + If so, then return either PC, or the PC after the prologue, whichever + is greater. */ + + post_prologue_pc = after_prologue (pc); + + if (post_prologue_pc != 0) + return max (pc, post_prologue_pc); + + + /* Can't determine prologue from the symbol table, (this can happen if there + is no debug information) so we need to fall back on the old code, which + looks at the instructions */ + /* FIXME (elz) !!!!: this may create a problem if, once the bp is hit, the user says + where: the backtrace info is not right: this is because the point at which we + break is at the very first instruction of the function. At this time the stuff that + needs to be saved on the stack, has not been saved yet, so the backtrace + cannot know all it needs to know. This will need to be fixed in the + actual backtrace code. (Note: this is what DDE does) */ + + else + + return (skip_prologue_hard_way(pc)); + +#if 0 +/* elz: I am keeping this code around just in case, but remember, all the + instructions are for alpha: you should change all to the hppa instructions */ + + /* Can't determine prologue from the symbol table, need to examine + instructions. */ + + /* Skip the typical prologue instructions. These are the stack adjustment + instruction and the instructions that save registers on the stack + or in the gcc frame. */ + for (offset = 0; offset < 100; offset += 4) + { + int status; + + status = read_memory_nobpt (pc + offset, buf, 4); + if (status) + memory_error (status, pc + offset); + inst = extract_unsigned_integer (buf, 4); + + /* The alpha has no delay slots. But let's keep the lenient stuff, + we might need it for something else in the future. */ + if (lenient && 0) + continue; + + if ((inst & 0xffff0000) == 0x27bb0000) /* ldah $gp,n($t12) */ + continue; + if ((inst & 0xffff0000) == 0x23bd0000) /* lda $gp,n($gp) */ + continue; + if ((inst & 0xffff0000) == 0x23de0000) /* lda $sp,n($sp) */ + continue; + else if ((inst & 0xfc1f0000) == 0xb41e0000 + && (inst & 0xffff0000) != 0xb7fe0000) + continue; /* stq reg,n($sp) */ + /* reg != $zero */ + else if ((inst & 0xfc1f0000) == 0x9c1e0000 + && (inst & 0xffff0000) != 0x9ffe0000) + continue; /* stt reg,n($sp) */ + /* reg != $zero */ + else if (inst == 0x47de040f) /* bis sp,sp,fp */ + continue; + else + break; + } + return pc + offset; +#endif /* 0 */ +} + /* Put here the code to store, into a struct frame_saved_regs, the addresses of the saved registers of frame described by FRAME_INFO. This includes special registers such as pc and fp saved in special @@ -2795,6 +3795,480 @@ hppa_frame_find_saved_regs (frame_info, frame_saved_regs) } } + +/* Exception handling support for the HP-UX ANSI C++ compiler. + The compiler (aCC) provides a callback for exception events; + GDB can set a breakpoint on this callback and find out what + exception event has occurred. */ + +/* The name of the hook to be set to point to the callback function */ +static char HP_ACC_EH_notify_hook[] = "__eh_notify_hook"; +/* The name of the function to be used to set the hook value */ +static char HP_ACC_EH_set_hook_value[] = "__eh_set_hook_value"; +/* The name of the callback function in end.o */ +static char HP_ACC_EH_notify_callback[] = "__d_eh_notify_callback"; +/* Name of function in end.o on which a break is set (called by above) */ +static char HP_ACC_EH_break[] = "__d_eh_break"; +/* Name of flag (in end.o) that enables catching throws */ +static char HP_ACC_EH_catch_throw[] = "__d_eh_catch_throw"; +/* Name of flag (in end.o) that enables catching catching */ +static char HP_ACC_EH_catch_catch[] = "__d_eh_catch_catch"; +/* The enum used by aCC */ +typedef enum { + __EH_NOTIFY_THROW, + __EH_NOTIFY_CATCH +} __eh_notification; + +/* Is exception-handling support available with this executable? */ +static int hp_cxx_exception_support = 0; +/* Has the initialize function been run? */ +int hp_cxx_exception_support_initialized = 0; +/* Similar to above, but imported from breakpoint.c -- non-target-specific */ +extern int exception_support_initialized; +/* Address of __eh_notify_hook */ +static CORE_ADDR eh_notify_hook_addr = NULL; +/* Address of __d_eh_notify_callback */ +static CORE_ADDR eh_notify_callback_addr = NULL; +/* Address of __d_eh_break */ +static CORE_ADDR eh_break_addr = NULL; +/* Address of __d_eh_catch_catch */ +static CORE_ADDR eh_catch_catch_addr = NULL; +/* Address of __d_eh_catch_throw */ +static CORE_ADDR eh_catch_throw_addr = NULL; +/* Sal for __d_eh_break */ +static struct symtab_and_line * break_callback_sal = NULL; + +/* Code in end.c expects __d_pid to be set in the inferior, + otherwise __d_eh_notify_callback doesn't bother to call + __d_eh_break! So we poke the pid into this symbol + ourselves. + 0 => success + 1 => failure */ +int +setup_d_pid_in_inferior () +{ + CORE_ADDR anaddr; + struct minimal_symbol * msymbol; + char buf[4]; /* FIXME 32x64? */ + + /* Slam the pid of the process into __d_pid; failing is only a warning! */ + msymbol = lookup_minimal_symbol ("__d_pid", NULL, symfile_objfile); + if (msymbol == NULL) + { + warning ("Unable to find __d_pid symbol in object file."); + warning ("Suggest linking executable with -g (links in /opt/langtools/lib/end.o)."); + return 1; + } + + anaddr = SYMBOL_VALUE_ADDRESS (msymbol); + store_unsigned_integer (buf, 4, inferior_pid); /* FIXME 32x64? */ + if (target_write_memory (anaddr, buf, 4)) /* FIXME 32x64? */ + { + warning ("Unable to write __d_pid"); + warning ("Suggest linking executable with -g (links in /opt/langtools/lib/end.o)."); + return 1; + } + return 0; +} + +/* Initialize exception catchpoint support by looking for the + necessary hooks/callbacks in end.o, etc., and set the hook value to + point to the required debug function + + Return 0 => failure + 1 => success */ + +static int +initialize_hp_cxx_exception_support () +{ + struct symtabs_and_lines sals; + struct cleanup * old_chain; + struct cleanup * canonical_strings_chain = NULL; + int i; + char * addr_start; + char * addr_end = NULL; + char ** canonical = (char **) NULL; + int thread = -1; + struct symbol * sym = NULL; + struct minimal_symbol * msym = NULL; + struct objfile * objfile; + asection *shlib_info; + + /* Detect and disallow recursion. On HP-UX with aCC, infinite + recursion is a possibility because finding the hook for exception + callbacks involves making a call in the inferior, which means + re-inserting breakpoints which can re-invoke this code */ + + static int recurse = 0; + if (recurse > 0) + { + hp_cxx_exception_support_initialized = 0; + exception_support_initialized = 0; + return 0; + } + + hp_cxx_exception_support = 0; + + /* First check if we have seen any HP compiled objects; if not, + it is very unlikely that HP's idiosyncratic callback mechanism + for exception handling debug support will be available! + This will percolate back up to breakpoint.c, where our callers + will decide to try the g++ exception-handling support instead. */ + if (!hp_som_som_object_present) + return 0; + + /* We have a SOM executable with SOM debug info; find the hooks */ + + /* First look for the notify hook provided by aCC runtime libs */ + /* If we find this symbol, we conclude that the executable must + have HP aCC exception support built in. If this symbol is not + found, even though we're a HP SOM-SOM file, we may have been + built with some other compiler (not aCC). This results percolates + back up to our callers in breakpoint.c which can decide to + try the g++ style of exception support instead. + If this symbol is found but the other symbols we require are + not found, there is something weird going on, and g++ support + should *not* be tried as an alternative. + + ASSUMPTION: Only HP aCC code will have __eh_notify_hook defined. + ASSUMPTION: HP aCC and g++ modules cannot be linked together. */ + + /* libCsup has this hook; it'll usually be non-debuggable */ + msym = lookup_minimal_symbol (HP_ACC_EH_notify_hook, NULL, NULL); + if (msym) + { + eh_notify_hook_addr = SYMBOL_VALUE_ADDRESS (msym); + hp_cxx_exception_support = 1; + } + else + { + warning ("Unable to find exception callback hook (%s).", HP_ACC_EH_notify_hook); + warning ("Executable may not have been compiled debuggable with HP aCC."); + warning ("GDB will be unable to intercept exception events."); + eh_notify_hook_addr = 0; + hp_cxx_exception_support = 0; + return 0; + } + +#if 0 /* DEBUGGING */ + printf ("Hook addr found is %lx\n", eh_notify_hook_addr); +#endif + + /* Next look for the notify callback routine in end.o */ + /* This is always available in the SOM symbol dictionary if end.o is linked in */ + msym = lookup_minimal_symbol (HP_ACC_EH_notify_callback, NULL, NULL); + if (msym) + { + eh_notify_callback_addr = SYMBOL_VALUE_ADDRESS (msym); + hp_cxx_exception_support = 1; + } + else + { + warning ("Unable to find exception callback routine (%s).", HP_ACC_EH_notify_callback); + warning ("Suggest linking executable with -g (links in /opt/langtools/lib/end.o)."); + warning ("GDB will be unable to intercept exception events."); + eh_notify_callback_addr = 0; + return 0; + } + + /* Check whether the executable is dynamically linked or archive bound */ + /* With an archive-bound executable we can use the raw addresses we find + for the callback function, etc. without modification. For an executable + with shared libraries, we have to do more work to find the plabel, which + can be the target of a call through $$dyncall from the aCC runtime support + library (libCsup) which is linked shared by default by aCC. */ + /* This test below was copied from somsolib.c/somread.c. It may not be a very + reliable one to test that an executable is linked shared. pai/1997-07-18 */ + shlib_info = bfd_get_section_by_name (symfile_objfile->obfd, "$SHLIB_INFO$"); + if (shlib_info && (bfd_section_size (symfile_objfile->obfd, shlib_info) != 0)) + { + /* The minsym we have has the local code address, but that's not the + plabel that can be used by an inter-load-module call. */ + /* Find solib handle for main image (which has end.o), and use that + and the min sym as arguments to __d_shl_get() (which does the equivalent + of shl_findsym()) to find the plabel. */ + + args_for_find_stub args; + static char message[] = "Error while finding exception callback hook:\n"; + + args.solib_handle = som_solib_get_solib_by_pc (eh_notify_callback_addr); + args.msym = msym; + + recurse++; + eh_notify_callback_addr = catch_errors ((int (*) PARAMS ((char *))) cover_find_stub_with_shl_get, + (char *) &args, + message, RETURN_MASK_ALL); + recurse--; + +#if 0 /* DEBUGGING */ + printf ("found plabel for eh notify callback: %x\n", eh_notify_callback_addr); +#endif + + exception_catchpoints_are_fragile = 1; + + if (!eh_notify_callback_addr) + { + /* We can get here either if there is no plabel in the export list + for the main image, or if something strange happened (??) */ + warning ("Couldn't find a plabel (indirect function label) for the exception callback."); + warning ("GDB will not be able to intercept exception events."); + return 0; + } + } + else + exception_catchpoints_are_fragile = 0; + +#if 0 /* DEBUGGING */ + printf ("Cb addr found is %lx\n", eh_notify_callback_addr); +#endif + + /* Now, look for the breakpointable routine in end.o */ + /* This should also be available in the SOM symbol dict. if end.o linked in */ + msym = lookup_minimal_symbol (HP_ACC_EH_break, NULL, NULL); + if (msym) + { + eh_break_addr = SYMBOL_VALUE_ADDRESS (msym); + hp_cxx_exception_support = 1; + } + else + { + warning ("Unable to find exception callback routine to set breakpoint (%s).", HP_ACC_EH_break); + warning ("Suggest linking executable with -g (link in /opt/langtools/lib/end.o)."); + warning ("GDB will be unable to intercept exception events."); + eh_break_addr = 0; + return 0; + } + +#if 0 /* DEBUGGING */ + printf ("break addr found is %lx\n", eh_break_addr); +#endif + + /* Next look for the catch enable flag provided in end.o */ + sym = lookup_symbol (HP_ACC_EH_catch_catch, (struct block *) NULL, + VAR_NAMESPACE, 0, (struct symtab **) NULL); + if (sym) /* sometimes present in debug info */ + { + eh_catch_catch_addr = SYMBOL_VALUE_ADDRESS (sym); + hp_cxx_exception_support = 1; + } + else /* otherwise look in SOM symbol dict. */ + { + msym = lookup_minimal_symbol (HP_ACC_EH_catch_catch, NULL, NULL); + if (msym) + { + eh_catch_catch_addr = SYMBOL_VALUE_ADDRESS (msym); + hp_cxx_exception_support = 1; + } + else + { + warning ("Unable to enable interception of exception catches."); + warning ("Executable may not have been compiled debuggable with HP aCC."); + warning ("Suggest linking executable with -g (link in /opt/langtools/lib/end.o)."); + return 0; + } + } + +#if 0 /* DEBUGGING */ + printf ("catch catch addr found is %lx\n", eh_catch_catch_addr); +#endif + + /* Next look for the catch enable flag provided end.o */ + sym = lookup_symbol (HP_ACC_EH_catch_catch, (struct block *) NULL, + VAR_NAMESPACE, 0, (struct symtab **) NULL); + if (sym) /* sometimes present in debug info */ + { + eh_catch_throw_addr = SYMBOL_VALUE_ADDRESS (sym); + hp_cxx_exception_support = 1; + } + else /* otherwise look in SOM symbol dict. */ + { + msym = lookup_minimal_symbol (HP_ACC_EH_catch_throw, NULL, NULL); + if (msym) + { + eh_catch_throw_addr = SYMBOL_VALUE_ADDRESS (msym); + hp_cxx_exception_support = 1; + } + else + { + warning ("Unable to enable interception of exception throws."); + warning ("Executable may not have been compiled debuggable with HP aCC."); + warning ("Suggest linking executable with -g (link in /opt/langtools/lib/end.o)."); + return 0; + } + } + +#if 0 /* DEBUGGING */ + printf ("catch throw addr found is %lx\n", eh_catch_throw_addr); +#endif + + /* Set the flags */ + hp_cxx_exception_support = 2; /* everything worked so far */ + hp_cxx_exception_support_initialized = 1; + exception_support_initialized = 1; + + return 1; +} + +/* Target operation for enabling or disabling interception of + exception events. + KIND is either EX_EVENT_THROW or EX_EVENT_CATCH + ENABLE is either 0 (disable) or 1 (enable). + Return value is NULL if no support found; + -1 if something went wrong, + or a pointer to a symtab/line struct if the breakpointable + address was found. */ + +struct symtab_and_line * +child_enable_exception_callback (kind, enable) + enum exception_event_kind kind; + int enable; +{ + char buf[4]; + + if (!exception_support_initialized || !hp_cxx_exception_support_initialized) + if (!initialize_hp_cxx_exception_support ()) + return NULL; + + switch (hp_cxx_exception_support) + { + case 0: + /* Assuming no HP support at all */ + return NULL; + case 1: + /* HP support should be present, but something went wrong */ + return (struct symtab_and_line *) -1; /* yuck! */ + /* there may be other cases in the future */ + } + + /* Set the EH hook to point to the callback routine */ + store_unsigned_integer (buf, 4, enable ? eh_notify_callback_addr : 0); /* FIXME 32x64 problem */ + /* pai: (temp) FIXME should there be a pack operation first? */ + if (target_write_memory (eh_notify_hook_addr, buf, 4)) /* FIXME 32x64 problem */ + { + warning ("Could not write to target memory for exception event callback."); + warning ("Interception of exception events may not work."); + return (struct symtab_and_line *) -1; + } + if (enable) + { + /* Ensure that __d_pid is set up correctly -- end.c code checks this. :-(*/ + if (inferior_pid > 0) + { + if (setup_d_pid_in_inferior ()) + return (struct symtab_and_line *) -1; + } + else + { + warning ("Internal error: Invalid inferior pid? Cannot intercept exception events."); /* purecov: deadcode */ + return (struct symtab_and_line *) -1; /* purecov: deadcode */ + } + } + + switch (kind) + { + case EX_EVENT_THROW: + store_unsigned_integer (buf, 4, enable ? 1 : 0); + if (target_write_memory (eh_catch_throw_addr, buf, 4)) /* FIXME 32x64? */ + { + warning ("Couldn't enable exception throw interception."); + return (struct symtab_and_line *) -1; + } + break; + case EX_EVENT_CATCH: + store_unsigned_integer (buf, 4, enable ? 1 : 0); + if (target_write_memory (eh_catch_catch_addr, buf, 4)) /* FIXME 32x64? */ + { + warning ("Couldn't enable exception catch interception."); + return (struct symtab_and_line *) -1; + } + break; + default: /* purecov: deadcode */ + error ("Request to enable unknown or unsupported exception event."); /* purecov: deadcode */ + } + + /* Copy break address into new sal struct, malloc'ing if needed. */ + if (!break_callback_sal) + { + break_callback_sal = (struct symtab_and_line *) xmalloc (sizeof (struct symtab_and_line)); + } + INIT_SAL(break_callback_sal); + break_callback_sal->symtab = NULL; + break_callback_sal->pc = eh_break_addr; + break_callback_sal->line = 0; + break_callback_sal->end = eh_break_addr; + + return break_callback_sal; +} + +/* Record some information about the current exception event */ +static struct exception_event_record current_ex_event; +/* Convenience struct */ +static struct symtab_and_line null_symtab_and_line = { NULL, 0, 0, 0 }; + +/* Report current exception event. Returns a pointer to a record + that describes the kind of the event, where it was thrown from, + and where it will be caught. More information may be reported + in the future */ +struct exception_event_record * +child_get_current_exception_event () +{ + CORE_ADDR event_kind; + CORE_ADDR throw_addr; + CORE_ADDR catch_addr; + struct frame_info *fi, *curr_frame; + int level = 1; + + curr_frame = get_current_frame(); + if (!curr_frame) + return (struct exception_event_record *) NULL; + + /* Go up one frame to __d_eh_notify_callback, because at the + point when this code is executed, there's garbage in the + arguments of __d_eh_break. */ + fi = find_relative_frame (curr_frame, &level); + if (level != 0) + return (struct exception_event_record *) NULL; + + select_frame (fi, -1); + + /* Read in the arguments */ + /* __d_eh_notify_callback() is called with 3 arguments: + 1. event kind catch or throw + 2. the target address if known + 3. a flag -- not sure what this is. pai/1997-07-17 */ + event_kind = read_register (ARG0_REGNUM); + catch_addr = read_register (ARG1_REGNUM); + + /* Now go down to a user frame */ + /* For a throw, __d_eh_break is called by + __d_eh_notify_callback which is called by + __notify_throw which is called + from user code. + For a catch, __d_eh_break is called by + __d_eh_notify_callback which is called by + <stackwalking stuff> which is called by + __throw__<stuff> or __rethrow_<stuff> which is called + from user code. */ + /* FIXME: Don't use such magic numbers; search for the frames */ + level = (event_kind == EX_EVENT_THROW) ? 3 : 4; + fi = find_relative_frame (curr_frame, &level); + if (level != 0) + return (struct exception_event_record *) NULL; + + select_frame (fi, -1); + throw_addr = fi->pc; + + /* Go back to original (top) frame */ + select_frame (curr_frame, -1); + + current_ex_event.kind = (enum exception_event_kind) event_kind; + current_ex_event.throw_sal = find_pc_line (throw_addr, 1); + current_ex_event.catch_sal = find_pc_line (catch_addr, 1); + + return ¤t_ex_event; +} + + #ifdef MAINTENANCE_CMDS static void @@ -2870,6 +4344,86 @@ unwind_command (exp, from_tty) } #endif /* MAINTENANCE_CMDS */ +#ifdef PREPARE_TO_PROCEED + +/* If the user has switched threads, and there is a breakpoint + at the old thread's pc location, then switch to that thread + and return TRUE, else return FALSE and don't do a thread + switch (or rather, don't seem to have done a thread switch). + + Ptrace-based gdb will always return FALSE to the thread-switch + query, and thus also to PREPARE_TO_PROCEED. + + The important thing is whether there is a BPT instruction, + not how many user breakpoints there are. So we have to worry + about things like these: + + o Non-bp stop -- NO + + o User hits bp, no switch -- NO + + o User hits bp, switches threads -- YES + + o User hits bp, deletes bp, switches threads -- NO + + o User hits bp, deletes one of two or more bps + at that PC, user switches threads -- YES + + o Plus, since we're buffering events, the user may have hit a + breakpoint, deleted the breakpoint and then gotten another + hit on that same breakpoint on another thread which + actually hit before the delete. (FIXME in breakpoint.c + so that "dead" breakpoints are ignored?) -- NO + + For these reasons, we have to violate information hiding and + call "breakpoint_here_p". If core gdb thinks there is a bpt + here, that's what counts, as core gdb is the one which is + putting the BPT instruction in and taking it out. */ +int +hppa_prepare_to_proceed() +{ + pid_t old_thread; + pid_t current_thread; + + old_thread = hppa_switched_threads(inferior_pid); + if (old_thread != 0) + { + /* Switched over from "old_thread". Try to do + as little work as possible, 'cause mostly + we're going to switch back. */ + CORE_ADDR new_pc; + CORE_ADDR old_pc = read_pc(); + + /* Yuk, shouldn't use global to specify current + thread. But that's how gdb does it. */ + current_thread = inferior_pid; + inferior_pid = old_thread; + + new_pc = read_pc(); + if (new_pc != old_pc /* If at same pc, no need */ + && breakpoint_here_p (new_pc)) + { + /* User hasn't deleted the BP. + Return TRUE, finishing switch to "old_thread". */ + flush_cached_frames (); + registers_changed (); +#if 0 + printf("---> PREPARE_TO_PROCEED (was %d, now %d)!\n", + current_thread, inferior_pid); +#endif + + return 1; + } + + /* Otherwise switch back to the user-chosen thread. */ + inferior_pid = current_thread; + new_pc = read_pc(); /* Re-prime register cache */ + } + + return 0; +} +#endif /* PREPARE_TO_PROCEED */ + void _initialize_hppa_tdep () { diff --git a/gdb/hppah-nat.c b/gdb/hppah-nat.c index d9210d5..4587715 100644 --- a/gdb/hppah-nat.c +++ b/gdb/hppah-nat.c @@ -1,6 +1,5 @@ -/* Machine-dependent hooks for the unix child process stratum, for HPUX PA-RISC. - - Copyright 1986, 1987, 1989, 1990, 1991, 1992, 1993 +/* Native support code for HPUX PA-RISC. + Copyright 1986, 1987, 1989, 1990, 1991, 1992, 1993, 1998 Free Software Foundation, Inc. Contributed by the Center for Software Science at the @@ -28,6 +27,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "target.h" #include <sys/ptrace.h> #include "gdbcore.h" +#include <wait.h> extern CORE_ADDR text_end; @@ -128,7 +128,8 @@ fetch_register (regno) if (errno != 0) { /* Warning, not error, in case we are attached; sometimes the - kernel doesn't let us at the registers. */ + * kernel doesn't let us at the registers. + */ char *err = safe_strerror (errno); char *msg = alloca (strlen (err) + 128); sprintf (msg, "reading register %s: %s", REGISTER_NAME (regno), err); @@ -165,8 +166,19 @@ child_xfer_memory (memaddr, myaddr, len, write, target) /* Round ending address up; get number of longwords that makes. */ register int count = (((memaddr + len) - addr) + sizeof (int) - 1) / sizeof (int); + /* Allocate buffer of that many longwords. */ - register int *buffer = (int *) alloca (count * sizeof (int)); + /* Note (RT) - This code formerly used alloca, which I have + * replaced with xmalloc and a matching free() at the end. + * The problem with alloca() is that there is no guarantee of + * when it'll be freed, and we were seeing cases of memory + * leaks on: + * (gdb) watch x + * (gdb) cont + * where the piled-up alloca's for the child_xfer_memory buffers + * were not getting freed. + */ + register int *buffer = (int *) xmalloc (count * sizeof (int)); if (write) { @@ -194,16 +206,38 @@ child_xfer_memory (memaddr, myaddr, len, write, target) for (i = 0; i < count; i++, addr += sizeof (int)) { -/* The HP-UX kernel crashes if you use PT_WDUSER to write into the text - segment. FIXME -- does it work to write into the data segment using - WIUSER, or do these idiots really expect us to figure out which segment - the address is in, so we can use a separate system call for it??! */ + int pt_status; + int pt_request; + /* The HP-UX kernel crashes if you use PT_WDUSER to write into the text + segment. FIXME -- does it work to write into the data segment using + WIUSER, or do these idiots really expect us to figure out which segment + the address is in, so we can use a separate system call for it??! */ errno = 0; - call_ptrace (addr < text_end ? PT_WIUSER : PT_WDUSER, inferior_pid, - (PTRACE_ARG3_TYPE) addr, - buffer[i]); - if (errno) - return 0; + pt_request = (addr < text_end) ? PT_WIUSER : PT_WDUSER; + pt_status = call_ptrace (pt_request, + inferior_pid, + (PTRACE_ARG3_TYPE) addr, + buffer[i]); + + /* Did we fail? Might we've guessed wrong about which + segment this address resides in? Try the other request, + and see if that works... + */ + if ((pt_status == -1) && errno) { + errno = 0; + pt_request = (pt_request == PT_WIUSER) ? PT_WDUSER : PT_WIUSER; + pt_status = call_ptrace (pt_request, + inferior_pid, + (PTRACE_ARG3_TYPE) addr, + buffer[i]); + + /* No, we still fail. Okay, time to punt. */ + if ((pt_status == -1) && errno) + { + free(buffer); + return 0; + } + } } } else @@ -214,13 +248,848 @@ child_xfer_memory (memaddr, myaddr, len, write, target) errno = 0; buffer[i] = call_ptrace (addr < text_end ? PT_RIUSER : PT_RDUSER, inferior_pid, (PTRACE_ARG3_TYPE) addr, 0); - if (errno) + if (errno) { + free(buffer); return 0; + } QUIT; } /* Copy appropriate bytes out of the buffer. */ memcpy (myaddr, (char *) buffer + (memaddr & (sizeof (int) - 1)), len); } + free(buffer); return len; } + + +void +child_post_follow_inferior_by_clone () +{ + int status; + + /* This function is used when following both the parent and child + of a fork. In this case, the debugger clones itself. The original + debugger follows the parent, the clone follows the child. The + original detaches from the child, delivering a SIGSTOP to it to + keep it from running away until the clone can attach itself. + + At this point, the clone has attached to the child. Because of + the SIGSTOP, we must now deliver a SIGCONT to the child, or it + won't behave properly. */ + status = kill (inferior_pid, SIGCONT); +} + + +void +child_post_follow_vfork (parent_pid, followed_parent, child_pid, followed_child) + int parent_pid; + int followed_parent; + int child_pid; + int followed_child; +{ + + /* Are we a debugger that followed the parent of a vfork? If so, + then recall that the child's vfork event was delivered to us + first. And, that the parent was suspended by the OS until the + child's exec or exit events were received. + + Upon receiving that child vfork, then, we were forced to remove + all breakpoints in the child and continue it so that it could + reach the exec or exit point. + + But also recall that the parent and child of a vfork share the + same address space. Thus, removing bp's in the child also + removed them from the parent. + + Now that the child has safely exec'd or exited, we must restore + the parent's breakpoints before we continue it. Else, we may + cause it run past expected stopping points. */ + if (followed_parent) + { + reattach_breakpoints (parent_pid); + } + + /* Are we a debugger that followed the child of a vfork? If so, + then recall that we don't actually acquire control of the child + until after it has exec'd or exited. + */ + if (followed_child) + { + /* If the child has exited, then there's nothing for us to do. + In the case of an exec event, we'll let that be handled by + the normal mechanism that notices and handles exec events, in + resume(). */ + + } +} + +/* Format a process id, given a pid. Be sure to terminate + * this with a null--it's going to be printed via a "%s". + */ +char * +hppa_pid_to_str( pid ) + pid_t pid; +{ + static char buf[30]; /* Static because address returned */ + + sprintf( buf, "process %d\0\0\0\0", pid ); + /* Extra NULLs for paranoia's sake */ + + return buf; +} + +/* Format a thread id, given a tid. Be sure to terminate + * this with a null--it's going to be printed via a "%s". + * + * Note: This is a core-gdb tid, not the actual system tid. + * See infttrace.c for details. + */ +char * +hppa_tid_to_str( tid ) + pid_t tid; +{ + static char buf[30]; /* Static because address returned */ + + sprintf( buf, "system thread %d\0\0\0\0", tid ); + /* Extra NULLs for paranoia's sake */ + + return buf; +} + +#if !defined (GDB_NATIVE_HPUX_11) + +/* The following code is a substitute for the infttrace.c versions used + with ttrace() in HPUX 11. */ + +/* This value is an arbitrary integer. */ +#define PT_VERSION 123456 + +/* This semaphore is used to coordinate the child and parent processes + after a fork(), and before an exec() by the child. See + parent_attach_all for details. */ + +typedef struct { + int parent_channel[2]; /* Parent "talks" to [1], child "listens" to [0] */ + int child_channel[2]; /* Child "talks" to [1], parent "listens" to [0] */ +} startup_semaphore_t; + +#define SEM_TALK (1) +#define SEM_LISTEN (0) + +static startup_semaphore_t startup_semaphore; + +extern int parent_attach_all PARAMS ((int, PTRACE_ARG3_TYPE, int)); + +#ifdef PT_SETTRC +/* This function causes the caller's process to be traced by its + parent. This is intended to be called after GDB forks itself, + and before the child execs the target. + + Note that HP-UX ptrace is rather funky in how this is done. + If the parent wants to get the initial exec event of a child, + it must set the ptrace event mask of the child to include execs. + (The child cannot do this itself.) This must be done after the + child is forked, but before it execs. + + To coordinate the parent and child, we implement a semaphore using + pipes. After SETTRC'ing itself, the child tells the parent that + it is now traceable by the parent, and waits for the parent's + acknowledgement. The parent can then set the child's event mask, + and notify the child that it can now exec. + + (The acknowledgement by parent happens as a result of a call to + child_acknowledge_created_inferior.) */ + +int +parent_attach_all (pid, addr, data) + int pid; + PTRACE_ARG3_TYPE addr; + int data; +{ + int pt_status = 0; + + /* We need a memory home for a constant. */ + int tc_magic_child = PT_VERSION; + int tc_magic_parent = 0; + + /* The remainder of this function is only useful for HPUX 10.0 and + later, as it depends upon the ability to request notification + of specific kinds of events by the kernel. */ +#if defined(PT_SET_EVENT_MASK) + + /* Notify the parent that we're potentially ready to exec(). */ + write (startup_semaphore.child_channel[SEM_TALK], + &tc_magic_child, + sizeof (tc_magic_child)); + + /* Wait for acknowledgement from the parent. */ + read (startup_semaphore.parent_channel[SEM_LISTEN], + &tc_magic_parent, + sizeof (tc_magic_parent)); + if (tc_magic_child != tc_magic_parent) + warning ("mismatched semaphore magic"); + + /* Discard our copy of the semaphore. */ + (void) close (startup_semaphore.parent_channel[SEM_LISTEN]); + (void) close (startup_semaphore.parent_channel[SEM_TALK]); + (void) close (startup_semaphore.child_channel[SEM_LISTEN]); + (void) close (startup_semaphore.child_channel[SEM_TALK]); +#endif + + return 0; +} +#endif + +int +hppa_require_attach (pid) + int pid; +{ + int pt_status; + CORE_ADDR pc; + CORE_ADDR pc_addr; + unsigned int regs_offset; + + /* Are we already attached? There appears to be no explicit way to + answer this via ptrace, so we try something which should be + innocuous if we are attached. If that fails, then we assume + we're not attached, and so attempt to make it so. */ + + errno = 0; + regs_offset = U_REGS_OFFSET; + pc_addr = register_addr (PC_REGNUM, regs_offset); + pc = call_ptrace (PT_READ_U, pid, (PTRACE_ARG3_TYPE) pc_addr, 0); + + if (errno) + { + errno = 0; + pt_status = call_ptrace (PT_ATTACH, pid, (PTRACE_ARG3_TYPE) 0, 0); + + if (errno) + return -1; + + /* Now we really are attached. */ + errno = 0; + } + attach_flag = 1; + return pid; +} + +int +hppa_require_detach (pid, signal) + int pid; + int signal; +{ + errno = 0; + call_ptrace (PT_DETACH, pid, (PTRACE_ARG3_TYPE) 1, signal); + errno = 0; /* Ignore any errors. */ + return pid; +} + +/* Since ptrace doesn't support memory page-protection events, which + are used to implement "hardware" watchpoints on HP-UX, these are + dummy versions, which perform no useful work. */ + +void +hppa_enable_page_protection_events (pid) + int pid; +{ +} + +void +hppa_disable_page_protection_events (pid) + int pid; +{ +} + +int +hppa_insert_hw_watchpoint (pid, start, len, type) + int pid; + CORE_ADDR start; + LONGEST len; + int type; +{ + error ("Hardware watchpoints not implemented on this platform."); +} + +int +hppa_remove_hw_watchpoint (pid, start, len, type) + int pid; + CORE_ADDR start; + LONGEST len; + enum bptype type; +{ + error ("Hardware watchpoints not implemented on this platform."); +} + +int +hppa_can_use_hw_watchpoint (type, cnt, ot) + enum bptype type; + int cnt; + enum bptype ot; +{ + return 0; +} + +int +hppa_range_profitable_for_hw_watchpoint (pid, start, len) + int pid; + CORE_ADDR start; + LONGEST len; +{ + error ("Hardware watchpoints not implemented on this platform."); +} + +char * +hppa_pid_or_tid_to_str (id) + pid_t id; +{ + /* In the ptrace world, there are only processes. */ + return hppa_pid_to_str (id); +} + +/* This function has no meaning in a non-threaded world. Thus, we + return 0 (FALSE). See the use of "hppa_prepare_to_proceed" in + hppa-tdep.c. */ + +pid_t +hppa_switched_threads (pid) + pid_t pid; +{ + return (pid_t) 0; +} + +void +hppa_ensure_vforking_parent_remains_stopped (pid) + int pid; +{ + /* This assumes that the vforked parent is presently stopped, and + that the vforked child has just delivered its first exec event. + Calling kill() this way will cause the SIGTRAP to be delivered as + soon as the parent is resumed, which happens as soon as the + vforked child is resumed. See wait_for_inferior for the use of + this function. */ + kill (pid, SIGTRAP); +} + +int +hppa_resume_execd_vforking_child_to_get_parent_vfork () +{ + return 1; /* Yes, the child must be resumed. */ +} + +#if defined(HPPA_GET_PROCESS_EVENTS) +process_event_vector +hppa_get_process_events (pid, wait_status, must_continue_pid_after) + int pid; + int wait_status; + int * must_continue_pid_after; +{ + int pt_status; + ptrace_state_t ptrace_state; + process_event_vector events = PEVT_NONE; + + /* This is always TRUE with ptrace. */ + *must_continue_pid_after = 1; + + errno = 0; + pt_status = call_ptrace (PT_GET_PROCESS_STATE, + pid, + (PTRACE_ARG3_TYPE) &ptrace_state, + sizeof (ptrace_state)); + if (errno) + perror_with_name ("ptrace"); + if (pt_status < 0) + return events; + + if (ptrace_state.pe_report_event & PTRACE_SIGNAL) + events |= PEVT_SIGNAL; + if (ptrace_state.pe_report_event & PTRACE_FORK) + events |= PEVT_FORK; + if (ptrace_state.pe_report_event & PTRACE_VFORK) + events |= PEVT_VFORK; + if (ptrace_state.pe_report_event & PTRACE_EXEC) + events |= PEVT_EXEC; + if (ptrace_state.pe_report_event & PTRACE_EXIT) + events |= PEVT_EXIT; + + return events; +} +#endif /* HPPA_GET_PROCESS_EVENTS */ + +void +require_notification_of_events (pid) + int pid; +{ +#if defined(PT_SET_EVENT_MASK) + int pt_status; + ptrace_event_t ptrace_events; + + /* Instruct the kernel as to the set of events we wish to be + informed of. (This support does not exist before HPUX 10.0. + We'll assume if PT_SET_EVENT_MASK has not been defined by + <sys/ptrace.h>, then we're being built on pre-10.0.) + */ + memset (&ptrace_events, 0, sizeof (ptrace_events)); + + /* Note: By default, all signals are visible to us. If we wish + the kernel to keep certain signals hidden from us, we do it + by calling sigdelset (ptrace_events.pe_signals, signal) for + each such signal here, before doing PT_SET_EVENT_MASK. + */ + sigemptyset (&ptrace_events.pe_signals); + + ptrace_events.pe_set_event = 0; + + ptrace_events.pe_set_event |= PTRACE_SIGNAL; + ptrace_events.pe_set_event |= PTRACE_EXEC; + ptrace_events.pe_set_event |= PTRACE_FORK; + ptrace_events.pe_set_event |= PTRACE_VFORK; + /* ??rehrauer: Add this one when we're prepared to catch it... + ptrace_events.pe_set_event |= PTRACE_EXIT; + */ + + errno = 0; + pt_status = call_ptrace (PT_SET_EVENT_MASK, + pid, + (PTRACE_ARG3_TYPE) &ptrace_events, + sizeof (ptrace_events)); + if (errno) + perror_with_name ("ptrace"); + if (pt_status < 0) + return; +#endif +} + +void +require_notification_of_exec_events (pid) + int pid; +{ +#if defined(PT_SET_EVENT_MASK) + int pt_status; + ptrace_event_t ptrace_events; + + /* Instruct the kernel as to the set of events we wish to be + informed of. (This support does not exist before HPUX 10.0. + We'll assume if PT_SET_EVENT_MASK has not been defined by + <sys/ptrace.h>, then we're being built on pre-10.0.) + */ + memset (&ptrace_events, 0, sizeof (ptrace_events)); + + /* Note: By default, all signals are visible to us. If we wish + the kernel to keep certain signals hidden from us, we do it + by calling sigdelset (ptrace_events.pe_signals, signal) for + each such signal here, before doing PT_SET_EVENT_MASK. + */ + sigemptyset (&ptrace_events.pe_signals); + + ptrace_events.pe_set_event = 0; + + ptrace_events.pe_set_event |= PTRACE_EXEC; + /* ??rehrauer: Add this one when we're prepared to catch it... + ptrace_events.pe_set_event |= PTRACE_EXIT; + */ + + errno = 0; + pt_status = call_ptrace (PT_SET_EVENT_MASK, + pid, + (PTRACE_ARG3_TYPE) &ptrace_events, + sizeof (ptrace_events)); + if (errno) + perror_with_name ("ptrace"); + if (pt_status < 0) + return; +#endif +} + +/* This function is called by the parent process, with pid being the + ID of the child process, after the debugger has forked. */ + +void +child_acknowledge_created_inferior (pid) + int pid; +{ + /* We need a memory home for a constant. */ + int tc_magic_parent = PT_VERSION; + int tc_magic_child = 0; + + /* Wait for the child to tell us that it has forked. */ + read (startup_semaphore.child_channel[SEM_LISTEN], + &tc_magic_child, + sizeof(tc_magic_child)); + + /* Notify the child that it can exec. + + In the infttrace.c variant of this function, we set the child's + event mask after the fork but before the exec. In the ptrace + world, it seems we can't set the event mask until after the exec. */ + + write (startup_semaphore.parent_channel[SEM_TALK], + &tc_magic_parent, + sizeof (tc_magic_parent)); + + /* We'd better pause a bit before trying to set the event mask, + though, to ensure that the exec has happened. We don't want to + wait() on the child, because that'll screw up the upper layers + of gdb's execution control that expect to see the exec event. + + After an exec, the child is no longer executing gdb code. Hence, + we can't have yet another synchronization via the pipes. We'll + just sleep for a second, and hope that's enough delay... */ + + sleep (1); + + /* Instruct the kernel as to the set of events we wish to be + informed of. */ + + require_notification_of_exec_events (pid); + + /* Discard our copy of the semaphore. */ + (void) close (startup_semaphore.parent_channel[SEM_LISTEN]); + (void) close (startup_semaphore.parent_channel[SEM_TALK]); + (void) close (startup_semaphore.child_channel[SEM_LISTEN]); + (void) close (startup_semaphore.child_channel[SEM_TALK]); +} + +void +child_post_startup_inferior (pid) + int pid; + +{ + require_notification_of_events (pid); +} + +void +child_post_attach (pid) + int pid; +{ + require_notification_of_events (pid); +} + +int +child_insert_fork_catchpoint (pid) + int pid; +{ + /* This request is only available on HPUX 10.0 and later. */ +#if !defined(PT_SET_EVENT_MASK) + error ("Unable to catch forks prior to HPUX 10.0"); +#else + /* Enable reporting of fork events from the kernel. */ + /* ??rehrauer: For the moment, we're always enabling these events, + and just ignoring them if there's no catchpoint to catch them. + */ + return 0; +#endif +} + +int +child_remove_fork_catchpoint (pid) + int pid; +{ + /* This request is only available on HPUX 10.0 and later. */ +#if !defined(PT_SET_EVENT_MASK) + error ("Unable to catch forks prior to HPUX 10.0"); +#else + /* Disable reporting of fork events from the kernel. */ + /* ??rehrauer: For the moment, we're always enabling these events, + and just ignoring them if there's no catchpoint to catch them. */ + return 0; +#endif +} + +int +child_insert_vfork_catchpoint (pid) + int pid; +{ + /* This request is only available on HPUX 10.0 and later. */ +#if !defined(PT_SET_EVENT_MASK) + error ("Unable to catch vforks prior to HPUX 10.0"); +#else + /* Enable reporting of vfork events from the kernel. */ + /* ??rehrauer: For the moment, we're always enabling these events, + and just ignoring them if there's no catchpoint to catch them. */ + return 0; +#endif +} + +int +child_remove_vfork_catchpoint (pid) + int pid; +{ + /* This request is only available on HPUX 10.0 and later. */ +#if !defined(PT_SET_EVENT_MASK) + error ("Unable to catch vforks prior to HPUX 10.0"); +#else + /* Disable reporting of vfork events from the kernel. */ + /* ??rehrauer: For the moment, we're always enabling these events, + and just ignoring them if there's no catchpoint to catch them. */ + return 0; +#endif +} + +int +child_has_forked (pid, childpid) + int pid; + int * childpid; +{ + /* This request is only available on HPUX 10.0 and later. */ +#if !defined(PT_GET_PROCESS_STATE) + *childpid = 0; + return 0; +#else + int pt_status; + ptrace_state_t ptrace_state; + + errno = 0; + pt_status = call_ptrace (PT_GET_PROCESS_STATE, + pid, + (PTRACE_ARG3_TYPE) &ptrace_state, + sizeof (ptrace_state)); + if (errno) + perror_with_name ("ptrace"); + if (pt_status < 0) + return 0; + + if (ptrace_state.pe_report_event & PTRACE_FORK) + { + *childpid = ptrace_state.pe_other_pid; + return 1; + } + + return 0; +#endif +} + +int +child_has_vforked (pid, childpid) + int pid; + int * childpid; +{ + /* This request is only available on HPUX 10.0 and later. */ +#if !defined(PT_GET_PROCESS_STATE) + *childpid = 0; + return 0; + +#else + int pt_status; + ptrace_state_t ptrace_state; + + errno = 0; + pt_status = call_ptrace (PT_GET_PROCESS_STATE, + pid, + (PTRACE_ARG3_TYPE) &ptrace_state, + sizeof (ptrace_state)); + if (errno) + perror_with_name ("ptrace"); + if (pt_status < 0) + return 0; + + if (ptrace_state.pe_report_event & PTRACE_VFORK) + { + *childpid = ptrace_state.pe_other_pid; + return 1; + } + + return 0; +#endif +} + +int +child_can_follow_vfork_prior_to_exec () +{ + /* ptrace doesn't allow this. */ + return 0; +} + +int +child_insert_exec_catchpoint (pid) + int pid; +{ + /* This request is only available on HPUX 10.0 and later. + */ +#if !defined(PT_SET_EVENT_MASK) + error ("Unable to catch execs prior to HPUX 10.0"); + +#else + /* Enable reporting of exec events from the kernel. */ + /* ??rehrauer: For the moment, we're always enabling these events, + and just ignoring them if there's no catchpoint to catch them. + */ + return 0; +#endif +} + +int +child_remove_exec_catchpoint (pid) + int pid; +{ + /* This request is only available on HPUX 10.0 and later. + */ +#if !defined(PT_SET_EVENT_MASK) + error ("Unable to catch execs prior to HPUX 10.0"); + +#else + /* Disable reporting of exec events from the kernel. */ + /* ??rehrauer: For the moment, we're always enabling these events, + and just ignoring them if there's no catchpoint to catch them. + */ + return 0; +#endif +} + +int +child_has_execd (pid, execd_pathname) + int pid; + char ** execd_pathname; +{ + + /* This request is only available on HPUX 10.0 and later. + */ +#if !defined(PT_GET_PROCESS_STATE) + *execd_pathname = NULL; + return 0; + +#else + int pt_status; + ptrace_state_t ptrace_state; + + errno = 0; + pt_status = call_ptrace (PT_GET_PROCESS_STATE, + pid, + (PTRACE_ARG3_TYPE) &ptrace_state, + sizeof (ptrace_state)); + if (errno) + perror_with_name ("ptrace"); + if (pt_status < 0) + return 0; + + if (ptrace_state.pe_report_event & PTRACE_EXEC) + { + char * exec_file = target_pid_to_exec_file (pid); + *execd_pathname = savestring (exec_file, strlen (exec_file)); + return 1; + } + + return 0; +#endif +} + +int +child_reported_exec_events_per_exec_call () +{ + return 2; /* ptrace reports the event twice per call. */ +} + +int +child_has_syscall_event (pid, kind, syscall_id) + int pid; + enum target_waitkind *kind; + int *syscall_id; +{ + /* This request is only available on HPUX 10.30 and later, via + the ttrace interface. */ + + *kind = TARGET_WAITKIND_SPURIOUS; + *syscall_id = -1; + return 0; +} + +char * +child_pid_to_exec_file (pid) + int pid; +{ + static char exec_file_buffer[1024]; + int pt_status; + CORE_ADDR top_of_stack; + char four_chars[4]; + int name_index; + int i; + int saved_inferior_pid; + boolean done; + + /* As of 10.x HP-UX, there's an explicit request to get the pathname. */ + pt_status = call_ptrace (PT_GET_PROCESS_PATHNAME, + pid, + (PTRACE_ARG3_TYPE) exec_file_buffer, + sizeof (exec_file_buffer) - 1); + if (pt_status == 0) + return exec_file_buffer; + + /* It appears that this request is broken prior to 10.30. + If it fails, try a really, truly amazingly gross hack + that DDE uses, of pawing through the process' data + segment to find the pathname. */ + + top_of_stack = 0x7b03a000; + name_index = 0; + done = 0; + + /* On the chance that pid != inferior_pid, set inferior_pid + to pid, so that (grrrr!) implicit uses of inferior_pid get + the right id. */ + + saved_inferior_pid = inferior_pid; + inferior_pid = pid; + + /* Try to grab a null-terminated string. */ + while (! done) + { + if (target_read_memory (top_of_stack, four_chars, 4) != 0) + { + inferior_pid = saved_inferior_pid; + return NULL; + } + for (i = 0; i < 4; i++) + { + exec_file_buffer[name_index++] = four_chars[i]; + done = (four_chars[i] == '\0'); + if (done) + break; + } + top_of_stack += 4; + } + + if (exec_file_buffer[0] == '\0') + { + inferior_pid = saved_inferior_pid; + return NULL; + } + + inferior_pid = saved_inferior_pid; + return exec_file_buffer; +} + +void +pre_fork_inferior () +{ + int status; + + status = pipe (startup_semaphore.parent_channel); + if (status < 0) + { + warning ("error getting parent pipe for startup semaphore"); + return; + } + + status = pipe (startup_semaphore.child_channel); + if (status < 0) + { + warning ("error getting child pipe for startup semaphore"); + return; + } +} + + +/* Check to see if the given thread is alive. + + This is a no-op, as ptrace doesn't support threads, so we just + return "TRUE". */ + +int +child_thread_alive (pid) + int pid; +{ + return 1; +} + +#endif /* ! GDB_NATIVE_HPUX_11 */ diff --git a/gdb/hpread.c b/gdb/hpread.c index 0e41746..0b464ec 100644 --- a/gdb/hpread.c +++ b/gdb/hpread.c @@ -976,13 +976,13 @@ hpread_psymtab_to_symtab_1 (pst) /* Inform about additional files that need to be read in. */ if (info_verbose) { - fputs_filtered (" ", stdout); + fputs_filtered (" ", gdb_stdout); wrap_here (""); - fputs_filtered ("and ", stdout); + fputs_filtered ("and ", gdb_stdout); wrap_here (""); printf_filtered ("%s...", pst->dependencies[i]->filename); wrap_here (""); /* Flush output */ - fflush (stdout); + gdb_flush (gdb_stdout); } hpread_psymtab_to_symtab_1 (pst->dependencies[i]); } @@ -1032,7 +1032,7 @@ hpread_psymtab_to_symtab (pst) if (info_verbose) { printf_filtered ("Reading in symbols for %s...", pst->filename); - fflush (stdout); + gdb_flush (gdb_stdout); } hpread_psymtab_to_symtab_1 (pst); diff --git a/gdb/hpread.h b/gdb/hpread.h new file mode 100644 index 0000000..7864dd6 --- /dev/null +++ b/gdb/hpread.h @@ -0,0 +1,150 @@ +/* hpread.h + * Common include file for: + * hp_symtab_read.c + * hp_psymtab_read.c + */ + +/* Copyright 1993, 1996 Free Software Foundation, Inc. + + This file is part of GDB. + + 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. + + Written by the Center for Software Science at the University of Utah + and by Cygnus Support. */ + +#include "defs.h" +#include "bfd.h" +#include "gdb_string.h" +#include "hp-symtab.h" +#include "syms.h" +#include "symtab.h" +#include "symfile.h" +#include "objfiles.h" +#include "buildsym.h" +#include "complaints.h" +#include "gdb-stabs.h" +#include "gdbtypes.h" +#include "demangle.h" + +/* Private information attached to an objfile which we use to find + and internalize the HP C debug symbols within that objfile. */ + +struct hpread_symfile_info +{ + /* The contents of each of the debug sections (there are 4 of them). */ + char *gntt; + char *lntt; + char *slt; + char *vt; + + /* We keep the size of the $VT$ section for range checking. */ + unsigned int vt_size; + + /* Some routines still need to know the number of symbols in the + main debug sections ($LNTT$ and $GNTT$). */ + unsigned int lntt_symcount; + unsigned int gntt_symcount; + + /* To keep track of all the types we've processed. */ + struct type **type_vector; + int type_vector_length; + + /* Keeps track of the beginning of a range of source lines. */ + sltpointer sl_index; + + /* Some state variables we'll need. */ + int within_function; + + /* Keep track of the current function's address. We may need to look + up something based on this address. */ + unsigned int current_function_value; +}; + +/* Accessor macros to get at the fields. */ +#define HPUX_SYMFILE_INFO(o) \ + ((struct hpread_symfile_info *)((o)->sym_private)) +#define GNTT(o) (HPUX_SYMFILE_INFO(o)->gntt) +#define LNTT(o) (HPUX_SYMFILE_INFO(o)->lntt) +#define SLT(o) (HPUX_SYMFILE_INFO(o)->slt) +#define VT(o) (HPUX_SYMFILE_INFO(o)->vt) +#define VT_SIZE(o) (HPUX_SYMFILE_INFO(o)->vt_size) +#define LNTT_SYMCOUNT(o) (HPUX_SYMFILE_INFO(o)->lntt_symcount) +#define GNTT_SYMCOUNT(o) (HPUX_SYMFILE_INFO(o)->gntt_symcount) +#define TYPE_VECTOR(o) (HPUX_SYMFILE_INFO(o)->type_vector) +#define TYPE_VECTOR_LENGTH(o) (HPUX_SYMFILE_INFO(o)->type_vector_length) +#define SL_INDEX(o) (HPUX_SYMFILE_INFO(o)->sl_index) +#define WITHIN_FUNCTION(o) (HPUX_SYMFILE_INFO(o)->within_function) +#define CURRENT_FUNCTION_VALUE(o) (HPUX_SYMFILE_INFO(o)->current_function_value) + +/* Given the native debug symbol SYM, set NAMEP to the name associated + with the debug symbol. Note we may be called with a debug symbol which + has no associated name, in that case we return an empty string. + + Also note we "know" that the name for any symbol is always in the + same place. Hence we don't have to conditionalize on the symbol type. */ +#define SET_NAMESTRING(SYM, NAMEP, OBJFILE) \ + if (! hpread_has_name ((SYM)->dblock.kind)) \ + *NAMEP = ""; \ + else if (((unsigned)(SYM)->dsfile.name) >= VT_SIZE (OBJFILE)) \ + { \ + complain (&string_table_offset_complaint, (char *) symnum); \ + *NAMEP = ""; \ + } \ + else \ + *NAMEP = (SYM)->dsfile.name + VT (OBJFILE) + +/* We put a pointer to this structure in the read_symtab_private field + of the psymtab. */ + +struct symloc +{ + /* The offset within the file symbol table of first local symbol for + this file. */ + + int ldsymoff; + + /* Length (in bytes) of the section of the symbol table devoted to + this file's symbols (actually, the section bracketed may contain + more than just this file's symbols). If ldsymlen is 0, the only + reason for this thing's existence is the dependency list. + Nothing else will happen when it is read in. */ + + int ldsymlen; +}; + +#define LDSYMOFF(p) (((struct symloc *)((p)->read_symtab_private))->ldsymoff) +#define LDSYMLEN(p) (((struct symloc *)((p)->read_symtab_private))->ldsymlen) +#define SYMLOC(p) ((struct symloc *)((p)->read_symtab_private)) + +/* FIXME: Shouldn't this stuff be in a .h file somewhere? */ +/* Nonzero means give verbose info on gdb action. */ +extern int info_verbose; + +/* Complaints about the symbols we have encountered. */ +extern struct complaint string_table_offset_complaint; +extern struct complaint lbrac_unmatched_complaint; +extern struct complaint lbrac_mismatch_complaint; + +extern union sltentry *hpread_get_slt + PARAMS ((int, struct objfile *)); + +extern union dnttentry *hpread_get_lntt + PARAMS ((int, struct objfile *)); + +int hpread_has_name + PARAMS ((enum dntt_entry_type)); + +/* end of hpread.h */ diff --git a/gdb/infcmd.c b/gdb/infcmd.c index 4c18f6d..89adb46 100644 --- a/gdb/infcmd.c +++ b/gdb/infcmd.c @@ -32,6 +32,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "target.h" #include "language.h" #include "symfile.h" +#include "objfiles.h" /* Functions exported for general use: */ @@ -91,6 +92,8 @@ static void run_command PARAMS ((char *, int)); void _initialize_infcmd PARAMS ((void)); +#define GO_USAGE "Usage: go <location>\n" + #ifdef CALL_DUMMY_BREAKPOINT_OFFSET static void breakpoint_auto_delete_contents PARAMS ((PTR)); #endif @@ -207,12 +210,19 @@ run_command (args, from_tty) Start it from the beginning? ")) error ("Program not restarted."); target_kill (); +#if defined(SOLIB_RESTART) + SOLIB_RESTART (); +#endif + init_wait_for_inferior (); } clear_breakpoint_hit_counts (); exec_file = (char *) get_exec_file (0); + /* Purge old solib objfiles. */ + objfile_purge_solibs (); + do_run_cleanups (NULL); /* The exec file is re-read every time we do a generic_mourn_inferior, so @@ -249,6 +259,17 @@ Start it from the beginning? ")) target_create_inferior (exec_file, inferior_args, environ_vector (inferior_environ)); } + + +static void +run_no_args_command (args, from_tty) + char *args; + int from_tty; +{ + execute_command("set args", from_tty); + run_command((char *)NULL, from_tty); +} + void continue_command (proc_count_exp, from_tty) @@ -481,6 +502,23 @@ jump_command (arg, from_tty) proceed (addr, TARGET_SIGNAL_0, 0); } + +/* Go to line or address in current procedure */ +static void +go_command(line_no, from_tty) + char *line_no; + int from_tty; +{ + if (line_no == (char *)NULL || !*line_no) + printf_filtered(GO_USAGE); + else + { + tbreak_command(line_no, from_tty); + jump_command(line_no, from_tty); + } +} + + /* Continue program giving it specified signal. */ static void @@ -615,8 +653,10 @@ run_stack_dummy (addr, buffer) } #endif /* CALL_DUMMY_BREAKPOINT_OFFSET. */ + disable_watchpoints_before_interactive_call_start (); proceed_to_finish = 1; /* We want stop_registers, please... */ proceed (addr, TARGET_SIGNAL_0, 0); + enable_watchpoints_after_interactive_call_stop (); discard_cleanups (old_cleanups); @@ -754,6 +794,7 @@ finish_command (arg, from_tty) struct type *value_type; register value_ptr val; CORE_ADDR funcaddr; + int struct_return; value_type = TYPE_TARGET_TYPE (SYMBOL_TYPE (function)); if (!value_type) @@ -764,15 +805,36 @@ finish_command (arg, from_tty) funcaddr = BLOCK_START (SYMBOL_BLOCK_VALUE (function)); - val = value_being_returned (value_type, stop_registers, - using_struct_return (value_of_variable (function, NULL), + struct_return = using_struct_return (value_of_variable (function, NULL), + funcaddr, check_typedef (value_type), - BLOCK_GCC_COMPILED (SYMBOL_BLOCK_VALUE (function)))); - - printf_filtered ("Value returned is $%d = ", record_latest_value (val)); - value_print (val, gdb_stdout, 0, Val_no_prettyprint); - printf_filtered ("\n"); + BLOCK_GCC_COMPILED (SYMBOL_BLOCK_VALUE (function))); + + if (!struct_return) + { + val = value_being_returned (value_type, stop_registers, struct_return); + printf_filtered ("Value returned is $%d = ", record_latest_value (val)); + value_print (val, gdb_stdout, 0, Val_no_prettyprint); + printf_filtered ("\n"); + } + else + { + /* elz: we cannot determine the contents of the structure because + it is on the stack, and we don't know where, since we did not + initiate the call, as opposed to the call_function_by_hand case */ +#ifdef VALUE_RETURNED_FROM_STACK + val = 0; + printf_filtered ("Value returned has type: %s.", TYPE_NAME (value_type)); + printf_filtered (" Cannot determine contents\n"); +#else + val = value_being_returned (value_type, stop_registers, struct_return); + printf_filtered ("Value returned is $%d = ", record_latest_value (val)); + value_print (val, gdb_stdout, 0, Val_no_prettyprint); + printf_filtered ("\n"); +#endif + + } } do_cleanups(old_chain); } @@ -971,6 +1033,7 @@ path_command (dirname, from_tty) if (from_tty) path_info ((char *)NULL, from_tty); } + #ifdef REGISTER_NAMES char *gdb_register_names[] = REGISTER_NAMES; @@ -1186,6 +1249,9 @@ attach_command (args, from_tty) extern int auto_solib_add; #endif + char * exec_file; + char * full_exec_path = NULL; + dont_repeat (); /* Not for the faint of heart */ if (target_has_execution) @@ -1217,15 +1283,43 @@ attach_command (args, from_tty) wait_for_inferior (); #endif + /* + * If no exec file is yet known, try to determine it from the + * process itself. + */ + exec_file = (char *) get_exec_file (0); + if (! exec_file) { + exec_file = target_pid_to_exec_file (inferior_pid); + if (exec_file) { + /* It's possible we don't have a full path, but rather just a + filename. Some targets, such as HP-UX, don't provide the + full path, sigh. + + Attempt to qualify the filename against the source path. + (If that fails, we'll just fall back on the original + filename. Not much more we can do...) + */ + if (!source_full_path_of (exec_file, &full_exec_path)) + full_exec_path = savestring (exec_file, strlen (exec_file)); + + exec_file_attach (full_exec_path, from_tty); + symbol_file_command (full_exec_path, from_tty); + } + } + #ifdef SOLIB_ADD if (auto_solib_add) { /* Add shared library symbols from the newly attached process, if any. */ - SOLIB_ADD ((char *)0, from_tty, (struct target_ops *)0); + SOLIB_ADD ((char *)0, from_tty, ¤t_target); re_enable_breakpoints_in_shlibs (); } #endif + /* Take any necessary post-attaching actions for this platform. + */ + target_post_attach (inferior_pid); + normal_stop (); } @@ -1247,6 +1341,9 @@ detach_command (args, from_tty) { dont_repeat (); /* Not for the faint of heart */ target_detach (args, from_tty); +#if defined(SOLIB_RESTART) + SOLIB_RESTART (); +#endif } /* ARGSUSED */ @@ -1332,12 +1429,15 @@ fully linked executable files and separately compiled object files as needed.", add_com ("attach", class_run, attach_command, "Attach to a process or file outside of GDB.\n\ This command attaches to another target, of the same type as your last\n\ -`target' command (`info files' will show your target stack).\n\ +\"target\" command (\"info files\" will show your target stack).\n\ The command may take as argument a process id or a device file.\n\ For a process id, you must have permission to send the process a signal,\n\ and it must have the same effective uid as the debugger.\n\ -When using \"attach\", you should use the \"file\" command to specify\n\ -the program running in the process, and to load its symbol table."); +When using \"attach\" with a process id, the debugger finds the\n\ +program running in the process, looking first in the current working\n\ +directory, or (if not found there) using the source file search path\n\ +(see the \"directory\" command). You can also use the \"file\" command\n\ +to specify the program, and to load its symbol table."); add_com ("detach", class_run, detach_command, "Detach a process or file previously attached.\n\ @@ -1368,6 +1468,8 @@ Like the \"step\" command as long as subroutine calls do not happen;\n\ when they do, the call is treated as one instruction.\n\ Argument N means do this N times (or till program stops for another reason)."); add_com_alias ("n", "next", class_run, 1); + if (xdb_commands) + add_com_alias("S", "next", class_run, 1); add_com ("step", class_run, step_command, "Step program until it reaches a different source line.\n\ @@ -1385,6 +1487,16 @@ Execution will also stop upon exit from the current stack frame."); Give as argument either LINENUM or *ADDR, where ADDR is an expression\n\ for an address to start at."); + add_com ("go", class_run, go_command, + "Usage: go <location>\n\ +Continue program being debugged, stopping at specified line or \n\ +address.\n\ +Give as argument either LINENUM or *ADDR, where ADDR is an \n\ +expression for an address to start at.\n\ +This command is a combination of tbreak and jump."); + if (xdb_commands) + add_com_alias("g", "g", class_run, 1); + add_com ("continue", class_run, continue_command, "Continue program being debugged, after signal or breakpoint.\n\ If proceeding from breakpoint, a number N may be used as an argument,\n\ @@ -1401,11 +1513,18 @@ With no arguments, uses arguments last specified (with \"run\" or \"set args\"). To cancel previous arguments and run with no arguments,\n\ use \"set args\" without arguments."); add_com_alias ("r", "run", class_run, 1); + if (xdb_commands) + add_com ("R", class_run, run_no_args_command, + "Start debugged program with no arguments."); add_info ("registers", nofp_registers_info, "List of integer registers and their contents, for selected stack frame.\n\ Register name as argument means describe only that register."); + if (xdb_commands) + add_com("lr", class_info, nofp_registers_info, + "List of integer registers and their contents, for selected stack frame.\n\ + Register name as argument means describe only that register."); add_info ("all-registers", all_registers_info, "List of all registers and their contents, for selected stack frame.\n\ Register name as argument means describe only that register."); diff --git a/gdb/inferior.h b/gdb/inferior.h index 02a4e2e..e228fce 100644 --- a/gdb/inferior.h +++ b/gdb/inferior.h @@ -1,6 +1,6 @@ /* Variables that describe the inferior process running under GDB: Where it is, why it stopped, and how to step it. - Copyright 1986, 1989, 1992, 1996 Free Software Foundation, Inc. + Copyright 1986, 1989, 1992, 1996, 1998 Free Software Foundation, Inc. This file is part of GDB. @@ -50,7 +50,6 @@ struct inferior_status { int stop_after_trap; int stop_soon_quietly; CORE_ADDR selected_frame_address; - int selected_level; char stop_registers[REGISTER_BYTES]; /* These are here because if call_function_by_hand has written some @@ -58,6 +57,7 @@ struct inferior_status { any registers. */ char registers[REGISTER_BYTES]; + int selected_level; int breakpoint_proceeded; int restore_stack_info; int proceed_to_finish; @@ -91,6 +91,27 @@ extern char *inferior_io_terminal; extern int inferior_pid; +/* This is only valid when inferior_pid is non-zero. + + If this is 0, then exec events should be noticed and responded to + by the debugger (i.e., be reported to the user). + + If this is > 0, then that many subsequent exec events should be + ignored (i.e., not be reported to the user). + */ +extern int inferior_ignoring_startup_exec_events; + +/* This is only valid when inferior_ignoring_startup_exec_events is + zero. + + Some targets (stupidly) report more than one exec event per actual + call to an event() system call. If only the last such exec event + need actually be noticed and responded to by the debugger (i.e., + be reported to the user), then this is the number of "leading" + exec events which should be ignored. + */ +extern int inferior_ignoring_leading_exec_events; + /* Inferior environment. */ extern struct environ *inferior_environ; @@ -100,9 +121,10 @@ extern struct environ *inferior_environ; extern char registers[]; /* Array of validity bits (one per register). Nonzero at position XXX_REGNUM - means that `registers' contains a valid copy of inferior register XXX. */ + means that `registers' contains a valid copy of inferior register XXX. + -1 if register value is not available. */ -extern char register_valid[NUM_REGS]; +extern SIGNED char register_valid[NUM_REGS]; extern void clear_proceed_status PARAMS ((void)); @@ -122,6 +144,8 @@ extern CORE_ADDR read_pc_pid PARAMS ((int)); extern void write_pc PARAMS ((CORE_ADDR)); +extern void write_pc_pid PARAMS ((CORE_ADDR, int)); + extern CORE_ADDR read_sp PARAMS ((void)); extern void write_sp PARAMS ((CORE_ADDR)); @@ -161,15 +185,23 @@ extern void terminal_inferior PARAMS ((void)); extern void terminal_init_inferior PARAMS ((void)); -#ifdef PROCESS_GROUP_TYPE -extern void terminal_init_inferior_with_pgrp PARAMS ((PROCESS_GROUP_TYPE pgrp)); -#endif +extern void terminal_init_inferior_with_pgrp PARAMS ((int pgrp)); -/* From infptrace.c */ +/* From infptrace.c or infttrace.c */ extern int attach PARAMS ((int)); -void detach PARAMS ((int)); +#if !defined(REQUIRE_ATTACH) +#define REQUIRE_ATTACH attach +#endif + +#if !defined(REQUIRE_DETACH) +#define REQUIRE_DETACH(pid,siggnal) detach (siggnal) +#endif + +extern void detach PARAMS ((int)); + +int proc_wait PARAMS ((int, int *)); extern void child_resume PARAMS ((int, int, enum target_signal)); @@ -179,15 +211,27 @@ extern void child_resume PARAMS ((int, int, enum target_signal)); extern int call_ptrace PARAMS ((int, int, PTRACE_ARG3_TYPE, int)); +extern void pre_fork_inferior PARAMS ((void)); + /* From procfs.c */ extern int proc_iterate_over_mappings PARAMS ((int (*) (int, CORE_ADDR))); +extern int procfs_first_available PARAMS ((void)); + +extern int procfs_get_pid_fd PARAMS ((int)); + /* From fork-child.c */ extern void fork_inferior PARAMS ((char *, char *, char **, - void (*) (void), - int (*) (int), char *)); + void (*) (void), + void (*) (int), + void (*) (void), + char *)); + + +extern void +clone_and_follow_inferior PARAMS ((int, int *)); extern void startup_inferior PARAMS ((int)); @@ -316,8 +360,8 @@ extern int attach_flag; #if !defined (IN_SIGTRAMP) # if defined (SIGTRAMP_START) # define IN_SIGTRAMP(pc, name) \ - ((pc) >= SIGTRAMP_START \ - && (pc) < SIGTRAMP_END \ + ((pc) >= SIGTRAMP_START(pc) \ + && (pc) < SIGTRAMP_END(pc) \ ) # else # define IN_SIGTRAMP(pc, name) \ @@ -373,7 +417,7 @@ extern CORE_ADDR text_end; allocate other kinds of code on the stack. */ #define PC_IN_CALL_DUMMY(pc, sp, frame_address) \ - ((sp) INNER_THAN (pc) && (frame_address != 0) && (pc) INNER_THAN (frame_address)) + (INNER_THAN ((sp), (pc)) && (frame_address != 0) && INNER_THAN ((pc), (frame_address))) #endif /* On stack. */ #if CALL_DUMMY_LOCATION == AT_ENTRY_POINT @@ -383,4 +427,45 @@ extern CORE_ADDR text_end; #endif /* At entry point. */ #endif /* No PC_IN_CALL_DUMMY. */ +/* It's often not enough for our clients to know whether the PC is merely + somewhere within the call dummy. They may need to know whether the + call dummy has actually completed. (For example, wait_for_inferior + wants to know when it should truly stop because the call dummy has + completed. If we're single-stepping because of slow watchpoints, + then we may find ourselves stopped at the entry of the call dummy, + and want to continue stepping until we reach the end.) + + Note that this macro is intended for targets (like HP-UX) which + require more than a single breakpoint in their call dummies, and + therefore cannot use the CALL_DUMMY_BREAKPOINT_OFFSET mechanism. + + If a target does define CALL_DUMMY_BREAKPOINT_OFFSET, then this + default implementation of CALL_DUMMY_HAS_COMPLETED is sufficient. + Else, a target may wish to supply an implementation that works in + the presense of multiple breakpoints in its call dummy. + */ +#if !defined(CALL_DUMMY_HAS_COMPLETED) +#define CALL_DUMMY_HAS_COMPLETED(pc, sp, frame_address) \ + PC_IN_CALL_DUMMY((pc), (sp), (frame_address)) +#endif + +/* If STARTUP_WITH_SHELL is set, GDB's "run" + * will attempts to start up the debugee under a shell. + * This is in order for argument-expansion to occur. E.g., + * (gdb) run * + * The "*" gets expanded by the shell into a list of files. + * While this is a nice feature, it turns out to interact badly + * with some of the catch-fork/catch-exec features we have added. + * In particular, if the shell does any fork/exec's before + * the exec of the target program, that can confuse GDB. + * To disable this feature, set STARTUP_WITH_SHELL to 0. + * To enable this feature, set STARTUP_WITH_SHELL to 1. + * The catch-exec traps expected during start-up will + * be 1 if target is not started up with a shell, 2 if it is. + * - RT + */ +#define STARTUP_WITH_SHELL 1 +#define START_INFERIOR_TRAPS_EXPECTED (STARTUP_WITH_SHELL + 1) + #endif /* !defined (INFERIOR_H) */ + diff --git a/gdb/infrun.c b/gdb/infrun.c index ffd95cb..2f1df81 100644 --- a/gdb/infrun.c +++ b/gdb/infrun.c @@ -51,6 +51,31 @@ static int hook_stop_stub PARAMS ((char *)); static void delete_breakpoint_current_contents PARAMS ((PTR)); +int inferior_ignoring_startup_exec_events = 0; +int inferior_ignoring_leading_exec_events = 0; + +#ifdef HPUXHPPA +/* wait_for_inferior and normal_stop use this to notify the user + when the inferior stopped in a different thread than it had been + running in. */ +static int switched_from_inferior_pid; +#endif + +/* resume and wait_for_inferior use this to ensure that when + stepping over a hit breakpoint in a threaded application + only the thread that hit the breakpoint is stepped and the + other threads don't continue. This prevents having another + thread run past the breakpoint while it is temporarily + removed. + + This is not thread-specific, so it isn't saved as part of + the infrun state. + + Versions of gdb which don't use the "step == this thread steps + and others continue" model but instead use the "step == this + thread steps and others wait" shouldn't do this. */ +static int thread_step_needed = 0; + void _initialize_infrun PARAMS ((void)); /* GET_LONGJMP_TARGET returns the PC at which longjmp() will resume the @@ -212,6 +237,350 @@ static int breakpoints_failed; static int stop_print_frame; +static struct breakpoint *step_resume_breakpoint = NULL; +static struct breakpoint *through_sigtramp_breakpoint = NULL; + +/* On some platforms (e.g., HP-UX), hardware watchpoints have bad + interactions with an inferior that is running a kernel function + (aka, a system call or "syscall"). wait_for_inferior therefore + may have a need to know when the inferior is in a syscall. This + is a count of the number of inferior threads which are known to + currently be running in a syscall. */ +static int number_of_threads_in_syscalls; + +/* This is used to remember when a fork, vfork or exec event + was caught by a catchpoint, and thus the event is to be + followed at the next resume of the inferior, and not + immediately. */ +static struct { + enum target_waitkind kind; + struct { + int parent_pid; + int saw_parent_fork; + int child_pid; + int saw_child_fork; + int saw_child_exec; + } fork_event; + char * execd_pathname; +} pending_follow; + +/* Some platforms don't allow us to do anything meaningful with a + vforked child until it has exec'd. Vforked processes on such + platforms can only be followed after they've exec'd. + + When this is set to 0, a vfork can be immediately followed, + and an exec can be followed merely as an exec. When this is + set to 1, a vfork event has been seen, but cannot be followed + until the exec is seen. + + (In the latter case, inferior_pid is still the parent of the + vfork, and pending_follow.fork_event.child_pid is the child. The + appropriate process is followed, according to the setting of + follow-fork-mode.) */ +static int follow_vfork_when_exec; + +static char * follow_fork_mode_kind_names [] = { +/* ??rehrauer: The "both" option is broken, by what may be a 10.20 + kernel problem. It's also not terribly useful without a GUI to + help the user drive two debuggers. So for now, I'm disabling + the "both" option. + "parent", "child", "both", "ask" }; + */ + "parent", "child", "ask" }; + +static char * follow_fork_mode_string = NULL; + + +#if defined(HPUXHPPA) +static void +follow_inferior_fork (parent_pid, child_pid, has_forked, has_vforked) + int parent_pid; + int child_pid; + int has_forked; + int has_vforked; +{ + int followed_parent = 0; + int followed_child = 0; + int ima_clone = 0; + + /* Which process did the user want us to follow? */ + char * follow_mode = + savestring (follow_fork_mode_string, strlen (follow_fork_mode_string)); + + /* Or, did the user not know, and want us to ask? */ + if (STREQ (follow_fork_mode_string, "ask")) + { + char requested_mode[100]; + + free (follow_mode); + error ("\"ask\" mode NYI"); + follow_mode = savestring (requested_mode, strlen (requested_mode)); + } + + /* If we're to be following the parent, then detach from child_pid. + We're already following the parent, so need do nothing explicit + for it. */ + if (STREQ (follow_mode, "parent")) + { + followed_parent = 1; + + /* We're already attached to the parent, by default. */ + + /* Before detaching from the child, remove all breakpoints from + it. (This won't actually modify the breakpoint list, but will + physically remove the breakpoints from the child.) */ + if (! has_vforked || ! follow_vfork_when_exec) + { + detach_breakpoints (child_pid); + SOLIB_REMOVE_INFERIOR_HOOK (child_pid); + } + + /* Detach from the child. */ + dont_repeat (); + + target_require_detach (child_pid, "", 1); + } + + /* If we're to be following the child, then attach to it, detach + from inferior_pid, and set inferior_pid to child_pid. */ + else if (STREQ (follow_mode, "child")) + { + char child_pid_spelling [100]; /* Arbitrary length. */ + + followed_child = 1; + + /* Before detaching from the parent, detach all breakpoints from + the child. But only if we're forking, or if we follow vforks + as soon as they happen. (If we're following vforks only when + the child has exec'd, then it's very wrong to try to write + back the "shadow contents" of inserted breakpoints now -- they + belong to the child's pre-exec'd a.out.) */ + if (! has_vforked || ! follow_vfork_when_exec) + { + detach_breakpoints (child_pid); + } + + /* Before detaching from the parent, remove all breakpoints from it. */ + remove_breakpoints (); + + /* Also reset the solib inferior hook from the parent. */ + SOLIB_REMOVE_INFERIOR_HOOK (inferior_pid); + + /* Detach from the parent. */ + dont_repeat (); + target_detach (NULL, 1); + + /* Attach to the child. */ + inferior_pid = child_pid; + sprintf (child_pid_spelling, "%d", child_pid); + dont_repeat (); + + target_require_attach (child_pid_spelling, 1); + + /* Was there a step_resume breakpoint? (There was if the user + did a "next" at the fork() call.) If so, explicitly reset its + thread number. + + step_resumes are a form of bp that are made to be per-thread. + Since we created the step_resume bp when the parent process + was being debugged, and now are switching to the child process, + from the breakpoint package's viewpoint, that's a switch of + "threads". We must update the bp's notion of which thread + it is for, or it'll be ignored when it triggers... */ + if (step_resume_breakpoint && (! has_vforked || ! follow_vfork_when_exec)) + breakpoint_re_set_thread (step_resume_breakpoint); + + /* Reinsert all breakpoints in the child. (The user may've set + breakpoints after catching the fork, in which case those + actually didn't get set in the child, but only in the parent.) */ + if (! has_vforked || ! follow_vfork_when_exec) + { + breakpoint_re_set (); + insert_breakpoints (); + } + } + + /* If we're to be following both parent and child, then fork ourselves, + and attach the debugger clone to the child. */ + else if (STREQ (follow_mode, "both")) + { + char pid_suffix [100]; /* Arbitrary length. */ + + /* Clone ourselves to follow the child. This is the end of our + involvement with child_pid; our clone will take it from here... */ + dont_repeat (); + target_clone_and_follow_inferior (child_pid, &followed_child); + followed_parent = !followed_child; + + /* We continue to follow the parent. To help distinguish the two + debuggers, though, both we and our clone will reset our prompts. */ + sprintf (pid_suffix, "[%d] ", inferior_pid); + set_prompt (strcat (get_prompt (), pid_suffix)); + } + + /* The parent and child of a vfork share the same address space. + Also, on some targets the order in which vfork and exec events + are received for parent in child requires some delicate handling + of the events. + + For instance, on ptrace-based HPUX we receive the child's vfork + event first, at which time the parent has been suspended by the + OS and is essentially untouchable until the child's exit or second + exec event arrives. At that time, the parent's vfork event is + delivered to us, and that's when we see and decide how to follow + the vfork. But to get to that point, we must continue the child + until it execs or exits. To do that smoothly, all breakpoints + must be removed from the child, in case there are any set between + the vfork() and exec() calls. But removing them from the child + also removes them from the parent, due to the shared-address-space + nature of a vfork'd parent and child. On HPUX, therefore, we must + take care to restore the bp's to the parent before we continue it. + Else, it's likely that we may not stop in the expected place. (The + worst scenario is when the user tries to step over a vfork() call; + the step-resume bp must be restored for the step to properly stop + in the parent after the call completes!) + + Sequence of events, as reported to gdb from HPUX: + + Parent Child Action for gdb to take + ------------------------------------------------------- + 1 VFORK Continue child + 2 EXEC + 3 EXEC or EXIT + 4 VFORK */ + if (has_vforked) + { + target_post_follow_vfork (parent_pid, + followed_parent, + child_pid, + followed_child); + } + + pending_follow.fork_event.saw_parent_fork = 0; + pending_follow.fork_event.saw_child_fork = 0; + + free (follow_mode); +} + +static void +follow_fork (parent_pid, child_pid) + int parent_pid; + int child_pid; +{ + follow_inferior_fork (parent_pid, child_pid, 1, 0); +} + + +/* Forward declaration. */ +static void follow_exec PARAMS((int, char *)); + +static void +follow_vfork (parent_pid, child_pid) + int parent_pid; + int child_pid; +{ + follow_inferior_fork (parent_pid, child_pid, 0, 1); + + /* Did we follow the child? Had it exec'd before we saw the parent vfork? */ + if (pending_follow.fork_event.saw_child_exec && (inferior_pid == child_pid)) + { + pending_follow.fork_event.saw_child_exec = 0; + pending_follow.kind = TARGET_WAITKIND_SPURIOUS; + follow_exec (inferior_pid, pending_follow.execd_pathname); + free (pending_follow.execd_pathname); + } +} +#endif /* HPUXHPPA */ + +static void +follow_exec (pid, execd_pathname) + int pid; + char * execd_pathname; +{ +#ifdef HPUXHPPA + int saved_pid = pid; + extern struct target_ops child_ops; + + /* Did this exec() follow a vfork()? If so, we must follow the + vfork now too. Do it before following the exec. */ + if (follow_vfork_when_exec && (pending_follow.kind == TARGET_WAITKIND_VFORKED)) + { + pending_follow.kind = TARGET_WAITKIND_SPURIOUS; + follow_vfork (inferior_pid, pending_follow.fork_event.child_pid); + follow_vfork_when_exec = 0; + saved_pid = inferior_pid; + + /* Did we follow the parent? If so, we're done. If we followed + the child then we must also follow its exec(). */ + if (inferior_pid == pending_follow.fork_event.parent_pid) + return; + } + + /* This is an exec event that we actually wish to pay attention to. + Refresh our symbol table to the newly exec'd program, remove any + momentary bp's, etc. + + If there are breakpoints, they aren't really inserted now, + since the exec() transformed our inferior into a fresh set + of instructions. + + We want to preserve symbolic breakpoints on the list, since + we have hopes that they can be reset after the new a.out's + symbol table is read. + + However, any "raw" breakpoints must be removed from the list + (e.g., the solib bp's), since their address is probably invalid + now. + + And, we DON'T want to call delete_breakpoints() here, since + that may write the bp's "shadow contents" (the instruction + value that was overwritten witha TRAP instruction). Since + we now have a new a.out, those shadow contents aren't valid. */ + update_breakpoints_after_exec (); + + /* If there was one, it's gone now. We cannot truly step-to-next + statement through an exec(). */ + step_resume_breakpoint = NULL; + step_range_start = 0; + step_range_end = 0; + + /* If there was one, it's gone now. */ + through_sigtramp_breakpoint = NULL; + + /* What is this a.out's name? */ + printf_unfiltered ("Executing new program: %s\n", execd_pathname); + + /* We've followed the inferior through an exec. Therefore, the + inferior has essentially been killed & reborn. */ + gdb_flush (gdb_stdout); + target_mourn_inferior (); + inferior_pid = saved_pid; /* Because mourn_inferior resets inferior_pid. */ + push_target (&child_ops); + + /* That a.out is now the one to use. */ + exec_file_attach (execd_pathname, 0); + + /* And also is where symbols can be found. */ + symbol_file_command (execd_pathname, 0); + + /* Reset the shared library package. This ensures that we get + a shlib event when the child reaches "_start", at which point + the dld will have had a chance to initialize the child. */ + SOLIB_RESTART (); + SOLIB_CREATE_INFERIOR_HOOK (inferior_pid); + + /* Reinsert all breakpoints. (Those which were symbolic have + been reset to the proper address in the new a.out, thanks + to symbol_file_command...) */ + insert_breakpoints (); + + /* The next resume of this inferior should bring it to the shlib + startup breakpoints. (If the user had also set bp's on + "main" from the old (parent) process, then they'll auto- + matically get reset there in the new process.) */ +#endif +} + /* Non-zero if we just simulating a single-step. This is needed because we cannot remove the breakpoints in the inferior process until after the `wait' in `wait_for_inferior'. */ @@ -240,6 +609,7 @@ resume (step, sig) int step; enum target_signal sig; { + int should_resume = 1; struct cleanup *old_cleanups = make_cleanup ((make_cleanup_func) resume_cleanups, 0); QUIT; @@ -268,10 +638,84 @@ resume (step, sig) DO_DEFERRED_STORES; #endif +#ifdef HPUXHPPA + /* If there were any forks/vforks/execs that were caught and are + now to be followed, then do so. */ + switch (pending_follow.kind) + { + case (TARGET_WAITKIND_FORKED): + pending_follow.kind = TARGET_WAITKIND_SPURIOUS; + follow_fork (inferior_pid, pending_follow.fork_event.child_pid); + break; + + case (TARGET_WAITKIND_VFORKED): + { + int saw_child_exec = pending_follow.fork_event.saw_child_exec; + + pending_follow.kind = TARGET_WAITKIND_SPURIOUS; + follow_vfork (inferior_pid, pending_follow.fork_event.child_pid); + + /* Did we follow the child, but not yet see the child's exec event? + If so, then it actually ought to be waiting for us; we respond to + parent vfork events. We don't actually want to resume the child + in this situation; we want to just get its exec event. */ + if (! saw_child_exec && (inferior_pid == pending_follow.fork_event.child_pid)) + should_resume = 0; + } + break; + + case (TARGET_WAITKIND_EXECD): + /* If we saw a vfork event but couldn't follow it until we saw + an exec, then now might be the time! */ + pending_follow.kind = TARGET_WAITKIND_SPURIOUS; + /* follow_exec is called as soon as the exec event is seen. */ + break; + + default: + break; + } +#endif /* HPUXHPPA */ + /* Install inferior's terminal modes. */ target_terminal_inferior (); - target_resume (-1, step, sig); + if (should_resume) + { +#ifdef HPUXHPPA + if (thread_step_needed) + { + /* We stopped on a BPT instruction; + don't continue other threads and + just step this thread. */ + thread_step_needed = 0; + + if (!breakpoint_here_p(read_pc())) + { + /* Breakpoint deleted: ok to do regular resume + where all the threads either step or continue. */ + target_resume (-1, step, sig); + } + else + { + if (!step) + { + warning ( "Internal error, changing continue to step." ); + remove_breakpoints (); + breakpoints_inserted = 0; + trap_expected = 1; + step = 1; + } + + target_resume (inferior_pid, step, sig); + } + } + else +#endif /* HPUXHPPA */ + + /* Vanilla resume. */ + target_resume (-1, step, sig); + } + discard_cleanups (old_cleanups); } @@ -325,7 +769,8 @@ proceed (addr, siggnal, step) { /* If there is a breakpoint at the address we will resume at, step one instruction before inserting breakpoints - so that we do not stop right away. */ + so that we do not stop right away (and report a second + hit at this breakpoint). */ if (read_pc () == stop_pc && breakpoint_here_p (read_pc ())) oneproc = 1; @@ -343,24 +788,38 @@ proceed (addr, siggnal, step) oneproc = 1; } else - write_pc (addr); + { + write_pc (addr); + + /* New address; we don't need to single-step a thread + over a breakpoint we just hit, 'cause we aren't + continuing from there. + + It's not worth worrying about the case where a user + asks for a "jump" at the current PC--if they get the + hiccup of re-hiting a hit breakpoint, what else do + they expect? */ + thread_step_needed = 0; + } #ifdef PREPARE_TO_PROCEED - /* In a multi-threaded task we may select another thread and then continue. - - In this case the thread that stopped at a breakpoint will immediately - cause another stop, if it is not stepped over first. On the other hand, - if (ADDR != -1) we only want to single step over the breakpoint if we did - switch to another thread. - - If we are single stepping, don't do any of the above. - (Note that in the current implementation single stepping another - thread after a breakpoint and then continuing will cause the original - breakpoint to be hit again, but you can always continue, so it's not - a big deal.) */ - - if (! step && PREPARE_TO_PROCEED (1) && breakpoint_here_p (read_pc ())) - oneproc = 1; + /* In a multi-threaded task we may select another thread + and then continue or step. + + But if the old thread was stopped at a breakpoint, it + will immediately cause another breakpoint stop without + any execution (i.e. it will report a breakpoint hit + incorrectly). So we must step over it first. + + PREPARE_TO_PROCEED checks the current thread against the thread + that reported the most recent event. If a step-over is required + it returns TRUE and sets the current thread to the old thread. */ + if (PREPARE_TO_PROCEED() && breakpoint_here_p (read_pc())) + { + oneproc = 1; + thread_step_needed = 1; + } + #endif /* PREPARE_TO_PROCEED */ #ifdef HP_OS_BUG @@ -388,6 +847,7 @@ proceed (addr, siggnal, step) error ("Cannot insert breakpoints.\n\ The same program may be running in another process."); } + breakpoints_inserted = 1; } @@ -430,7 +890,6 @@ start_remote () { init_thread_list (); init_wait_for_inferior (); - clear_proceed_status (); stop_soon_quietly = 1; trap_expected = 0; wait_for_inferior (); @@ -451,10 +910,21 @@ init_wait_for_inferior () trap_expected_after_continue = 0; #endif breakpoints_inserted = 0; - breakpoint_init_inferior (); + breakpoint_init_inferior (inf_starting); /* Don't confuse first call to proceed(). */ stop_signal = TARGET_SIGNAL_0; + + /* The first resume is not following a fork/vfork/exec. */ + pending_follow.kind = TARGET_WAITKIND_SPURIOUS; /* I.e., none. */ + pending_follow.fork_event.saw_parent_fork = 0; + pending_follow.fork_event.saw_child_fork = 0; + pending_follow.fork_event.saw_child_exec = 0; + + /* See wait_for_inferior's handling of SYSCALL_ENTRY/RETURN events. */ + number_of_threads_in_syscalls = 0; + + clear_proceed_status (); } static void @@ -462,8 +932,11 @@ delete_breakpoint_current_contents (arg) PTR arg; { struct breakpoint **breakpointp = (struct breakpoint **)arg; - if (*breakpointp != NULL) - delete_breakpoint (*breakpointp); + if (*breakpointp != NULL) + { + delete_breakpoint (*breakpointp); + *breakpointp = NULL; + } } /* Wait for control to return from inferior to debugger. @@ -491,10 +964,18 @@ wait_for_inferior () int current_line; struct symtab *current_symtab; int handling_longjmp = 0; /* FIXME */ - struct breakpoint *step_resume_breakpoint = NULL; - struct breakpoint *through_sigtramp_breakpoint = NULL; int pid; + int saved_inferior_pid; int update_step_sp = 0; + int stepping_through_solib_after_catch = 0; + bpstat stepping_through_solib_catchpoints = NULL; + int enable_hw_watchpoints_after_wait = 0; + int stepping_through_sigtramp = 0; + int new_thread_event; + +#ifdef HAVE_NONSTEPPABLE_WATCHPOINT + int stepped_after_stopped_by_watchpoint; +#endif old_cleanups = make_cleanup (delete_breakpoint_current_contents, &step_resume_breakpoint); @@ -510,8 +991,16 @@ wait_for_inferior () && !handling_longjmp \ && ((step_range_end && step_resume_breakpoint == NULL) \ || trap_expected)) \ + || stepping_through_solib_after_catch \ || bpstat_should_step ()) +thread_step_needed = 0; + +#ifdef HPUXHPPA + /* We'll update this if & when we switch to a new thread. */ + switched_from_inferior_pid = inferior_pid; +#endif + while (1) { extern int overlay_cache_invalid; /* declared in symfile.h */ @@ -530,6 +1019,24 @@ wait_for_inferior () else pid = target_wait (-1, &w); + /* Since we've done a wait, we have a new event. Don't carry + over any expectations about needing to step over a + breakpoint. */ + thread_step_needed = 0; + + /* See comments where a TARGET_WAITKIND_SYSCALL_RETURN event is + serviced in this loop, below. */ + if (enable_hw_watchpoints_after_wait) + { + TARGET_ENABLE_HW_WATCHPOINTS(inferior_pid); + enable_hw_watchpoints_after_wait = 0; + } + + +#ifdef HAVE_NONSTEPPABLE_WATCHPOINT +stepped_after_stopped_by_watchpoint = 0; +#endif + /* Gross. We goto this label from elsewhere in wait_for_inferior when we want @@ -541,14 +1048,23 @@ wait_for_inferior () /* If it's a new process, add it to the thread database */ + new_thread_event = ((pid != inferior_pid) && !in_thread_list (pid)); + if (w.kind != TARGET_WAITKIND_EXITED && w.kind != TARGET_WAITKIND_SIGNALLED - && pid != inferior_pid - && !in_thread_list (pid)) + && new_thread_event) { add_thread (pid); + + +#ifdef HPUXHPPA + fprintf_unfiltered (gdb_stderr, "[New %s]\n", target_pid_or_tid_to_str (pid)); + +#else printf_filtered ("[New %s]\n", target_pid_to_str (pid)); +#endif +#if 0 /* We may want to consider not doing a resume here in order to give the user a chance to play with the new thread. It might be good to make that a user-settable option. */ @@ -559,7 +1075,8 @@ wait_for_inferior () target_resume (-1, 0, TARGET_SIGNAL_0); continue; - } +#endif + } switch (w.kind) { @@ -648,13 +1165,235 @@ wait_for_inferior () singlestep_breakpoints_inserted_p = 0; /*SOFTWARE_SINGLE_STEP_P*/ goto stop_stepping; + /* The following are the only cases in which we keep going; + the above cases end in a continue or goto. */ + case TARGET_WAITKIND_FORKED: + stop_signal = TARGET_SIGNAL_TRAP; + pending_follow.kind = w.kind; + + /* Ignore fork events reported for the parent; we're only + interested in reacting to forks of the child. Note that + we expect the child's fork event to be available if we + waited for it now. */ + if (inferior_pid == pid) + { + pending_follow.fork_event.saw_parent_fork = 1; + pending_follow.fork_event.parent_pid = pid; + pending_follow.fork_event.child_pid = w.value.related_pid; + continue; + } + else + { + pending_follow.fork_event.saw_child_fork = 1; + pending_follow.fork_event.child_pid = pid; + pending_follow.fork_event.parent_pid = w.value.related_pid; + } + + stop_pc = read_pc_pid (pid); + saved_inferior_pid = inferior_pid; + inferior_pid = pid; + stop_bpstat = bpstat_stop_status (&stop_pc, +#if DECR_PC_AFTER_BREAK + (prev_pc != stop_pc - DECR_PC_AFTER_BREAK + && CURRENTLY_STEPPING ()) +#else /* DECR_PC_AFTER_BREAK zero */ + 0 +#endif /* DECR_PC_AFTER_BREAK zero */ + ); + random_signal = ! bpstat_explains_signal (stop_bpstat); + inferior_pid = saved_inferior_pid; + goto process_event_stop_test; + + /* If this a platform which doesn't allow a debugger to touch a + vfork'd inferior until after it exec's, then we'd best keep + our fingers entirely off the inferior, other than continuing + it. This has the unfortunate side-effect that catchpoints + of vforks will be ignored. But since the platform doesn't + allow the inferior be touched at vfork time, there's really + little choice. */ + case TARGET_WAITKIND_VFORKED: + stop_signal = TARGET_SIGNAL_TRAP; + pending_follow.kind = w.kind; + + /* Is this a vfork of the parent? If so, then give any + vfork catchpoints a chance to trigger now. (It's + dangerous to do so if the child canot be touched until + it execs, and the child has not yet exec'd. We probably + should warn the user to that effect when the catchpoint + triggers...) */ + if (pid == inferior_pid) + { + pending_follow.fork_event.saw_parent_fork = 1; + pending_follow.fork_event.parent_pid = pid; + pending_follow.fork_event.child_pid = w.value.related_pid; + } + + /* If we've seen the child's vfork event but cannot really touch + the child until it execs, then we must continue the child now. + Else, give any vfork catchpoints a chance to trigger now. */ + else + { + pending_follow.fork_event.saw_child_fork = 1; + pending_follow.fork_event.child_pid = pid; + pending_follow.fork_event.parent_pid = w.value.related_pid; + target_post_startup_inferior (pending_follow.fork_event.child_pid); + follow_vfork_when_exec = ! target_can_follow_vfork_prior_to_exec (); + if (follow_vfork_when_exec) + { + target_resume (pid, 0, TARGET_SIGNAL_0); + continue; + } + } + + stop_pc = read_pc (); + stop_bpstat = bpstat_stop_status (&stop_pc, +#if DECR_PC_AFTER_BREAK + (prev_pc != stop_pc - DECR_PC_AFTER_BREAK + && CURRENTLY_STEPPING ()) +#else /* DECR_PC_AFTER_BREAK zero */ + 0 +#endif /* DECR_PC_AFTER_BREAK zero */ + ); + random_signal = ! bpstat_explains_signal (stop_bpstat); + goto process_event_stop_test; + + case TARGET_WAITKIND_EXECD: + stop_signal = TARGET_SIGNAL_TRAP; + + /* Is this a target which reports multiple exec events per actual + call to exec()? (HP-UX using ptrace does, for example.) If so, + ignore all but the last one. Just resume the exec'r, and wait + for the next exec event. */ + if (inferior_ignoring_leading_exec_events) + { + inferior_ignoring_leading_exec_events--; + if (pending_follow.kind == TARGET_WAITKIND_VFORKED) + ENSURE_VFORKING_PARENT_REMAINS_STOPPED(pending_follow.fork_event.parent_pid); + target_resume (pid, 0, TARGET_SIGNAL_0); + continue; + } + inferior_ignoring_leading_exec_events = + target_reported_exec_events_per_exec_call () - 1; + + pending_follow.execd_pathname = savestring (w.value.execd_pathname, + strlen (w.value.execd_pathname)); + + /* Did inferior_pid exec, or did a (possibly not-yet-followed) + child of a vfork exec? + + ??rehrauer: This is unabashedly an HP-UX specific thing. On + HP-UX, events associated with a vforking inferior come in + threes: a vfork event for the child (always first), followed + a vfork event for the parent and an exec event for the child. + The latter two can come in either order. + + If we get the parent vfork event first, life's good: We follow + either the parent or child, and then the child's exec event is + a "don't care". + + But if we get the child's exec event first, then we delay + responding to it until we handle the parent's vfork. Because, + otherwise we can't satisfy a "catch vfork". */ + if (pending_follow.kind == TARGET_WAITKIND_VFORKED) + { + pending_follow.fork_event.saw_child_exec = 1; + + /* On some targets, the child must be resumed before + the parent vfork event is delivered. A single-step + suffices. */ + if (RESUME_EXECD_VFORKING_CHILD_TO_GET_PARENT_VFORK()) + target_resume (pid, 1, TARGET_SIGNAL_0); + continue; /* We expect the parent vfork event to be available now. */ + } + + /* This causes the eventpoints and symbol table to be reset. Must + do this now, before trying to determine whether to stop. */ + follow_exec (inferior_pid, pending_follow.execd_pathname); + free (pending_follow.execd_pathname); + + stop_pc = read_pc_pid (pid); + saved_inferior_pid = inferior_pid; + inferior_pid = pid; + stop_bpstat = bpstat_stop_status (&stop_pc, +#if DECR_PC_AFTER_BREAK + (prev_pc != stop_pc - DECR_PC_AFTER_BREAK + && CURRENTLY_STEPPING ()) +#else /* DECR_PC_AFTER_BREAK zero */ + 0 +#endif /* DECR_PC_AFTER_BREAK zero */ + ); + random_signal = ! bpstat_explains_signal (stop_bpstat); + inferior_pid = saved_inferior_pid; + goto process_event_stop_test; + + /* These syscall events are returned on HP-UX, as part of its + implementation of page-protection-based "hardware" watchpoints. + HP-UX has unfortunate interactions between page-protections and + some system calls. Our solution is to disable hardware watches + when a system call is entered, and reenable them when the syscall + completes. The downside of this is that we may miss the precise + point at which a watched piece of memory is modified. "Oh well." + + Note that we may have multiple threads running, which may each + enter syscalls at roughly the same time. Since we don't have a + good notion currently of whether a watched piece of memory is + thread-private, we'd best not have any page-protections active + when any thread is in a syscall. Thus, we only want to reenable + hardware watches when no threads are in a syscall. + + Also, be careful not to try to gather much state about a thread + that's in a syscall. It's frequently a losing proposition. */ + case TARGET_WAITKIND_SYSCALL_ENTRY: + number_of_threads_in_syscalls++; + if (number_of_threads_in_syscalls == 1) + { + TARGET_DISABLE_HW_WATCHPOINTS(inferior_pid); + } + resume (0, TARGET_SIGNAL_0); + continue; + + /* Before examining the threads further, step this thread to + get it entirely out of the syscall. (We get notice of the + event when the thread is just on the verge of exiting a + syscall. Stepping one instruction seems to get it back + into user code.) + + Note that although the logical place to reenable h/w watches + is here, we cannot. We cannot reenable them before stepping + the thread (this causes the next wait on the thread to hang). + + Nor can we enable them after stepping until we've done a + wait. Thus, we simply set the flag enable_hw_watchpoints_after_wait + here, which will be serviced immediately after the target + is waited on. */ + case TARGET_WAITKIND_SYSCALL_RETURN: + target_resume (pid, 1, TARGET_SIGNAL_0); + + if (number_of_threads_in_syscalls > 0) + { + number_of_threads_in_syscalls--; + enable_hw_watchpoints_after_wait = + (number_of_threads_in_syscalls == 0); + } + continue; + case TARGET_WAITKIND_STOPPED: - /* This is the only case in which we keep going; the above cases - end in a continue or goto. */ + stop_signal = w.value.sig; break; } - stop_signal = w.value.sig; + /* We may want to consider not doing a resume here in order to give + the user a chance to play with the new thread. It might be good + to make that a user-settable option. */ + + /* At this point, all threads are stopped (happens automatically in + either the OS or the native code). Therefore we need to continue + all threads in order to make progress. */ + if (new_thread_event) + { + target_resume (-1, 0, TARGET_SIGNAL_0); + continue; + } stop_pc = read_pc_pid (pid); @@ -673,10 +1412,24 @@ wait_for_inferior () random_signal = 0; if (!breakpoint_thread_match (stop_pc - DECR_PC_AFTER_BREAK, pid)) { + int remove_status; + /* Saw a breakpoint, but it was hit by the wrong thread. Just continue. */ write_pc_pid (stop_pc - DECR_PC_AFTER_BREAK, pid); - remove_breakpoints (); + remove_status = remove_breakpoints (); + /* Did we fail to remove breakpoints? If so, try to set the + PC past the bp. (There's at least one situation in which + we can fail to remove the bp's: On HP-UX's that use ttrace, + we can't change the address space of a vforking child process + until the child exits (well, okay, not then either :-) or + execs. */ + if (remove_status != 0) + { + write_pc_pid (stop_pc - DECR_PC_AFTER_BREAK + 4, pid); + } + else + { target_resume (pid, 1, TARGET_SIGNAL_0); /* Single step */ /* FIXME: What if a signal arrives instead of the single-step happening? */ @@ -686,36 +1439,60 @@ wait_for_inferior () else target_wait (pid, &w); insert_breakpoints (); + } /* We need to restart all the threads now. */ target_resume (-1, 0, TARGET_SIGNAL_0); continue; } - } + else + { + /* This breakpoint matches--either it is the right + thread or it's a generic breakpoint for all threads. + Remember that we'll need to step just _this_ thread + on any following user continuation! */ + thread_step_needed = 1; + } + } } else random_signal = 1; /* See if something interesting happened to the non-current thread. If so, then switch to that thread, and eventually give control back to - the user. */ + the user. - if (pid != inferior_pid) + Note that if there's any kind of pending follow (i.e., of a fork, + vfork or exec), we don't want to do this now. Rather, we'll let + the next resume handle it. */ + if ((pid != inferior_pid) && + (pending_follow.kind == TARGET_WAITKIND_SPURIOUS)) { int printed = 0; /* If it's a random signal for a non-current thread, notify user - if he's expressed an interest. */ - + if he's expressed an interest. */ if (random_signal && signal_print[stop_signal]) { +/* ??rehrauer: I don't understand the rationale for this code. If the + inferior will stop as a result of this signal, then the act of handling + the stop ought to print a message that's couches the stoppage in user + terms, e.g., "Stopped for breakpoint/watchpoint". If the inferior + won't stop as a result of the signal -- i.e., if the signal is merely + a side-effect of something GDB's doing "under the covers" for the + user, such as stepping threads over a breakpoint they shouldn't stop + for -- then the message seems to be a serious annoyance at best. + + For now, remove the message altogether. */ +#if 0 printed = 1; target_terminal_ours_for_output (); printf_filtered ("\nProgram received signal %s, %s.\n", target_signal_to_name (stop_signal), target_signal_to_string (stop_signal)); gdb_flush (gdb_stdout); +#endif } /* If it's not SIGTRAP and not a signal we want to stop for, then @@ -745,7 +1522,14 @@ wait_for_inferior () through_sigtramp_breakpoint, step_range_start, step_range_end, step_frame_address, handling_longjmp, - another_trap); + another_trap, + stepping_through_solib_after_catch, + stepping_through_solib_catchpoints, + stepping_through_sigtramp); + +#ifdef HPUXHPPA + switched_from_inferior_pid = inferior_pid; +#endif inferior_pid = pid; @@ -756,7 +1540,10 @@ wait_for_inferior () &through_sigtramp_breakpoint, &step_range_start, &step_range_end, &step_frame_address, &handling_longjmp, - &another_trap); + &another_trap, + &stepping_through_solib_after_catch, + &stepping_through_solib_catchpoints, + &stepping_through_sigtramp); if (context_hook) context_hook (pid_to_thread_id (pid)); @@ -776,10 +1563,16 @@ wait_for_inferior () it so that the user won't be confused when GDB appears to be ready to execute it. */ +#if 0 /* XXX DEBUG */ + printf ("infrun.c:1607: pc = 0x%x\n", read_pc ()); +#endif + /* if (INSTRUCTION_NULLIFIED && CURRENTLY_STEPPING ()) */ if (INSTRUCTION_NULLIFIED) { struct target_waitstatus tmpstatus; - +#if 0 + all_registers_info ((char *)0, 0); +#endif registers_changed (); target_resume (pid, 1, TARGET_SIGNAL_0); @@ -792,7 +1585,6 @@ wait_for_inferior () else target_wait (pid, &tmpstatus); - goto have_waited; } @@ -839,9 +1631,11 @@ wait_for_inferior () else target_wait (pid, &w); insert_breakpoints (); + /* FIXME-maybe: is this cleaner than setting a flag? Does it handle things like signals arriving and other things happening in combination correctly? */ + stepped_after_stopped_by_watchpoint = 1; goto have_waited; } #endif @@ -927,7 +1721,7 @@ wait_for_inferior () (CURRENTLY_STEPPING () && prev_pc != stop_pc - DECR_PC_AFTER_BREAK && !(step_range_end - && read_sp () INNER_THAN (step_sp - 16))) : + && INNER_THAN (read_sp (), (step_sp - 16)))) : 0) ); /* Following in case break condition called a @@ -944,6 +1738,7 @@ wait_for_inferior () FRAME_FP (get_current_frame ())) #endif /* No CALL_DUMMY_BREAKPOINT_OFFSET. */ || (step_range_end && step_resume_breakpoint == NULL)); + else { random_signal @@ -960,8 +1755,57 @@ wait_for_inferior () stop_signal = TARGET_SIGNAL_TRAP; } } + + /* When we reach this point, we've pretty much decided + that the reason for stopping must've been a random + (unexpected) signal. */ + else random_signal = 1; + /* If a fork, vfork or exec event was seen, then there are two + possible responses we can make: + + 1. If a catchpoint triggers for the event (random_signal == 0), + then we must stop now and issue a prompt. We will resume + the inferior when the user tells us to. + 2. If no catchpoint triggers for the event (random_signal == 1), + then we must resume the inferior now and keep checking. + + In either case, we must take appropriate steps to "follow" the + the fork/vfork/exec when the inferior is resumed. For example, + if follow-fork-mode is "child", then we must detach from the + parent inferior and follow the new child inferior. + + In either case, setting pending_follow causes the next resume() + to take the appropriate following action. */ +process_event_stop_test: + if (w.kind == TARGET_WAITKIND_FORKED) + { + if (random_signal) /* I.e., no catchpoint triggered for this. */ + { + trap_expected = 1; + stop_signal = TARGET_SIGNAL_0; + goto keep_going; + } + } + else if (w.kind == TARGET_WAITKIND_VFORKED) + { + if (random_signal) /* I.e., no catchpoint triggered for this. */ + { + stop_signal = TARGET_SIGNAL_0; + goto keep_going; + } + } + else if (w.kind == TARGET_WAITKIND_EXECD) + { + pending_follow.kind = w.kind; + if (random_signal) /* I.e., no catchpoint triggered for this. */ + { + trap_expected = 1; + stop_signal = TARGET_SIGNAL_0; + goto keep_going; + } + } /* For the program's own signals, act according to the signal handling tables. */ @@ -1000,9 +1844,16 @@ wait_for_inferior () if (signal_program[stop_signal] == 0) stop_signal = TARGET_SIGNAL_0; - /* I'm not sure whether this needs to be check_sigtramp2 or - whether it could/should be keep_going. */ - goto check_sigtramp2; + /* If we're in the middle of a "next" command, let the code for + stepping over a function handle this. pai/1997-09-10 + + A previous comment here suggested it was possible to change + this to jump to keep_going in all cases. */ + + if (step_over_calls > 0) + goto step_over_function; + else + goto check_sigtramp2; } /* Handle cases caused by hitting a breakpoint. */ @@ -1064,8 +1915,8 @@ wait_for_inferior () #if 0 /* FIXME - Need to implement nested temporary breakpoints */ if (step_over_calls - && (FRAME_FP (get_current_frame ()) - INNER_THAN step_frame_address)) + && (INNER_THAN (FRAME_FP (get_current_frame ()), + step_frame_address))) { another_trap = 1; goto keep_going; @@ -1079,7 +1930,10 @@ wait_for_inferior () case BPSTAT_WHAT_SINGLE: if (breakpoints_inserted) - remove_breakpoints (); + { + thread_step_needed = 1; + remove_breakpoints (); + } breakpoints_inserted = 0; another_trap = 1; /* Still need to check other stuff, at least the case @@ -1105,6 +1959,21 @@ wait_for_inferior () goto stop_stepping; case BPSTAT_WHAT_STEP_RESUME: + /* This proably demands a more elegant solution, but, yeah + right... + + This function's use of the simple variable step_resume_breakpoint + doesn't seem to accomodate simultaneously active step-resume bp's, + although the breakpoint list certainly can. + + If we reach here and step_resume_breakpoint is already NULL, then + apparently we have multiple active step-resume bp's. We'll just + delete the breakpoint we stopped at, and carry on. */ + if (step_resume_breakpoint == NULL) + { + step_resume_breakpoint = + bpstat_find_step_resume_breakpoint (stop_bpstat); + } delete_breakpoint (step_resume_breakpoint); step_resume_breakpoint = NULL; break; @@ -1121,6 +1990,7 @@ wait_for_inferior () break; case BPSTAT_WHAT_CHECK_SHLIBS: + case BPSTAT_WHAT_CHECK_SHLIBS_RESUME_FROM_HOOK: #ifdef SOLIB_ADD { extern int auto_solib_add; @@ -1156,6 +2026,40 @@ wait_for_inferior () stop_print_frame = 0; goto stop_stepping; } + + /* If we stopped due to an explicit catchpoint, then the + (see above) call to SOLIB_ADD pulled in any symbols + from a newly-loaded library, if appropriate. + + We do want the inferior to stop, but not where it is + now, which is in the dynamic linker callback. Rather, + we would like it stop in the user's program, just after + the call that caused this catchpoint to trigger. That + gives the user a more useful vantage from which to + examine their program's state. */ + else if (what.main_action == BPSTAT_WHAT_CHECK_SHLIBS_RESUME_FROM_HOOK) + { + /* ??rehrauer: If I could figure out how to get the + right return PC from here, we could just set a temp + breakpoint and resume. I'm not sure we can without + cracking open the dld's shared libraries and sniffing + their unwind tables and text/data ranges, and that's + not a terribly portable notion. + + Until that time, we must step the inferior out of the + dld callback, and also out of the dld itself (and any + code or stubs in libdld.sl, such as "shl_load" and + friends) until we reach non-dld code. At that point, + we can stop stepping. */ + bpstat_get_triggered_catchpoints (stop_bpstat, + &stepping_through_solib_catchpoints); + stepping_through_solib_after_catch = 1; + + /* Be sure to lift all breakpoints, so the inferior does + actually step past this point... */ + another_trap = 1; + break; + } else { /* We want to step over this breakpoint, then keep going. */ @@ -1180,6 +2084,29 @@ wait_for_inferior () test for stepping. But, if not stepping, do not stop. */ + /* Are we stepping to get the inferior out of the dynamic + linker's hook (and possibly the dld itself) after catching + a shlib event? */ + if (stepping_through_solib_after_catch) + { +#if defined(SOLIB_ADD) + /* Have we reached our destination? If not, keep going. */ + if (SOLIB_IN_DYNAMIC_LINKER (pid, stop_pc)) + { + another_trap = 1; + goto keep_going; + } +#endif + /* Else, stop and report the catchpoint(s) whose triggering + caused us to begin stepping. */ + stepping_through_solib_after_catch = 0; + bpstat_clear (&stop_bpstat); + stop_bpstat = bpstat_copy (stepping_through_solib_catchpoints); + bpstat_clear (&stepping_through_solib_catchpoints); + stop_print_frame = 1; + goto stop_stepping; + } + #ifndef CALL_DUMMY_BREAKPOINT_OFFSET /* This is the old way of detecting the end of the stack dummy. An architecture which defines CALL_DUMMY_BREAKPOINT_OFFSET gets @@ -1190,7 +2117,7 @@ wait_for_inferior () just stop silently, unless the user was doing an si/ni, in which case she'd better know what she's doing. */ - if (PC_IN_CALL_DUMMY (stop_pc, read_sp (), FRAME_FP (get_current_frame ())) + if (CALL_DUMMY_HAS_COMPLETED (stop_pc, read_sp (), FRAME_FP (get_current_frame ())) && !step_range_end) { stop_print_frame = 0; @@ -1216,7 +2143,11 @@ wait_for_inferior () whether it could/should be keep_going. */ goto check_sigtramp2; - /* If stepping through a line, keep going if still within it. */ + /* If stepping through a line, keep going if still within it. + + Note that step_range_end is the address of the first instruction + beyond the step range, and NOT the address of the last instruction + within it! */ if (stop_pc >= step_range_start && stop_pc < step_range_end #if 0 @@ -1229,7 +2160,7 @@ wait_for_inferior () just changed, we've stepped outside */ && !(stop_pc == step_range_start && FRAME_FP (get_current_frame ()) - && (read_sp () INNER_THAN step_sp + && (INNER_THAN (read_sp (), step_sp) || FRAME_FP (get_current_frame ()) != step_frame_address)) #endif ) @@ -1256,34 +2187,70 @@ wait_for_inferior () /* Did we just take a signal? */ if (IN_SIGTRAMP (stop_pc, stop_func_name) && !IN_SIGTRAMP (prev_pc, prev_func_name) - && read_sp () INNER_THAN step_sp) + && INNER_THAN (read_sp (), step_sp)) { /* We've just taken a signal; go until we are back to the point where we took it and one more. */ - /* This code is needed at least in the following case: - The user types "next" and then a signal arrives (before - the "next" is done). */ + /* Note: The test above succeeds not only when we stepped + into a signal handler, but also when we step past the last + statement of a signal handler and end up in the return stub + of the signal handler trampoline. To distinguish between + these two cases, check that the frame is INNER_THAN the + previous one below. pai/1997-09-11 */ - /* Note that if we are stopped at a breakpoint, then we need - the step_resume breakpoint to override any breakpoints at - the same location, so that we will still step over the - breakpoint even though the signal happened. */ { - struct symtab_and_line sr_sal; - - INIT_SAL (&sr_sal); /* initialize to zeroes */ - sr_sal.pc = prev_pc; - sr_sal.section = find_pc_overlay (sr_sal.pc); - /* We could probably be setting the frame to - step_frame_address; I don't think anyone thought to try it. */ - step_resume_breakpoint = - set_momentary_breakpoint (sr_sal, NULL, bp_step_resume); - if (breakpoints_inserted) - insert_breakpoints (); + CORE_ADDR current_frame = FRAME_FP (get_current_frame()); + + if (INNER_THAN (current_frame, step_frame_address)) + { + /* We have just taken a signal; go until we are back to + the point where we took it and one more. */ + + /* This code is needed at least in the following case: + The user types "next" and then a signal arrives (before + the "next" is done). */ + + /* Note that if we are stopped at a breakpoint, then we need + the step_resume breakpoint to override any breakpoints at + the same location, so that we will still step over the + breakpoint even though the signal happened. */ + struct symtab_and_line sr_sal; + + INIT_SAL(&sr_sal); + sr_sal.symtab = NULL; + sr_sal.line = 0; + sr_sal.pc = prev_pc; + /* We could probably be setting the frame to + step_frame_address; I don't think anyone thought to try it. */ + step_resume_breakpoint = + set_momentary_breakpoint (sr_sal, NULL, bp_step_resume); + if (breakpoints_inserted) + insert_breakpoints (); + } + else + { + /* We just stepped out of a signal handler and into its calling + trampoline. + + Normally, we'd jump to step_over_function from here, + but for some reason GDB can't unwind the stack correctly to find + the real PC for the point user code where the signal trampoline will + return -- FRAME_SAVED_PC fails, at least on HP-UX 10.20. But signal + trampolines are pretty small stubs of code, anyway, so it's OK instead + to just single-step out. Note: assuming such trampolines don't + exhibit recursion on any platform... */ + find_pc_partial_function (stop_pc, &stop_func_name, &stop_func_start, + &stop_func_end); + /* Readjust stepping range */ + step_range_start = stop_func_start; + step_range_end = stop_func_end; + stepping_through_sigtramp = 1; + } } + /* If this is stepi or nexti, make sure that the stepping range gets us past that instruction. */ if (step_range_end == 1) @@ -1320,7 +2287,7 @@ wait_for_inferior () SKIP_PROLOGUE (prologue_pc); } - if (!(step_sp INNER_THAN read_sp ()) /* don't mistake (sig)return as a call */ + if (!(INNER_THAN (step_sp, read_sp ())) /* don't mistake (sig)return as a call */ && (/* Might be a non-recursive call. If the symbols are missing enough that stop_func_start == prev_func_start even though they are really two functions, we will treat some calls as @@ -1373,7 +2340,8 @@ wait_for_inferior () Mips, PA, and Power architectures with good results. */ if (stop_pc == stop_func_start /* Quick test */ - || in_prologue (stop_pc, stop_func_start) + || (in_prologue (stop_pc, stop_func_start) && + ! IN_SOLIB_RETURN_TRAMPOLINE (stop_pc, stop_func_name)) || IN_SOLIB_CALL_TRAMPOLINE (stop_pc, stop_func_name) || stop_func_name == 0) #endif @@ -1440,15 +2408,47 @@ step_over_function: /* Set a special breakpoint after the return */ struct symtab_and_line sr_sal; - INIT_SAL (&sr_sal); /* initialize to zeroes */ - sr_sal.pc = - ADDR_BITS_REMOVE (SAVED_PC_AFTER_CALL (get_current_frame ())); - sr_sal.section = find_pc_overlay (sr_sal.pc); - step_resume_breakpoint = - set_momentary_breakpoint (sr_sal, get_current_frame (), - bp_step_resume); - if (!IN_SOLIB_DYNSYM_RESOLVE_CODE (sr_sal.pc)) - step_resume_breakpoint->frame = step_frame_address; + INIT_SAL(&sr_sal); + sr_sal.symtab = NULL; + sr_sal.line = 0; + + /* If we came here after encountering a signal in the middle of + a "next", use the stashed-away previous frame pc */ + sr_sal.pc + = stopped_by_random_signal + ? prev_pc + : ADDR_BITS_REMOVE (SAVED_PC_AFTER_CALL (get_current_frame ())); + + step_resume_breakpoint = + set_momentary_breakpoint (sr_sal, + stopped_by_random_signal ? NULL : get_current_frame (), + bp_step_resume); + + /* We've just entered a callee, and we wish to resume until + it returns to the caller. Setting a step_resume bp on + the return PC will catch a return from the callee. + + However, if the callee is recursing, we want to be careful + not to catch returns of those recursive calls, but of THIS + instance of the call. + + To do this, we set the step_resume bp's frame to our current + caller's frame (step_frame_address, which is set by the "next" + or "until" command, before execution begins). + + But ... don't do it if we're single-stepping out of a sigtramp, + because the reason we're single-stepping is precisely because + unwinding is a problem (HP-UX 10.20, e.g.) and the frame address + is likely to be incorrect. No danger of sigtramp recursion */ + + if (stepping_through_sigtramp) + { + step_resume_breakpoint->frame = NULL; + stepping_through_sigtramp = 0; + } + else if (!IN_SOLIB_DYNSYM_RESOLVE_CODE (sr_sal.pc)) + step_resume_breakpoint->frame = step_frame_address; + if (breakpoints_inserted) insert_breakpoints (); } @@ -1510,7 +2510,7 @@ step_into_function: /* We've wandered out of the step range. */ sal = find_pc_line(stop_pc, 0); - + if (step_range_end == 1) { /* It is stepi or nexti. We always want to stop stepping after @@ -1561,7 +2561,7 @@ step_into_function: break; } - if (stop_pc == sal.pc + if ((stop_pc == sal.pc) && (current_line != sal.line || current_symtab != sal.symtab)) { /* We are at the start of a different line. So stop. Note that @@ -1594,13 +2594,24 @@ step_into_function: step_frame_address = FRAME_FP (get_current_frame ()); current_line = sal.line; current_symtab = sal.symtab; + + /* In the case where we just stepped out of a function into the middle + of a line of the caller, continue stepping, but step_frame_address + must be modified to current frame */ + { + CORE_ADDR current_frame = FRAME_FP (get_current_frame()); + if (!(INNER_THAN (current_frame, step_frame_address))) + step_frame_address = current_frame; + } + + goto keep_going; check_sigtramp2: if (trap_expected && IN_SIGTRAMP (stop_pc, stop_func_name) && !IN_SIGTRAMP (prev_pc, prev_func_name) - && read_sp () INNER_THAN step_sp) + && INNER_THAN (read_sp (), step_sp)) { /* What has happened here is that we have just stepped the inferior with a signal (because it is a signal which shouldn't make @@ -1634,6 +2645,17 @@ step_into_function: It's really much cleaner to do a goto than a maze of if-else conditions. */ + /* ??rehrauer: ttrace on HP-UX theoretically allows one to debug + a vforked child beetween its creation and subsequent exit or + call to exec(). However, I had big problems in this rather + creaky exec engine, getting that to work. The fundamental + problem is that I'm trying to debug two processes via an + engine that only understands a single process with possibly + multiple threads. + + Hence, this spot is known to have problems when + target_can_follow_vfork_prior_to_exec returns 1. */ + /* Save the pc before execution, to compare with pc after stop. */ prev_pc = read_pc (); /* Might have been DECR_AFTER_BREAK */ prev_func_start = stop_func_start; /* Ok, since if DECR_PC_AFTER @@ -1714,6 +2736,28 @@ step_into_function: stop_stepping: if (target_has_execution) { + /* Are we stopping for a vfork event? We only stop when we see + the child's event. However, we may not yet have seen the + parent's event. And, inferior_pid is still set to the parent's + pid, until we resume again and follow either the parent or child. + + To ensure that we can really touch inferior_pid (aka, the + parent process) -- which calls to functions like read_pc + implicitly do -- wait on the parent if necessary. */ + if ((pending_follow.kind == TARGET_WAITKIND_VFORKED) + && ! pending_follow.fork_event.saw_parent_fork) + { + int parent_pid; + + do { + if (target_wait_hook) + parent_pid = target_wait_hook (-1, &w); + else + parent_pid = target_wait (-1, &w); + } while (parent_pid != inferior_pid); + } + + /* Assuming the inferior still exists, set these up for next time, just like we did above if we didn't break out of the loop. */ @@ -1723,6 +2767,72 @@ step_into_function: } do_cleanups (old_cleanups); } + +/* This function returns TRUE if ep is an internal breakpoint + set to catch generic shared library (aka dynamically-linked + library) events. (This is *NOT* the same as a catchpoint for a + shlib event. The latter is something a user can set; this is + something gdb sets for its own use, and isn't ever shown to a + user.) */ +static int +is_internal_shlib_eventpoint (ep) + struct breakpoint * ep; +{ + return + (ep->type == bp_shlib_event) + ; +} + +/* This function returns TRUE if bs indicates that the inferior + stopped due to a shared library (aka dynamically-linked library) + event. */ +static int +stopped_for_internal_shlib_event (bs) + bpstat bs; +{ + /* Note that multiple eventpoints may've caused the stop. Any + that are associated with shlib events will be accepted. */ + for (;bs != NULL; bs = bs->next) + { + if ((bs->breakpoint_at != NULL) + && is_internal_shlib_eventpoint (bs->breakpoint_at)) + return 1; + } + + /* If we get here, then no candidate was found. */ + return 0; +} + +/* This function returns TRUE if bs indicates that the inferior + stopped due to a shared library (aka dynamically-linked library) + event caught by a catchpoint. + + If TRUE, cp_p is set to point to the catchpoint. + + Else, the value of cp_p is undefined. */ +static int +stopped_for_shlib_catchpoint (bs, cp_p) + bpstat bs; + struct breakpoint ** cp_p; +{ + /* Note that multiple eventpoints may've caused the stop. Any + that are associated with shlib events will be accepted. */ + *cp_p = NULL; + + for (;bs != NULL; bs = bs->next) + { + if ((bs->breakpoint_at != NULL) + && ep_is_shlib_catchpoint (bs->breakpoint_at)) + { + *cp_p = bs->breakpoint_at; + return 1; + } + } + + /* If we get here, then no candidate was found. */ + return 0; +} + /* Here to return control to GDB when the inferior stops for real. Print appropriate messages, remove breakpoints, give terminal our modes. @@ -1735,6 +2845,23 @@ step_into_function: void normal_stop () { + +#ifdef HPUXHPPA + /* As with the notification of thread events, we want to delay + notifying the user that we've switched thread context until + the inferior actually stops. + + (Note that there's no point in saying anything if the inferior + has exited!) */ + if ((switched_from_inferior_pid != inferior_pid) && + target_has_execution) + { + target_terminal_ours_for_output (); + printf_filtered ("[Switched to %s]\n", target_pid_or_tid_to_str (inferior_pid)); + switched_from_inferior_pid = inferior_pid; + } +#endif + /* Make sure that the current_frame's pc is correct. This is a correction for setting up the frame info before doing DECR_PC_AFTER_BREAK */ @@ -1750,6 +2877,7 @@ The same program may be running in another process.\n"); } if (target_has_execution && breakpoints_inserted) + { if (remove_breakpoints ()) { target_terminal_ours_for_output (); @@ -1757,7 +2885,7 @@ The same program may be running in another process.\n"); It might be running in another process.\n\ Further execution is probably impossible.\n"); } - + } breakpoints_inserted = 0; /* Delete the breakpoint we stopped at, if it wants to be deleted. @@ -1771,46 +2899,80 @@ Further execution is probably impossible.\n"); if (stopped_by_random_signal) disable_current_display (); + /* Don't print a message if in the middle of doing a "step n" + operation for n > 1 */ if (step_multi && stop_step) goto done; target_terminal_ours (); - if (stop_bpstat - && stop_bpstat->breakpoint_at - && stop_bpstat->breakpoint_at->type == bp_shlib_event) - printf_filtered ("Stopped due to shared library event\n"); + /* Did we stop because the user set the stop_on_solib_events + variable? (If so, we report this as a generic, "Stopped due + to shlib event" message.) */ + if (stopped_for_internal_shlib_event (stop_bpstat)) + { + printf_filtered ("Stopped due to shared library event\n"); + } /* Look up the hook_stop and run it if it exists. */ - if (stop_command->hook) + if (stop_command && stop_command->hook) { catch_errors (hook_stop_stub, (char *)stop_command->hook, "Error while running hook_stop:\n", RETURN_MASK_ALL); } if (!target_has_stack) + { + goto done; + } + + /* Select innermost stack frame - i.e., current frame is frame 0, + and current location is based on that. + Don't do this on return from a stack dummy routine, + or if the program has exited. */ - /* Select innermost stack frame except on return from a stack dummy routine, - or if the program has exited. Print it without a level number if - we have changed functions or hit a breakpoint. Print source line - if we have one. */ if (!stop_stack_dummy) { select_frame (get_current_frame (), 0); + /* Print current location without a level number, if + we have changed functions or hit a breakpoint. + Print source line if we have one. + bpstat_print() contains the logic deciding in detail + what to print, based on the event(s) that just occurred. */ + if (stop_print_frame) { - int source_only; - - source_only = bpstat_print (stop_bpstat); - source_only = source_only || - ( stop_step - && step_frame_address == FRAME_FP (get_current_frame ()) - && step_start_function == find_pc_function (stop_pc)); - - print_stack_frame (selected_frame, -1, source_only? -1: 1); + int bpstat_ret; + int source_flag; + + bpstat_ret = bpstat_print (stop_bpstat); + /* bpstat_print() returned one of: + -1: Didn't print anything + 0: Printed preliminary "Breakpoint n, " message, desires + location tacked on + 1: Printed something, don't tack on location */ + + if (bpstat_ret == -1) + if ( stop_step + && step_frame_address == FRAME_FP (get_current_frame ()) + && step_start_function == find_pc_function (stop_pc)) + source_flag = -1; /* finished step, just print source line */ + else + source_flag = 1; /* print location and source line */ + else if (bpstat_ret == 0) /* hit bpt, desire location */ + source_flag = 1; /* print location and source line */ + else /* bpstat_ret == 1, hit bpt, do not desire location */ + source_flag = -1; /* just print source line */ + + /* The behavior of this routine with respect to the source + flag is: + -1: Print only source line + 0: Print only location + 1: Print location and source line */ + show_and_print_stack_frame (selected_frame, -1, source_flag); /* Display the auto-display expressions. */ do_displays (); @@ -1834,6 +2996,10 @@ Further execution is probably impossible.\n"); stop_pc = read_pc(); select_frame (get_current_frame (), 0); } + + + TUIDO (((TuiOpaqueFuncPtr)tui_vCheckDataValues, selected_frame)); + done: annotate_stopped (); } @@ -2071,6 +3237,75 @@ Are you sure you want to change it? ", do_cleanups (old_chain); } +static void +xdb_handle_command (args, from_tty) + char *args; + int from_tty; +{ + char **argv; + struct cleanup *old_chain; + + /* Break the command line up into args. */ + + argv = buildargv (args); + if (argv == NULL) + { + nomem (0); + } + old_chain = make_cleanup (freeargv, (char *) argv); + if (argv[1] != (char *)NULL) + { + char *argBuf; + int bufLen; + + bufLen = strlen(argv[0]) + 20; + argBuf = (char *)xmalloc(bufLen); + if (argBuf) + { + int validFlag = 1; + enum target_signal oursig; + + oursig = target_signal_from_name (argv[0]); + memset(argBuf, 0, bufLen); + if (strcmp(argv[1], "Q") == 0) + sprintf(argBuf, "%s %s", argv[0], "noprint"); + else + { + if (strcmp(argv[1], "s") == 0) + { + if (!signal_stop[oursig]) + sprintf(argBuf, "%s %s", argv[0], "stop"); + else + sprintf(argBuf, "%s %s", argv[0], "nostop"); + } + else if (strcmp(argv[1], "i") == 0) + { + if (!signal_program[oursig]) + sprintf(argBuf, "%s %s", argv[0], "pass"); + else + sprintf(argBuf, "%s %s", argv[0], "nopass"); + } + else if (strcmp(argv[1], "r") == 0) + { + if (!signal_print[oursig]) + sprintf(argBuf, "%s %s", argv[0], "print"); + else + sprintf(argBuf, "%s %s", argv[0], "noprint"); + } + else + validFlag = 0; + } + if (validFlag) + handle_command(argBuf, from_tty); + else + printf_filtered("Invalid signal handling flag.\n"); + if (argBuf) + free(argBuf); + } + } + do_cleanups (old_chain); +} + /* Print current contents of the tables set by the handle command. It is possible we should just be printing signals actually used by the current target (but for things to work right when switching @@ -2180,13 +3415,19 @@ restore_selected_frame (args) /* If inf_status->selected_frame_address is NULL, there was no previously selected frame. */ if (frame == NULL || - FRAME_FP (frame) != fr->frame_address || + /* FRAME_FP (frame) != fr->frame_address ||*/ /* elz: deleted this check as a quick fix + to the problem that for function called by hand + gdb creates no internal frame structure + and the real stack and gdb's idea of stack + are different if nested calls by hands are made*/ level != 0) - { + { warning ("Unable to restore previously selected frame.\n"); return 0; } + select_frame (frame, fr->level); + return(1); } @@ -2241,16 +3482,40 @@ restore_inferior_status (inf_status) RETURN_MASK_ERROR) == 0) /* Error in restoring the selected frame. Select the innermost frame. */ - select_frame (get_current_frame (), 0); + + + select_frame (get_current_frame (), 0); + } } + + +void +set_follow_fork_mode_command (arg, from_tty, c) + char * arg; + int from_tty; + struct cmd_list_element * c; +{ + if (! STREQ (arg, "parent") && + ! STREQ (arg, "child") && + ! STREQ (arg, "both") && + ! STREQ (arg, "ask")) + error ("follow-fork-mode must be one of \"parent\", \"child\", \"both\" or \"ask\"."); + + if (follow_fork_mode_string != NULL) + free (follow_fork_mode_string); + follow_fork_mode_string = savestring (arg, strlen (arg)); +} + + void _initialize_infrun () { register int i; register int numsigs; + struct cmd_list_element * c; add_info ("signals", signals_info, "What debugger does when program gets various signals.\n\ @@ -2272,8 +3537,31 @@ Print means print a message if this signal happens.\n\ Pass means let program see this signal; otherwise program doesn't know.\n\ Ignore is a synonym for nopass and noignore is a synonym for pass.\n\ Pass and Stop may be combined.", NULL)); + if (xdb_commands) + { + add_com("lz", class_info, signals_info, + "What debugger does when program gets various signals.\n\ +Specify a signal as argument to print info on that signal only."); + add_com("z", class_run, xdb_handle_command, + concat ("Specify how to handle a signal.\n\ +Args are signals and actions to apply to those signals.\n\ +Symbolic signals (e.g. SIGSEGV) are recommended but numeric signals\n\ +from 1-15 are allowed for compatibility with old versions of GDB.\n\ +Numeric ranges may be specified with the form LOW-HIGH (e.g. 1-5).\n\ +The special arg \"all\" is recognized to mean all signals except those\n\ +used by the debugger, typically SIGTRAP and SIGINT.\n", +"Recognized actions include \"s\" (toggles between stop and nostop), \n\ +\"r\" (toggles between print and noprint), \"i\" (toggles between pass and \ +nopass), \"Q\" (noprint)\n\ +Stop means reenter debugger if this signal happens (implies print).\n\ +Print means print a message if this signal happens.\n\ +Pass means let program see this signal; otherwise program doesn't know.\n\ +Ignore is a synonym for nopass and noignore is a synonym for pass.\n\ +Pass and Stop may be combined.", NULL)); + } - stop_command = add_cmd ("stop", class_obscure, not_just_help_class_command, + if (!dbx_commands) + stop_command = add_cmd ("stop", class_obscure, not_just_help_class_command, "There is no `stop' command, but you can set a hook on `stop'.\n\ This allows you to set a list of commands to be run each time execution\n\ of the program stops.", &cmdlist); @@ -2324,4 +3612,38 @@ to the user would be loading/unloading of a new library.\n", &setlist), &showlist); #endif + + c = add_set_enum_cmd ("follow-fork-mode", + class_run, + follow_fork_mode_kind_names, + (char *) &follow_fork_mode_string, +/* ??rehrauer: The "both" option is broken, by what may be a 10.20 + kernel problem. It's also not terribly useful without a GUI to + help the user drive two debuggers. So for now, I'm disabling + the "both" option. +/* "Set debugger response to a program call of fork or vfork.\n\ +A fork or vfork creates a new process. follow-fork-mode can be:\n\ + parent - the original process is debugged after a fork\n\ + child - the new process is debugged after a fork\n\ + both - both the parent and child are debugged after a fork\n\ + ask - the debugger will ask for one of the above choices\n\ +For \"both\", another copy of the debugger will be started to follow\n\ +the new child process. The original debugger will continue to follow\n\ +the original parent process. To distinguish their prompts, the\n\ +debugger copy's prompt will be changed.\n\ +For \"parent\" or \"child\", the unfollowed process will run free.\n\ +By default, the debugger will follow the parent process.", +*/ + "Set debugger response to a program call of fork or vfork.\n\ +A fork or vfork creates a new process. follow-fork-mode can be:\n\ + parent - the original process is debugged after a fork\n\ + child - the new process is debugged after a fork\n\ + ask - the debugger will ask for one of the above choices\n\ +For \"parent\" or \"child\", the unfollowed process will run free.\n\ +By default, the debugger will follow the parent process.", + &setlist); +/* c->function.sfunc = ;*/ + add_show_from_set (c, &showlist); + + set_follow_fork_mode_command ("parent", 0, NULL); } diff --git a/gdb/inftarg.c b/gdb/inftarg.c index 95f6c3a..921cbbe 100644 --- a/gdb/inftarg.c +++ b/gdb/inftarg.c @@ -3,6 +3,8 @@ Free Software Foundation, Inc. Contributed by Cygnus Support. +## Contains temporary hacks.. + This file is part of GDB. This program is free software; you can redistribute it and/or modify @@ -25,6 +27,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "target.h" #include "gdbcore.h" #include "command.h" +#include "gdb_stat.h" #include <signal.h> #include <sys/types.h> #include <fcntl.h> @@ -39,6 +42,13 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* "wait.h" fills in the gaps left by <wait.h> */ #include "wait.h" +#include <sys/unistd.h> + +extern struct symtab_and_line * +child_enable_exception_callback PARAMS ((enum exception_event_kind, int)); + +extern struct exception_event_record * +child_get_current_exception_event PARAMS ((void)); static void child_prepare_to_store PARAMS ((void)); @@ -47,6 +57,11 @@ child_prepare_to_store PARAMS ((void)); static int child_wait PARAMS ((int, struct target_waitstatus *)); #endif /* CHILD_WAIT */ +#if !defined(CHILD_POST_WAIT) +void +child_post_wait PARAMS ((int, int)); +#endif + static void child_open PARAMS ((char *, int)); static void @@ -56,15 +71,33 @@ static void child_detach PARAMS ((char *, int)); static void +child_detach_from_process PARAMS ((int, char *, int, int)); + +static void child_attach PARAMS ((char *, int)); static void +child_attach_to_process PARAMS ((char *, int, int)); + +#if !defined(CHILD_POST_ATTACH) +static void +child_post_attach PARAMS ((int)); +#endif + +static void +child_require_attach PARAMS ((char *, int)); + +static void +child_require_detach PARAMS ((int, char *, int)); + +static void ptrace_me PARAMS ((void)); -static int +static void ptrace_him PARAMS ((int)); -static void child_create_inferior PARAMS ((char *, char *, char **)); +static void +child_create_inferior PARAMS ((char *, char *, char **)); static void child_mourn_inferior PARAMS ((void)); @@ -72,14 +105,11 @@ child_mourn_inferior PARAMS ((void)); static int child_can_run PARAMS ((void)); -static int -proc_wait PARAMS ((int, int*)); - static void child_stop PARAMS ((void)); #ifndef CHILD_THREAD_ALIVE -static int child_thread_alive PARAMS ((int)); +int child_thread_alive PARAMS ((int)); #endif extern char **environ; @@ -91,18 +121,19 @@ int child_suppress_run = 0; /* Non-zero if inftarg should pretend not to be a runnable target. Used by targets that can sit atop inftarg, such as HPUX thread support. */ -static int -proc_wait (pid, status) - int pid; - int *status; -{ -#ifndef __GO32__ - return wait (status); -#endif -} #ifndef CHILD_WAIT +/*##*/ +/* Enable HACK for ttrace work. In + * infttrace.c/require_notification_of_events, + * this is set to 0 so that the loop in child_wait + * won't loop. + */ +int not_same_real_pid = 1; +/*##*/ + + /* Wait for child to do something. Return pid of child, or -1 in case of error; store status through argument pointer OURSTATUS. */ @@ -113,6 +144,11 @@ child_wait (pid, ourstatus) { int save_errno; int status; + char * execd_pathname; + int exit_status; + int related_pid; + int syscall_id; + enum target_waitkind kind; do { set_sigint_trap(); /* Causes SIGINT to be passed on to the @@ -120,6 +156,7 @@ child_wait (pid, ourstatus) set_sigio_trap (); pid = proc_wait (inferior_pid, &status); + save_errno = errno; clear_sigio_trap (); @@ -130,19 +167,98 @@ child_wait (pid, ourstatus) { if (save_errno == EINTR) continue; + fprintf_unfiltered (gdb_stderr, "Child process unexpectedly missing: %s.\n", safe_strerror (save_errno)); + /* Claim it exited with unknown signal. */ ourstatus->kind = TARGET_WAITKIND_SIGNALLED; ourstatus->value.sig = TARGET_SIGNAL_UNKNOWN; return -1; } - } while (pid != inferior_pid); /* Some other child died or stopped */ + + /* Did it exit? + */ + if (target_has_exited (pid, status, &exit_status)) + { + /* ??rehrauer: For now, ignore this. */ + continue; + } + + if (!target_thread_alive (pid)) + { + ourstatus->kind = TARGET_WAITKIND_SPURIOUS; + return pid; + } + + if (target_has_forked (pid, &related_pid) + && (pid == inferior_pid) || (related_pid == inferior_pid)) + { + ourstatus->kind = TARGET_WAITKIND_FORKED; + ourstatus->value.related_pid = related_pid; + return pid; + } + + if (target_has_vforked (pid, &related_pid) + && (pid == inferior_pid) || (related_pid == inferior_pid)) + { + ourstatus->kind = TARGET_WAITKIND_VFORKED; + ourstatus->value.related_pid = related_pid; + return pid; + } + + if (target_has_execd (pid, &execd_pathname)) + { + /* Are we ignoring initial exec events? (This is likely because + we're in the process of starting up the inferior, and another + (older) mechanism handles those.) If so, we'll report this + as a regular stop, not an exec. + */ + if (inferior_ignoring_startup_exec_events) + { + inferior_ignoring_startup_exec_events--; + } + else + { + ourstatus->kind = TARGET_WAITKIND_EXECD; + ourstatus->value.execd_pathname = execd_pathname; + return pid; + } + } + + /* All we must do with these is communicate their occurrence + to wait_for_inferior... + */ + if (target_has_syscall_event (pid, &kind, &syscall_id)) + { + ourstatus->kind = kind; + ourstatus->value.syscall_id = syscall_id; + return pid; + } + +/*## } while (pid != inferior_pid); ##*/ /* Some other child died or stopped */ +/* hack for thread testing */ + } while( (pid != inferior_pid) && not_same_real_pid ); +/*##*/ + store_waitstatus (ourstatus, status); return pid; } #endif /* CHILD_WAIT */ +#if !defined(CHILD_POST_WAIT) +void +child_post_wait (pid, wait_status) + int pid; + int wait_status; +{ + /* This version of Unix doesn't require a meaningful "post wait" + operation. + */ +} +#endif + + #ifndef CHILD_THREAD_ALIVE /* Check to see if the given thread is alive. @@ -150,7 +266,7 @@ child_wait (pid, ourstatus) FIXME: Is kill() ever the right way to do this? I doubt it, but for now we're going to try and be compatable with the old thread code. */ -static int +int child_thread_alive (pid) int pid; { @@ -159,12 +275,11 @@ child_thread_alive (pid) #endif -/* Attach to process PID, then initialize for debugging it. */ - static void -child_attach (args, from_tty) - char *args; - int from_tty; +child_attach_to_process (args, from_tty, after_fork) + char * args; + int from_tty; + int after_fork; { if (!args) error_no_arg ("process-id to attach"); @@ -175,8 +290,14 @@ child_attach (args, from_tty) { char *exec_file; int pid; + struct target_waitstatus *wstatus; + char * dummy; - pid = atoi (args); + dummy = args; + pid = strtol (args, &dummy, 0); + /* Some targets don't set errno on errors, grrr! */ + if ((pid == 0) && (args == dummy)) + error ("Illegal process-id: %s\n", args); if (pid == getpid()) /* Trying to masturbate? */ error ("I refuse to debug myself!"); @@ -185,16 +306,23 @@ child_attach (args, from_tty) { exec_file = (char *) get_exec_file (0); - if (exec_file) - printf_unfiltered ("Attaching to program `%s', %s\n", exec_file, + if (after_fork) + printf_unfiltered ("Attaching after fork to %s\n", target_pid_to_str (pid)); - else - printf_unfiltered ("Attaching to %s\n", target_pid_to_str (pid)); + else if (exec_file) + printf_unfiltered ("Attaching to program: %s, %s\n", exec_file, + target_pid_to_str (pid)); + else + printf_unfiltered ("Attaching to %s\n", target_pid_to_str (pid)); gdb_flush (gdb_stdout); } - attach (pid); + if (!after_fork) + attach (pid); + else + REQUIRE_ATTACH (pid); + inferior_pid = pid; push_target (&child_ops); } @@ -202,19 +330,43 @@ child_attach (args, from_tty) } -/* Take a program previously attached to and detaches it. - The program resumes execution and will no longer stop - on signals, etc. We'd better not have left any breakpoints - in the program or it'll die when it hits one. For this - to work, it may be necessary for the process to have been - previously attached. It *might* work if the program was - started via the normal ptrace (PTRACE_TRACEME). */ +/* Attach to process PID, then initialize for debugging it. */ static void -child_detach (args, from_tty) +child_attach (args, from_tty) + char *args; + int from_tty; +{ + child_attach_to_process (args, from_tty, 0); +} + +#if !defined(CHILD_POST_ATTACH) +static void +child_post_attach (pid) + int pid; +{ + /* This version of Unix doesn't require a meaningful "post attach" + operation by a debugger. + */ +} +#endif + + +static void +child_require_attach (args, from_tty) char *args; int from_tty; { + child_attach_to_process (args, from_tty, 1); +} + +static void +child_detach_from_process (pid, args, from_tty, after_fork) + int pid; + char * args; + int from_tty; + int after_fork; +{ #ifdef ATTACH_DETACH { int siggnal = 0; @@ -224,22 +376,55 @@ child_detach (args, from_tty) char *exec_file = get_exec_file (0); if (exec_file == 0) exec_file = ""; - printf_unfiltered ("Detaching from program: %s %s\n", exec_file, - target_pid_to_str (inferior_pid)); + if (after_fork) + printf_unfiltered ("Detaching after fork from %s\n", + target_pid_to_str (pid)); + else + printf_unfiltered ("Detaching from program: %s, %s\n", exec_file, + target_pid_to_str (pid)); gdb_flush (gdb_stdout); } if (args) siggnal = atoi (args); - detach (siggnal); - inferior_pid = 0; - unpush_target (&child_ops); + if (!after_fork) + detach (siggnal); + else + REQUIRE_DETACH (pid, siggnal); } #else error ("This version of Unix does not support detaching a process."); #endif } +/* Take a program previously attached to and detaches it. + The program resumes execution and will no longer stop + on signals, etc. We'd better not have left any breakpoints + in the program or it'll die when it hits one. For this + to work, it may be necessary for the process to have been + previously attached. It *might* work if the program was + started via the normal ptrace (PTRACE_TRACEME). */ + +static void +child_detach (args, from_tty) + char *args; + int from_tty; +{ + child_detach_from_process (inferior_pid, args, from_tty, 0); + inferior_pid = 0; + unpush_target (&child_ops); +} + +static void +child_require_detach (pid, args, from_tty) + int pid; + char * args; + int from_tty; +{ + child_detach_from_process (pid, args, from_tty, 1); +} + + /* Get ready to modify the registers array. On machines which store individual registers, this doesn't need to do anything. On machines which store all the registers in one fell swoop, this makes sure @@ -286,20 +471,30 @@ ptrace_me () /* Stub function which causes the GDB that runs it, to start ptrace-ing the child process. */ -static int +static void ptrace_him (pid) int pid; { push_target (&child_ops); -#ifdef START_INFERIOR_TRAPS_EXPECTED + /* On some targets, there must be some explicit synchronization + between the parent and child processes after the debugger + forks, and before the child execs the debuggee program. This + call basically gives permission for the child to exec. + */ + + target_acknowledge_created_inferior (pid); + + /* START_INFERIOR_TRAPS_EXPECTED is defined in inferior.h, + * and will be 1 or 2 depending on whether we're starting + * without or with a shell. + */ startup_inferior (START_INFERIOR_TRAPS_EXPECTED); -#else - /* One trap to exec the shell, one to exec the program being debugged. */ - startup_inferior (2); -#endif - return pid; + /* On some targets, there must be some explicit actions taken after + the inferior has been started up. + */ + target_post_startup_inferior (pid); } /* Start an inferior Unix child process and sets inferior_pid to its pid. @@ -313,12 +508,307 @@ child_create_inferior (exec_file, allargs, env) char *allargs; char **env; { - fork_inferior (exec_file, allargs, env, ptrace_me, ptrace_him, NULL); + +#ifdef HPUXHPPA + char *tryname; + char *shell_file; + char *p; + char *p1; + char *path = getenv ("PATH"); + int len; + struct stat statbuf; + + /* On HP-UX, we have a possible bad interaction between + * the start-up-with-shell code and our catch-fork/catch-exec + * logic. To avoid the bad interaction, we start up with the + * C shell ("csh") and pass it the "-f" flag (fast start-up, + * don't run .cshrc code). + * See further comments in inferior.h toward the bottom + * (STARTUP_WITH_SHELL flag) and in fork-child.c + */ + + /* Rather than passing in a hard-wired path like "/bin/csh", + * we look down the PATH to find csh. I took this code from + * procfs.c, which is the file in the Sun-specific part of GDB + * analogous to inftarg.c. See procfs.c for more detailed + * comments. - RT + */ + shell_file = "csh"; + if (path == NULL) + path = "/bin:/usr/bin"; + tryname = alloca (strlen (path) + strlen (shell_file) + 2); + for (p = path; p != NULL; p = p1 ? p1 + 1: NULL) + { + p1 = strchr (p, ':'); + if (p1 != NULL) + len = p1 - p; + else + len = strlen (p); + strncpy (tryname, p, len); + tryname[len] = '\0'; + strcat (tryname, "/"); + strcat (tryname, shell_file); + if (access (tryname, X_OK) < 0) + continue; + if (stat (tryname, &statbuf) < 0) + continue; + if (!S_ISREG (statbuf.st_mode)) + /* We certainly need to reject directories. I'm not quite + as sure about FIFOs, sockets, etc., but I kind of doubt + that people want to exec() these things. */ + continue; + break; + } + if (p == NULL) + /* Not found. I replaced the error() which existed in procfs.c + * with simply passing in NULL and hoping fork_inferior() + * can deal with it. - RT + */ + /* error ("Can't find shell %s in PATH", shell_file); */ + shell_file = NULL; + else + shell_file = tryname; + + fork_inferior (exec_file, allargs, env, ptrace_me, ptrace_him, pre_fork_inferior, shell_file); +#else + fork_inferior (exec_file, allargs, env, ptrace_me, ptrace_him, NULL, NULL); +#endif /* We are at the first instruction we care about. */ /* Pedal to the metal... */ proceed ((CORE_ADDR) -1, TARGET_SIGNAL_0, 0); } +#if !defined(CHILD_POST_STARTUP_INFERIOR) +void +child_post_startup_inferior (pid) + int pid; +{ + /* This version of Unix doesn't require a meaningful "post startup inferior" + operation by a debugger. + */ +} +#endif + +#if !defined(CHILD_ACKNOWLEDGE_CREATED_INFERIOR) +void +child_acknowledge_created_inferior (pid) + int pid; +{ + /* This version of Unix doesn't require a meaningful "acknowledge created inferior" + operation by a debugger. + */ +} +#endif + + +void +child_clone_and_follow_inferior (child_pid, followed_child) + int child_pid; + int *followed_child; +{ + clone_and_follow_inferior (child_pid, followed_child); + + /* Don't resume CHILD_PID; it's stopped where it ought to be, until + the decision gets made elsewhere how to continue it. + */ +} + + +#if !defined(CHILD_POST_FOLLOW_INFERIOR_BY_CLONE) +void +child_post_follow_inferior_by_clone () +{ + /* This version of Unix doesn't require a meaningful "post follow inferior" + operation by a clone debugger. + */ +} +#endif + + +#if !defined(CHILD_INSERT_FORK_CATCHPOINT) +int +child_insert_fork_catchpoint (pid) + int pid; +{ + /* This version of Unix doesn't support notification of fork events. + */ +} +#endif + + +#if !defined(CHILD_REMOVE_FORK_CATCHPOINT) +int +child_remove_fork_catchpoint (pid) + int pid; +{ + /* This version of Unix doesn't support notification of fork events. + */ +} +#endif + + +#if !defined(CHILD_INSERT_VFORK_CATCHPOINT) +int +child_insert_vfork_catchpoint (pid) + int pid; +{ + /* This version of Unix doesn't support notification of vfork events. + */ +} +#endif + + +#if !defined(CHILD_REMOVE_VFORK_CATCHPOINT) +int +child_remove_vfork_catchpoint (pid) + int pid; +{ + /* This version of Unix doesn't support notification of vfork events. + */ +} +#endif + + +#if !defined(CHILD_HAS_FORKED) +int +child_has_forked (pid, child_pid) + int pid; + int * child_pid; +{ + /* This version of Unix doesn't support notification of fork events. + */ + return 0; +} +#endif + + +#if !defined(CHILD_HAS_VFORKED) +int +child_has_vforked (pid, child_pid) + int pid; + int * child_pid; +{ + /* This version of Unix doesn't support notification of vfork events. + */ + return 0; +} +#endif + + +#if !defined(CHILD_CAN_FOLLOW_VFORK_PRIOR_TO_EXEC) +int +child_can_follow_vfork_prior_to_exec () +{ + /* This version of Unix doesn't support notification of vfork events. + However, if it did, it probably wouldn't allow vforks to be followed + before the following exec. + */ + return 0; +} +#endif + + +#if !defined(CHILD_POST_FOLLOW_VFORK) +void +child_post_follow_vfork (parent_pid, followed_parent, child_pid, followed_child) + int parent_pid; + int followed_parent; + int child_pid; + int followed_child; +{ + /* This version of Unix doesn't require a meaningful "post follow vfork" + operation by a clone debugger. + */ +} +#endif + +#if !defined(CHILD_INSERT_EXEC_CATCHPOINT) +int +child_insert_exec_catchpoint (pid) + int pid; +{ + /* This version of Unix doesn't support notification of exec events. + */ +} +#endif + + +#if !defined(CHILD_REMOVE_EXEC_CATCHPOINT) +int +child_remove_exec_catchpoint (pid) + int pid; +{ + /* This version of Unix doesn't support notification of exec events. + */ +} +#endif + + +#if !defined(CHILD_HAS_EXECD) +int +child_has_execd (pid, execd_pathname) + int pid; + char ** execd_pathname; +{ + /* This version of Unix doesn't support notification of exec events. + */ + return 0; +} +#endif + + +#if !defined(CHILD_REPORTED_EXEC_EVENTS_PER_EXEC_CALL) +int +child_reported_exec_events_per_exec_call () +{ + /* This version of Unix doesn't support notification of exec events. + */ + return 1; +} +#endif + + +#if !defined(CHILD_HAS_SYSCALL_EVENT) +int +child_has_syscall_event (pid, kind, syscall_id) + int pid; + enum target_waitkind * kind; + int * syscall_id; +{ + /* This version of Unix doesn't support notification of syscall events. + */ + return 0; +} +#endif + + +#if !defined(CHILD_HAS_EXITED) +int +child_has_exited (pid, wait_status, exit_status) + int pid; + int wait_status; + int * exit_status; +{ + if (WIFEXITED (wait_status)) + { + *exit_status = WEXITSTATUS (wait_status); + return 1; + } + + if (WIFSIGNALED (wait_status)) + { + *exit_status = 0; /* ?? Don't know what else to say here. */ + return 1; + } + + /* ?? Do we really need to consult the event state, too? Assume the + wait_state alone suffices. + */ + return 0; +} +#endif + + static void child_mourn_inferior () { @@ -353,6 +843,49 @@ child_stop () kill (-inferior_process_group, SIGINT); } + +#if !defined(CHILD_ENABLE_EXCEPTION_CALLBACK) +struct symtab_and_line * +child_enable_exception_callback (kind, enable) + enum exception_event_kind kind; + int enable; +{ + return (struct symtab_and_line *) NULL; +} +#endif + +#if !defined(CHILD_GET_CURRENT_EXCEPTION_EVENT) +struct exception_event_record * +child_get_current_exception_event () +{ + return (struct exception_event_record *) NULL; +} +#endif + + +#if !defined(CHILD_PID_TO_EXEC_FILE) +char * +child_pid_to_exec_file (pid) + int pid; +{ + /* This version of Unix doesn't support translation of a process ID + to the filename of the executable file. + */ + return NULL; +} +#endif + +char * +child_core_file_to_sym_file (core) + char * core; +{ + /* The target stratum for a running executable need not support + this operation. + */ + return NULL; +} + + struct target_ops child_ops = { "child", /* to_shortname */ @@ -361,13 +894,13 @@ struct target_ops child_ops = { child_open, /* to_open */ 0, /* to_close */ child_attach, /* to_attach */ - NULL, /* to_post_attach */ - NULL, /* to_require_attach */ + child_post_attach, /* to_post_attach */ + child_require_attach, /* to_require_attach */ child_detach, /* to_detach */ - NULL, /* to_require_detach */ + child_require_detach, /* to_require_detach */ child_resume, /* to_resume */ child_wait, /* to_wait */ - NULL, /* to_post_wait */ + child_post_wait, /* to_post_wait */ fetch_inferior_registers, /* to_fetch_registers */ store_inferior_registers, /* to_store_registers */ child_prepare_to_store, /* to_prepare_to_store */ @@ -384,33 +917,33 @@ struct target_ops child_ops = { 0, /* to_load */ 0, /* to_lookup_symbol */ child_create_inferior, /* to_create_inferior */ - NULL, /* to_post_startup_inferior */ - NULL, /* to_acknowledge_created_inferior */ - NULL, /* to_clone_and_follow_inferior */ - NULL, /* to_post_follow_inferior_by_clone */ - NULL, /* to_insert_fork_catchpoint */ - NULL, /* to_remove_fork_catchpoint */ - NULL, /* to_insert_vfork_catchpoint */ - NULL, /* to_remove_vfork_catchpoint */ - NULL, /* to_has_forked */ - NULL, /* to_has_vforked */ - NULL, /* to_can_follow_vfork_prior_to_exec */ - NULL, /* to_post_follow_vfork */ - NULL, /* to_insert_exec_catchpoint */ - NULL, /* to_remove_exec_catchpoint */ - NULL, /* to_has_execd */ - NULL, /* to_reported_exec_events_per_exec_call */ - NULL, /* to_has_syscall_event */ - NULL, /* to_has_exited */ + child_post_startup_inferior, /* to_post_startup_inferior */ + child_acknowledge_created_inferior, /* to_acknowledge_created_inferior */ + child_clone_and_follow_inferior, /* to_clone_and_follow_inferior */ + child_post_follow_inferior_by_clone, /* to_post_follow_inferior_by_clone */ + child_insert_fork_catchpoint, /* to_insert_fork_catchpoint */ + child_remove_fork_catchpoint, /* to_remove_fork_catchpoint */ + child_insert_vfork_catchpoint, /* to_insert_vfork_catchpoint */ + child_remove_vfork_catchpoint, /* to_remove_vfork_catchpoint */ + child_has_forked, /* to_has_forked */ + child_has_vforked, /* to_has_vforked */ + child_can_follow_vfork_prior_to_exec, /* to_can_follow_vfork_prior_to_exec */ + child_post_follow_vfork, /* to_post_follow_vfork */ + child_insert_exec_catchpoint, /* to_insert_exec_catchpoint */ + child_remove_exec_catchpoint, /* to_remove_exec_catchpoint */ + child_has_execd, /* to_has_execd */ + child_reported_exec_events_per_exec_call, /* to_reported_exec_events_per_exec_call */ + child_has_syscall_event, /* to_has_syscall_event */ + child_has_exited, /* to_has_exited */ child_mourn_inferior, /* to_mourn_inferior */ child_can_run, /* to_can_run */ 0, /* to_notice_signals */ child_thread_alive, /* to_thread_alive */ child_stop, /* to_stop */ - NULL, /* to_enable_exception_callback */ - NULL, /* to_get_current_exception_event */ - NULL, /* to_pid_to_exec_file */ - NULL, /* to_core_file_to_sym_file */ + child_enable_exception_callback, /* to_enable_exception_callback */ + child_get_current_exception_event, /* to_get_current_exception_event */ + child_pid_to_exec_file, /* to_pid_to_exec_file */ + child_core_file_to_sym_file, /* to_core_file_to_sym_file */ process_stratum, /* to_stratum */ 0, /* to_next */ 1, /* to_has_all_memory */ diff --git a/gdb/infttrace.c b/gdb/infttrace.c new file mode 100644 index 0000000..e23f163 --- /dev/null +++ b/gdb/infttrace.c @@ -0,0 +1,5709 @@ +/* Low level Unix child interface to ttrace, for GDB when running under HP-UX. + Copyright 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996 + Free Software Foundation, Inc. + +This file is part of GDB. + +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. */ + +#include "defs.h" +#include "frame.h" +#include "inferior.h" +#include "target.h" +#include "gdb_string.h" +#include "wait.h" +#include "command.h" + +/* Some hackery to work around a use of the #define name NO_FLAGS + * in both gdb and HPUX (bfd.h and /usr/include/machine/vmparam.h). + */ +#ifdef NO_FLAGS +#define INFTTRACE_TEMP_HACK NO_FLAGS +#undef NO_FLAGS +#endif + +#ifdef USG +#include <sys/types.h> +#endif + +#include <sys/param.h> +#include <sys/dir.h> +#include <signal.h> +#include <sys/ioctl.h> + +#include <sys/ttrace.h> +#include <sys/unistd.h> +#include <sys/mman.h> + +#ifndef NO_PTRACE_H +#ifdef PTRACE_IN_WRONG_PLACE +#include <ptrace.h> +#else +#include <sys/ptrace.h> +#endif +#endif /* NO_PTRACE_H */ + +/* Second half of the hackery above. Non-ANSI C, so + * we can't use "#error", alas. + */ +#ifdef NO_FLAGS +#if (NO_FLAGS != INFTTRACE_TEMP_HACK ) + /* #error "Hackery to remove warning didn't work right" */ +#else + /* Ok, new def'n of NO_FLAGS is same as old one; no action needed. */ +#endif +#else + /* #error "Didn't get expected re-definition of NO_FLAGS" */ +#define NO_FLAGS INFTTRACE_TEMP_HACK +#endif + +#if !defined (PT_SETTRC) +#define PT_SETTRC 0 /* Make process traceable by parent */ +#endif +#if !defined (PT_READ_I) +#define PT_READ_I 1 /* Read word from text space */ +#endif +#if !defined (PT_READ_D) +#define PT_READ_D 2 /* Read word from data space */ +#endif +#if !defined (PT_READ_U) +#define PT_READ_U 3 /* Read word from kernel user struct */ +#endif +#if !defined (PT_WRITE_I) +#define PT_WRITE_I 4 /* Write word to text space */ +#endif +#if !defined (PT_WRITE_D) +#define PT_WRITE_D 5 /* Write word to data space */ +#endif +#if !defined (PT_WRITE_U) +#define PT_WRITE_U 6 /* Write word to kernel user struct */ +#endif +#if !defined (PT_CONTINUE) +#define PT_CONTINUE 7 /* Continue after signal */ +#endif +#if !defined (PT_STEP) +#define PT_STEP 9 /* Set flag for single stepping */ +#endif +#if !defined (PT_KILL) +#define PT_KILL 8 /* Send child a SIGKILL signal */ +#endif + +#ifndef PT_ATTACH +#define PT_ATTACH PTRACE_ATTACH +#endif +#ifndef PT_DETACH +#define PT_DETACH PTRACE_DETACH +#endif + +#include "gdbcore.h" +#ifndef NO_SYS_FILE +#include <sys/file.h> +#endif + +/* This semaphore is used to coordinate the child and parent processes + after a fork(), and before an exec() by the child. See parent_attach_all + for details. + */ +typedef struct { + int parent_channel[2]; /* Parent "talks" to [1], child "listens" to [0] */ + int child_channel[2]; /* Child "talks" to [1], parent "listens" to [0] */ +} startup_semaphore_t; + +#define SEM_TALK (1) +#define SEM_LISTEN (0) + +static startup_semaphore_t startup_semaphore; + +/* See can_touch_threads_of_process for details. */ +static int vforking_child_pid = 0; +static int vfork_in_flight = 0; + +/* To support PREPARE_TO_PROCEED (hppa_prepare_to_proceed). + */ +static pid_t old_gdb_pid = 0; +static pid_t reported_pid = 0; +static int reported_bpt = 0; + +/* 1 if ok as results of a ttrace or ttrace_wait call, 0 otherwise. + */ +#define TT_OK( _status, _errno ) \ + (((_status) == 1) && ((_errno) == 0)) + +#define TTRACE_ARG_TYPE uint64_t + +/* When supplied as the "addr" operand, ttrace interprets this + to mean, "from the current address". + */ +#define TT_USE_CURRENT_PC ((TTRACE_ARG_TYPE) TT_NOPC) + +/* When supplied as the "addr", "data" or "addr2" operand for most + requests, ttrace interprets this to mean, "pay no heed to this + argument". + */ +#define TT_NIL ((TTRACE_ARG_TYPE) TT_NULLARG) + +/* This is capable of holding the value of a 32-bit register. The + value is always left-aligned in the buffer; i.e., [0] contains + the most-significant byte of the register's value, and [sizeof(reg)] + contains the least-significant value. + + ??rehrauer: Yes, this assumes that an int is 32-bits on HP-UX, and + that registers are 32-bits on HP-UX. The latter assumption changes + with PA2.0. + */ +typedef int register_value_t; + +/******************************************************************** + + How this works: + + 1. Thread numbers + + The rest of GDB sees threads as being things with different + "pid" (process id) values. See "thread.c" for details. The + separate threads will be seen and reacted to if infttrace passes + back different pid values (for _events_). See wait_for_inferior + in inftarg.c. + + So infttrace is going to use thread ids externally, pretending + they are process ids, and keep track internally so that it can + use the real process id (and thread id) when calling ttrace. + + The data structure that supports this is a linked list of the + current threads. Since at some date infttrace will have to + deal with multiple processes, each list element records its + corresponding pid, rather than having a single global. + + Note that the list is only approximately current; that's ok, as + it's up to date when we need it (we hope!). Also, it can contain + dead threads, as there's no harm if it does. + + The approach taken here is to bury the translation from external + to internal inside "call_ttrace" and a few other places. + + There are some wrinkles: + + o When GDB forks itself to create the debug target process, + there's only a pid of 0 around in the child, so the + TT_PROC_SETTRC operation uses a more direct call to ttrace; + Similiarly, the initial setting of the event mask happens + early as well, and so is also special-cased, and an attach + uses a real pid; + + o We define an unthreaded application as having a "pseudo" + thread; + + o To keep from confusing the rest of GDB, we don't switch + the PID for the pseudo thread to a TID. A table will help: + + Rest of GDB sees these PIDs: pid tid1 tid2 tid3 ... + + Our thread list stores: pid pid pid pid ... + tid0 tid1 tid2 tid3 + + Ttrace sees these TIDS: tid0 tid1 tid2 tid3 ... + + Both pid and tid0 will map to tid0, as there are infttrace.c-internal + calls to ttrace using tid0. + + 2. Step and Continue + + Since we're implementing the "stop the world" model, sub-model + "other threads run during step", we have some stuff to do: + + o User steps require continuing all threads other than the + one the user is stepping; + + o Internal debugger steps (such as over a breakpoint or watchpoint, + but not out of a library load thunk) require stepping only + the selected thread; this means that we have to report the + step finish on that thread, which can lead to complications; + + o When a thread is created, it is created running, rather + than stopped--so we have to stop it. + + The OS doesn't guarantee the stopped thread list will be stable, + no does it guarantee where on the stopped thread list a thread + that is single-stepped will wind up: it's possible that it will + be off the list for a while, it's possible the step will complete + and it will be re-posted to the end... + + This means we have to scan the stopped thread list, build up + a work-list, and then run down the work list; we can't do the + step/continue during the scan. + + 3. Buffering events + + Then there's the issue of waiting for an event. We do this by + noticing how many events are reported at the end of each wait. + From then on, we "fake" all resumes and steps, returning instantly, + and don't do another wait. Once all pending events are reported, + we can really resume again. + + To keep this hidden, all the routines which know about tids and + pids or real events and simulated ones are static (file-local). + + This code can make lots of calls to ttrace, in particular it + can spin down the list of thread states more than once. If this + becomes a performance hit, the spin could be done once and the + various "tsp" blocks saved, keeping all later spins in this + process. + + The O/S doesn't promise to keep the list straight, and so we must + re-scan a lot. By observation, it looks like a single-step/wait + puts the stepped thread at the end of the list but doesn't change + it otherwise. + +**************************************************************** +*/ + +/* Uncomment these to turn on various debugging output */ +/* #define THREAD_DEBUG */ +/* #define WAIT_BUFFER_DEBUG */ +/* #define PARANOIA */ + + +#define INFTTRACE_ALL_THREADS (-1) +#define INFTTRACE_STEP (1) +#define INFTTRACE_CONTINUE (0) + +/* FIX: this is used in inftarg.c/child_wait, in a hack. + */ +extern int not_same_real_pid; + +/* This is used to count buffered events. + */ +static unsigned int more_events_left = 0; + +/* Process state. + */ +typedef enum process_state_enum { + STOPPED, + FAKE_STEPPING, + FAKE_CONTINUE, /* For later use */ + RUNNING, + FORKING, + VFORKING +} process_state_t; + +static process_state_t process_state = STOPPED; + +/* User-specified stepping modality. + */ +typedef enum stepping_mode_enum { + DO_DEFAULT, /* ...which is a continue! */ + DO_STEP, + DO_CONTINUE +} stepping_mode_t; + +/* Action to take on an attach, depends on + * what kind (user command, fork, vfork). + * + * At the moment, this is either: + * + * o continue with a SIGTRAP signal, or + * + * o leave stopped. + */ +typedef enum attach_continue_enum { + DO_ATTACH_CONTINUE, + DONT_ATTACH_CONTINUE +} attach_continue_t; + +/* This flag is true if we are doing a step-over-bpt + * with buffered events. We will have to be sure to + * report the right thread, as otherwise the spaghetti + * code in "infrun.c/wait_for_inferior" will get + * confused. + */ +static int doing_fake_step = 0; +static lwpid_t fake_step_tid = 0; + + +/**************************************************** + * Thread information structure routines and types. * + **************************************************** + */ +typedef +struct thread_info_struct +{ + int am_pseudo; /* This is a pseudo-thread for the process. */ + int pid; /* Process ID */ + lwpid_t tid; /* Thread ID */ + int handled; /* 1 if a buffered event was handled. */ + int seen; /* 1 if this thread was seen on a traverse. */ + int terminated; /* 1 if thread has terminated. */ + int have_signal; /* 1 if signal to be sent */ + enum target_signal signal_value; /* Signal to send */ + int have_start; /* 1 if alternate starting address */ + stepping_mode_t stepping_mode; /* Whether to step or continue */ + CORE_ADDR start; /* Where to start */ + int have_state; /* 1 if the event state has been set */ + ttstate_t last_stop_state;/* The most recently-waited event for this thread. */ + struct thread_info_struct + *next; /* All threads are linked via this field. */ + struct thread_info_struct + *next_pseudo; /* All pseudo-threads are linked via this field. */ +} thread_info; + +typedef +struct thread_info_header_struct +{ + int count; + thread_info *head; + thread_info *head_pseudo; + +} thread_info_header; + +static thread_info_header thread_head = { 0, NULL, NULL }; +static thread_info_header deleted_threads = { 0, NULL, NULL }; + +static saved_real_pid = 0; + + +/************************************************* + * Debugging support functions * + ************************************************* + */ +CORE_ADDR +get_raw_pc( ttid ) + lwpid_t ttid; +{ + unsigned long pc_val; + int offset; + int res; + + offset = register_addr( PC_REGNUM, U_REGS_OFFSET ); + res = read_from_register_save_state( + ttid, + (TTRACE_ARG_TYPE) offset, + (char *) &pc_val, + sizeof( pc_val )); + if( res <= 0 ) { + return (CORE_ADDR) pc_val; + } + else { + return (CORE_ADDR) 0; + } +} + +static char * +get_printable_name_of_stepping_mode( mode ) + stepping_mode_t mode; +{ + switch( mode ) { + case DO_DEFAULT: return "DO_DEFAULT"; + case DO_STEP: return "DO_STEP"; + case DO_CONTINUE: return "DO_CONTINUE"; + default: return "?unknown mode?"; + } +} + +/* This function returns a pointer to a string describing the + * ttrace event being reported. + */ +char * +get_printable_name_of_ttrace_event (event) + ttevents_t event; +{ + /* This enumeration is "gappy", so don't use a table. */ + switch (event) { + + case TTEVT_NONE: + return "TTEVT_NONE"; + case TTEVT_SIGNAL: + return "TTEVT_SIGNAL"; + case TTEVT_FORK: + return "TTEVT_FORK"; + case TTEVT_EXEC: + return "TTEVT_EXEC"; + case TTEVT_EXIT: + return "TTEVT_EXIT"; + case TTEVT_VFORK: + return "TTEVT_VFORK"; + case TTEVT_SYSCALL_RETURN: + return "TTEVT_SYSCALL_RETURN"; + case TTEVT_LWP_CREATE: + return "TTEVT_LWP_CREATE"; + case TTEVT_LWP_TERMINATE: + return "TTEVT_LWP_TERMINATE"; + case TTEVT_LWP_EXIT: + return "TTEVT_LWP_EXIT"; + case TTEVT_LWP_ABORT_SYSCALL: + return "TTEVT_LWP_ABORT_SYSCALL"; + case TTEVT_SYSCALL_ENTRY: + return "TTEVT_SYSCALL_ENTRY"; + case TTEVT_SYSCALL_RESTART: + return "TTEVT_SYSCALL_RESTART"; + default : + return "?new event?"; + } +} + + +/* This function translates the ttrace request enumeration into + * a character string that is its printable (aka "human readable") + * name. + */ +char * +get_printable_name_of_ttrace_request (request) + ttreq_t request; +{ + if (!IS_TTRACE_REQ (request)) + return "?bad req?"; + + /* This enumeration is "gappy", so don't use a table. */ + switch (request) { + case TT_PROC_SETTRC : + return "TT_PROC_SETTRC"; + case TT_PROC_ATTACH : + return "TT_PROC_ATTACH"; + case TT_PROC_DETACH : + return "TT_PROC_DETACH"; + case TT_PROC_RDTEXT : + return "TT_PROC_RDTEXT"; + case TT_PROC_WRTEXT : + return "TT_PROC_WRTEXT"; + case TT_PROC_RDDATA : + return "TT_PROC_RDDATA"; + case TT_PROC_WRDATA : + return "TT_PROC_WRDATA"; + case TT_PROC_STOP : + return "TT_PROC_STOP"; + case TT_PROC_CONTINUE : + return "TT_PROC_CONTINUE"; + case TT_PROC_GET_PATHNAME : + return "TT_PROC_GET_PATHNAME"; + case TT_PROC_GET_EVENT_MASK : + return "TT_PROC_GET_EVENT_MASK"; + case TT_PROC_SET_EVENT_MASK : + return "TT_PROC_SET_EVENT_MASK"; + case TT_PROC_GET_FIRST_LWP_STATE : + return "TT_PROC_GET_FIRST_LWP_STATE"; + case TT_PROC_GET_NEXT_LWP_STATE : + return "TT_PROC_GET_NEXT_LWP_STATE"; + case TT_PROC_EXIT : + return "TT_PROC_EXIT"; + case TT_PROC_GET_MPROTECT : + return "TT_PROC_GET_MPROTECT"; + case TT_PROC_SET_MPROTECT : + return "TT_PROC_SET_MPROTECT"; + case TT_PROC_SET_SCBM : + return "TT_PROC_SET_SCBM"; + case TT_LWP_STOP : + return "TT_LWP_STOP"; + case TT_LWP_CONTINUE : + return "TT_LWP_CONTINUE"; + case TT_LWP_SINGLE : + return "TT_LWP_SINGLE"; + case TT_LWP_RUREGS : + return "TT_LWP_RUREGS"; + case TT_LWP_WUREGS : + return "TT_LWP_WUREGS"; + case TT_LWP_GET_EVENT_MASK : + return "TT_LWP_GET_EVENT_MASK"; + case TT_LWP_SET_EVENT_MASK : + return "TT_LWP_SET_EVENT_MASK"; + case TT_LWP_GET_STATE : + return "TT_LWP_GET_STATE"; + default : + return "?new req?"; + } +} + + +/* This function translates the process state enumeration into + * a character string that is its printable (aka "human readable") + * name. + */ +static char * +get_printable_name_of_process_state (process_state) + process_state_t process_state; +{ + switch (process_state) { + case STOPPED: + return "STOPPED"; + case FAKE_STEPPING: + return "FAKE_STEPPING"; + case RUNNING: + return "RUNNING"; + case FORKING: + return "FORKING"; + case VFORKING: + return "VFORKING"; + default: + return "?some unknown state?"; + } +} + +/* Set a ttrace thread state to a safe, initial state. + */ +static void +clear_ttstate_t (tts) + ttstate_t * tts; +{ + tts->tts_pid = 0; + tts->tts_lwpid = 0; + tts->tts_user_tid = 0; + tts->tts_event = TTEVT_NONE; +} + +/* Copy ttrace thread state TTS_FROM into TTS_TO. + */ +static void +copy_ttstate_t (tts_to, tts_from) + ttstate_t * tts_to; + ttstate_t * tts_from; +{ + memcpy ((char *) tts_to, (char *) tts_from, sizeof (*tts_to)); +} + +/* Are there any live threads we know about? + */ +static int +any_thread_records() +{ + return( thread_head.count > 0 ); +} + +/* Create, fill in and link in a thread descriptor. + */ +static thread_info * +create_thread_info (pid, tid) + int pid; + lwpid_t tid; +{ + thread_info * new_p; + thread_info * p; + int thread_count_of_pid; + + new_p = malloc( sizeof( thread_info )); + new_p->pid = pid; + new_p->tid = tid; + new_p->have_signal = 0; + new_p->have_start = 0; + new_p->have_state = 0; + clear_ttstate_t( &new_p->last_stop_state ); + new_p->am_pseudo = 0; + new_p->handled = 0; + new_p->seen = 0; + new_p->terminated = 0; + new_p->next = NULL; + new_p->next_pseudo = NULL; + new_p->stepping_mode = DO_DEFAULT; + + if( 0 == thread_head.count ) { +#ifdef THREAD_DEBUG + if( debug_on ) + printf( "First thread, pid %d tid %d!\n", pid, tid ); +#endif + saved_real_pid = inferior_pid; + } + else { +#ifdef THREAD_DEBUG + if( debug_on ) + printf( "Subsequent thread, pid %d tid %d\n", pid, tid ); +#endif + } + + /* Another day, another thread... + */ + thread_head.count++; + + /* The new thread always goes at the head of the list. + */ + new_p->next = thread_head.head; + thread_head.head = new_p; + + /* Is this the "pseudo" thread of a process? It is if there's + * no other thread for this process on the list. (Note that this + * accomodates multiple processes, such as we see even for simple + * cases like forking "non-threaded" programs.) + */ + p = thread_head.head; + thread_count_of_pid = 0; + while (p) + { + if (p->pid == new_p->pid) + thread_count_of_pid++; + p = p->next; + } + + /* Did we see any other threads for this pid? (Recall that we just + * added this thread to the list...) + */ + if (thread_count_of_pid == 1) + { + new_p->am_pseudo = 1; + new_p->next_pseudo = thread_head.head_pseudo; + thread_head.head_pseudo = new_p; + } + + return new_p; +} + +/* Get rid of our thread info. + */ +static void +clear_thread_info () +{ + thread_info *p; + thread_info *q; + +#ifdef THREAD_DEBUG + if( debug_on ) + printf( "Clearing all thread info\n" ); +#endif + + p = thread_head.head; + while( p ) { + q = p; + p = p->next; + free( q ); + } + + thread_head.head = NULL; + thread_head.head_pseudo = NULL; + thread_head.count = 0; + + p = deleted_threads.head; + while( p ) { + q = p; + p = p->next; + free( q ); + } + + deleted_threads.head = NULL; + deleted_threads.head_pseudo = NULL; + deleted_threads.count = 0; + + /* No threads, so can't have pending events. + */ + more_events_left = 0; +} + +/* Given a tid, find the thread block for it. + */ +static thread_info * +find_thread_info (tid) + lwpid_t tid; +{ + thread_info *p; + + for( p = thread_head.head; p; p = p->next ) { + if( p->tid == tid ) { + return p; + } + } + + for( p = deleted_threads.head; p; p = p->next ) { + if( p->tid == tid ) { + return p; + } + } + + return NULL; +} + +/* For any but the pseudo thread, this maps to the + * thread ID. For the pseudo thread, if you pass either + * the thread id or the PID, you get the pseudo thread ID. + * + * We have to be prepared for core gdb to ask about + * deleted threads. We do the map, but we don't like it. + */ +static lwpid_t +map_from_gdb_tid( gdb_tid ) + lwpid_t gdb_tid; +{ + thread_info *p; + + /* First assume gdb_tid really is a tid, and try to find a + * matching entry on the threads list. + */ + for( p = thread_head.head; p; p = p->next ) { + if( p->tid == gdb_tid ) + return gdb_tid; + } + + /* It doesn't appear to be a tid; perhaps it's really a pid? + * Try to find a "pseudo" thread entry on the threads list. + */ + for (p = thread_head.head_pseudo; p != NULL; p = p->next_pseudo) + { + if (p->pid == gdb_tid) + return p->tid; + } + + /* Perhaps it's the tid of a deleted thread we may still + * have some knowledge of? + */ + for( p = deleted_threads.head; p; p = p-> next ) { + if( p->tid == gdb_tid ) + return gdb_tid; + } + + /* Or perhaps it's the pid of a deleted process we may still + * have knowledge of? + */ + for (p = deleted_threads.head_pseudo; p != NULL; p = p->next_pseudo) + { + if (p->pid == gdb_tid) + return p->tid; + } + + return 0; /* Error? */ +} + +/* Map the other way: from a real tid to the + * "pid" known by core gdb. This tid may be + * for a thread that just got deleted, so we + * also need to consider deleted threads. + */ +static lwpid_t +map_to_gdb_tid( real_tid ) + lwpid_t real_tid; +{ + thread_info *p; + + for( p = thread_head.head; p; p = p->next ) { + if( p->tid == real_tid ) { + if( p->am_pseudo ) + return p->pid; + else + return real_tid; + } + } + + for( p = deleted_threads.head; p; p = p-> next ) { + if( p->tid == real_tid ) + if( p->am_pseudo ) + return p->pid; /* Error? */ + else + return real_tid; + } + + return 0; /* Error? Never heard of this thread! */ +} + +/* Do any threads have saved signals? + */ +static int +saved_signals_exist () +{ + thread_info *p; + + for( p = thread_head.head; p; p = p->next ) { + if( p->have_signal ) { + return 1; + } + } + + return 0; +} + +/* Is this the tid for the zero-th thread? + */ +static int +is_pseudo_thread (tid) + lwpid_t tid; +{ + thread_info *p = find_thread_info( tid ); + if( NULL == p || p->terminated ) + return 0; + else + return p->am_pseudo; +} + +/* Is this thread terminated? + */ +static int +is_terminated (tid) + lwpid_t tid; +{ + thread_info *p = find_thread_info( tid ); + + if( NULL != p ) + return p->terminated; + + return 0; +} + +/* Is this pid a real PID or a TID? + */ +static int +is_process_id (pid) + int pid; +{ + lwpid_t tid; + thread_info * tinfo; + pid_t this_pid; + int this_pid_count; + + /* What does PID really represent? + */ + tid = map_from_gdb_tid (pid); + if (tid <= 0) + return 0; /* Actually, is probably an error... */ + + tinfo = find_thread_info (tid); + + /* Does it appear to be a true thread? + */ + if (! tinfo->am_pseudo) + return 0; + + /* Else, it looks like it may be a process. See if there's any other + * threads with the same process ID, though. If there are, then TID + * just happens to be the first thread of several for this process. + */ + this_pid = tinfo->pid; + this_pid_count = 0; + for (tinfo = thread_head.head; tinfo; tinfo = tinfo->next) + { + if (tinfo->pid == this_pid) + this_pid_count++; + } + + return (this_pid_count == 1); +} + + +/* Add a thread to our info. Prevent duplicate entries. + */ +static thread_info * +add_tthread (pid, tid) + int pid; + lwpid_t tid; +{ + thread_info *p; + + p = find_thread_info( tid ); + if( NULL == p ) + p = create_thread_info( pid, tid ); + + return p; +} + +/* Notice that a thread was deleted. + */ +static void +del_tthread (tid) + lwpid_t tid; +{ + thread_info *p; + thread_info *chase; + + if( thread_head.count <= 0 ) { + error( "Internal error in thread database." ); + return; + } + + chase = NULL; + for( p = thread_head.head; p; p = p->next ) { + if( p->tid == tid ) { + +#ifdef THREAD_DEBUG + if( debug_on ) + printf( "Delete here: %d \n", tid ); +#endif + + if( p->am_pseudo ) { + /* + * Deleting a main thread is ok if we're doing + * a parent-follow on a child; this is odd but + * not wrong. It apparently _doesn't_ happen + * on the child-follow, as we don't just delete + * the pseudo while keeping the rest of the + * threads around--instead, we clear out the whole + * thread list at once. + */ + thread_info *q; + thread_info *q_chase; + + q_chase = NULL; + for( q = thread_head.head_pseudo; q; q = q -> next ) { + if( q == p ) { + /* Remove from pseudo list. + */ + if( q_chase == NULL ) + thread_head.head_pseudo = p->next_pseudo; + else + q_chase-> next = p->next_pseudo; + } + else + q_chase = q; + } + } + + /* Remove from live list. + */ + thread_head.count--; + + if( NULL == chase ) + thread_head.head = p->next; + else + chase->next = p->next; + + /* Add to deleted thread list. + */ + p->next = deleted_threads.head; + deleted_threads.head = p; + deleted_threads.count++; + if( p->am_pseudo ) { + p->next_pseudo = deleted_threads.head_pseudo; + deleted_threads.head_pseudo = p; + } + p->terminated = 1; + + return; + } + + else + chase = p; + } +} + +/* Get the pid for this tid. (Has to be a real TID!). + */ +static int +get_pid_for (tid) + lwpid_t tid; +{ + thread_info *p; + + for( p = thread_head.head; p; p = p->next ) { + if( p->tid == tid ) { + return p->pid; + } + } + + for( p = deleted_threads.head; p; p = p->next ) { + if( p->tid == tid ) { + return p->pid; + } + } + + return 0; +} + +/* Note that this thread's current event has been handled. + */ +static void +set_handled( pid, tid ) + int pid; + lwpid_t tid; +{ + thread_info *p; + + p = find_thread_info( tid ); + if( NULL == p ) + p = add_tthread( pid, tid ); + + p->handled = 1; +} + +/* Was this thread's current event handled? + */ +static int +was_handled( tid ) + lwpid_t tid; +{ + thread_info *p; + + p = find_thread_info( tid ); + if( NULL != p ) + return p->handled; + + return 0; /* New threads have not been handled */ +} + +/* Set this thread to unhandled. + */ +static void +clear_handled( tid ) + lwpid_t tid; +{ + thread_info * p; + +#ifdef WAIT_BUFFER_DEBUG + if( debug_on ) + printf( "clear_handled %d\n", (int) tid ); +#endif + + p = find_thread_info (tid); + if (p == NULL) + error ("Internal error: No thread state to clear?"); + + p->handled = 0; +} + +/* Set all threads to unhandled. + */ +static void +clear_all_handled () +{ + thread_info *p; + +#ifdef WAIT_BUFFER_DEBUG + if( debug_on ) + printf( "clear_all_handled\n" ); +#endif + + for( p = thread_head.head; p; p = p->next ) { + p->handled = 0; + } + + for( p = deleted_threads.head; p; p = p->next ) { + p->handled = 0; + } +} + +/* Set this thread to default stepping mode. + */ +static void +clear_stepping_mode( tid ) + lwpid_t tid; +{ + thread_info * p; + +#ifdef WAIT_BUFFER_DEBUG + if( debug_on ) + printf( "clear_stepping_mode %d\n", (int) tid ); +#endif + + p = find_thread_info (tid); + if (p == NULL) + error ("Internal error: No thread state to clear?"); + + p->stepping_mode = DO_DEFAULT; +} + +/* Set all threads to do default continue on resume. + */ +static void +clear_all_stepping_mode () +{ + thread_info *p; + +#ifdef WAIT_BUFFER_DEBUG + if( debug_on ) + printf( "clear_all_stepping_mode\n" ); +#endif + + for( p = thread_head.head; p; p = p->next ) { + p->stepping_mode = DO_DEFAULT; + } + + for( p = deleted_threads.head; p; p = p->next ) { + p->stepping_mode = DO_DEFAULT; + } +} + +/* Set all threads to unseen on this pass. + */ +static void +set_all_unseen () +{ + thread_info *p; + + for( p = thread_head.head; p; p = p->next ) { + p->seen = 0; + } +} + +#if (defined( THREAD_DEBUG ) || defined( PARANOIA )) +/* debugging routine. + */ +static void +print_tthread (p) + thread_info * p; +{ + printf( " Thread pid %d, tid %d", p->pid, p->tid ); + if( p->have_state ) + printf( ", event is %s", + get_printable_name_of_ttrace_event( p->last_stop_state.tts_event )); + + if( p->am_pseudo ) + printf( ", pseudo thread" ); + + if( p->have_signal ) + printf( ", have signal 0x%x", p->signal_value ); + + if( p->have_start ) + printf( ", have start at 0x%x", p->start ); + + printf( ", step is %s", get_printable_name_of_stepping_mode( p->stepping_mode )); + + if( p->handled ) + printf( ", handled" ); + else + printf( ", not handled" ); + + if( p->seen ) + printf( ", seen" ); + else + printf( ", not seen" ); + + printf( "\n" ); +} + +static void +print_tthreads () +{ + thread_info *p; + + if( thread_head.count == 0 ) + printf( "Thread list is empty\n" ); + else { + printf( "Thread list has " ); + if( thread_head.count == 1 ) + printf( "1 entry:\n" ); + else + printf( "%d entries:\n", thread_head.count ); + for( p = thread_head.head; p; p = p->next ) { + print_tthread (p); + } + } + + if( deleted_threads.count == 0 ) + printf( "Deleted thread list is empty\n" ); + else { + printf( "Deleted thread list has " ); + if( deleted_threads.count == 1 ) + printf( "1 entry:\n" ); + else + printf( "%d entries:\n", deleted_threads.count ); + + for( p = deleted_threads.head; p; p = p->next ) { + print_tthread (p); + } + } +} +#endif + +/* Update the thread list based on the "seen" bits. + */ +static void +update_thread_list () +{ + thread_info *p; + thread_info *chase; + + chase = NULL; + for( p = thread_head.head; p; p = p->next ) { + /* Is this an "unseen" thread which really happens to be a process? + If so, is it inferior_pid and is a vfork in flight? If yes to + all, then DON'T REMOVE IT! We're in the midst of moving a vfork + operation, which is a multiple step thing, to the point where we + can touch the parent again. We've most likely stopped to examine + the child at a late stage in the vfork, and if we're not following + the child, we'd best not treat the parent as a dead "thread"... + */ + if( (!p->seen) && p->am_pseudo && vfork_in_flight + && (p->pid != vforking_child_pid)) + p->seen = 1; + + if( !p->seen ) { + /* Remove this one + */ + +#ifdef THREAD_DEBUG + if( debug_on ) + printf( "Delete unseen thread: %d \n", p->tid ); +#endif + del_tthread( p->tid ); + } + } +} + + + +/************************************************ + * O/S call wrappers * + ************************************************ + */ + +/* This function simply calls ttrace with the given arguments. + * It exists so that all calls to ttrace are isolated. All + * parameters should be as specified by "man 2 ttrace". + * + * No other "raw" calls to ttrace should exist in this module. + */ +static int +call_real_ttrace( request, pid, tid, addr, data, addr2 ) + ttreq_t request; + pid_t pid; + lwpid_t tid; + TTRACE_ARG_TYPE addr, data, addr2; +{ + int tt_status; + + errno = 0; + tt_status = ttrace( request, pid, tid, addr, data, addr2 ); + +#ifdef THREAD_DEBUG + if (errno) { + /* Don't bother for a known benign error: if you ask for the + * first thread state, but there is only one thread and it's + * not stopped, ttrace complains. + * + * We have this inside the #ifdef because our caller will do + * this check for real. + */ + if( request != TT_PROC_GET_FIRST_LWP_STATE + || errno != EPROTO ) { + if( debug_on ) + printf( "TT fail for %s, with pid %d, tid %d, status %d \n", + get_printable_name_of_ttrace_request (request), + pid, tid, tt_status ); + } + } +#endif + +#if 0 + /* ??rehrauer: It would probably be most robust to catch and report + * failed requests here. However, some clients of this interface + * seem to expect to catch & deal with them, so we'd best not. + */ + if (errno) { + strcpy (reason_for_failure, "ttrace ("); + strcat (reason_for_failure, get_printable_name_of_ttrace_request (request)); + strcat (reason_for_failure, ")"); + printf( "ttrace error, errno = %d\n", errno ); + perror_with_name (reason_for_failure); + } +#endif + + return tt_status; +} + + +/* This function simply calls ttrace_wait with the given arguments. + * It exists so that all calls to ttrace_wait are isolated. + * + * No "raw" calls to ttrace_wait should exist elsewhere. + */ +static int +call_real_ttrace_wait( pid, tid, option, tsp, tsp_size ) + int pid; + lwpid_t tid; + ttwopt_t option; + ttstate_t *tsp; + size_t tsp_size; +{ + int ttw_status; + thread_info * tinfo = NULL; + + errno = 0; + ttw_status = ttrace_wait (pid, tid, option, tsp, tsp_size); + + if (errno) { +#ifdef THREAD_DEBUG + if( debug_on ) + printf( "TW fail with pid %d, tid %d \n", pid, tid ); +#endif + + perror_with_name ("ttrace wait"); + } + + return ttw_status; +} + + +/* A process may have one or more kernel threads, of which all or + none may be stopped. This function returns the ID of the first + kernel thread in a stopped state, or 0 if none are stopped. + + This function can be used with get_process_next_stopped_thread_id + to iterate over the IDs of all stopped threads of this process. + */ +static lwpid_t +get_process_first_stopped_thread_id (pid, thread_state) + int pid; + ttstate_t * thread_state; +{ + int tt_status; + + tt_status = call_real_ttrace ( + TT_PROC_GET_FIRST_LWP_STATE, + (pid_t) pid, + (lwpid_t) TT_NIL, + (TTRACE_ARG_TYPE) thread_state, + (TTRACE_ARG_TYPE) sizeof (*thread_state), + TT_NIL); + + if (errno) { + if( errno == EPROTO) { + /* This is an error we can handle: there isn't any stopped + * thread. This happens when we're re-starting the application + * and it has only one thread. GET_NEXT handles the case of + * no more stopped threads well; GET_FIRST doesn't. (A ttrace + * "feature".) + */ + tt_status = 1; + errno = 0; + return 0; + } + else + perror_with_name ("ttrace"); + } + + if( tt_status < 0 ) + /* Failed somehow. + */ + return 0; + + return thread_state->tts_lwpid; +} + + +/* This function returns the ID of the "next" kernel thread in a + stopped state, or 0 if there are none. "Next" refers to the + thread following that of the last successful call to this + function or to get_process_first_stopped_thread_id, using + the value of thread_state returned by that call. + + This function can be used with get_process_first_stopped_thread_id + to iterate over the IDs of all stopped threads of this process. + */ +static lwpid_t +get_process_next_stopped_thread_id (pid, thread_state) + int pid; + ttstate_t * thread_state; +{ + int tt_status; + + tt_status = call_real_ttrace ( + TT_PROC_GET_NEXT_LWP_STATE, + (pid_t) pid, + (lwpid_t) TT_NIL, + (TTRACE_ARG_TYPE) thread_state, + (TTRACE_ARG_TYPE) sizeof (*thread_state), + TT_NIL); + if (errno) + perror_with_name ("ttrace"); + + if (tt_status < 0) + /* Failed + */ + return 0; + + else if( tt_status == 0 ) { + /* End of list, no next state. Don't return the + * tts_lwpid, as it's a meaningless "240". + * + * This is an HPUX "feature". + */ + return 0; + } + + return thread_state->tts_lwpid; +} + +/* ??rehrauer: Eventually this function perhaps should be calling + pid_to_thread_id. However, that function currently does nothing + for HP-UX. Even then, I'm not clear whether that function + will return a "kernel" thread ID, or a "user" thread ID. If + the former, we can just call it here. If the latter, we must + map from the "user" tid to a "kernel" tid. + + NOTE: currently not called. + */ +static lwpid_t +get_active_tid_of_pid (pid) + int pid; +{ + ttstate_t thread_state; + + return get_process_first_stopped_thread_id (pid, &thread_state); +} + +/* This function returns 1 if tt_request is a ttrace request that + * operates upon all threads of a (i.e., the entire) process. + */ +int +is_process_ttrace_request (tt_request) + ttreq_t tt_request; +{ + return IS_TTRACE_PROCREQ (tt_request); +} + + +/* This function translates a thread ttrace request into + * the equivalent process request for a one-thread process. + */ +static ttreq_t +make_process_version( request ) + ttreq_t request; +{ + if (!IS_TTRACE_REQ (request)) { + error( "Internal error, bad ttrace request made\n" ); + return -1; + } + + switch (request) { + case TT_LWP_STOP : + return TT_PROC_STOP; + + case TT_LWP_CONTINUE : + return TT_PROC_CONTINUE; + + case TT_LWP_GET_EVENT_MASK : + return TT_PROC_GET_EVENT_MASK; + + case TT_LWP_SET_EVENT_MASK : + return TT_PROC_SET_EVENT_MASK; + + case TT_LWP_SINGLE : + case TT_LWP_RUREGS : + case TT_LWP_WUREGS : + case TT_LWP_GET_STATE : + return -1; /* No equivalent */ + + default : + return request; + } +} + + +/* This function translates the "pid" used by the rest of + * gdb to a real pid and a tid. It then calls "call_real_ttrace" + * with the given arguments. + * + * In general, other parts of this module should call this + * function when they are dealing with external users, who only + * have tids to pass (but they call it "pid" for historical + * reasons). + */ +static int +call_ttrace( request, gdb_tid, addr, data, addr2 ) + ttreq_t request; + int gdb_tid; + TTRACE_ARG_TYPE addr, data, addr2; +{ + lwpid_t real_tid; + int real_pid; + ttreq_t new_request; + int tt_status; + char reason_for_failure [100]; /* Arbitrary size, should be big enough. */ + +#ifdef THREAD_DEBUG + int is_interesting = 0; + + if( TT_LWP_RUREGS == request ) { + is_interesting = 1; /* Adjust code here as desired */ + } + + if( is_interesting && 0 && debug_on ) { + if( !is_process_ttrace_request( request )) { + printf( "TT: Thread request, tid is %d", gdb_tid ); + printf( "== SINGLE at %x", addr ); + } + else { + printf( "TT: Process request, tid is %d\n", gdb_tid ); + printf( "==! SINGLE at %x", addr ); + } + } +#endif + + /* The initial SETTRC and SET_EVENT_MASK calls (and all others + * which happen before any threads get set up) should go + * directly to "call_real_ttrace", so they don't happen here. + * + * But hardware watchpoints do a SET_EVENT_MASK, so we can't + * rule them out.... + */ +#ifdef THREAD_DEBUG + if( request == TT_PROC_SETTRC && debug_on ) + printf( "Unexpected call for TT_PROC_SETTRC\n" ); +#endif + + /* Sometimes we get called with a bogus tid (e.g., if a + * thread has terminated, we return 0; inftarg later asks + * whether the thread has exited/forked/vforked). + */ + if( gdb_tid == 0 ) + { + errno = ESRCH; /* ttrace's response would probably be "No such process". */ + return -1; + } + + /* All other cases should be able to expect that there are + * thread records. + */ + if( !any_thread_records()) { +#ifdef THREAD_DEBUG + if( debug_on ) + warning ("No thread records for ttrace call"); +#endif + errno = ESRCH; /* ttrace's response would be "No such process". */ + return -1; + } + + /* OK, now the task is to translate the incoming tid into + * a pid/tid pair. + */ + real_tid = map_from_gdb_tid( gdb_tid ); + real_pid = get_pid_for( real_tid ); + + /* Now check the result. "Real_pid" is NULL if our list + * didn't find it. We have some tricks we can play to fix + * this, however. + */ + if( 0 == real_pid ) { + ttstate_t thread_state; + +#ifdef THREAD_DEBUG + if( debug_on ) + printf( "No saved pid for tid %d\n", gdb_tid ); +#endif + + if( is_process_ttrace_request( request )) { + + /* Ok, we couldn't get a tid. Try to translate to + * the equivalent process operation. We expect this + * NOT to happen, so this is a desparation-type + * move. It can happen if there is an internal + * error and so no "wait()" call is ever done. + */ + new_request = make_process_version( request ); + if( new_request == -1 ) { + +#ifdef THREAD_DEBUG + if( debug_on ) + printf( "...and couldn't make process version of thread operation\n" ); +#endif + + /* Use hacky saved pid, which won't always be correct + * in the multi-process future. Use tid as thread, + * probably dooming this to failure. FIX! + */ + if( saved_real_pid != 0 ) { +#ifdef THREAD_DEBUG + if( debug_on ) + printf( "...using saved pid %d\n", saved_real_pid ); +#endif + + real_pid = saved_real_pid; + real_tid = gdb_tid; + } + + else + error( "Unable to perform thread operation" ); + } + + else { + /* Sucessfully translated this to a process request, + * which needs no thread value. + */ + real_pid = gdb_tid; + real_tid = 0; + request = new_request; + +#ifdef THREAD_DEBUG + if( debug_on ) { + printf( "Translated thread request to process request\n" ); + if( saved_real_pid == 0 ) + printf( "...but there's no saved pid\n" ); + + else { + if( gdb_tid != saved_real_pid ) + printf( "...but have the wrong pid (%d rather than %d)\n", + gdb_tid, saved_real_pid ); + } + } +#endif + } /* Translated to a process request */ + } /* Is a process request */ + + else { + /* We have to have a thread. Ooops. + */ + error( "Thread request with no threads (%s)", + get_printable_name_of_ttrace_request( request )); + } + } + + /* Ttrace doesn't like to see tid values on process requests, + * even if we have the right one. + */ + if (is_process_ttrace_request (request)) { + real_tid = 0; + } + +#ifdef THREAD_DEBUG + if( is_interesting && 0 && debug_on ) { + printf( " now tid %d, pid %d\n", real_tid, real_pid ); + printf( " request is %s\n", get_printable_name_of_ttrace_request (request)); + } +#endif + + /* Finally, the (almost) real call. + */ + tt_status = call_real_ttrace (request, real_pid, real_tid, addr, data, addr2); + +#ifdef THREAD_DEBUG + if(is_interesting && debug_on ) { + if( !TT_OK( tt_status, errno ) + && !(tt_status == 0 & errno == 0)) + printf( " got error (errno==%d, status==%d)\n", errno, tt_status ); + } +#endif + + return tt_status; +} + + +/* Stop all the threads of a process. + * + * NOTE: use of TT_PROC_STOP can cause a thread with a real event + * to get a TTEVT_NONE event, discarding the old event. Be + * very careful, and only call TT_PROC_STOP when you mean it! + */ +static void +stop_all_threads_of_process( real_pid ) + pid_t real_pid; +{ + int ttw_status; + + ttw_status = call_real_ttrace (TT_PROC_STOP, + (pid_t) real_pid, + (lwpid_t) TT_NIL, + (TTRACE_ARG_TYPE) TT_NIL, + (TTRACE_ARG_TYPE) TT_NIL, + TT_NIL ); + if (errno) + perror_with_name ("ttrace stop of other threads"); +} + + +/* Under some circumstances, it's unsafe to attempt to stop, or even + query the state of, a process' threads. + + In ttrace-based HP-UX, an example is a vforking child process. The + vforking parent and child are somewhat fragile, w/r/t what we can do + what we can do to them with ttrace, until after the child exits or + execs, or until the parent's vfork event is delivered. Until that + time, we must not try to stop the process' threads, or inquire how + many there are, or even alter its data segments, or it typically dies + with a SIGILL. Sigh. + + This function returns 1 if this stopped process, and the event that + we're told was responsible for its current stopped state, cannot safely + have its threads examined. + */ +#define CHILD_VFORKED(evt,pid) \ + (((evt) == TTEVT_VFORK) && ((pid) != inferior_pid)) +#define CHILD_URPED(evt,pid) \ + ((((evt) == TTEVT_EXEC) || ((evt) == TTEVT_EXIT)) && ((pid) != vforking_child_pid)) +#define PARENT_VFORKED(evt,pid) \ + (((evt) == TTEVT_VFORK) && ((pid) == inferior_pid)) + +static int +can_touch_threads_of_process (pid, stopping_event) + int pid; + ttevents_t stopping_event; +{ + if (CHILD_VFORKED (stopping_event, pid)) + { + vforking_child_pid = pid; + vfork_in_flight = 1; + } + + else if (vfork_in_flight && + (PARENT_VFORKED (stopping_event, pid) || + CHILD_URPED (stopping_event, pid))) + { + vfork_in_flight = 0; + vforking_child_pid = 0; + } + + return ! vfork_in_flight; +} + + +/* If we can find an as-yet-unhandled thread state of a + * stopped thread of this process return 1 and set "tsp". + * Return 0 if we can't. + * + * If this function is used when the threads of PIS haven't + * been stopped, undefined behaviour is guaranteed! + */ +static int +select_stopped_thread_of_process (pid, tsp) + int pid; + ttstate_t * tsp; +{ + lwpid_t candidate_tid, tid; + ttstate_t candidate_tstate, tstate; + + /* If we're not allowed to touch the process now, then just + * return the current value of *TSP. + * + * This supports "vfork". It's ok, really, to double the + * current event (the child EXEC, we hope!). + */ + if (! can_touch_threads_of_process (pid, tsp->tts_event)) + return 1; + + /* Decide which of (possibly more than one) events to + * return as the first one. We scan them all so that + * we always return the result of a fake-step first. + */ + candidate_tid = 0; + for (tid = get_process_first_stopped_thread_id (pid, &tstate); + tid != 0; + tid = get_process_next_stopped_thread_id (pid, &tstate)) + { + /* TTEVT_NONE events are uninteresting to our clients. They're + * an artifact of our "stop the world" model--the thread is + * stopped because we stopped it. + */ + if (tstate.tts_event == TTEVT_NONE) { + set_handled( pid, tstate.tts_lwpid ); + } + + /* Did we just single-step a single thread, without letting any + * of the others run? Is this an event for that thread? + * + * If so, we believe our client would prefer to see this event + * over any others. (Typically the client wants to just push + * one thread a little farther forward, and then go around + * checking for what all threads are doing.) + */ + else if (doing_fake_step && (tstate.tts_lwpid == fake_step_tid)) + { +#ifdef WAIT_BUFFER_DEBUG + /* It's possible here to see either a SIGTRAP (due to + * successful completion of a step) or a SYSCALL_ENTRY + * (due to a step completion with active hardware + * watchpoints). + */ + if( debug_on ) + printf( "Ending fake step with tid %d, state %s\n", + tstate.tts_lwpid, + get_printable_name_of_ttrace_event( tstate.tts_event )); +#endif + + /* Remember this one, and throw away any previous + * candidate. + */ + candidate_tid = tstate.tts_lwpid; + candidate_tstate = tstate; + } + +#ifdef FORGET_DELETED_BPTS + + /* We can't just do this, as if we do, and then wind + * up the loop with no unhandled events, we need to + * handle that case--the appropriate reaction is to + * just continue, but there's no easy way to do that. + * + * Better to put this in the ttrace_wait call--if, when + * we fake a wait, we update our events based on the + * breakpoint_here_pc call and find there are no more events, + * then we better continue and so on. + * + * Or we could put it in the next/continue fake. + * But it has to go in the buffering code, not in the + * real go/wait code. + */ + else if( (TTEVT_SIGNAL == tstate.tts_event) + && (5 == tstate.tts_u.tts_signal.tts_signo) + && (0 != get_raw_pc( tstate.tts_lwpid )) + && ! breakpoint_here_p( get_raw_pc( tstate.tts_lwpid )) ) { + /* + * If the user deleted a breakpoint while this + * breakpoint-hit event was buffered, we can forget + * it now. + */ +#ifdef WAIT_BUFFER_DEBUG + if( debug_on ) + printf( "Forgetting deleted bp hit for thread %d\n", + tstate.tts_lwpid ); +#endif + + set_handled( pid, tstate.tts_lwpid ); + } +#endif + + /* Else, is this the first "unhandled" event? If so, + * we believe our client wants to see it (if we don't + * see a fake-step later on in the scan). + */ + else if( !was_handled( tstate.tts_lwpid ) && candidate_tid == 0 ) { + candidate_tid = tstate.tts_lwpid; + candidate_tstate = tstate; + } + + /* This is either an event that has already been "handled", + * and thus we believe is uninteresting to our client, or we + * already have a candidate event. Ignore it... + */ + } + + /* What do we report? + */ + if( doing_fake_step ) { + if( candidate_tid == fake_step_tid ) { + /* Fake step. + */ + tstate = candidate_tstate; + } + else { + warning( "Internal error: fake-step failed to complete." ); + return 0; + } + } + else if( candidate_tid != 0 ) { + /* Found a candidate unhandled event. + */ + tstate = candidate_tstate; + } + else if( tid != 0 ) { + warning( "Internal error in call of ttrace_wait." ); + return 0; + } + else { + warning ("Internal error: no unhandled thread event to select"); + return 0; + } + + copy_ttstate_t (tsp, &tstate); + return 1; +} /* End of select_stopped_thread_of_process */ + +#ifdef PARANOIA +/* Check our internal thread data against the real thing. + */ +static void +check_thread_consistency( real_pid ) + pid_t real_pid; +{ + int tid; /* really lwpid_t */ + ttstate_t tstate; + thread_info *p; + + /* Spin down the O/S list of threads, checking that they + * match what we've got. + */ + for (tid = get_process_first_stopped_thread_id( real_pid, &tstate ); + tid != 0; + tid = get_process_next_stopped_thread_id( real_pid, &tstate )) { + + p = find_thread_info( tid ); + + if( NULL == p ) { + warning( "No internal thread data for thread %d.", tid ); + continue; + } + + if( !p->seen ) { + warning( "Inconsistent internal thread data for thread %d.", tid ); + } + + if( p->terminated ) { + warning( "Thread %d is not terminated, internal error.", tid ); + continue; + } + + +#define TT_COMPARE( fld ) \ + tstate.fld != p->last_stop_state.fld + + if( p->have_state ) { + if( TT_COMPARE( tts_pid ) + || TT_COMPARE( tts_lwpid ) + || TT_COMPARE( tts_user_tid ) + || TT_COMPARE( tts_event ) + || TT_COMPARE( tts_flags ) + || TT_COMPARE( tts_scno ) + || TT_COMPARE( tts_scnargs )) { + warning( "Internal thread data for thread %d is wrong.", tid ); + continue; + } + } + } +} +#endif /* PARANOIA */ + + +/* This function wraps calls to "call_real_ttrace_wait" so + * that a actual wait is only done when all pending events + * have been reported. + * + * Note that typically it is called with a pid of "0", i.e. + * the "don't care" value. + * + * Return value is the status of the pseudo wait. + */ +static int +call_ttrace_wait( pid, option, tsp, tsp_size ) + int pid; + ttwopt_t option; + ttstate_t *tsp; + size_t tsp_size; +{ + /* This holds the actual, for-real, true process ID. + */ + static int real_pid; + + /* As an argument to ttrace_wait, zero pid + * means "Any process", and zero tid means + * "Any thread of the specified process". + */ + int wait_pid = 0; + lwpid_t wait_tid = 0; + lwpid_t real_tid; + + int ttw_status = 0; /* To be returned */ + + thread_info * tinfo = NULL; + + if( pid != 0 ) { + /* Unexpected case. + */ +#ifdef THREAD_DEBUG + if( debug_on ) + printf( "TW: Pid to wait on is %d\n", pid ); +#endif + + if( !any_thread_records()) + error( "No thread records for ttrace call w. specific pid" ); + + /* OK, now the task is to translate the incoming tid into + * a pid/tid pair. + */ + real_tid = map_from_gdb_tid( pid ); + real_pid = get_pid_for( real_tid ); +#ifdef THREAD_DEBUG + if( debug_on ) + printf( "==TW: real pid %d, real tid %d\n", real_pid, real_tid ); +#endif + } + + + /* Sanity checks and set-up. + * Process State + * + * Stopped Running Fake-step (v)Fork + * \________________________________________ + * | + * No buffered events | error wait wait wait + * | + * Buffered events | debuffer error wait debuffer (?) + * + */ + if( more_events_left == 0 ) { + + if( process_state == RUNNING ) { + /* OK--normal call of ttrace_wait with no buffered events. + */ + ; + } + else if( process_state == FAKE_STEPPING ) { + /* Ok--call of ttrace_wait to support + * fake stepping with no buffered events. + * + * But we better be fake-stepping! + */ + if( !doing_fake_step ) { + warning( "Inconsistent thread state." ); + } + } + else if( (process_state == FORKING) + || (process_state == VFORKING)) { + /* Ok--there are two processes, so waiting + * for the second while the first is stopped + * is ok. Handled bits stay as they were. + */ + ; + } + else if( process_state == STOPPED ) { + warning( "Process not running at wait call." ); + } + else + /* No known state. + */ + warning( "Inconsistent process state." ); + } + + else { + /* More events left + */ + if( process_state == STOPPED ) { + /* OK--buffered events being unbuffered. + */ + ; + } + else if( process_state == RUNNING ) { + /* An error--shouldn't have buffered events + * when running. + */ + warning( "Trying to continue with buffered events:" ); + } + else if( process_state == FAKE_STEPPING ) { + /* + * Better be fake-stepping! + */ + if( !doing_fake_step ) { + warning( "Losing buffered thread events!\n" ); + } + } + else if( (process_state == FORKING) + || (process_state == VFORKING)) { + /* Ok--there are two processes, so waiting + * for the second while the first is stopped + * is ok. Handled bits stay as they were. + */ + ; + } + else + warning( "Process in unknown state with buffered events." ); + } + + /* Sometimes we have to wait for a particular thread + * (if we're stepping over a bpt). In that case, we + * _know_ it's going to complete the single-step we + * asked for (because we're only doing the step under + * certain very well-understood circumstances), so it + * can't block. + */ + if( doing_fake_step ) { + wait_tid = fake_step_tid; + wait_pid = get_pid_for( fake_step_tid ); + +#ifdef WAIT_BUFFER_DEBUG + if( debug_on ) + printf( "Doing a wait after a fake-step for %d, pid %d\n", + wait_tid, wait_pid ); +#endif + } + + if( more_events_left == 0 /* No buffered events, need real ones. */ + || process_state != STOPPED ) { + /* If there are no buffered events, and so we need + * real ones, or if we are FORKING, VFORKING, + * FAKE_STEPPING or RUNNING, and thus have to do + * a real wait, then do a real wait. + */ + +#ifdef WAIT_BUFFER_DEBUG + /* Normal case... */ + if( debug_on ) + printf( "TW: do it for real; pid %d, tid %d\n", wait_pid, wait_tid ); +#endif + + /* The actual wait call. + */ + ttw_status = call_real_ttrace_wait( wait_pid, wait_tid, option, tsp, tsp_size); + + /* Note that the routines we'll call will be using "call_real_ttrace", + * not "call_ttrace", and thus need the real pid rather than the pseudo-tid + * the rest of the world uses (which is actually the tid). + */ + real_pid = tsp->tts_pid; + + /* For most events: Stop the world! + * + * It's sometimes not safe to stop all threads of a process. + * Sometimes it's not even safe to ask for the thread state + * of a process! + */ + if (can_touch_threads_of_process (real_pid, tsp->tts_event)) + { + /* If we're really only stepping a single thread, then don't + * try to stop all the others -- we only do this single-stepping + * business when all others were already stopped...and the stop + * would mess up other threads' events. + * + * Similiarly, if there are other threads with events, + * don't do the stop. + */ + if( !doing_fake_step ) { + if( more_events_left > 0 ) + warning( "Internal error in stopping process" ); + + stop_all_threads_of_process (real_pid); + + /* At this point, we could scan and update_thread_list(), + * and only use the local list for the rest of the + * module! We'd get rid of the scans in the various + * continue routines (adding one in attach). It'd + * be great--UPGRADE ME! + */ + } + } + +#ifdef PARANOIA + else if( debug_on ) { + if( more_events_left > 0 ) + printf( "== Can't stop process; more events!\n" ); + else + printf( "== Can't stop process!\n" ); + } +#endif + + process_state = STOPPED; + +#ifdef WAIT_BUFFER_DEBUG + if( debug_on ) + printf( "Process set to STOPPED\n" ); +#endif + } + + else { + /* Fake a call to ttrace_wait. The process must be + * STOPPED, as we aren't going to do any wait. + */ +#ifdef WAIT_BUFFER_DEBUG + if( debug_on ) + printf( "TW: fake it\n" ); +#endif + + if( process_state != STOPPED ) { + warning( "Process not stopped at wait call, in state '%s'.\n", + get_printable_name_of_process_state( process_state )); + } + + if( doing_fake_step ) + error( "Internal error in stepping over breakpoint" ); + + ttw_status = 0; /* Faking it is always successful! */ + } /* End of fake or not? if */ + + /* Pick an event to pass to our caller. Be paranoid. + */ + if( !select_stopped_thread_of_process( real_pid, tsp )) + warning( "Can't find event, using previous event." ); + + else if( tsp->tts_event == TTEVT_NONE ) + warning( "Internal error: no thread has a real event." ); + + else if( doing_fake_step ) { + if( fake_step_tid != tsp->tts_lwpid ) + warning( "Internal error in stepping over breakpoint." ); + + /* This wait clears the (current) fake-step if there was one. + */ + doing_fake_step = 0; + fake_step_tid = 0; + } + + /* We now have a correct tsp and ttw_status for the thread + * which we want to report. So it's "handled"! This call + * will add it to our list if it's not there already. + */ + set_handled( real_pid, tsp->tts_lwpid ); + + /* Save a copy of the ttrace state of this thread, in our local + thread descriptor. + + This caches the state. The implementation of queries like + target_has_execd can then use this cached state, rather than + be forced to make an explicit ttrace call to get it. + + (Guard against the condition that this is the first time we've + waited on, i.e., seen this thread, and so haven't yet entered + it into our list of threads.) + */ + tinfo = find_thread_info (tsp->tts_lwpid); + if (tinfo != NULL) { + copy_ttstate_t (&tinfo->last_stop_state, tsp); + tinfo->have_state = 1; + } + + return ttw_status; +} /* call_ttrace_wait */ + +#if defined(CHILD_REPORTED_EXEC_EVENTS_PER_EXEC_CALL) +int +child_reported_exec_events_per_exec_call () +{ + return 1; /* ttrace reports the event once per call. */ +} +#endif + + + +/* Our implementation of hardware watchpoints involves making memory + pages write-protected. We must remember a page's original permissions, + and we must also know when it is appropriate to restore a page's + permissions to its original state. + + We use a "dictionary" of hardware-watched pages to do this. Each + hardware-watched page is recorded in the dictionary. Each page's + dictionary entry contains the original permissions and a reference + count. Pages are hashed into the dictionary by their start address. + + When hardware watchpoint is set on page X for the first time, page X + is added to the dictionary with a reference count of 1. If other + hardware watchpoints are subsequently set on page X, its reference + count is incremented. When hardware watchpoints are removed from + page X, its reference count is decremented. If a page's reference + count drops to 0, it's permissions are restored and the page's entry + is thrown out of the dictionary. + */ +typedef struct memory_page { + CORE_ADDR page_start; + int reference_count; + int original_permissions; + struct memory_page * next; + struct memory_page * previous; +} memory_page_t; + +#define MEMORY_PAGE_DICTIONARY_BUCKET_COUNT 128 + +static struct { + LONGEST page_count; + int page_size; + int page_protections_allowed; + /* These are just the heads of chains of actual page descriptors. */ + memory_page_t buckets [MEMORY_PAGE_DICTIONARY_BUCKET_COUNT]; +} memory_page_dictionary; + + +static void +require_memory_page_dictionary () +{ + int i; + + /* Is the memory page dictionary ready for use? If so, we're done. */ + if (memory_page_dictionary.page_count >= (LONGEST) 0) + return; + + /* Else, initialize it. */ + memory_page_dictionary.page_count = (LONGEST) 0; + + for (i=0; i<MEMORY_PAGE_DICTIONARY_BUCKET_COUNT; i++) + { + memory_page_dictionary.buckets[i].page_start = (CORE_ADDR) 0; + memory_page_dictionary.buckets[i].reference_count = 0; + memory_page_dictionary.buckets[i].next = NULL; + memory_page_dictionary.buckets[i].previous = NULL; + } +} + + +static void +retire_memory_page_dictionary () +{ + memory_page_dictionary.page_count = (LONGEST) -1; +} + + +/* Write-protect the memory page that starts at this address. + + Returns the original permissions of the page. + */ +static int +write_protect_page (pid, page_start) + int pid; + CORE_ADDR page_start; +{ + int tt_status; + int original_permissions; + int new_permissions; + + tt_status = call_ttrace (TT_PROC_GET_MPROTECT, + pid, + (TTRACE_ARG_TYPE) page_start, + TT_NIL, + (TTRACE_ARG_TYPE) &original_permissions); + if (errno || (tt_status < 0)) + { + return 0; /* What else can we do? */ + } + + /* We'll also write-protect the page now, if that's allowed. */ + if (memory_page_dictionary.page_protections_allowed) + { + new_permissions = original_permissions & ~PROT_WRITE; + tt_status = call_ttrace (TT_PROC_SET_MPROTECT, + pid, + (TTRACE_ARG_TYPE) page_start, + (TTRACE_ARG_TYPE) memory_page_dictionary.page_size, + (TTRACE_ARG_TYPE) new_permissions); + if (errno || (tt_status < 0)) + { + return 0; /* What else can we do? */ + } + } + + return original_permissions; +} + + +/* Unwrite-protect the memory page that starts at this address, restoring + (what we must assume are) its original permissions. + */ +static void +unwrite_protect_page (pid, page_start, original_permissions) + int pid; + CORE_ADDR page_start; + int original_permissions; +{ + int tt_status; + + tt_status = call_ttrace (TT_PROC_SET_MPROTECT, + pid, + (TTRACE_ARG_TYPE) page_start, + (TTRACE_ARG_TYPE) memory_page_dictionary.page_size, + (TTRACE_ARG_TYPE) original_permissions); + if (errno || (tt_status < 0)) + { + return; /* What else can we do? */ + } +} + + +/* Memory page-protections are used to implement "hardware" watchpoints + on HP-UX. + + For every memory page that is currently being watched (i.e., that + presently should be write-protected), write-protect it. + */ +void +hppa_enable_page_protection_events (pid) + int pid; +{ + int bucket; + + memory_page_dictionary.page_protections_allowed = 1; + + for (bucket=0; bucket<MEMORY_PAGE_DICTIONARY_BUCKET_COUNT; bucket++) + { + memory_page_t * page; + + page = memory_page_dictionary.buckets[bucket].next; + while (page != NULL) + { + page->original_permissions = write_protect_page (pid, page->page_start); + page = page->next; + } + } +} + + +/* Memory page-protections are used to implement "hardware" watchpoints + on HP-UX. + + For every memory page that is currently being watched (i.e., that + presently is or should be write-protected), un-write-protect it. + */ +void +hppa_disable_page_protection_events (pid) + int pid; +{ + int bucket; + + for (bucket=0; bucket<MEMORY_PAGE_DICTIONARY_BUCKET_COUNT; bucket++) + { + memory_page_t * page; + + page = memory_page_dictionary.buckets[bucket].next; + while (page != NULL) + { + unwrite_protect_page (pid, page->page_start, page->original_permissions); + page = page->next; + } + } + + memory_page_dictionary.page_protections_allowed = 0; +} + +/* Count the number of outstanding events. At this + * point, we have selected one thread and its event + * as the one to be "reported" upwards to core gdb. + * That thread is already marked as "handled". + * + * Note: we could just scan our own thread list. FIXME! + */ +static int +count_unhandled_events( real_pid, real_tid ) + int real_pid; + lwpid_t real_tid; +{ + ttstate_t tstate; + lwpid_t ttid; + int events_left; + + /* Ok, find out how many threads have real events to report. + */ + events_left = 0; + ttid = get_process_first_stopped_thread_id( real_pid, &tstate ); + +#ifdef THREAD_DEBUG + if( debug_on ) { + if( ttid == 0 ) + printf( "Process %d has no threads\n", real_pid ); + else + printf( "Process %d has these threads:\n", real_pid ); + } +#endif + + while (ttid > 0 ) { + if( tstate.tts_event != TTEVT_NONE + && !was_handled( ttid )) { + /* TTEVT_NONE implies we just stopped it ourselves + * because we're the stop-the-world guys, so it's + * not an event from our point of view. + * + * If "was_handled" is true, this is an event we + * already handled, so don't count it. + * + * Note that we don't count the thread with the + * currently-reported event, as it's already marked + * as handled. + */ + events_left++; + } + +#if defined( THREAD_DEBUG ) || defined( WAIT_BUFFER_DEBUG ) + if( debug_on ) { + if( ttid == real_tid ) + printf( "*" ); /* Thread we're reporting */ + else + printf( " " ); + + if( tstate.tts_event != TTEVT_NONE ) + printf( "+" ); /* Thread with a real event */ + else + printf( " " ); + + if( was_handled( ttid )) + printf( "h" ); /* Thread has been handled */ + else + printf( " " ); + + printf( " %d, with event %s", ttid, + get_printable_name_of_ttrace_event( tstate.tts_event )); + + if( tstate.tts_event == TTEVT_SIGNAL + && 5 == tstate.tts_u.tts_signal.tts_signo ) { + CORE_ADDR pc_val; + + pc_val = get_raw_pc( ttid ); + + if( pc_val > 0 ) + printf( " breakpoint at 0x%x\n", pc_val ); + else + printf( " bpt, can't fetch pc.\n" ); + } + else + printf( "\n" ); + } +#endif + + ttid = get_process_next_stopped_thread_id (real_pid, &tstate); + } + +#if defined( THREAD_DEBUG ) || defined( WAIT_BUFFER_DEBUG ) + if( debug_on ) + if( events_left > 0 ) + printf( "There are thus %d pending events\n", events_left ); +#endif + + return events_left; +} + +/* This function is provided as a sop to clients that are calling + * proc_wait to wait for a process to stop. (see the + * implementation of child_wait.) Return value is the pid for + * the event that ended the wait. + * + * Note: used by core gdb and so uses the pseudo-pid (really tid). + */ +int +proc_wait (pid, status) + int pid; + int *status; +{ + ttstate_t tsp; + int ttwait_return; + int real_pid; + ttstate_t state; + lwpid_t real_tid; + int return_pid; + + /* The ptrace implementation of this also ignores pid. + */ + *status = 0; + + ttwait_return = call_ttrace_wait( 0, TTRACE_WAITOK, &tsp, sizeof (tsp) ); + if (ttwait_return < 0) + { + /* ??rehrauer: It appears that if our inferior exits and we + haven't asked for exit events, that we're not getting any + indication save a negative return from ttrace_wait and an + errno set to ESRCH? + */ + if (errno == ESRCH) + { + *status = 0; /* WIFEXITED */ + return inferior_pid; + } + + warning( "Call of ttrace_wait returned with errno %d.", + errno ); + *status = ttwait_return; + return inferior_pid; + } + + real_pid = tsp.tts_pid; + real_tid = tsp.tts_lwpid; + + /* One complication is that the "tts_event" structure has + * a set of flags, and more than one can be set. So we + * either have to force an order (as we do here), or handle + * more than one flag at a time. + */ + if (tsp.tts_event & TTEVT_LWP_CREATE) { + + /* Unlike what you might expect, this event is reported in + * the _creating_ thread, and the _created_ thread (whose tid + * we have) is still running. So we have to stop it. This + * has already been done in "call_ttrace_wait", but should we + * ever abandon the "stop-the-world" model, here's the command + * to use: + * + * call_ttrace( TT_LWP_STOP, real_tid, TT_NIL, TT_NIL, TT_NIL ); + * + * Note that this would depend on being called _after_ "add_tthread" + * below for the tid-to-pid translation to be done in "call_ttrace". + */ + +#ifdef THREAD_DEBUG + if( debug_on ) + printf( "New thread: pid %d, tid %d, creator tid %d\n", + real_pid, tsp.tts_u.tts_thread.tts_target_lwpid, + real_tid ); +#endif + + /* Now we have to return the tid of the created thread, not + * the creating thread, or "wait_for_inferior" won't know we + * have a new "process" (thread). Plus we should record it + * right, too. + */ + real_tid = tsp.tts_u.tts_thread.tts_target_lwpid; + + add_tthread( real_pid, real_tid ); + } + + else if( (tsp.tts_event & TTEVT_LWP_TERMINATE ) + || (tsp.tts_event & TTEVT_LWP_EXIT) ) { + +#ifdef THREAD_DEBUG + if( debug_on ) + printf( "Thread dies: %d\n", real_tid ); +#endif + + del_tthread( real_tid ); + } + + else if (tsp.tts_event & TTEVT_EXEC) { + +#ifdef THREAD_DEBUG + if( debug_on ) + printf( "Pid %d has zero'th thread %d; inferior pid is %d\n", + real_pid, real_tid, inferior_pid ); +#endif + + add_tthread( real_pid, real_tid ); + } + +#ifdef THREAD_DEBUG + else if( debug_on ) { + printf( "Process-level event %s, using tid %d\n", + get_printable_name_of_ttrace_event( tsp.tts_event ), + real_tid ); + + /* OK to do this, as "add_tthread" won't add + * duplicate entries. Also OK not to do it, + * as this event isn't one which can change the + * thread state. + */ + add_tthread( real_pid, real_tid ); + } +#endif + + + /* How many events are left to report later? + * In a non-stop-the-world model, this isn't needed. + * + * Note that it's not always safe to query the thread state of a process, + * which is what count_unhandled_events does. (If unsafe, we're left with + * no other resort than to assume that no more events remain...) + */ + if (can_touch_threads_of_process (real_pid, tsp.tts_event)) + more_events_left = count_unhandled_events( real_pid, real_tid ); + + else { + if( more_events_left > 0 ) + warning( "Vfork or fork causing loss of %d buffered events.", + more_events_left ); + + more_events_left = 0; + } + + /* Attempt to translate the ttrace_wait-returned status into the + ptrace equivalent. + + ??rehrauer: This is somewhat fragile. We really ought to rewrite + clients that expect to pick apart a ptrace wait status, to use + something a little more abstract. + */ + if ( (tsp.tts_event & TTEVT_EXEC) + || (tsp.tts_event & TTEVT_FORK) + || (tsp.tts_event & TTEVT_VFORK)) + { + /* Forks come in pairs (parent and child), so core gdb + * will do two waits. Be ready to notice this. + */ + if (tsp.tts_event & TTEVT_FORK) + { + process_state = FORKING; + +#ifdef WAIT_BUFFER_DEBUG + if( debug_on ) + printf( "Process set to FORKING\n" ); +#endif + } + else if (tsp.tts_event & TTEVT_VFORK) + { + process_state = VFORKING; + +#ifdef WAIT_BUFFER_DEBUG + if( debug_on ) + printf( "Process set to VFORKING\n" ); +#endif + } + + /* Make an exec or fork look like a breakpoint. Definitely a hack, + but I don't think non HP-UX-specific clients really carefully + inspect the first events they get after inferior startup, so + it probably almost doesn't matter what we claim this is. + */ + +#ifdef THREAD_DEBUG + if( debug_on ) + printf( "..a process 'event'\n" ); +#endif + + /* Also make fork and exec events look like bpts, so they can be caught. + */ + *status = 0177 | (_SIGTRAP << 8); + } + + /* Special-cases: We ask for syscall entry and exit events to implement + "fast" (aka "hardware") watchpoints. + + When we get a syscall entry, we want to disable page-protections, + and resume the inferior; this isn't an event we wish for + wait_for_inferior to see. Note that we must resume ONLY the + thread that reported the syscall entry; we don't want to allow + other threads to run with the page protections off, as they might + then be able to write to watch memory without it being caught. + + When we get a syscall exit, we want to reenable page-protections, + but we don't want to resume the inferior; this is an event we wish + wait_for_inferior to see. Make it look like the signal we normally + get for a single-step completion. This should cause wait_for_inferior + to evaluate whether any watchpoint triggered. + + Or rather, that's what we'd LIKE to do for syscall exit; we can't, + due to some HP-UX "features". Some syscalls have problems with + write-protections on some pages, and some syscalls seem to have + pending writes to those pages at the time we're getting the return + event. So, we'll single-step the inferior to get out of the syscall, + and then reenable protections. + + Note that we're intentionally allowing the syscall exit case to + fall through into the succeeding cases, as sometimes we single- + step out of one syscall only to immediately enter another... + */ + else if ((tsp.tts_event & TTEVT_SYSCALL_ENTRY) + || (tsp.tts_event & TTEVT_SYSCALL_RETURN)) + { + /* Make a syscall event look like a breakpoint. Same comments + as for exec & fork events. + */ +#ifdef THREAD_DEBUG + if( debug_on ) + printf( "..a syscall 'event'\n" ); +#endif + + /* Also make syscall events look like bpts, so they can be caught. + */ + *status = 0177 | (_SIGTRAP << 8); + } + + else if ((tsp.tts_event & TTEVT_LWP_CREATE) + || (tsp.tts_event & TTEVT_LWP_TERMINATE) + || (tsp.tts_event & TTEVT_LWP_EXIT)) + { + /* Make a thread event look like a breakpoint. Same comments + * as for exec & fork events. + */ +#ifdef THREAD_DEBUG + if( debug_on ) + printf( "..a thread 'event'\n" ); +#endif + + /* Also make thread events look like bpts, so they can be caught. + */ + *status = 0177 | (_SIGTRAP << 8); + } + + else if ((tsp.tts_event & TTEVT_EXIT)) + { /* WIFEXITED */ + +#ifdef THREAD_DEBUG + if( debug_on ) + printf( "..an exit\n" ); +#endif + + /* Prevent rest of gdb from thinking this is + * a new thread if for some reason it's never + * seen the main thread before. + */ + inferior_pid = map_to_gdb_tid( real_tid ); /* HACK, FIX */ + + *status = 0 | (tsp.tts_u.tts_exit.tts_exitcode); + } + + else if (tsp.tts_event & TTEVT_SIGNAL) + { /* WIFSTOPPED */ +#ifdef THREAD_DEBUG + if( debug_on ) + printf( "..a signal, %d\n", tsp.tts_u.tts_signal.tts_signo ); +#endif + + *status = 0177 | (tsp.tts_u.tts_signal.tts_signo << 8); + } + + else + { /* !WIFSTOPPED */ + + /* This means the process or thread terminated. But we should've + caught an explicit exit/termination above. So warn (this is + really an internal error) and claim the process or thread + terminated with a SIGTRAP. + */ + + warning ("process_wait: unknown process state"); + +#ifdef THREAD_DEBUG + if( debug_on ) + printf( "Process-level event %s, using tid %d\n", + get_printable_name_of_ttrace_event( tsp.tts_event ), + real_tid ); +#endif + + *status = _SIGTRAP; + } + + target_post_wait (tsp.tts_pid, *status); + + +#ifdef THREAD_DEBUG + if( debug_on ) + printf( "Done waiting, pid is %d, tid %d\n", real_pid, real_tid ); +#endif + + /* All code external to this module uses the tid, but calls + * it "pid". There's some tweaking so that the outside sees + * the first thread as having the same number as the starting + * pid. + */ + return_pid = map_to_gdb_tid( real_tid ); + + /* Remember this for later use in "hppa_prepare_to_proceed". + */ + old_gdb_pid = inferior_pid; + reported_pid = return_pid; + reported_bpt = ((tsp.tts_event & TTEVT_SIGNAL) && (5 == tsp.tts_u.tts_signal.tts_signo)); + + if( real_tid == 0 || return_pid == 0 ) { + warning( "Internal error: process-wait failed." ); + } + + return return_pid; +} + + +/* This function causes the caller's process to be traced by its + parent. This is intended to be called after GDB forks itself, + and before the child execs the target. Despite the name, it + is called by the child. + + Note that HP-UX ttrace is rather funky in how this is done. + If the parent wants to get the initial exec event of a child, + it must set the ttrace event mask of the child to include execs. + (The child cannot do this itself.) This must be done after the + child is forked, but before it execs. + + To coordinate the parent and child, we implement a semaphore using + pipes. After SETTRC'ing itself, the child tells the parent that + it is now traceable by the parent, and waits for the parent's + acknowledgement. The parent can then set the child's event mask, + and notify the child that it can now exec. + + (The acknowledgement by parent happens as a result of a call to + child_acknowledge_created_inferior.) + */ +int +parent_attach_all () +{ + int tt_status; + + /* We need a memory home for a constant, to pass it to ttrace. + The value of the constant is arbitrary, so long as both + parent and child use the same value. Might as well use the + "magic" constant provided by ttrace... + */ + uint64_t tc_magic_child = TT_VERSION; + uint64_t tc_magic_parent = 0; + + tt_status = call_real_ttrace ( + TT_PROC_SETTRC, + (int) TT_NIL, + (lwpid_t) TT_NIL, + TT_NIL, + (TTRACE_ARG_TYPE) TT_VERSION, + TT_NIL ); + + if (tt_status < 0) + return tt_status; + + /* Notify the parent that we're potentially ready to exec(). */ + write (startup_semaphore.child_channel[SEM_TALK], + &tc_magic_child, + sizeof (tc_magic_child)); + + /* Wait for acknowledgement from the parent. */ + read (startup_semaphore.parent_channel[SEM_LISTEN], + &tc_magic_parent, + sizeof (tc_magic_parent)); + + if (tc_magic_child != tc_magic_parent) + warning ("mismatched semaphore magic"); + + /* Discard our copy of the semaphore. */ + (void) close (startup_semaphore.parent_channel[SEM_LISTEN]); + (void) close (startup_semaphore.parent_channel[SEM_TALK]); + (void) close (startup_semaphore.child_channel[SEM_LISTEN]); + (void) close (startup_semaphore.child_channel[SEM_TALK]); + + return tt_status; +} + +/* Despite being file-local, this routine is dealing with + * actual process IDs, not thread ids. That's because it's + * called before the first "wait" call, and there's no map + * yet from tids to pids. + * + * When it is called, a forked child is running, but waiting on + * the semaphore. If you stop the child and re-start it, + * things get confused, so don't do that! An attached child is + * stopped. + * + * Since this is called after either attach or run, we + * have to be the common part of both. + */ +static void +require_notification_of_events ( real_pid ) + int real_pid; +{ + int tt_status; + ttevent_t notifiable_events; + + lwpid_t tid; + ttstate_t thread_state; + +#ifdef THREAD_DEBUG + if( debug_on ) + printf( "Require notif, pid is %d\n", real_pid ); +#endif + + /* Temporary HACK: tell inftarg.c/child_wait to not + * loop until pids are the same. + */ + not_same_real_pid = 0; + + sigemptyset (¬ifiable_events.tte_signals); + notifiable_events.tte_opts = TTEO_NONE; + + /* This ensures that forked children inherit their parent's + * event mask, which we're setting here. + * + * NOTE: if you debug gdb with itself, then the ultimate + * debuggee gets flags set by the outermost gdb, as + * a child of a child will still inherit. + */ + notifiable_events.tte_opts |= TTEO_PROC_INHERIT; + + notifiable_events.tte_events = TTEVT_DEFAULT; + notifiable_events.tte_events |= TTEVT_SIGNAL; + notifiable_events.tte_events |= TTEVT_EXEC; + notifiable_events.tte_events |= TTEVT_EXIT; + notifiable_events.tte_events |= TTEVT_FORK; + notifiable_events.tte_events |= TTEVT_VFORK; + notifiable_events.tte_events |= TTEVT_LWP_CREATE; + notifiable_events.tte_events |= TTEVT_LWP_EXIT; + notifiable_events.tte_events |= TTEVT_LWP_TERMINATE; + + tt_status = call_real_ttrace ( + TT_PROC_SET_EVENT_MASK, + real_pid, + (lwpid_t) TT_NIL, + (TTRACE_ARG_TYPE) ¬ifiable_events, + (TTRACE_ARG_TYPE) sizeof (notifiable_events), + TT_NIL); +} + +static void +require_notification_of_exec_events ( real_pid ) + int real_pid; +{ + int tt_status; + ttevent_t notifiable_events; + + lwpid_t tid; + ttstate_t thread_state; + +#ifdef THREAD_DEBUG + if( debug_on ) + printf( "Require notif, pid is %d\n", real_pid ); +#endif + + /* Temporary HACK: tell inftarg.c/child_wait to not + * loop until pids are the same. + */ + not_same_real_pid = 0; + + sigemptyset (¬ifiable_events.tte_signals); + notifiable_events.tte_opts = TTEO_NOSTRCCHLD; + + /* This ensures that forked children don't inherit their parent's + * event mask, which we're setting here. + */ + notifiable_events.tte_opts &= ~TTEO_PROC_INHERIT; + + notifiable_events.tte_events = TTEVT_DEFAULT; + notifiable_events.tte_events |= TTEVT_EXEC; + notifiable_events.tte_events |= TTEVT_EXIT; + + tt_status = call_real_ttrace ( + TT_PROC_SET_EVENT_MASK, + real_pid, + (lwpid_t) TT_NIL, + (TTRACE_ARG_TYPE) ¬ifiable_events, + (TTRACE_ARG_TYPE) sizeof (notifiable_events), + TT_NIL); +} + + +/* This function is called by the parent process, with pid being the + * ID of the child process, after the debugger has forked. + */ +void +child_acknowledge_created_inferior (pid) + int pid; +{ + /* We need a memory home for a constant, to pass it to ttrace. + The value of the constant is arbitrary, so long as both + parent and child use the same value. Might as well use the + "magic" constant provided by ttrace... + */ + uint64_t tc_magic_parent = TT_VERSION; + uint64_t tc_magic_child = 0; + + /* Wait for the child to tell us that it has forked. */ + read (startup_semaphore.child_channel[SEM_LISTEN], + &tc_magic_child, + sizeof(tc_magic_child)); + + /* Clear thread info now. We'd like to do this in + * "require...", but that messes up attach. + */ + clear_thread_info(); + + /* Tell the "rest of gdb" that the initial thread exists. + * This isn't really a hack. Other thread-based versions + * of gdb (e.g. gnu-nat.c) seem to do the same thing. + * + * Q: Why don't we also add this thread to the local + * list via "add_tthread"? + * + * A: Because we don't know the tid, and can't stop the + * the process safely to ask what it is. Anyway, we'll + * add it when it gets the EXEC event. + */ + add_thread( pid ); /* in thread.c */ + + /* We can now set the child's ttrace event mask. + */ + require_notification_of_exec_events (pid); + + /* Tell ourselves that the process is running. + */ + process_state = RUNNING; + + /* Notify the child that it can exec. */ + write (startup_semaphore.parent_channel[SEM_TALK], + &tc_magic_parent, + sizeof (tc_magic_parent)); + + /* Discard our copy of the semaphore. */ + (void) close (startup_semaphore.parent_channel[SEM_LISTEN]); + (void) close (startup_semaphore.parent_channel[SEM_TALK]); + (void) close (startup_semaphore.child_channel[SEM_LISTEN]); + (void) close (startup_semaphore.child_channel[SEM_TALK]); +} + + +/* + * arrange for notification of all events by + * calling require_notification_of_events. + */ +void +child_post_startup_inferior ( real_pid) + int real_pid; +{ + require_notification_of_events (real_pid); +} + +/* From here on, we should expect tids rather than pids. + */ +static void +hppa_enable_catch_fork (tid) + int tid; +{ + int tt_status; + ttevent_t ttrace_events; + + /* Get the set of events that are currently enabled. + */ + tt_status = call_ttrace (TT_PROC_GET_EVENT_MASK, + tid, + (TTRACE_ARG_TYPE) &ttrace_events, + (TTRACE_ARG_TYPE) sizeof (ttrace_events), + TT_NIL ); + if (errno) + perror_with_name ("ttrace"); + + /* Add forks to that set. */ + ttrace_events.tte_events |= TTEVT_FORK; + +#ifdef THREAD_DEBUG + if( debug_on ) + printf( "enable fork, tid is %d\n", tid ); +#endif + + tt_status = call_ttrace (TT_PROC_SET_EVENT_MASK, + tid, + (TTRACE_ARG_TYPE) &ttrace_events, + (TTRACE_ARG_TYPE) sizeof (ttrace_events), + TT_NIL); + if (errno) + perror_with_name ("ttrace"); +} + + +static void +hppa_disable_catch_fork (tid) + int tid; +{ + int tt_status; + ttevent_t ttrace_events; + + /* Get the set of events that are currently enabled. + */ + tt_status = call_ttrace (TT_PROC_GET_EVENT_MASK, + tid, + (TTRACE_ARG_TYPE) &ttrace_events, + (TTRACE_ARG_TYPE) sizeof (ttrace_events), + TT_NIL); + + if (errno) + perror_with_name ("ttrace"); + + /* Remove forks from that set. */ + ttrace_events.tte_events &= ~TTEVT_FORK; + +#ifdef THREAD_DEBUG + if( debug_on ) + printf("disable fork, tid is %d\n", tid ); +#endif + + tt_status = call_ttrace (TT_PROC_SET_EVENT_MASK, + tid, + (TTRACE_ARG_TYPE) &ttrace_events, + (TTRACE_ARG_TYPE) sizeof (ttrace_events), + TT_NIL); + + if (errno) + perror_with_name ("ttrace"); +} + + +#if defined(CHILD_INSERT_FORK_CATCHPOINT) +int +child_insert_fork_catchpoint (tid) + int tid; +{ + /* Enable reporting of fork events from the kernel. */ + /* ??rehrauer: For the moment, we're always enabling these events, + and just ignoring them if there's no catchpoint to catch them. + */ + return 0; +} +#endif + + +#if defined(CHILD_REMOVE_FORK_CATCHPOINT) +int +child_remove_fork_catchpoint (tid) + int tid; +{ + /* Disable reporting of fork events from the kernel. */ + /* ??rehrauer: For the moment, we're always enabling these events, + and just ignoring them if there's no catchpoint to catch them. + */ + return 0; +} +#endif + + +static void +hppa_enable_catch_vfork (tid) + int tid; +{ + int tt_status; + ttevent_t ttrace_events; + + /* Get the set of events that are currently enabled. + */ + tt_status = call_ttrace (TT_PROC_GET_EVENT_MASK, + tid, + (TTRACE_ARG_TYPE) &ttrace_events, + (TTRACE_ARG_TYPE) sizeof (ttrace_events), + TT_NIL); + + if (errno) + perror_with_name ("ttrace"); + + /* Add vforks to that set. */ + ttrace_events.tte_events |= TTEVT_VFORK; + +#ifdef THREAD_DEBUG + if( debug_on ) + printf("enable vfork, tid is %d\n", tid ); +#endif + + tt_status = call_ttrace (TT_PROC_SET_EVENT_MASK, + tid, + (TTRACE_ARG_TYPE) &ttrace_events, + (TTRACE_ARG_TYPE) sizeof (ttrace_events), + TT_NIL); + + if (errno) + perror_with_name ("ttrace"); +} + + +static void +hppa_disable_catch_vfork (tid) + int tid; +{ + int tt_status; + ttevent_t ttrace_events; + + /* Get the set of events that are currently enabled. */ + tt_status = call_ttrace (TT_PROC_GET_EVENT_MASK, + tid, + (TTRACE_ARG_TYPE) &ttrace_events, + (TTRACE_ARG_TYPE) sizeof (ttrace_events), + TT_NIL); + + if (errno) + perror_with_name ("ttrace"); + + /* Remove vforks from that set. */ + ttrace_events.tte_events &= ~TTEVT_VFORK; + +#ifdef THREAD_DEBUG + if( debug_on ) + printf("disable vfork, tid is %d\n", tid ); +#endif + tt_status = call_ttrace (TT_PROC_SET_EVENT_MASK, + tid, + (TTRACE_ARG_TYPE) &ttrace_events, + (TTRACE_ARG_TYPE) sizeof (ttrace_events), + TT_NIL); + + if (errno) + perror_with_name ("ttrace"); +} + + +#if defined(CHILD_INSERT_VFORK_CATCHPOINT) +int +child_insert_vfork_catchpoint (tid) + int tid; +{ + /* Enable reporting of vfork events from the kernel. */ + /* ??rehrauer: For the moment, we're always enabling these events, + and just ignoring them if there's no catchpoint to catch them. + */ + return 0; +} +#endif + + +#if defined(CHILD_REMOVE_VFORK_CATCHPOINT) +int +child_remove_vfork_catchpoint (tid) + int tid; +{ + /* Disable reporting of vfork events from the kernel. */ + /* ??rehrauer: For the moment, we're always enabling these events, + and just ignoring them if there's no catchpoint to catch them. + */ + return 0; +} +#endif + +#if defined(CHILD_HAS_FORKED) + +/* Q: Do we need to map the returned process ID to a thread ID? + * + * A: I don't think so--here we want a _real_ pid. Any later + * operations will call "require_notification_of_events" and + * start the mapping. + */ +int +child_has_forked (tid, childpid) + int tid; + int *childpid; +{ + int tt_status; + ttstate_t ttrace_state; + thread_info * tinfo; + + /* Do we have cached thread state that we can consult? If so, use it. */ + tinfo = find_thread_info (map_from_gdb_tid (tid)); + if (tinfo != NULL) { + copy_ttstate_t (&ttrace_state, &tinfo->last_stop_state); + } + + /* Nope, must read the thread's current state */ + else + { + tt_status = call_ttrace (TT_LWP_GET_STATE, + tid, + (TTRACE_ARG_TYPE) &ttrace_state, + (TTRACE_ARG_TYPE) sizeof (ttrace_state), + TT_NIL); + + if (errno) + perror_with_name ("ttrace"); + + if (tt_status < 0) + return 0; + } + + if (ttrace_state.tts_event & TTEVT_FORK) + { + *childpid = ttrace_state.tts_u.tts_fork.tts_fpid; + return 1; + } + + return 0; +} +#endif + + +#if defined(CHILD_HAS_VFORKED) + +/* See child_has_forked for pid discussion. + */ +int +child_has_vforked (tid, childpid) + int tid; + int * childpid; +{ + int tt_status; + ttstate_t ttrace_state; + thread_info * tinfo; + + /* Do we have cached thread state that we can consult? If so, use it. */ + tinfo = find_thread_info (map_from_gdb_tid (tid)); + if (tinfo != NULL) + copy_ttstate_t (&ttrace_state, &tinfo->last_stop_state); + + /* Nope, must read the thread's current state */ + else + { + tt_status = call_ttrace (TT_LWP_GET_STATE, + tid, + (TTRACE_ARG_TYPE) &ttrace_state, + (TTRACE_ARG_TYPE) sizeof (ttrace_state), + TT_NIL); + + if (errno) + perror_with_name ("ttrace"); + + if (tt_status < 0) + return 0; + } + + if (ttrace_state.tts_event & TTEVT_VFORK) + { + *childpid = ttrace_state.tts_u.tts_fork.tts_fpid; + return 1; + } + + return 0; +} +#endif + + +#if defined(CHILD_CAN_FOLLOW_VFORK_PRIOR_TO_EXEC) +int +child_can_follow_vfork_prior_to_exec () +{ + /* ttrace does allow this. + + ??rehrauer: However, I had major-league problems trying to + convince wait_for_inferior to handle that case. Perhaps when + it is rewritten to grok multiple processes in an explicit way... + */ + return 0; +} +#endif + + +#if defined(CHILD_INSERT_EXEC_CATCHPOINT) +int +child_insert_exec_catchpoint (tid) + int tid; +{ + /* Enable reporting of exec events from the kernel. */ + /* ??rehrauer: For the moment, we're always enabling these events, + and just ignoring them if there's no catchpoint to catch them. + */ + return 0; +} +#endif + + +#if defined(CHILD_REMOVE_EXEC_CATCHPOINT) +int +child_remove_exec_catchpoint (tid) + int tid; +{ + /* Disable reporting of execevents from the kernel. */ + /* ??rehrauer: For the moment, we're always enabling these events, + and just ignoring them if there's no catchpoint to catch them. + */ + return 0; +} +#endif + + +#if defined(CHILD_HAS_EXECD) +int +child_has_execd (tid, execd_pathname) + int tid; + char ** execd_pathname; +{ + int tt_status; + ttstate_t ttrace_state; + thread_info * tinfo; + + /* Do we have cached thread state that we can consult? If so, use it. */ + tinfo = find_thread_info (map_from_gdb_tid (tid)); + if (tinfo != NULL) + copy_ttstate_t (&ttrace_state, &tinfo->last_stop_state); + + /* Nope, must read the thread's current state */ + else + { + tt_status = call_ttrace (TT_LWP_GET_STATE, + tid, + (TTRACE_ARG_TYPE) &ttrace_state, + (TTRACE_ARG_TYPE) sizeof (ttrace_state), + TT_NIL); + + if (errno) + perror_with_name ("ttrace"); + + if (tt_status < 0) + return 0; + } + + if (ttrace_state.tts_event & TTEVT_EXEC) + { + /* See child_pid_to_exec_file in this file: this is a macro. + */ + char * exec_file = target_pid_to_exec_file (tid); + + *execd_pathname = savestring (exec_file, strlen (exec_file)); + return 1; + } + + return 0; +} +#endif + + +#if defined(CHILD_HAS_SYSCALL_EVENT) +int +child_has_syscall_event (pid, kind, syscall_id) + int pid; + enum target_waitkind * kind; + int * syscall_id; +{ + int tt_status; + ttstate_t ttrace_state; + thread_info * tinfo; + + /* Do we have cached thread state that we can consult? If so, use it. */ + tinfo = find_thread_info (map_from_gdb_tid (pid)); + if (tinfo != NULL) + copy_ttstate_t (&ttrace_state, &tinfo->last_stop_state); + + /* Nope, must read the thread's current state */ + else + { + tt_status = call_ttrace (TT_LWP_GET_STATE, + pid, + (TTRACE_ARG_TYPE) &ttrace_state, + (TTRACE_ARG_TYPE) sizeof (ttrace_state), + TT_NIL); + + if (errno) + perror_with_name ("ttrace"); + + if (tt_status < 0) + return 0; + } + + *kind = TARGET_WAITKIND_SPURIOUS; /* Until proven otherwise... */ + *syscall_id = -1; + + if (ttrace_state.tts_event & TTEVT_SYSCALL_ENTRY) + *kind = TARGET_WAITKIND_SYSCALL_ENTRY; + else if (ttrace_state.tts_event & TTEVT_SYSCALL_RETURN) + *kind = TARGET_WAITKIND_SYSCALL_RETURN; + else + return 0; + + *syscall_id = ttrace_state.tts_scno; + return 1; +} +#endif + + + +#if defined(CHILD_THREAD_ALIVE) + +/* Check to see if the given thread is alive. + * + * We'll trust the thread list, as the more correct + * approach of stopping the process and spinning down + * the OS's thread list is _very_ expensive. + * + * May need a FIXME for that reason. + */ +int +child_thread_alive (gdb_tid) + lwpid_t gdb_tid; +{ + lwpid_t tid; + + /* This spins down the lists twice. + * Possible peformance improvement here! + */ + tid = map_from_gdb_tid( gdb_tid ); + return !is_terminated( tid ); +} + +#endif + + + +/* This function attempts to read the specified number of bytes from the + save_state_t that is our view into the hardware registers, starting at + ss_offset, and ending at ss_offset + sizeof_buf - 1 + + If this function succeeds, it deposits the fetched bytes into buf, + and returns 0. + + If it fails, it returns a negative result. The contents of buf are + undefined it this function fails. + */ +int +read_from_register_save_state (tid, ss_offset, buf, sizeof_buf) + int tid; + TTRACE_ARG_TYPE ss_offset; + char * buf; + int sizeof_buf; +{ + int tt_status; + register_value_t register_value = 0; + + tt_status = call_ttrace (TT_LWP_RUREGS, + tid, + ss_offset, + (TTRACE_ARG_TYPE) sizeof_buf, + (TTRACE_ARG_TYPE) buf); + + if( tt_status == 1 ) + /* Map ttrace's version of success to our version. + * Sometime ttrace returns 0, but that's ok here. + */ + return 0; + + return tt_status; +} + + +/* This function attempts to write the specified number of bytes to the + save_state_t that is our view into the hardware registers, starting at + ss_offset, and ending at ss_offset + sizeof_buf - 1 + + If this function succeeds, it deposits the bytes in buf, and returns 0. + + If it fails, it returns a negative result. The contents of the save_state_t + are undefined it this function fails. + */ +int +write_to_register_save_state (tid, ss_offset, buf, sizeof_buf) + int tid; + TTRACE_ARG_TYPE ss_offset; + char * buf; + int sizeof_buf; +{ + int tt_status; + register_value_t register_value = 0; + + tt_status = call_ttrace (TT_LWP_WUREGS, + tid, + ss_offset, + (TTRACE_ARG_TYPE) sizeof_buf, + (TTRACE_ARG_TYPE) buf); + return tt_status; +} + + +/* This function is a sop to the largeish number of direct calls + to call_ptrace that exist in other files. Rather than create + functions whose name abstracts away from ptrace, and change all + the present callers of call_ptrace, we'll do the expedient (and + perhaps only practical) thing. + + Note HP-UX explicitly disallows a mix of ptrace & ttrace on a traced + process. Thus, we must translate all ptrace requests into their + process-specific, ttrace equivalents. + */ +int +call_ptrace (pt_request, gdb_tid, addr, data) + int pt_request; + int gdb_tid; + PTRACE_ARG3_TYPE addr; + int data; +{ + ttreq_t tt_request; + TTRACE_ARG_TYPE tt_addr = (TTRACE_ARG_TYPE) addr; + TTRACE_ARG_TYPE tt_data = (TTRACE_ARG_TYPE) data; + TTRACE_ARG_TYPE tt_addr2 = TT_NIL; + int tt_status; + register_value_t register_value; + int read_buf; + + /* Perform the necessary argument translation. Note that some + cases are funky enough in the ttrace realm that we handle them + very specially. + */ + switch (pt_request) { + /* The following cases cannot conveniently be handled conveniently + by merely adjusting the ptrace arguments and feeding into the + generic call to ttrace at the bottom of this function. + + Note that because all branches of this switch end in "return", + there's no need for any "break" statements. + */ + case PT_SETTRC : + return parent_attach_all (); + + case PT_RUREGS : + tt_status = read_from_register_save_state (gdb_tid, + tt_addr, + ®ister_value, + sizeof (register_value)); + if (tt_status < 0) + return tt_status; + return register_value; + + case PT_WUREGS : + register_value = (int) tt_data; + tt_status = write_to_register_save_state (gdb_tid, + tt_addr, + ®ister_value, + sizeof (register_value)); + return tt_status; + break; + + case PT_READ_I : + tt_status = call_ttrace (TT_PROC_RDTEXT, /* Implicit 4-byte xfer becomes block-xfer. */ + gdb_tid, + tt_addr, + (TTRACE_ARG_TYPE) 4, + (TTRACE_ARG_TYPE) &read_buf); + if (tt_status < 0) + return tt_status; + return read_buf; + + case PT_READ_D : + tt_status = call_ttrace (TT_PROC_RDDATA, /* Implicit 4-byte xfer becomes block-xfer. */ + gdb_tid, + tt_addr, + (TTRACE_ARG_TYPE) 4, + (TTRACE_ARG_TYPE) &read_buf); + if (tt_status < 0) + return tt_status; + return read_buf; + + case PT_ATTACH : + tt_status = call_real_ttrace (TT_PROC_ATTACH, + map_from_gdb_tid (gdb_tid), + (lwpid_t) TT_NIL, + tt_addr, + (TTRACE_ARG_TYPE) TT_VERSION, + tt_addr2); + if (tt_status < 0) + return tt_status; + return tt_status; + + /* The following cases are handled by merely adjusting the ptrace + arguments and feeding into the generic call to ttrace. + */ + case PT_DETACH : + tt_request = TT_PROC_DETACH; + break; + + case PT_WRITE_I : + tt_request = TT_PROC_WRTEXT; /* Translates 4-byte xfer to block-xfer. */ + tt_data = 4; /* This many bytes. */ + tt_addr2 = (TTRACE_ARG_TYPE) &data; /* Address of xfer source. */ + break; + + case PT_WRITE_D : + tt_request = TT_PROC_WRDATA; /* Translates 4-byte xfer to block-xfer. */ + tt_data = 4; /* This many bytes. */ + tt_addr2 = (TTRACE_ARG_TYPE) &data; /* Address of xfer source. */ + break; + + case PT_RDTEXT : + tt_request = TT_PROC_RDTEXT; + break; + + case PT_RDDATA : + tt_request = TT_PROC_RDDATA; + break; + + case PT_WRTEXT : + tt_request = TT_PROC_WRTEXT; + break; + + case PT_WRDATA : + tt_request = TT_PROC_WRDATA; + break; + + case PT_CONTINUE : + tt_request = TT_PROC_CONTINUE; + break; + + case PT_STEP : + tt_request = TT_LWP_SINGLE; /* Should not be making this request? */ + break; + + case PT_KILL : + tt_request = TT_PROC_EXIT; + break; + + case PT_GET_PROCESS_PATHNAME : + tt_request = TT_PROC_GET_PATHNAME; + break; + + default : + tt_request = pt_request; /* Let ttrace be the one to complain. */ + break; + } + + return call_ttrace (tt_request, + gdb_tid, + tt_addr, + tt_data, + tt_addr2); +} + +/* Kill that pesky process! + */ +void +kill_inferior () +{ + int tid; + int wait_status; + thread_info * t; + thread_info **paranoia; + int para_count, i; + + if (inferior_pid == 0) + return; + + /* Walk the list of "threads", some of which are "pseudo threads", + aka "processes". For each that is NOT inferior_pid, stop it, + and detach it. + + You see, we may not have just a single process to kill. If we're + restarting or quitting or detaching just after the inferior has + forked, then we've actually two processes to clean up. + + But we can't just call target_mourn_inferior() for each, since that + zaps the target vector. + */ + + paranoia = (thread_info **) malloc( thread_head.count * + sizeof(thread_info *)); + para_count = 0; + + t = thread_head.head; + while (t) { + + paranoia[ para_count ] = t; + for( i = 0; i < para_count; i++ ){ + if( t->next == paranoia[i] ) { + warning( "Bad data in gdb's thread data; repairing." ); + t->next = 0; + } + } + para_count++; + + if (t->am_pseudo && (t->pid != inferior_pid)) + { + /* TT_PROC_STOP doesn't require a subsequent ttrace_wait, as it + * generates no event. + */ + call_ttrace (TT_PROC_STOP, + t->pid, + TT_NIL, + TT_NIL, + TT_NIL); + + call_ttrace (TT_PROC_DETACH, + t->pid, + TT_NIL, + (TTRACE_ARG_TYPE) TARGET_SIGNAL_0, + TT_NIL); + } + t = t->next; + } + + free( paranoia ); + + call_ttrace (TT_PROC_STOP, + inferior_pid, + TT_NIL, + TT_NIL, + TT_NIL); + target_mourn_inferior (); + clear_thread_info(); +} + + +#ifndef CHILD_RESUME + +/* Sanity check a thread about to be continued. + */ +static void +thread_dropping_event_check( p ) + thread_info *p; +{ + if( !p->handled ) { + /* + * This seems to happen when we "next" over a + * "fork()" while following the parent. If it's + * the FORK event, that's ok. If it's a SIGNAL + * in the unfollowed child, that's ok to--but + * how can we know that's what's going on? + * + * FIXME! + */ + if( p->have_state ) { + if( p->last_stop_state.tts_event == TTEVT_FORK ) { + /* Ok */ + ; + } + else if( p->last_stop_state.tts_event == TTEVT_SIGNAL ) { + /* Ok, close eyes and let it happen. + */ + ; + } + else { + /* This shouldn't happen--we're dropping a + * real event. + */ + warning( "About to continue process %d, thread %d with unhandled event %s.", + p->pid, p->tid, + get_printable_name_of_ttrace_event( + p->last_stop_state.tts_event )); + +#ifdef PARANOIA + if( debug_on ) + print_tthread( p ); +#endif + } + } + else { + /* No saved state, have to assume it failed. + */ + warning( "About to continue process %d, thread %d with unhandled event.", + p->pid, p->tid ); +#ifdef PARANOIA + if( debug_on ) + print_tthread( p ); +#endif + } + } + +} /* thread_dropping_event_check */ + +/* Use a loop over the threads to continue all the threads but + * the one specified, which is to be stepped. + */ +static void +threads_continue_all_but_one( gdb_tid, signal ) + lwpid_t gdb_tid; + int signal; +{ + thread_info *p; + int thread_signal; + lwpid_t real_tid; + lwpid_t scan_tid; + ttstate_t state; + int real_pid; + +#ifdef THREAD_DEBUG + if( debug_on ) + printf( "Using loop over threads to step/resume with signals\n" ); +#endif + + /* First update the thread list. + */ + set_all_unseen(); + real_tid = map_from_gdb_tid( gdb_tid ); + real_pid = get_pid_for( real_tid ); + + scan_tid = get_process_first_stopped_thread_id( real_pid, &state ); + while ( 0 != scan_tid ) { + +#ifdef THREAD_DEBUG + /* FIX: later should check state is stopped; + * state.tts_flags & TTS_STATEMASK == TTS_WASSUSPENDED + */ + if( debug_on ) + if( state.tts_flags & TTS_STATEMASK != TTS_WASSUSPENDED ) + printf( "About to continue non-stopped thread %d\n", scan_tid ); +#endif + + p = find_thread_info( scan_tid ); + if( NULL == p ) { + add_tthread( real_pid, scan_tid ); + p = find_thread_info( scan_tid ); + + /* This is either a newly-created thread or the + * result of a fork; in either case there's no + * actual event to worry about. + */ + p->handled = 1; + + if( state.tts_event != TTEVT_NONE ) { + /* Oops, do need to worry! + */ + warning( "Unexpected thread with \"%s\" event.", + get_printable_name_of_ttrace_event( state.tts_event )); + } + } + else if( scan_tid != p->tid ) + error( "Bad data in thread database." ); + +#ifdef THREAD_DEBUG + if( debug_on ) + if( p->terminated ) + printf( "Why are we continuing a dead thread?\n" ); +#endif + + p->seen = 1; + + scan_tid = get_process_next_stopped_thread_id( real_pid, &state ); + } + + /* Remove unseen threads. + */ + update_thread_list(); + + /* Now run down the thread list and continue or step. + */ + for( p = thread_head.head; p; p = p->next ) { + + /* Sanity check. + */ + thread_dropping_event_check( p ); + + /* Pass the correct signals along. + */ + if( p->have_signal ) { + thread_signal = p->signal_value; + p->have_signal = 0; + } + else + thread_signal = 0; + + if( p->tid != real_tid ) { + /* + * Not the thread of interest, so continue it + * as the user expects. + */ + if( p->stepping_mode == DO_STEP ) { + /* Just step this thread. + */ + call_ttrace( + TT_LWP_SINGLE, + p->tid, + TT_USE_CURRENT_PC, + (TTRACE_ARG_TYPE) target_signal_to_host( signal ), + TT_NIL ); + } + else { + /* Regular continue (default case). + */ + call_ttrace( + TT_LWP_CONTINUE, + p->tid, + TT_USE_CURRENT_PC, + (TTRACE_ARG_TYPE) target_signal_to_host( thread_signal ), + TT_NIL ); + } + } + else { + /* Step the thread of interest. + */ + call_ttrace( + TT_LWP_SINGLE, + real_tid, + TT_USE_CURRENT_PC, + (TTRACE_ARG_TYPE) target_signal_to_host( signal ), + TT_NIL ); + } + } /* Loop over threads */ +} /* End threads_continue_all_but_one */ + +/* Use a loop over the threads to continue all the threads. + * This is done when a signal must be sent to any of the threads. + */ +static void +threads_continue_all_with_signals( gdb_tid, signal ) + lwpid_t gdb_tid; + int signal; +{ + thread_info *p; + int thread_signal; + lwpid_t real_tid; + lwpid_t scan_tid; + ttstate_t state; + int real_pid; + +#ifdef THREAD_DEBUG + if( debug_on ) + printf( "Using loop over threads to resume with signals\n" ); +#endif + + /* Scan and update thread list. + */ + set_all_unseen(); + real_tid = map_from_gdb_tid( gdb_tid ); + real_pid = get_pid_for( real_tid ); + + scan_tid = get_process_first_stopped_thread_id( real_pid, &state ); + while ( 0 != scan_tid ) { + +#ifdef THREAD_DEBUG + if( debug_on ) + if( state.tts_flags & TTS_STATEMASK != TTS_WASSUSPENDED ) + warning( "About to continue non-stopped thread %d\n", scan_tid ); +#endif + + p = find_thread_info( scan_tid ); + if( NULL == p ) { + add_tthread( real_pid, scan_tid ); + p = find_thread_info( scan_tid ); + + /* This is either a newly-created thread or the + * result of a fork; in either case there's no + * actual event to worry about. + */ + p->handled = 1; + + if( state.tts_event != TTEVT_NONE ) { + /* Oops, do need to worry! + */ + warning( "Unexpected thread with \"%s\" event.", + get_printable_name_of_ttrace_event( state.tts_event )); + } + } + +#ifdef THREAD_DEBUG + if( debug_on ) + if( p->terminated ) + printf( "Why are we continuing a dead thread? (1)\n" ); +#endif + + p->seen = 1; + + scan_tid = get_process_next_stopped_thread_id( real_pid, &state ); + } + + /* Remove unseen threads from our list. + */ + update_thread_list(); + + /* Continue the threads. + */ + for( p = thread_head.head; p; p = p->next ) { + + /* Sanity check. + */ + thread_dropping_event_check( p ); + + /* Pass the correct signals along. + */ + if( p->tid == real_tid ) { + thread_signal = signal; + p->have_signal = 0; + } + else if( p->have_signal ) { + thread_signal = p->signal_value; + p->have_signal = 0; + } + else + thread_signal = 0; + + if( p->stepping_mode == DO_STEP ) { + call_ttrace( + TT_LWP_SINGLE, + p->tid, + TT_USE_CURRENT_PC, + (TTRACE_ARG_TYPE) target_signal_to_host( signal ), + TT_NIL ); + } + else { + /* Continue this thread (default case). + */ + call_ttrace( + TT_LWP_CONTINUE, + p->tid, + TT_USE_CURRENT_PC, + (TTRACE_ARG_TYPE) target_signal_to_host( thread_signal ), + TT_NIL ); + } + } +} /* End threads_continue_all_with_signals */ + +/* Step one thread only. + */ +static void +thread_fake_step( tid, signal ) + lwpid_t tid; + enum target_signal signal; +{ + thread_info *p; + +#ifdef THREAD_DEBUG + if( debug_on ) { + printf( "Doing a fake-step over a bpt, etc. for %d\n", tid ); + + if( is_terminated( tid )) + printf( "Why are we continuing a dead thread? (4)\n" ); + } +#endif + + if( doing_fake_step ) + warning( "Step while step already in progress." ); + + /* See if there's a saved signal value for this + * thread to be passed on, but no current signal. + */ + p = find_thread_info( tid ); + if( p != NULL ) { + if( p->have_signal && signal == NULL ) { + /* Pass on a saved signal. + */ + signal = p->signal_value; + } + + p->have_signal = 0; + } + + if( !p->handled ) + warning( "Internal error: continuing unhandled thread." ); + + call_ttrace( TT_LWP_SINGLE, + tid, + TT_USE_CURRENT_PC, + (TTRACE_ARG_TYPE) target_signal_to_host (signal), + TT_NIL ); + + /* Do bookkeeping so "call_ttrace_wait" knows it has to wait + * for this thread only, and clear any saved signal info. + */ + doing_fake_step = 1; + fake_step_tid = tid; + +} /* End thread_fake_step */ + +/* Continue one thread when a signal must be sent to it. + */ +static void +threads_continue_one_with_signal( gdb_tid, signal ) + lwpid_t gdb_tid; + int signal; +{ + thread_info *p; + lwpid_t real_tid; + int real_pid; + +#ifdef THREAD_DEBUG + if( debug_on ) + printf( "Continuing one thread with a signal\n" ); +#endif + + real_tid = map_from_gdb_tid( gdb_tid ); + real_pid = get_pid_for( real_tid ); + + p = find_thread_info( real_tid ); + if( NULL == p ) { + add_tthread( real_pid, real_tid ); + } + +#ifdef THREAD_DEBUG + if( debug_on ) + if( p->terminated ) + printf( "Why are we continuing a dead thread? (2)\n" ); +#endif + + if( !p->handled ) + warning( "Internal error: continuing unhandled thread." ); + + p->have_signal = 0; + + call_ttrace( TT_LWP_CONTINUE, + gdb_tid, + TT_USE_CURRENT_PC, + (TTRACE_ARG_TYPE) target_signal_to_host( signal ), + TT_NIL ); +} +#endif + +#ifndef CHILD_RESUME + +/* Resume execution of the inferior process. + * + * This routine is in charge of setting the "handled" bits. + * + * If STEP is zero, continue it. + * If STEP is nonzero, single-step it. + * + * If SIGNAL is nonzero, give it that signal. + * + * If TID is -1, apply to all threads. + * If TID is not -1, apply to specified thread. + * + * STEP + * \ !0 0 + * TID \________________________________________________ + * | + * -1 | Step current Continue all threads + * | thread and (but which gets any + * | continue others signal?--We look at + * | "inferior_pid") + * | + * N | Step _this_ thread Continue _this_ thread + * | and leave others and leave others + * | stopped; internally stopped; used only for + * | used by gdb, never hardware watchpoints + * | a user command. and attach, never a + * | user command. + */ +void +child_resume( gdb_tid, step, signal ) + lwpid_t gdb_tid; + int step; + enum target_signal signal; +{ + int resume_all_threads; + lwpid_t tid; + process_state_t new_process_state; + + resume_all_threads = + (gdb_tid == INFTTRACE_ALL_THREADS) || + (vfork_in_flight); + + if (resume_all_threads) { + /* Resume all threads, but first pick a tid value + * so we can get the pid when in call_ttrace doing + * the map. + */ + if (vfork_in_flight) + tid = vforking_child_pid; + else + tid = map_from_gdb_tid( inferior_pid ); + } + else + tid = map_from_gdb_tid( gdb_tid ); + +#ifdef THREAD_DEBUG + if( debug_on ) { + if( more_events_left ) + printf( "More events; " ); + + if( signal != 0 ) + printf( "Sending signal %d; ", signal ); + + if( resume_all_threads ) { + if( step == 0 ) + printf( "Continue process %d\n", tid ); + else + printf( "Step/continue thread %d\n", tid ); + } + else { + if( step == 0 ) + printf( "Continue thread %d\n", tid ); + else + printf( "Step just thread %d\n", tid ); + } + + if( vfork_in_flight ) + printf( "Vfork in flight\n" ); + } +#endif + + if( process_state == RUNNING ) + warning( "Internal error in resume logic; doing resume or step anyway." ); + + if( !step /* Asked to continue... */ + && resume_all_threads /* whole process.. */ + && signal != 0 /* with a signal... */ + && more_events_left > 0 ) { /* but we can't yet--save it! */ + + /* Continue with signal means we have to set the pending + * signal value for this thread. + */ + thread_info *k; + +#ifdef THREAD_DEBUG + if( debug_on ) + printf( "Saving signal %d for thread %d\n", signal, tid ); +#endif + + k = find_thread_info( tid ); + if( k != NULL ) { + k->have_signal = 1; + k->signal_value = signal; + +#ifdef THREAD_DEBUG + if( debug_on ) + if( k->terminated ) + printf( "Why are we continuing a dead thread? (3)\n" ); +#endif + + } + +#ifdef THREAD_DEBUG + else if( debug_on ) { + printf( "No thread info for tid %d\n", tid ); + } +#endif + } + + /* Are we faking this "continue" or "step"? + * + * We used to do steps by continuing all the threads for + * which the events had been handled already. While + * conceptually nicer (hides it all in a lower level), this + * can lead to starvation and a hang (e.g. all but one thread + * are unhandled at a breakpoint just before a "join" operation, + * and one thread is in the join, and the user wants to step that + * thread). + */ + if( resume_all_threads /* Whole process, therefore user command */ + && more_events_left > 0 ) { /* But we can't do this yet--fake it! */ + thread_info *p; + + if( !step ) { + /* No need to do any notes on a per-thread + * basis--we're done! + */ +#ifdef WAIT_BUFFER_DEBUG + if( debug_on ) + printf( "Faking a process resume.\n" ); +#endif + + return; + } + else { + +#ifdef WAIT_BUFFER_DEBUG + if( debug_on ) + printf( "Faking a process step.\n" ); +#endif + + } + + p = find_thread_info( tid ); + if( p == NULL ) { + warning( "No thread information for tid %d, 'next' command ignored.\n", tid ); + return; + } + else { + +#ifdef THREAD_DEBUG + if( debug_on ) + if( p->terminated ) + printf( "Why are we continuing a dead thread? (3.5)\n" ); +#endif + + if( p->stepping_mode != DO_DEFAULT ) { + warning( "Step or continue command applied to thread which is already stepping or continuing; command ignored." ); + + return; + } + + if( step ) + p->stepping_mode = DO_STEP; + else + p->stepping_mode = DO_CONTINUE; + + return; + } /* Have thread info */ + } /* Must fake step or go */ + + /* Execept for fake-steps, from here on we know we are + * going to wind up with a running process which will + * need a real wait. + */ + new_process_state = RUNNING; + + /* An address of TT_USE_CURRENT_PC tells ttrace to continue from where + * it was. (If GDB wanted it to start some other way, we have already + * written a new PC value to the child.) + * + * If this system does not support PT_STEP, a higher level function will + * have called single_step() to transmute the step request into a + * continue request (by setting breakpoints on all possible successor + * instructions), so we don't have to worry about that here. + */ + if (step) { + if( resume_all_threads ) { + /* + * Regular user step: other threads get a "continue". + */ + threads_continue_all_but_one( tid, signal ); + clear_all_handled(); + clear_all_stepping_mode(); + } + + else { + /* "Fake step": gdb is stepping one thread over a + * breakpoint, watchpoint, or out of a library load + * event, etc. The rest just stay where they are. + * + * Also used when there are pending events: we really + * step the current thread, but leave the rest stopped. + * Users can't request this, but "wait_for_inferior" + * does--a lot! + */ + thread_fake_step( tid, signal ); + + /* Clear the "handled" state of this thread, because + * we'll soon get a new event for it. Other events + * stay as they were. + */ + clear_handled( tid ); + clear_stepping_mode( tid ); + new_process_state = FAKE_STEPPING; + } + } + + else { + /* TT_LWP_CONTINUE can pass signals to threads, + * TT_PROC_CONTINUE can't. So if there are any + * signals to pass, we have to use the (slower) + * loop over the stopped threads. + * + * Equally, if we have to not continue some threads, + * due to saved events, we have to use the loop. + */ + if( (signal != 0) || saved_signals_exist()) { + if( resume_all_threads ) { + +#ifdef THREAD_DEBUG + if( debug_on ) + printf( "Doing a continue by loop of all threads\n" ); +#endif + + threads_continue_all_with_signals( tid, signal ); + + clear_all_handled(); + clear_all_stepping_mode(); + } + + else { +#ifdef THREAD_DEBUG + printf( "Doing a continue w/signal of just thread %d\n", tid ); +#endif + + threads_continue_one_with_signal( tid, signal ); + + /* Clear the "handled" state of this thread, because + * we'll soon get a new event for it. Other events + * can stay as they were. + */ + clear_handled( tid ); + clear_stepping_mode( tid ); + } + } + + else { + /* No signals to send. + */ + if( resume_all_threads ) { +#ifdef THREAD_DEBUG + if( debug_on ) + printf( "Doing a continue by process of process %d\n", tid ); +#endif + + if( more_events_left > 0 ) { + warning( "Losing buffered events on continue." ); + more_events_left = 0; + } + + call_ttrace( TT_PROC_CONTINUE, + tid, + TT_NIL, + TT_NIL, + TT_NIL ); + + clear_all_handled(); + clear_all_stepping_mode(); + } + + else { +#ifdef THREAD_DEBUG + if( debug_on ) { + printf( "Doing a continue of just thread %d\n", tid ); + if( is_terminated( tid )) + printf( "Why are we continuing a dead thread? (5)\n" ); + } +#endif + + call_ttrace( TT_LWP_CONTINUE, + tid, + TT_NIL, + TT_NIL, + TT_NIL ); + + /* Clear the "handled" state of this thread, because + * we'll soon get a new event for it. Other events + * can stay as they were. + */ + clear_handled( tid ); + clear_stepping_mode( tid ); + } + } + } + + process_state = new_process_state; + +#ifdef WAIT_BUFFER_DEBUG + if( debug_on ) + printf( "Process set to %s\n", + get_printable_name_of_process_state (process_state) ); +#endif + +} +#endif /* CHILD_RESUME */ + + +#ifdef ATTACH_DETACH +/* + * Like it says. + * + * One worry is that we may not be attaching to "inferior_pid" + * and thus may not want to clear out our data. FIXME? + * + */ +static void +update_thread_state_after_attach( pid, kind_of_go ) + int pid; + attach_continue_t kind_of_go; +{ + int tt_status; + ttstate_t thread_state; + lwpid_t a_thread; + lwpid_t tid; + + /* The process better be stopped. + */ + if( process_state != STOPPED + && process_state != VFORKING ) + warning( "Internal error attaching." ); + + /* Clear out old tthread info and start over. This has the + * side effect of ensuring that the TRAP is reported as being + * in the right thread (re-mapped from tid to pid). + * + * It's because we need to add the tthread _now_ that we + * need to call "clear_thread_info" _now_, and that's why + * "require_notification_of_events" doesn't clear the thread + * info (it's called later than this routine). + */ + clear_thread_info(); + a_thread = 0; + + for (tid = get_process_first_stopped_thread_id (pid, &thread_state); + tid != 0; + tid = get_process_next_stopped_thread_id (pid, &thread_state)) + { + thread_info *p; + + if (a_thread == 0) + { + a_thread = tid; +#ifdef THREAD_DEBUG + if( debug_on ) + printf( "Attaching to process %d, thread %d\n", + pid, a_thread ); +#endif + } + + /* Tell ourselves and the "rest of gdb" that this thread + * exists. + * + * This isn't really a hack. Other thread-based versions + * of gdb (e.g. gnu-nat.c) seem to do the same thing. + * + * We don't need to do mapping here, as we know this + * is the first thread and thus gets the real pid + * (and is "inferior_pid"). + * + * NOTE: it probably isn't the originating thread, + * but that doesn't matter (we hope!). + */ + add_tthread( pid, tid ); + p = find_thread_info( tid ); + if( NULL == p ) /* ?We just added it! */ + error( "Internal error adding a thread on attach." ); + + copy_ttstate_t( &p->last_stop_state, thread_state ); + p->have_state = 1; + + if( DO_ATTACH_CONTINUE == kind_of_go ) { + /* + * If we are going to CONTINUE afterwards, + * raising a SIGTRAP, don't bother trying to + * handle this event. But check first! + */ + switch( p->last_stop_state.tts_event ) { + + case TTEVT_NONE: + /* Ok to set this handled. + */ + break; + + default: + warning( "Internal error; skipping event %s on process %d, thread %d.", + get_printable_name_of_ttrace_event( + p->last_stop_state.tts_event ), + p->pid, p->tid); + } + + set_handled( pid, tid ); + + } + else { + /* There will be no "continue" opertion, so the + * process remains stopped. Don't set any events + * handled except the "gimmies". + */ + switch( p->last_stop_state.tts_event ) { + + case TTEVT_NONE: + /* Ok to ignore this. + */ + set_handled( pid, tid ); + break; + + case TTEVT_EXEC: + case TTEVT_FORK: + /* Expected "other" FORK or EXEC event from a + * fork or vfork. + */ + break; + + default: + printf( "Internal error: failed to handle event %s on process %d, thread %d.", + get_printable_name_of_ttrace_event( + p->last_stop_state.tts_event ), + p->pid, p->tid); + } + } + + add_thread( tid ); /* in thread.c */ + } + +#ifdef PARANOIA + if( debug_on ) + print_tthreads(); +#endif + + /* One mustn't call ttrace_wait() after attaching via ttrace, + 'cause the process is stopped already. + + However, the upper layers of gdb's execution control will + want to wait after attaching (but not after forks, in + which case they will be doing a "target_resume", anticipating + a later TTEVT_EXEC or TTEVT_FORK event). + + To make this attach() implementation more compatible with + others, we'll make the attached-to process raise a SIGTRAP. + + Issue: this continues only one thread. That could be + dangerous if the thread is blocked--the process won't run + and no trap will be raised. FIX! (check state.tts_flags? + need one that's either TTS_WASRUNNING--but we've stopped + it and made it TTS_WASSUSPENDED. Hum...FIXME!) + */ + if( DO_ATTACH_CONTINUE == kind_of_go ) { + tt_status = call_real_ttrace( + TT_LWP_CONTINUE, + pid, + a_thread, + TT_USE_CURRENT_PC, + (TTRACE_ARG_TYPE) target_signal_to_host (TARGET_SIGNAL_TRAP), + TT_NIL); + if (errno) + perror_with_name ("ttrace"); + + clear_handled( a_thread ); /* So TRAP will be reported. */ + + /* Now running. + */ + process_state = RUNNING; + } + + attach_flag = 1; +} +#endif /* ATTACH_DETACH */ + + +#ifdef ATTACH_DETACH +/* Start debugging the process whose number is PID. + * (A _real_ pid). + */ +int +attach( pid ) + int pid; +{ + int tt_status; + + tt_status = call_real_ttrace ( + TT_PROC_ATTACH, + pid, + (lwpid_t) TT_NIL, + TT_NIL, + (TTRACE_ARG_TYPE) TT_VERSION, + TT_NIL); + if (errno) + perror_with_name ("ttrace attach"); + + /* If successful, the process is now stopped. + */ + process_state = STOPPED; + + /* Our caller ("attach_command" in "infcmd.c") + * expects to do a "wait_for_inferior" after + * the attach, so make sure the inferior is + * running when we're done. + */ + update_thread_state_after_attach( pid, DO_ATTACH_CONTINUE ); + + return pid; +} + + +#if defined(CHILD_POST_ATTACH) +void +child_post_attach (pid) + int pid; +{ +#ifdef THREAD_DEBUG + if( debug_on ) + printf( "child-post-attach call\n" ); +#endif + + require_notification_of_events (pid); +} +#endif + + +/* Stop debugging the process whose number is PID + and continue it with signal number SIGNAL. + SIGNAL = 0 means just continue it. + */ +void +detach( signal ) + int signal; +{ + errno = 0; + call_ttrace (TT_PROC_DETACH, + inferior_pid, + TT_NIL, + (TTRACE_ARG_TYPE) signal, + TT_NIL); + attach_flag = 0; + + clear_thread_info(); + + /* Process-state? */ +} +#endif /* ATTACH_DETACH */ + + +/* Default the type of the ttrace transfer to int. */ +#ifndef TTRACE_XFER_TYPE +#define TTRACE_XFER_TYPE int +#endif + +void +_initialize_kernel_u_addr () +{ +} + +#if !defined (CHILD_XFER_MEMORY) +/* NOTE! I tried using TTRACE_READDATA, etc., to read and write memory + in the NEW_SUN_TTRACE case. + It ought to be straightforward. But it appears that writing did + not write the data that I specified. I cannot understand where + it got the data that it actually did write. */ + +/* Copy LEN bytes to or from inferior's memory starting at MEMADDR + to debugger memory starting at MYADDR. Copy to inferior if + WRITE is nonzero. + + Returns the length copied, which is either the LEN argument or zero. + This xfer function does not do partial moves, since child_ops + doesn't allow memory operations to cross below us in the target stack + anyway. */ + +int +child_xfer_memory (memaddr, myaddr, len, write, target) + CORE_ADDR memaddr; + char *myaddr; + int len; + int write; + struct target_ops *target; /* ignored */ +{ + register int i; + /* Round starting address down to longword boundary. */ + register CORE_ADDR addr = memaddr & - sizeof (TTRACE_XFER_TYPE); + /* Round ending address up; get number of longwords that makes. */ + register int count + = (((memaddr + len) - addr) + sizeof (TTRACE_XFER_TYPE) - 1) + / sizeof (TTRACE_XFER_TYPE); + /* Allocate buffer of that many longwords. */ + register TTRACE_XFER_TYPE *buffer + = (TTRACE_XFER_TYPE *) alloca (count * sizeof (TTRACE_XFER_TYPE)); + + if (write) + { + /* Fill start and end extra bytes of buffer with existing memory data. */ + + if (addr != memaddr || len < (int) sizeof (TTRACE_XFER_TYPE)) { + /* Need part of initial word -- fetch it. */ + buffer[0] = call_ttrace (TT_LWP_RDTEXT, + inferior_pid, + (TTRACE_ARG_TYPE) addr, + TT_NIL, + TT_NIL); + } + + if (count > 1) /* FIXME, avoid if even boundary */ + { + buffer[count - 1] = call_ttrace (TT_LWP_RDTEXT, + inferior_pid, + ((TTRACE_ARG_TYPE) + (addr + (count - 1) * sizeof (TTRACE_XFER_TYPE))), + TT_NIL, + TT_NIL); + } + + /* Copy data to be written over corresponding part of buffer */ + + memcpy ((char *) buffer + (memaddr & (sizeof (TTRACE_XFER_TYPE) - 1)), + myaddr, + len); + + /* Write the entire buffer. */ + + for (i = 0; i < count; i++, addr += sizeof (TTRACE_XFER_TYPE)) + { + errno = 0; + call_ttrace (TT_LWP_WRDATA, + inferior_pid, + (TTRACE_ARG_TYPE) addr, + (TTRACE_ARG_TYPE) buffer[i], + TT_NIL); + if (errno) + { + /* Using the appropriate one (I or D) is necessary for + Gould NP1, at least. */ + errno = 0; + call_ttrace (TT_LWP_WRTEXT, + inferior_pid, + (TTRACE_ARG_TYPE) addr, + (TTRACE_ARG_TYPE) buffer[i], + TT_NIL); + } + if (errno) + return 0; + } + } + else + { + /* Read all the longwords */ + for (i = 0; i < count; i++, addr += sizeof (TTRACE_XFER_TYPE)) + { + errno = 0; + buffer[i] = call_ttrace (TT_LWP_RDTEXT, + inferior_pid, + (TTRACE_ARG_TYPE) addr, + TT_NIL, + TT_NIL); + if (errno) + return 0; + QUIT; + } + + /* Copy appropriate bytes out of the buffer. */ + memcpy (myaddr, + (char *) buffer + (memaddr & (sizeof (TTRACE_XFER_TYPE) - 1)), + len); + } + return len; +} + + +static void +udot_info () +{ + int udot_off; /* Offset into user struct */ + int udot_val; /* Value from user struct at udot_off */ + char mess[128]; /* For messages */ + + if (!target_has_execution) + { + error ("The program is not being run."); + } + +#if !defined (KERNEL_U_SIZE) + + /* Adding support for this command is easy. Typically you just add a + routine, called "kernel_u_size" that returns the size of the user + struct, to the appropriate *-nat.c file and then add to the native + config file "#define KERNEL_U_SIZE kernel_u_size()" */ + error ("Don't know how large ``struct user'' is in this version of gdb."); + +#else + + for (udot_off = 0; udot_off < KERNEL_U_SIZE; udot_off += sizeof (udot_val)) + { + if ((udot_off % 24) == 0) + { + if (udot_off > 0) + { + printf_filtered ("\n"); + } + printf_filtered ("%04x:", udot_off); + } + udot_val = call_ttrace (TT_LWP_RUREGS, + inferior_pid, + (TTRACE_ARG_TYPE) udot_off, + TT_NIL, + TT_NIL); + if (errno != 0) + { + sprintf (mess, "\nreading user struct at offset 0x%x", udot_off); + perror_with_name (mess); + } + /* Avoid using nonportable (?) "*" in print specs */ + printf_filtered (sizeof (int) == 4 ? " 0x%08x" : " 0x%16x", udot_val); + } + printf_filtered ("\n"); + +#endif +} +#endif /* !defined (CHILD_XFER_MEMORY). */ + +/* TTrace version of "target_pid_to_exec_file" + */ +char * +child_pid_to_exec_file (tid) + int tid; +{ + static char exec_file_buffer[1024]; + int tt_status; + CORE_ADDR top_of_stack; + char four_chars[4]; + int name_index; + int i; + int done; + int saved_inferior_pid; + + /* As of 10.x HP-UX, there's an explicit request to get the + *pathname. + */ + tt_status = call_ttrace (TT_PROC_GET_PATHNAME, + tid, + (TTRACE_ARG_TYPE) exec_file_buffer, + (TTRACE_ARG_TYPE) sizeof (exec_file_buffer) - 1, + TT_NIL); + if (tt_status >= 0) + return exec_file_buffer; + + /* ??rehrauer: The above request may or may not be broken. It + doesn't seem to work when I use it. But, it may be designed + to only work immediately after an exec event occurs. (I'm + waiting for COSL to explain.) + + In any case, if it fails, try a really, truly amazingly gross + hack that DDE uses, of pawing through the process' data + segment to find the pathname. + */ + top_of_stack = 0x7b03a000; + name_index = 0; + done = 0; + + /* On the chance that pid != inferior_pid, set inferior_pid + to pid, so that (grrrr!) implicit uses of inferior_pid get + the right id. + */ + saved_inferior_pid = inferior_pid; + inferior_pid = tid; + + /* Try to grab a null-terminated string. */ + while (! done) { + if (target_read_memory (top_of_stack, four_chars, 4) != 0) + { + inferior_pid = saved_inferior_pid; + return NULL; + } + for (i = 0; i < 4; i++) { + exec_file_buffer[name_index++] = four_chars[i]; + done = (four_chars[i] == '\0'); + if (done) + break; + } + top_of_stack += 4; + } + + if (exec_file_buffer[0] == '\0') + { + inferior_pid = saved_inferior_pid; + return NULL; + } + + inferior_pid = saved_inferior_pid; + return exec_file_buffer; +} + + +void +pre_fork_inferior () +{ + int status; + + status = pipe (startup_semaphore.parent_channel); + if (status < 0) { + warning ("error getting parent pipe for startup semaphore"); + return; + } + + status = pipe (startup_semaphore.child_channel); + if (status < 0) { + warning ("error getting child pipe for startup semaphore"); + return; + } +} + +/* Called via #define REQUIRE_ATTACH from inftarg.c, + * ultimately from "follow_inferior_fork" in infrun.c, + * itself called from "resume". + * + * This seems to be intended to attach after a fork or + * vfork, while "attach" is used to attach to a pid + * given by the user. The check for an existing attach + * seems odd--it always fails in our test system. + */ +int +hppa_require_attach (pid) + int pid; +{ + int tt_status; + CORE_ADDR pc; + CORE_ADDR pc_addr; + unsigned int regs_offset; + process_state_t old_process_state = process_state; + + /* Are we already attached? There appears to be no explicit + * way to answer this via ttrace, so we try something which + * should be innocuous if we are attached. If that fails, + * then we assume we're not attached, and so attempt to make + * it so. + */ + errno = 0; + tt_status = call_real_ttrace (TT_PROC_STOP, + pid, + (lwpid_t) TT_NIL, + (TTRACE_ARG_TYPE) TT_NIL, + (TTRACE_ARG_TYPE) TT_NIL, + TT_NIL); + + if (errno) + { + /* No change to process-state! + */ + errno = 0; + pid = attach (pid); + } + else + { + /* If successful, the process is now stopped. But if + * we're VFORKING, the parent is still running, so don't + * change the process state. + */ + if( process_state != VFORKING ) + process_state = STOPPED; + + /* If we were already attached, you'd think that we + * would need to start going again--but you'd be wrong, + * as the fork-following code is actually in the middle + * of the "resume" routine in in "infrun.c" and so + * will (almost) immediately do a resume. + * + * On the other hand, if we are VFORKING, which means + * that the child and the parent share a process for a + * while, we know that "resume" won't be resuming + * until the child EXEC event is seen. But we still + * don't want to continue, as the event is already + * there waiting. + */ + update_thread_state_after_attach( pid, DONT_ATTACH_CONTINUE ); + } /* STOP succeeded */ + + return pid; +} + +int +hppa_require_detach (pid, signal) + int pid; + int signal; +{ + int tt_status; + + /* If signal is non-zero, we must pass the signal on to the active + thread prior to detaching. We do this by continuing the threads + with the signal. + */ + if (signal != 0) + { + errno = 0; + threads_continue_all_with_signals( pid, signal ); + } + + errno = 0; + tt_status = call_ttrace (TT_PROC_DETACH, + pid, + TT_NIL, + TT_NIL, + TT_NIL); + + errno = 0; /* Ignore any errors. */ + + /* process_state? */ + + return pid; +} + +#if defined(HPPA_GET_PROCESS_EVENTS) +process_event_vector +hppa_get_process_events (pid, wait_status, must_continue_pid_after) + int pid; + int wait_status; + int * must_continue_pid_after; +{ + int tt_status; + ttstate_t ttrace_state; + process_event_vector events = PEVT_NONE; + + /* This is always 1 with ptrace. */ + *must_continue_pid_after = 0; + + errno = 0; + tt_status = call_ttrace (TT_LWP_GET_STATE, + pid, + (TTRACE_ARG_TYPE) &ttrace_state, + (TTRACE_ARG_TYPE) sizeof (ttrace_state), + TT_NIL); + if (errno) + perror_with_name ("ttrace"); + if (tt_status < 0) + return 0; + + if (ttrace_state.tts_event & TTEVT_SIGNAL) + events |= PEVT_SIGNAL; + if (ttrace_state.tts_event & TTEVT_FORK) + events |= PEVT_FORK; + if (ttrace_state.tts_event & TTEVT_VFORK) + events |= PEVT_VFORK; + if (ttrace_state.tts_event & TTEVT_EXEC) + events |= PEVT_EXEC; + if (ttrace_state.tts_event & TTEVT_EXIT) + events |= PEVT_EXIT; + + return events; +} +#endif /* HPPA_GET_PROCESS_EVENTS */ + + +/* Given the starting address of a memory page, hash it to a bucket in + the memory page dictionary. + */ +static int +get_dictionary_bucket_of_page (page_start) + CORE_ADDR page_start; +{ + int hash; + + hash = (page_start / memory_page_dictionary.page_size); + hash = hash % MEMORY_PAGE_DICTIONARY_BUCKET_COUNT; + + return hash; +} + + +/* Given a memory page's starting address, get (i.e., find an existing + or create a new) dictionary entry for the page. The page will be + write-protected when this function returns, but may have a reference + count of 0 (if the page was newly-added to the dictionary). + */ +static memory_page_t * +get_dictionary_entry_of_page (pid, page_start) + int pid; + CORE_ADDR page_start; +{ + int bucket; + memory_page_t * page = NULL; + memory_page_t * previous_page = NULL; + + /* We're going to be using the dictionary now, than-kew. */ + require_memory_page_dictionary (pid); + + /* Try to find an existing dictionary entry for this page. Hash + on the page's starting address. + */ + bucket = get_dictionary_bucket_of_page (page_start); + page = &memory_page_dictionary.buckets[bucket]; + while (page != NULL) + { + if (page->page_start == page_start) + break; + previous_page = page; + page = page->next; + } + + /* Did we find a dictionary entry for this page? If not, then + add it to the dictionary now. + */ + if (page == NULL) + { + /* Create a new entry. */ + page = (memory_page_t *) xmalloc (sizeof (memory_page_t)); + page->page_start = page_start; + page->reference_count = 0; + page->next = NULL; + page->previous = NULL; + + /* We'll write-protect the page now, if that's allowed. */ + page->original_permissions = write_protect_page (pid, page_start); + + /* Add the new entry to the dictionary. */ + page->previous = previous_page; + previous_page->next = page; + + memory_page_dictionary.page_count++; + } + + return page; +} + + +static void +remove_dictionary_entry_of_page (pid, page) + int pid; + memory_page_t * page; +{ + /* Restore the page's original permissions. */ + unwrite_protect_page (pid, page->page_start, page->original_permissions); + + /* Kick the page out of the dictionary. */ + if (page->previous != NULL) + page->previous->next = page->next; + if (page->next != NULL) + page->next->previous = page->previous; + + /* Just in case someone retains a handle to this after it's freed. */ + page->page_start = (CORE_ADDR) 0; + + memory_page_dictionary.page_count--; + + free (page); +} + + +static void +hppa_enable_syscall_events (pid) + int pid; +{ + int tt_status; + ttevent_t ttrace_events; + + /* Get the set of events that are currently enabled. */ + tt_status = call_ttrace (TT_PROC_GET_EVENT_MASK, + pid, + (TTRACE_ARG_TYPE) &ttrace_events, + (TTRACE_ARG_TYPE) sizeof (ttrace_events), + TT_NIL); + if (errno) + perror_with_name ("ttrace"); + + /* Add syscall events to that set. */ + ttrace_events.tte_events |= TTEVT_SYSCALL_ENTRY; + ttrace_events.tte_events |= TTEVT_SYSCALL_RETURN; + + tt_status = call_ttrace (TT_PROC_SET_EVENT_MASK, + pid, + (TTRACE_ARG_TYPE) &ttrace_events, + (TTRACE_ARG_TYPE) sizeof (ttrace_events), + TT_NIL); + if (errno) + perror_with_name ("ttrace"); +} + + +static void +hppa_disable_syscall_events (pid) + int pid; +{ + int tt_status; + ttevent_t ttrace_events; + + /* Get the set of events that are currently enabled. */ + tt_status = call_ttrace (TT_PROC_GET_EVENT_MASK, + pid, + (TTRACE_ARG_TYPE) &ttrace_events, + (TTRACE_ARG_TYPE) sizeof (ttrace_events), + TT_NIL); + if (errno) + perror_with_name ("ttrace"); + + /* Remove syscall events from that set. */ + ttrace_events.tte_events &= ~TTEVT_SYSCALL_ENTRY; + ttrace_events.tte_events &= ~TTEVT_SYSCALL_RETURN; + + tt_status = call_ttrace (TT_PROC_SET_EVENT_MASK, + pid, + (TTRACE_ARG_TYPE) &ttrace_events, + (TTRACE_ARG_TYPE) sizeof (ttrace_events), + TT_NIL); + if (errno) + perror_with_name ("ttrace"); +} + + +/* The address range beginning with START and ending with START+LEN-1 + (inclusive) is to be watched via page-protection by a new watchpoint. + Set protection for all pages that overlap that range. + + Note that our caller sets TYPE to: + 0 for a bp_hardware_watchpoint, + 1 for a bp_read_watchpoint, + 2 for a bp_access_watchpoint + + (Yes, this is intentionally (though lord only knows why) different + from the TYPE that is passed to hppa_remove_hw_watchpoint.) + */ +int +hppa_insert_hw_watchpoint (pid, start, len, type) + int pid; + CORE_ADDR start; + LONGEST len; + int type; +{ + CORE_ADDR page_start; + int dictionary_was_empty; + int page_size; + int page_id; + LONGEST range_size_in_pages; + + if (type != 0) + error ("read or access hardware watchpoints not supported on HP-UX"); + + /* Examine all pages in the address range. */ + require_memory_page_dictionary (); + + dictionary_was_empty = (memory_page_dictionary.page_count == (LONGEST) 0); + + page_size = memory_page_dictionary.page_size; + page_start = (start / page_size) * page_size; + range_size_in_pages = ((LONGEST) len + (LONGEST) page_size - 1) / (LONGEST) page_size; + + for (page_id=0; page_id < range_size_in_pages; page_id++, page_start+=page_size) + { + memory_page_t * page; + + /* This gets the page entered into the dictionary if it was + not already entered. + */ + page = get_dictionary_entry_of_page (pid, page_start); + page->reference_count++; + } + + /* Our implementation depends on seeing calls to kernel code, for the + following reason. Here we ask to be notified of syscalls. + + When a protected page is accessed by user code, HP-UX raises a SIGBUS. + Fine. + + But when kernel code accesses the page, it doesn't give a SIGBUS. + Rather, the system call that touched the page fails, with errno=EFAULT. + Not good for us. + + We could accomodate this "feature" by asking to be notified of syscall + entries & exits; upon getting an entry event, disabling page-protections; + upon getting an exit event, reenabling page-protections and then checking + if any watchpoints triggered. + + However, this turns out to be a real performance loser. syscalls are + usually a frequent occurrence. Having to unprotect-reprotect all watched + pages, and also to then read all watched memory locations and compare for + triggers, can be quite expensive. + + Instead, we'll only ask to be notified of syscall exits. When we get + one, we'll check whether errno is set. If not, or if it's not EFAULT, + we can just continue the inferior. + + If errno is set upon syscall exit to EFAULT, we must perform some fairly + hackish stuff to determine whether the failure really was due to a + page-protect trap on a watched location. + */ + if (dictionary_was_empty) + hppa_enable_syscall_events (pid); +} + + +/* The address range beginning with START and ending with START+LEN-1 + (inclusive) was being watched via page-protection by a watchpoint + which has been removed. Remove protection for all pages that + overlap that range, which are not also being watched by other + watchpoints. + */ +int +hppa_remove_hw_watchpoint (pid, start, len, type) + int pid; + CORE_ADDR start; + LONGEST len; + enum bptype type; +{ + CORE_ADDR page_start; + int dictionary_is_empty; + int page_size; + int page_id; + LONGEST range_size_in_pages; + + if (type != 0) + error ("read or access hardware watchpoints not supported on HP-UX"); + + /* Examine all pages in the address range. */ + require_memory_page_dictionary (); + + page_size = memory_page_dictionary.page_size; + page_start = (start / page_size) * page_size; + range_size_in_pages = ((LONGEST) len + (LONGEST) page_size - 1) / (LONGEST) page_size; + + for (page_id=0; page_id < range_size_in_pages; page_id++, page_start+=page_size) + { + memory_page_t * page; + + page = get_dictionary_entry_of_page (pid, page_start); + page->reference_count--; + + /* Was this the last reference of this page? If so, then we + must scrub the entry from the dictionary, and also restore + the page's original permissions. + */ + if (page->reference_count == 0) + remove_dictionary_entry_of_page (pid, page); + } + + dictionary_is_empty = (memory_page_dictionary.page_count == (LONGEST) 0); + + /* If write protections are currently disallowed, then that implies that + wait_for_inferior believes that the inferior is within a system call. + Since we want to see both syscall entry and return, it's clearly not + good to disable syscall events in this state! + + ??rehrauer: Yeah, it'd be better if we had a specific flag that said, + "inferior is between syscall events now". Oh well. + */ + if (dictionary_is_empty && memory_page_dictionary.page_protections_allowed) + hppa_disable_syscall_events (pid); +} + + +/* Could we implement a watchpoint of this type via our available + hardware support? + + This query does not consider whether a particular address range + could be so watched, but just whether support is generally available + for such things. See hppa_range_profitable_for_hw_watchpoint for a + query that answers whether a particular range should be watched via + hardware support. + */ +int +hppa_can_use_hw_watchpoint (type, cnt, ot) + enum bptype type; + int cnt; + enum bptype ot; +{ + return (type == bp_hardware_watchpoint); +} + + +/* Assuming we could set a hardware watchpoint on this address, do + we think it would be profitable ("a good idea") to do so? If not, + we can always set a regular (aka single-step & test) watchpoint + on the address... + */ +int +hppa_range_profitable_for_hw_watchpoint (pid, start, len) + int pid; + CORE_ADDR start; + LONGEST len; +{ + int range_is_stack_based; + int range_is_accessible; + CORE_ADDR page_start; + int page_size; + int page; + LONGEST range_size_in_pages; + + /* ??rehrauer: For now, say that all addresses are potentially + profitable. Possibly later we'll want to test the address + for "stackness"? + */ + range_is_stack_based = 0; + + /* If any page in the range is inaccessible, then we cannot + really use hardware watchpointing, even though our client + thinks we can. In that case, it's actually an error to + attempt to use hw watchpoints, so we'll tell our client + that the range is "unprofitable", and hope that they listen... + */ + range_is_accessible = 1; /* Until proven otherwise. */ + + /* Examine all pages in the address range. */ + errno = 0; + page_size = sysconf (_SC_PAGE_SIZE); + + /* If we can't determine page size, we're hosed. Tell our + client it's unprofitable to use hw watchpoints for this + range. + */ + if (errno || (page_size <= 0)) + { + errno = 0; + return 0; + } + + page_start = (start / page_size) * page_size; + range_size_in_pages = len / (LONGEST)page_size; + + for (page=0; page < range_size_in_pages; page++, page_start+=page_size) + { + int tt_status; + int page_permissions; + + /* Is this page accessible? */ + errno = 0; + tt_status = call_ttrace (TT_PROC_GET_MPROTECT, + pid, + (TTRACE_ARG_TYPE) page_start, + TT_NIL, + (TTRACE_ARG_TYPE) &page_permissions); + if (errno || (tt_status < 0)) + { + errno = 0; + range_is_accessible = 0; + break; + } + + /* Yes, go for another... */ + } + + return (! range_is_stack_based && range_is_accessible); +} + + +char * +hppa_pid_or_tid_to_str (id) + pid_t id; +{ + static char buf[100]; /* Static because address returned. */ + + /* Does this appear to be a process? If so, print it that way. */ + if (is_process_id (id)) + return hppa_pid_to_str (id); + + /* Else, print both the GDB thread number and the system thread id. */ + sprintf (buf, "thread %d (", pid_to_thread_id (id)); + strcat (buf, hppa_tid_to_str (id)); + strcat (buf, ")\0"); + + return buf; +} + + +/* If the current pid is not the pid this module reported + * from "proc_wait" with the most recent event, then the + * user has switched threads. + * + * If the last reported event was a breakpoint, then return + * the old thread id, else return 0. + */ +pid_t +hppa_switched_threads( gdb_pid ) + pid_t gdb_pid; +{ + if( gdb_pid == old_gdb_pid ) { + /* + * Core gdb is working with the same pid that it + * was before we reported the last event. This + * is ok: e.g. we reported hitting a thread-specific + * breakpoint, but we were reporting the wrong + * thread, so the core just ignored the event. + * + * No thread switch has happened. + */ + return (pid_t) 0; + } + else if( gdb_pid == reported_pid ) { + /* + * Core gdb is working with the pid we reported, so + * any continue or step will be able to figure out + * that it needs to step over any hit breakpoints + * without our (i.e. PREPARE_TO_PROCEED's) help. + */ + return (pid_t) 0; + } + else if( !reported_bpt ) { + /* + * The core switched, but we didn't just report a + * breakpoint, so there's no just-hit breakpoint + * instruction at "reported_pid"'s PC, and thus there + * is no need to step over it. + */ + return (pid_t) 0; + } + else { + /* There's been a real switch, and we reported + * a hit breakpoint. Let "hppa_prepare_to_proceed" + * know, so it can see whether the breakpoint is + * still active. + */ + return reported_pid; + } + + /* Keep compiler happy with an obvious return at the end. + */ + return (pid_t) 0; +} + +void +hppa_ensure_vforking_parent_remains_stopped (pid) + int pid; +{ + /* Nothing to do when using ttrace. Only the ptrace-based implementation + must do real work. + */ +} + + +int +hppa_resume_execd_vforking_child_to_get_parent_vfork () +{ + return 0; /* No, the parent vfork is available now. */ +} + + + +void +_initialize_infttrace () +{ + /* Initialize the ttrace-based hardware watchpoint implementation. */ + memory_page_dictionary.page_count = (LONGEST) -1; + memory_page_dictionary.page_protections_allowed = 1; + + errno = 0; + memory_page_dictionary.page_size = sysconf (_SC_PAGE_SIZE); + + if (errno || (memory_page_dictionary.page_size <= 0)) + perror_with_name ("sysconf"); +} + diff --git a/gdb/irix5-nat.c b/gdb/irix5-nat.c index 88bbf96..9a2379d 100644 --- a/gdb/irix5-nat.c +++ b/gdb/irix5-nat.c @@ -846,7 +846,7 @@ symbol_add_stub (arg) so -> objfile = symbol_file_add (so -> so_name, so -> from_tty, text_addr, - 0, 0, 0); + 0, 0, 0, 0, 0); return (1); } diff --git a/gdb/jv-lang.c b/gdb/jv-lang.c index c852446..8ada2e7 100644 --- a/gdb/jv-lang.c +++ b/gdb/jv-lang.c @@ -59,7 +59,13 @@ get_dynamics_objfile () { if (dynamics_objfile == NULL) { - dynamics_objfile = allocate_objfile (NULL, 0); + +/* CHECK WITH STU -- edie. Params 3 and 4 are USER_LOADED and IS_SOLIB. + USER_LOADED - used by run_command to remove old objfile entries which + are no longer valid + IS_SOLIB - 1 if the object file is a shared library */ + + dynamics_objfile = allocate_objfile (NULL, 0, 0, 0); } return dynamics_objfile; } diff --git a/gdb/m3-nat.c b/gdb/m3-nat.c index d146bdf..01594e1 100644 --- a/gdb/m3-nat.c +++ b/gdb/m3-nat.c @@ -3925,7 +3925,7 @@ m3_create_inferior (exec_file, allargs, env) char *allargs; char **env; { - fork_inferior (exec_file, allargs, env, m3_trace_me, m3_trace_him, NULL); + fork_inferior (exec_file, allargs, env, m3_trace_me, m3_trace_him, NULL, NULL); /* We are at the first instruction we care about. */ /* Pedal to the metal... */ proceed ((CORE_ADDR) -1, 0, 0); @@ -54,12 +54,18 @@ int display_time; int display_space; +/* Whether this is the command line version or not */ +int tui_version = 0; + /* Whether xdb commands will be handled */ int xdb_commands = 0; /* Whether dbx commands will be handled */ int dbx_commands = 0; +GDB_FILE *gdb_stdout; +GDB_FILE *gdb_stderr; + static void print_gdb_help PARAMS ((GDB_FILE *)); extern void gdb_init PARAMS ((char *)); #ifdef __CYGWIN__ @@ -108,6 +114,8 @@ main (argc, argv) long time_at_startup = get_run_time (); + int gdb_file_size; + START_PROGRESS (argv[0], 0); #ifdef MPW @@ -144,6 +152,20 @@ main (argc, argv) getcwd (gdb_dirbuf, sizeof (gdb_dirbuf)); current_directory = gdb_dirbuf; + gdb_file_size = sizeof(GDB_FILE); + + gdb_stdout = (GDB_FILE *)xmalloc (gdb_file_size); + gdb_stdout->ts_streamtype = afile; + gdb_stdout->ts_filestream = stdout; + gdb_stdout->ts_strbuf = NULL; + gdb_stdout->ts_buflen = 0; + + gdb_stderr = (GDB_FILE *)xmalloc (gdb_file_size); + gdb_stderr->ts_streamtype = afile; + gdb_stderr->ts_filestream = stderr; + gdb_stderr->ts_strbuf = NULL; + gdb_stderr->ts_buflen = 0; + /* Parse arguments and options. */ { int c; @@ -152,6 +174,11 @@ main (argc, argv) with no equivalent). */ static struct option long_options[] = { +#if defined(TUI) + {"tui", no_argument, &tui_version, 1}, +#endif + {"xdb", no_argument, &xdb_commands, 1}, + {"dbx", no_argument, &dbx_commands, 1}, {"readnow", no_argument, &readnow_symbol_files, 1}, {"r", no_argument, &readnow_symbol_files, 1}, {"mapped", no_argument, &mapped_symbol_files, 1}, @@ -340,7 +367,20 @@ main (argc, argv) /* If --help or --version, disable window interface. */ if (print_help || print_version) + { + use_windows = 0; +#ifdef TUI + /* Disable the TUI as well. */ + tui_version = 0; +#endif + } + +#ifdef TUI + /* An explicit --tui flag overrides the default UI, which is the + window system. */ + if (tui_version) use_windows = 0; +#endif /* OK, that's all the options. The other arguments are filenames. */ count = 0; @@ -364,6 +404,10 @@ main (argc, argv) quiet = 1; } +#if defined(TUI) + if (tui_version) + init_ui_hook = tuiInit; +#endif gdb_init (argv[0]); /* Do these (and anything which might call wrap_here or *_filtered) @@ -496,10 +540,12 @@ main (argc, argv) warning_pre_print = "\nwarning: "; if (corearg != NULL) - if (!SET_TOP_LEVEL ()) - core_file_command (corearg, !batch); - else if (isdigit (corearg[0]) && !SET_TOP_LEVEL ()) - attach_command (corearg, !batch); + { + if (!SET_TOP_LEVEL ()) + core_file_command (corearg, !batch); + else if (isdigit (corearg[0]) && !SET_TOP_LEVEL ()) + attach_command (corearg, !batch); + } do_cleanups (ALL_CLEANUPS); if (ttyarg != NULL) @@ -626,6 +672,7 @@ Options:\n\n\ --core=COREFILE Analyze the core dump COREFILE.\n\ ", stream); fputs_unfiltered ("\ + --dbx DBX compatibility mode.\n\ --directory=DIR Search for source files in DIR.\n\ --epoch Output information used by epoch emacs-GDB interface.\n\ --exec=EXECFILE Use EXECFILE as the executable.\n\ @@ -643,7 +690,15 @@ Options:\n\n\ --se=FILE Use FILE as symbol file and executable file.\n\ --symbols=SYMFILE Read symbols from SYMFILE.\n\ --tty=TTY Use TTY for input/output by the program being debugged.\n\ +", stream); +#if defined(TUI) + fputs_unfiltered ("\ + --tui Use a terminal user interface.\n\ +", stream); +#endif + fputs_unfiltered ("\ --version Print version information and then exit.\n\ + --xdb XDB compatibility mode.\n\ ", stream); #ifdef ADDITIONAL_OPTION_HELP fputs_unfiltered (ADDITIONAL_OPTION_HELP, stream); @@ -676,12 +731,57 @@ proc_remove_foreign (pid) void fputs_unfiltered (linebuffer, stream) const char *linebuffer; - FILE *stream; + GDB_FILE *stream; { +#if defined(TUI) + extern int tui_owns_terminal; +#endif + /* If anything (GUI, TUI) wants to capture GDB output, this is + * the place... the way to do it is to set up + * fputs_unfiltered_hook. + * Our TUI ("gdb -tui") used to hook output, but in the + * new (XDB style) scheme, we do not do that anymore... - RT + */ if (fputs_unfiltered_hook && (stream == gdb_stdout || stream == gdb_stderr)) fputs_unfiltered_hook (linebuffer, stream); else - fputs (linebuffer, stream); + { +#if defined(TUI) + if (tui_version && tui_owns_terminal) { + /* If we get here somehow while updating the TUI (from + * within a tuiDo(), then we need to temporarily + * set up the terminal for GDB output. This probably just + * happens on error output. + */ + + if (stream->ts_streamtype == astring) { + gdb_file_adjust_strbuf(strlen(linebuffer), stream); + strcat(stream->ts_strbuf, linebuffer); + } else { + tuiTermUnsetup(0, (tui_version) ? cmdWin->detail.commandInfo.curch : 0); + fputs (linebuffer, stream->ts_filestream); + tuiTermSetup(0); + if (linebuffer[strlen(linebuffer) - 1] == '\n') + tuiClearCommandCharCount(); + else + tuiIncrCommandCharCountBy(strlen(linebuffer)); + } + } else { + /* The normal case - just do a fputs() */ + if (stream->ts_streamtype == astring) { + gdb_file_adjust_strbuf(strlen(linebuffer), stream); + strcat(stream->ts_strbuf, linebuffer); + } else fputs (linebuffer, stream->ts_filestream); + } + + +#else + if (stream->ts_streamtype == astring) { + gdb_file_adjust_strbuf(strlen(linebuffer), stream); + strcat(stream->ts_strbuf, linebuffer); + } else fputs (linebuffer, stream->ts_filestream); +#endif + } } diff --git a/gdb/objfiles.c b/gdb/objfiles.c index 8a64b64..971a7d4 100644 --- a/gdb/objfiles.c +++ b/gdb/objfiles.c @@ -123,12 +123,21 @@ build_objfile_section_table (objfile) /* Given a pointer to an initialized bfd (ABFD) and a flag that indicates whether or not an objfile is to be mapped (MAPPED), allocate a new objfile struct, fill it in as best we can, link it into the list of all known - objfiles, and return a pointer to the new objfile struct. */ + objfiles, and return a pointer to the new objfile struct. + + USER_LOADED is simply recorded in the objfile. This record offers a way for + run_command to remove old objfile entries which are no longer valid (i.e., + are associated with an old inferior), but to preserve ones that the user + explicitly loaded via the add-symbol-file command. + + IS_SOLIB is also simply recorded in the objfile. */ struct objfile * -allocate_objfile (abfd, mapped) +allocate_objfile (abfd, mapped, user_loaded, is_solib) bfd *abfd; int mapped; + int user_loaded; + int is_solib; { struct objfile *objfile = NULL; struct objfile *last_one = NULL; @@ -287,6 +296,15 @@ allocate_objfile (abfd, mapped) last_one = last_one -> next); last_one -> next = objfile; } + + /* Record whether this objfile was created because the user explicitly + caused it (e.g., used the add-symbol-file command). + */ + objfile -> user_loaded = user_loaded; + + /* Record whether this objfile definitely represents a solib. */ + objfile -> is_solib = is_solib; + return (objfile); } @@ -541,7 +559,8 @@ objfile_relocate (objfile, new_offsets) But I'm leaving out that test, on the theory that they can't possibly pass the tests below. */ if ((SYMBOL_CLASS (sym) == LOC_LABEL - || SYMBOL_CLASS (sym) == LOC_STATIC) + || SYMBOL_CLASS (sym) == LOC_STATIC + || SYMBOL_CLASS (sym) == LOC_INDIRECT) && SYMBOL_SECTION (sym) >= 0) { SYMBOL_VALUE_ADDRESS (sym) += @@ -699,6 +718,28 @@ have_full_symbols () return 0; } + +/* This operations deletes all objfile entries that represent solibs that + weren't explicitly loaded by the user, via e.g., the add-symbol-file + command. + */ +void +objfile_purge_solibs () +{ + struct objfile * objf; + struct objfile * temp; + + ALL_OBJFILES_SAFE (objf, temp) + { + /* We assume that the solib package has been purged already, or will + be soon. + */ + if (! objf->user_loaded && objf->is_solib) + free_objfile (objf); + } +} + + /* Many places in gdb want to test just to see if we have any minimal symbols available. This function returns zero if none are currently available, nonzero otherwise. */ @@ -903,8 +944,13 @@ find_pc_sect_section (pc, section) ALL_OBJFILES (objfile) for (s = objfile->sections; s < objfile->sections_end; ++s) +#if defined(HPUXHPPA) + if ((section == 0 || section == s->the_bfd_section) && + s->addr <= pc && pc <= s->endaddr) +#else if ((section == 0 || section == s->the_bfd_section) && s->addr <= pc && pc < s->endaddr) +#endif return(s); return(NULL); diff --git a/gdb/objfiles.h b/gdb/objfiles.h index a124822..2a5df06 100644 --- a/gdb/objfiles.h +++ b/gdb/objfiles.h @@ -159,6 +159,24 @@ struct obj_section { int ovly_mapped; }; +/* An import entry contains information about a symbol that + is used in this objfile but not defined in it, and so needs + to be imported from some other objfile */ +/* Currently we just store the name; no attributes. 1997-08-05 */ +typedef char * ImportEntry; + + +/* An export entry contains information about a symbol that + is defined in this objfile and available for use in other + objfiles */ +typedef struct { + char * name; /* name of exported symbol */ + int address; /* offset subject to relocation */ + /* Currently no other attributes 1997-08-05 */ +} ExportEntry; + + + /* The "objstats" structure provides a place for gdb to record some interesting information about its internal state at runtime, on a per objfile basis, such as information about the number of symbols @@ -219,6 +237,22 @@ struct objfile char *name; + /* TRUE if this objfile was created because the user explicitly caused + it (e.g., used the add-symbol-file command). + */ + int user_loaded; + + /* TRUE if this objfile was explicitly created to represent a solib. + + (If FALSE, the objfile may actually be a solib. This can happen if + the user created the objfile by using the add-symbol-file command. + GDB doesn't in that situation actually check whether the file is a + solib. Rather, the target's implementation of the solib interface + is responsible for setting this flag when noticing solibs used by + an inferior.) + */ + int is_solib; + /* Some flag bits for this objfile. */ unsigned short flags; @@ -358,6 +392,14 @@ struct objfile /* two auxiliary fields, used to hold the fp of separate symbol files */ FILE *auxf1, *auxf2; + /* Imported symbols */ + ImportEntry * import_list; + int import_list_size; + + /* Exported symbols */ + ExportEntry * export_list; + int export_list_size; + /* Place to stash various statistics about this objfile */ OBJSTATS; }; @@ -428,7 +470,7 @@ extern struct objfile *object_files; /* Declarations for functions defined in objfiles.c */ extern struct objfile * -allocate_objfile PARAMS ((bfd *, int)); +allocate_objfile PARAMS ((bfd *, int, int, int)); extern int build_objfile_section_table PARAMS ((struct objfile *)); @@ -453,6 +495,13 @@ have_partial_symbols PARAMS ((void)); extern int have_full_symbols PARAMS ((void)); +/* This operation deletes all objfile entries that represent solibs that + weren't explicitly loaded by the user, via e.g., the add-symbol-file + command. + */ +extern void +objfile_purge_solibs PARAMS ((void)); + /* Functions for dealing with the minimal symbol table, really a misc address<->symbol mapping for things we don't have debug symbols for. */ diff --git a/gdb/osfsolib.c b/gdb/osfsolib.c index 21b423b..0bed522 100644 --- a/gdb/osfsolib.c +++ b/gdb/osfsolib.c @@ -592,7 +592,7 @@ symbol_add_stub (arg) so -> objfile = symbol_file_add (so -> so_name, so -> from_tty, text_addr, - 0, 0, 0); + 0, 0, 0, 0, 1); return (1); } diff --git a/gdb/parse.c b/gdb/parse.c index f90cece..480b41b 100644 --- a/gdb/parse.c +++ b/gdb/parse.c @@ -59,6 +59,8 @@ int comma_terminates; static int expressiondebug = 0; #endif +extern int hp_som_som_object_present; + static void free_funcalls PARAMS ((void)); @@ -477,6 +479,9 @@ write_dollar_variable (str) /* Handle the tokens $digits; also $ (short for $0) and $$ (short for $$1) and $$digits (equivalent to $<-digits> if you could type that). */ + struct symbol * sym = NULL; + struct minimal_symbol * msym = NULL; + int negate = 0; int i = 1; /* Double dollar means negate the number and add -1 as well. @@ -510,6 +515,29 @@ write_dollar_variable (str) if( i >= 0 ) goto handle_register; + /* On HP-UX, certain system routines (millicode) have names beginning + with $ or $$, e.g. $$dyncall, which handles inter-space procedure + calls on PA-RISC. Check for those, first. */ + + sym = lookup_symbol (copy_name (str), (struct block *) NULL, + VAR_NAMESPACE, (int *) NULL, (struct symtab **) NULL); + if (sym) + { + write_exp_elt_opcode (OP_VAR_VALUE); + write_exp_elt_block (block_found); /* set by lookup_symbol */ + write_exp_elt_sym (sym); + write_exp_elt_opcode (OP_VAR_VALUE); + return; + } + msym = lookup_minimal_symbol (copy_name (str), NULL, NULL); + if (msym) + { + write_exp_msymbol (msym, + lookup_function_type (builtin_type_int), + builtin_type_int); + return; + } + /* Any other names starting in $ are debugger internal variables. */ write_exp_elt_opcode (OP_INTERNALVAR); @@ -527,6 +555,257 @@ write_dollar_variable (str) write_exp_elt_opcode (OP_REGISTER); return; } + + +/* Parse a string that is possibly a namespace / nested class + specification, i.e., something of the form A::B::C::x. Input + (NAME) is the entire string; LEN is the current valid length; the + output is a string, TOKEN, which points to the largest recognized + prefix which is a series of namespaces or classes. CLASS_PREFIX is + another output, which records whether a nested class spec was + recognized (= 1) or a fully qualified variable name was found (= + 0). ARGPTR is side-effected (if non-NULL) to point to beyond the + string recognized and consumed by this routine. + + The return value is a pointer to the symbol for the base class or + variable if found, or NULL if not found. Callers must check this + first -- if NULL, the outputs may not be correct. + + This function is used c-exp.y. This is used specifically to get + around HP aCC (and possibly other compilers), which insists on + generating names with embedded colons for namespace or nested class + members. + + (Argument LEN is currently unused. 1997-08-27) + + Callers must free memory allocated for the output string TOKEN. */ + +static const char coloncolon[2] = {':',':'}; + +struct symbol * +parse_nested_classes_for_hpacc (name, len, token, class_prefix, argptr) + char * name; + int len; + char ** token; + int * class_prefix; + char ** argptr; +{ + /* Comment below comes from decode_line_1 which has very similar + code, which is called for "break" command parsing. */ + + /* We have what looks like a class or namespace + scope specification (A::B), possibly with many + levels of namespaces or classes (A::B::C::D). + + Some versions of the HP ANSI C++ compiler (as also possibly + other compilers) generate class/function/member names with + embedded double-colons if they are inside namespaces. To + handle this, we loop a few times, considering larger and + larger prefixes of the string as though they were single + symbols. So, if the initially supplied string is + A::B::C::D::foo, we have to look up "A", then "A::B", + then "A::B::C", then "A::B::C::D", and finally + "A::B::C::D::foo" as single, monolithic symbols, because + A, B, C or D may be namespaces. + + Note that namespaces can nest only inside other + namespaces, and not inside classes. So we need only + consider *prefixes* of the string; there is no need to look up + "B::C" separately as a symbol in the previous example. */ + + register char * p; + char * start, * end; + char * prefix = NULL; + char * tmp; + struct symbol * sym_class = NULL; + struct symbol * sym_var = NULL; + struct type * t; + register int i; + int colons_found = 0; + int prefix_len = 0; + int done = 0; + char * q; + + /* Check for HP-compiled executable -- in other cases + return NULL, and caller must default to standard GDB + behaviour. */ + + if (!hp_som_som_object_present) + return (struct symbol *) NULL; + + p = name; + + /* Skip over whitespace and possible global "::" */ + while (*p && (*p == ' ' || *p == '\t')) p++; + if (p[0] == ':' && p[1] == ':') + p += 2; + while (*p && (*p == ' ' || *p == '\t')) p++; + + while (1) + { + /* Get to the end of the next namespace or class spec. */ + /* If we're looking at some non-token, fail immediately */ + start = p; + if (!(isalpha (*p) || *p == '$' || *p == '_')) + return (struct symbol *) NULL; + p++; + while (*p && (isalnum (*p) || *p == '$' || *p == '_')) p++; + + if (*p == '<') + { + /* If we have the start of a template specification, + scan right ahead to its end */ + q = find_template_name_end (p); + if (q) + p = q; + } + + end = p; + + /* Skip over "::" and whitespace for next time around */ + while (*p && (*p == ' ' || *p == '\t')) p++; + if (p[0] == ':' && p[1] == ':') + p += 2; + while (*p && (*p == ' ' || *p == '\t')) p++; + + /* Done with tokens? */ + if (!*p || !(isalpha (*p) || *p == '$' || *p == '_')) + done = 1; + + tmp = (char *) alloca (prefix_len + end - start + 3); + if (prefix) + { + memcpy (tmp, prefix, prefix_len); + memcpy (tmp + prefix_len, coloncolon, 2); + memcpy (tmp + prefix_len + 2, start, end - start); + tmp[prefix_len + 2 + end - start] = '\000'; + } + else + { + memcpy (tmp, start, end - start); + tmp[end - start] = '\000'; + } + + prefix = tmp; + prefix_len = strlen (prefix); + +#if 0 /* DEBUGGING */ + printf ("Searching for nested class spec: Prefix is %s\n", prefix); +#endif + + /* See if the prefix we have now is something we know about */ + + if (!done) + { + /* More tokens to process, so this must be a class/namespace */ + sym_class = lookup_symbol (prefix, 0, STRUCT_NAMESPACE, + 0, (struct symtab **) NULL); + } + else + { + /* No more tokens, so try as a variable first */ + sym_var = lookup_symbol (prefix, 0, VAR_NAMESPACE, + 0, (struct symtab **) NULL); + /* If failed, try as class/namespace */ + if (!sym_var) + sym_class = lookup_symbol (prefix, 0, STRUCT_NAMESPACE, + 0, (struct symtab **) NULL); + } + + if (sym_var || + (sym_class && + (t = check_typedef (SYMBOL_TYPE (sym_class)), + (TYPE_CODE (t) == TYPE_CODE_STRUCT + || TYPE_CODE (t) == TYPE_CODE_UNION)))) + { + /* We found a valid token */ + *token = (char *) xmalloc (prefix_len + 1 ); + memcpy (*token, prefix, prefix_len); + (*token)[prefix_len] = '\000'; + break; + } + + /* No variable or class/namespace found, no more tokens */ + if (done) + return (struct symbol *) NULL; + } + + /* Out of loop, so we must have found a valid token */ + if (sym_var) + *class_prefix = 0; + else + *class_prefix = 1; + + if (argptr) + *argptr = done ? p : end; + +#if 0 /* DEBUGGING */ + printf ("Searching for nested class spec: Token is %s, class_prefix %d\n", *token, *class_prefix); +#endif + + return sym_var ? sym_var : sym_class; /* found */ +} + +char * +find_template_name_end (p) + char * p; +{ + int depth = 1; + int just_seen_right = 0; + int just_seen_colon = 0; + int just_seen_space = 0; + + if (!p || (*p != '<')) + return 0; + + while (*++p) + { + switch (*p) + { + case '\'': case '\"': + case '{': case '}': + /* In future, may want to allow these?? */ + return 0; + case '<': + depth++; /* start nested template */ + if (just_seen_colon || just_seen_right || just_seen_space) + return 0; /* but not after : or :: or > or space */ + break; + case '>': + if (just_seen_colon || just_seen_right) + return 0; /* end a (nested?) template */ + just_seen_right = 1; /* but not after : or :: */ + if (--depth == 0) /* also disallow >>, insist on > > */ + return ++p; /* if outermost ended, return */ + break; + case ':': + if (just_seen_space || (just_seen_colon > 1)) + return 0; /* nested class spec coming up */ + just_seen_colon++; /* we allow :: but not :::: */ + break; + case ' ': + break; + default: + if (!((*p >= 'a' && *p <= 'z') || /* allow token chars */ + (*p >= 'A' && *p <= 'Z') || + (*p >= '0' && *p <= '9') || + (*p == '_') || (*p == ',') || /* commas for template args */ + (*p == '&') || (*p == '*') || /* pointer and ref types */ + (*p == '(') || (*p == ')') || /* function types */ + (*p == '[') || (*p == ']') )) /* array types */ + return 0; + } + if (*p != ' ') + just_seen_space = 0; + if (*p != ':') + just_seen_colon = 0; + if (*p != '>') + just_seen_right = 0; + } + return 0; +} + + /* Return a null-terminated temporary copy of the name of a string token. */ diff --git a/gdb/procfs.c b/gdb/procfs.c index d1fb010..b5b331d 100644 --- a/gdb/procfs.c +++ b/gdb/procfs.c @@ -534,7 +534,7 @@ static void procfs_attach PARAMS ((char *, int)); static void proc_set_exec_trap PARAMS ((void)); -static int procfs_init_inferior PARAMS ((int)); +static void procfs_init_inferior PARAMS ((int)); static struct procinfo *create_procinfo PARAMS ((int)); @@ -2519,7 +2519,7 @@ LOCAL FUNCTION SYNOPSIS - int procfs_init_inferior (int pid) + void procfs_init_inferior (int pid) DESCRIPTION @@ -2537,7 +2537,7 @@ NOTES */ -static int +static void procfs_init_inferior (pid) int pid; { @@ -2563,7 +2563,6 @@ procfs_init_inferior (pid) /* One trap to exec the shell, one to exec the program being debugged. */ startup_inferior (2); #endif - return (pid); } /* @@ -5574,7 +5573,7 @@ procfs_create_inferior (exec_file, allargs, env) } fork_inferior (exec_file, allargs, env, - proc_set_exec_trap, procfs_init_inferior, shell_file); + proc_set_exec_trap, procfs_init_inferior, NULL, shell_file); /* We are at the first instruction we care about. */ /* Pedal to the metal... */ diff --git a/gdb/pyr-tdep.c b/gdb/pyr-tdep.c index c60decf..85e9a07 100644 --- a/gdb/pyr-tdep.c +++ b/gdb/pyr-tdep.c @@ -70,9 +70,9 @@ pyr_do_registers_info (regnum, fpregs) if (i == regnum) { long val = raw_regs[i]; - fputs_filtered (REGISTER_NAME (i), stdout); + fputs_filtered (REGISTER_NAME (i), gdb_stdout); printf_filtered(":"); - print_spaces_filtered (6 - strlen (REGISTER_NAME (i)), stdout); + print_spaces_filtered (6 - strlen (REGISTER_NAME (i)), gdb_stdout); if (val == 0) printf_filtered ("0"); else @@ -89,7 +89,7 @@ CORE_ADDR frame_locals_address (frame) register int addr = find_saved_register (frame,CFP_REGNUM); register int result = read_memory_integer (addr, 4); #ifdef PYRAMID_CONTROL_FRAME_DEBUGGING - fprintf_unfiltered (stderr, + fprintf_unfiltered (gdb_stderr, "\t[[..frame_locals:%8x, %s= %x @%x fcfp= %x foo= %x\n\t gr13=%x pr13=%x tr13=%x @%x]]\n", frame->frame, REGISTER_NAME (CFP_REGNUM), @@ -115,7 +115,7 @@ CORE_ADDR frame_args_addr (frame) register int result = read_memory_integer (addr, 4); #ifdef PYRAMID_CONTROL_FRAME_DEBUGGING - fprintf_unfiltered (stderr, + fprintf_unfiltered (gdb_stderr, "\t[[..frame_args:%8x, %s= %x @%x fcfp= %x r_r= %x\n\t gr13=%x pr13=%x tr13=%x @%x]]\n", frame->frame, REGISTER_NAME (CFP_REGNUM), @@ -184,7 +184,7 @@ CORE_ADDR pyr_saved_pc(frame) int pyr_print_insn (memaddr, stream) CORE_ADDR memaddr; - FILE *stream; + GDB_FILE *stream; { unsigned char buffer[MAXLEN]; register int i, nargs, insn_size =4; diff --git a/gdb/pyr-xdep.c b/gdb/pyr-xdep.c index 7864fd6..dd6d9a9 100644 --- a/gdb/pyr-xdep.c +++ b/gdb/pyr-xdep.c @@ -115,7 +115,7 @@ fetch_inferior_registers (regno) supply_register(CSP_REGNUM, reg_buf+CSP_REGNUM); #ifdef PYRAMID_CONTROL_FRAME_DEBUGGING if (skipped_frames) { - fprintf_unfiltered (stderr, + fprintf_unfiltered (gdb_stderr, "skipped %d frames from %x to %x; cfp was %x, now %x\n", skipped_frames, reg_buf[CSP_REGNUM]); } diff --git a/gdb/remote-udi.c b/gdb/remote-udi.c index cdebf4c..bbfe0bd 100644 --- a/gdb/remote-udi.c +++ b/gdb/remote-udi.c @@ -1299,7 +1299,7 @@ udi_load (args, from_tty) /* As a convenience, pick up any symbol info that is in the program being loaded. Note that we assume that the program is the``mainline''; if this is not always true, then this code will need to be augmented. */ - symbol_file_add (strtok (args, " \t"), from_tty, 0, 1, 0, 0); + symbol_file_add (strtok (args, " \t"), from_tty, 0, 1, 0, 0, 0, 0); /* Getting new symbols may change our opinion about what is frameless. */ diff --git a/gdb/remote-vx.c b/gdb/remote-vx.c index 4ac2a34..a6f9c55 100644 --- a/gdb/remote-vx.c +++ b/gdb/remote-vx.c @@ -691,7 +691,7 @@ vx_add_symbols (name, from_tty, text_addr, data_addr, bss_addr) /* It might be nice to suppress the breakpoint_re_set which happens here because we are going to do one again after the objfile_relocate. */ - objfile = symbol_file_add (name, from_tty, 0, 0, 0, 0); + objfile = symbol_file_add (name, from_tty, 0, 0, 0, 0, 0, 0); /* This is a (slightly cheesy) way of superceding the old symbols. A less cheesy way would be to find the objfile with the same name and diff --git a/gdb/serial.c b/gdb/serial.c index 89a116e..07638ed 100644 --- a/gdb/serial.c +++ b/gdb/serial.c @@ -39,10 +39,10 @@ static serial_t scb_base; suitable for playback by gdbserver. */ static char *serial_logfile = NULL; -static FILE *serial_logfp = NULL; +static GDB_FILE *serial_logfp = NULL; static struct serial_ops *serial_interface_lookup PARAMS ((char *)); -static void serial_logchar PARAMS ((int chtype, int ch, int timeout)); +static void serial_logchar PARAMS ((int, int, int)); static char logbase_hex[] = "hex"; static char logbase_octal[] = "octal"; static char logbase_ascii[] = "ascii"; @@ -59,15 +59,15 @@ static int serial_current_type = 0; #define SERIAL_BREAK 1235 static void -serial_logchar (chtype, ch, timeout) - int chtype; +serial_logchar (ch_type, ch, timeout) + int ch_type; int ch; int timeout; { - if (chtype != serial_current_type) + if (ch_type != serial_current_type) { - fprintf_unfiltered (serial_logfp, "\n%c ", chtype); - serial_current_type = chtype; + fprintf_unfiltered (serial_logfp, "\n%c ", ch_type); + serial_current_type = ch_type; } if (serial_logbase != logbase_ascii) @@ -308,7 +308,8 @@ serial_close (scb, really_close) fputs_unfiltered ("\nEnd of log\n", serial_logfp); serial_current_type = 0; - fclose (serial_logfp); /* XXX - What if serial_logfp == stdout or stderr? */ + /* XXX - What if serial_logfp == gdb_stdout or gdb_stderr? */ + gdb_fclose (serial_logfp); serial_logfp = NULL; } diff --git a/gdb/solib.c b/gdb/solib.c index 3f6e730..ba907f0 100644 --- a/gdb/solib.c +++ b/gdb/solib.c @@ -207,6 +207,8 @@ solib_add_common_symbols PARAMS ((struct rtc_symb *)); #endif +void _initialize_solib PARAMS ((void)); + /* If non-zero, this is a prefix that will be added to the front of the name shared libraries with an absolute filename for loading. */ static char *solib_absolute_prefix = NULL; @@ -412,7 +414,7 @@ solib_add_common_symbols (rtc_symp) } init_minimal_symbol_collection (); - make_cleanup (discard_minimal_symbols, 0); + make_cleanup ((make_cleanup_func) discard_minimal_symbols, 0); while (rtc_symp) { @@ -583,7 +585,7 @@ look_for_base (fd, baseaddr) if (fd == -1 || (exec_bfd != NULL - && fdmatch (fileno ((GDB_FILE *)(exec_bfd -> iostream)), fd))) + && fdmatch (fileno ((FILE *)(exec_bfd -> iostream)), fd))) { return (0); } @@ -969,7 +971,7 @@ find_solib (so_list_ptr) if (! solib_cleanup_queued) { - make_run_cleanup (do_clear_solib); + make_run_cleanup (do_clear_solib, NULL); solib_cleanup_queued = 1; } @@ -1040,7 +1042,7 @@ symbol_add_stub (arg) so -> objfile = symbol_file_add (so -> so_name, so -> from_tty, text_addr, - 0, 0, 0); + 0, 0, 0, 0, 1); return (1); } @@ -1119,7 +1121,7 @@ solib_add (arg_string, from_tty, target) here, otherwise we dereference a potential dangling pointer for each call to target_read/write_memory within this routine. */ update_coreops = core_ops.to_sections == target->to_sections; - + /* Reallocate the target's section table including the new size. */ if (target -> to_sections) { diff --git a/gdb/solib.h b/gdb/solib.h index 995f58e..eafef48 100644 --- a/gdb/solib.h +++ b/gdb/solib.h @@ -44,9 +44,140 @@ solib_add PARAMS ((char *, int, struct target_ops *)); #define SOLIB_CREATE_INFERIOR_HOOK(PID) solib_create_inferior_hook() +/* Function to be called to remove the connection between debugger and + dynamic linker that was established by SOLIB_CREATE_INFERIOR_HOOK. + (This operation does not remove shared library information from + the debugger, as CLEAR_SOLIB does.) + + This functionality is presently not implemented for this target. + */ +#define SOLIB_REMOVE_INFERIOR_HOOK(PID) (0) + extern void solib_create_inferior_hook PARAMS((void)); /* solib.c */ +/* This function is called by the "catch load" command. It allows + the debugger to be notified by the dynamic linker when a specified + library file (or any library file, if filename is NULL) is loaded. + + Presently, this functionality is not implemented. + */ +#define SOLIB_CREATE_CATCH_LOAD_HOOK(pid,tempflag,filename,cond_string) \ + error("catch of library loads/unloads not yet implemented on this platform") + +/* This function is called by the "catch unload" command. It allows + the debugger to be notified by the dynamic linker when a specified + library file (or any library file, if filename is NULL) is unloaded. + + Presently, this functionality is not implemented. + */ +#define SOLIB_CREATE_CATCH_UNLOAD_HOOK(pid,tempflag,filename,cond_string) \ + error("catch of library loads/unloads not yet implemented on this platform") + +/* This function returns TRUE if the dynamic linker has just reported + a load of a library. + + This function must be used only when the inferior has stopped in + the dynamic linker hook, or undefined results are guaranteed. + + Presently, this functionality is not implemented. + */ + +/* +#define SOLIB_HAVE_LOAD_EVENT(pid) \ + error("catch of library loads/unloads not yet implemented on this platform") +*/ + +#define SOLIB_HAVE_LOAD_EVENT(pid) \ +(0) + +/* This function returns a pointer to the string representation of the + pathname of the dynamically-linked library that has just been loaded. + + This function must be used only when SOLIB_HAVE_LOAD_EVENT is TRUE, + or undefined results are guaranteed. + + This string's contents are only valid immediately after the inferior + has stopped in the dynamic linker hook, and becomes invalid as soon + as the inferior is continued. Clients should make a copy of this + string if they wish to continue the inferior and then access the string. + + Presently, this functionality is not implemented. + */ + +/* +#define SOLIB_LOADED_LIBRARY_PATHNAME(pid) \ + error("catch of library loads/unloads not yet implemented on this platform") +*/ + +#define SOLIB_LOADED_LIBRARY_PATHNAME(pid) \ +(0) + +/* This function returns TRUE if the dynamic linker has just reported + an unload of a library. + + This function must be used only when the inferior has stopped in + the dynamic linker hook, or undefined results are guaranteed. + + Presently, this functionality is not implemented. + */ +/* +#define SOLIB_HAVE_UNLOAD_EVENT(pid) \ + error("catch of library loads/unloads not yet implemented on this platform") +*/ + +#define SOLIB_HAVE_UNLOAD_EVENT(pid) \ +(0) + +/* This function returns a pointer to the string representation of the + pathname of the dynamically-linked library that has just been unloaded. + + This function must be used only when SOLIB_HAVE_UNLOAD_EVENT is TRUE, + or undefined results are guaranteed. + + This string's contents are only valid immediately after the inferior + has stopped in the dynamic linker hook, and becomes invalid as soon + as the inferior is continued. Clients should make a copy of this + string if they wish to continue the inferior and then access the string. + + Presently, this functionality is not implemented. + */ +/* +#define SOLIB_UNLOADED_LIBRARY_PATHNAME(pid) \ + error("catch of library loads/unloads not yet implemented on this platform") +*/ + +#define SOLIB_UNLOADED_LIBRARY_PATHNAME(pid) \ +(0) + +/* This function returns TRUE if pc is the address of an instruction that + lies within the dynamic linker (such as the event hook, or the dld + itself). + + This function must be used only when a dynamic linker event has been + caught, and the inferior is being stepped out of the hook, or undefined + results are guaranteed. + + Presently, this functionality is not implemented. + */ + +/* +#define SOLIB_IN_DYNAMIC_LINKER(pid,pc) \ + error("catch of library loads/unloads not yet implemented on this platform") +*/ + +#define SOLIB_IN_DYNAMIC_LINKER(pid,pc) \ +(0) + +/* This function must be called when the inferior is killed, and the program + restarted. This is not the same as CLEAR_SOLIB, in that it doesn't discard + any symbol tables. + + Presently, this functionality is not implemented. + */ +#define SOLIB_RESTART() \ + (0) + /* If we can't set a breakpoint, and it's in a shared library, just disable it. */ diff --git a/gdb/somread.c b/gdb/somread.c index ed0fe97..5f0d0c7 100644 --- a/gdb/somread.c +++ b/gdb/somread.c @@ -123,11 +123,19 @@ som_symtab_read (abfd, objfile, section_offsets) There's nothing in the header which easily allows us to do this. The only reliable way I know of is to check for the existance of a $SHLIB_INFO$ section with a non-zero size. */ - shlib_info = bfd_get_section_by_name (objfile->obfd, "$SHLIB_INFO$"); - if (shlib_info) - dynamic = (bfd_section_size (objfile->obfd, shlib_info) != 0); - else - dynamic = 0; + /* The code below is not a reliable way to check whether an + * executable is dynamic, so I commented it out - RT + * shlib_info = bfd_get_section_by_name (objfile->obfd, "$SHLIB_INFO$"); + * if (shlib_info) + * dynamic = (bfd_section_size (objfile->obfd, shlib_info) != 0); + * else + * dynamic = 0; + */ + /* I replaced the code with a simple check for text offset not being + * zero. Still not 100% reliable, but a more reliable way of asking + * "is this a dynamic executable?" than the above. RT + */ + dynamic = (text_offset != 0); endbufp = buf + number_of_symbols; for (bufp = buf; bufp < endbufp; ++bufp) @@ -355,19 +363,43 @@ som_symfile_read (objfile, section_offsets, mainline) init_minimal_symbol_collection (); back_to = make_cleanup (discard_minimal_symbols, 0); - /* Process the normal SOM symbol table first. */ + /* Read in the import list and the export list. Currently + the export list isn't used; the import list is used in + hp-symtab-read.c to handle static vars declared in other + shared libraries. */ + init_import_symbols (objfile); +#if 0 /* Export symbols not used today 1997-08-05 */ + init_export_symbols (objfile); +#else + objfile->export_list = NULL; + objfile->export_list_size = 0; +#endif + /* Process the normal SOM symbol table first. + This reads in the DNTT and string table, but doesn't + actually scan the DNTT. It does scan the linker symbol + table and thus build up a "minimal symbol table". */ + som_symtab_read (abfd, objfile, section_offsets); - /* Now read information from the stabs debug sections. */ + /* Now read information from the stabs debug sections. + This is a no-op for SOM. + Perhaps it is intended for some kind of mixed STABS/SOM + situation? */ stabsect_build_psymtabs (objfile, section_offsets, mainline, "$GDB_SYMBOLS$", "$GDB_STRINGS$", "$TEXT$"); - /* Now read the native debug information. */ + /* Now read the native debug information. + This builds the psymtab. This used to be done via a scan of + the DNTT, but is now done via the PXDB-built quick-lookup tables + together with a scan of the GNTT. See hp-psymtab-read.c. */ hpread_build_psymtabs (objfile, section_offsets, mainline); /* Install any minimal symbols that have been collected as the current - minimal symbols for this objfile. */ + minimal symbols for this objfile. + Further symbol-reading is done incrementally, file-by-file, + in a step known as "psymtab-to-symtab" expansion. hp-symtab-read.c + contains the code to do the actual DNTT scanning and symtab building. */ install_minimal_symbols (objfile); /* Force hppa-tdep.c to re-read the unwind descriptors. */ @@ -444,6 +476,307 @@ som_symfile_offsets (objfile, addr) return section_offsets; } + + + +/* Check if a given symbol NAME is in the import list + of OBJFILE. + 1 => true, 0 => false + This is used in hp_symtab_read.c to deal with static variables + that are defined in a different shared library than the one + whose symbols are being processed. */ + +int is_in_import_list (name, objfile) + char * name; + struct objfile * objfile; +{ + register int i; + + if (!objfile || + !name || + !*name) + return 0; + + for (i=0; i < objfile->import_list_size; i++) + if (objfile->import_list[i] && STREQ (name, objfile->import_list[i])) + return 1; + return 0; +} + + +/* Read in and initialize the SOM import list which is present + for all executables and shared libraries. The import list + consists of the symbols that are referenced in OBJFILE but + not defined there. (Variables that are imported are dealt + with as "loc_indirect" vars.) + Return value = number of import symbols read in. */ +int +init_import_symbols (objfile) + struct objfile * objfile; +{ + unsigned int import_list; + unsigned int import_list_size; + unsigned int string_table; + unsigned int string_table_size; + char * string_buffer; + register int i; + register int j; + register int k; + asection * text_section; /* section handle */ + unsigned int dl_header[12]; /* SOM executable header */ + + /* A struct for an entry in the SOM import list */ + typedef struct { + int name; /* index into the string table */ + short dont_care1; /* we don't use this */ + unsigned char type; /* 0 = NULL, 2 = Data, 3 = Code, 7 = Storage, 13 = Plabel */ + unsigned int reserved2 : 8; /* not used */ + } SomImportEntry; + + /* We read 100 entries in at a time from the disk file. */ +# define SOM_READ_IMPORTS_NUM 100 +# define SOM_READ_IMPORTS_CHUNK_SIZE (sizeof (SomImportEntry) * SOM_READ_IMPORTS_NUM) + SomImportEntry buffer[SOM_READ_IMPORTS_NUM]; + + /* Initialize in case we error out */ + objfile->import_list = NULL; + objfile->import_list_size = 0; + +#if 0 /* DEBUGGING */ + printf ("Processing import list for %s\n", objfile->name); +#endif + + /* It doesn't work, for some reason, to read in space $TEXT$; + the subspace $SHLIB_INFO$ has to be used. Some BFD quirk? pai/1997-08-05 */ + text_section = bfd_get_section_by_name (objfile->obfd, "$SHLIB_INFO$"); + if (!text_section) + return 0; + /* Get the SOM executable header */ + bfd_get_section_contents (objfile->obfd, text_section, dl_header, 0, 12 * sizeof (int)); + + /* Check header version number for 10.x HP-UX */ + /* Currently we deal only with 10.x systems; on 9.x the version # is 89060912. + FIXME: Change for future HP-UX releases and mods to the SOM executable format */ + if (dl_header[0] != 93092112) + return 0; + + import_list = dl_header[4]; + import_list_size = dl_header[5]; + if (!import_list_size) + return 0; + string_table = dl_header[10]; + string_table_size = dl_header[11]; + if (!string_table_size) + return 0; + + /* Suck in SOM string table */ + string_buffer = (char *) xmalloc (string_table_size); + bfd_get_section_contents (objfile->obfd, text_section, string_buffer, + string_table, string_table_size); + + /* Allocate import list in the psymbol obstack; this has nothing + to do with psymbols, just a matter of convenience. We want the + import list to be freed when the objfile is deallocated */ + objfile->import_list + = (ImportEntry *) obstack_alloc (&objfile->psymbol_obstack, + import_list_size * sizeof (ImportEntry)); + + /* Read in the import entries, a bunch at a time */ + for (j=0, k=0; + j < (import_list_size / SOM_READ_IMPORTS_NUM); + j++) + { + bfd_get_section_contents (objfile->obfd, text_section, buffer, + import_list + j * SOM_READ_IMPORTS_CHUNK_SIZE, + SOM_READ_IMPORTS_CHUNK_SIZE); + for (i=0; i < SOM_READ_IMPORTS_NUM; i++, k++) + { + if (buffer[i].type != (unsigned char) 0) + { + objfile->import_list[k] + = (char *) obstack_alloc (&objfile->psymbol_obstack, strlen (string_buffer + buffer[i].name) + 1); + strcpy (objfile->import_list[k], string_buffer + buffer[i].name); + /* Some day we might want to record the type and other information too */ + } + else /* null type */ + objfile->import_list[k] = NULL; + +#if 0 /* DEBUGGING */ + printf ("Import String %d:%d (%d), type %d is %s\n", j, i, k, + (int) buffer[i].type, objfile->import_list[k]); +#endif + } + } + + /* Get the leftovers */ + if (k < import_list_size) + bfd_get_section_contents (objfile->obfd, text_section, buffer, + import_list + k * sizeof (SomImportEntry), + (import_list_size - k) * sizeof (SomImportEntry)); + for (i=0; k < import_list_size; i++, k++) + { + if (buffer[i].type != (unsigned char) 0) + { + objfile->import_list[k] + = (char *) obstack_alloc (&objfile->psymbol_obstack, strlen (string_buffer + buffer[i].name) + 1); + strcpy (objfile->import_list[k], string_buffer + buffer[i].name); + /* Some day we might want to record the type and other information too */ + } + else + objfile->import_list[k] = NULL; +#if 0 /* DEBUGGING */ + printf ("Import String F:%d (%d), type %d, is %s\n", i, k, + (int) buffer[i].type, objfile->import_list[k]); +#endif + } + + objfile->import_list_size = import_list_size; + free (string_buffer); + return import_list_size; +} + +/* Read in and initialize the SOM export list which is present + for all executables and shared libraries. The import list + consists of the symbols that are referenced in OBJFILE but + not defined there. (Variables that are imported are dealt + with as "loc_indirect" vars.) + Return value = number of import symbols read in. */ +int +init_export_symbols (objfile) + struct objfile * objfile; +{ + unsigned int export_list; + unsigned int export_list_size; + unsigned int string_table; + unsigned int string_table_size; + char * string_buffer; + register int i; + register int j; + register int k; + asection * text_section; /* section handle */ + unsigned int dl_header[12]; /* SOM executable header */ + + /* A struct for an entry in the SOM export list */ + typedef struct { + int next; /* for hash table use -- we don't use this */ + int name; /* index into string table */ + int value; /* offset or plabel */ + int dont_care1; /* not used */ + unsigned char type; /* 0 = NULL, 2 = Data, 3 = Code, 7 = Storage, 13 = Plabel */ + char dont_care2; /* not used */ + short dont_care3; /* not used */ + } SomExportEntry; + + /* We read 100 entries in at a time from the disk file. */ +# define SOM_READ_EXPORTS_NUM 100 +# define SOM_READ_EXPORTS_CHUNK_SIZE (sizeof (SomExportEntry) * SOM_READ_EXPORTS_NUM) + SomExportEntry buffer[SOM_READ_EXPORTS_NUM]; + + /* Initialize in case we error out */ + objfile->export_list = NULL; + objfile->export_list_size = 0; + +#if 0 /* DEBUGGING */ + printf ("Processing export list for %s\n", objfile->name); +#endif + + /* It doesn't work, for some reason, to read in space $TEXT$; + the subspace $SHLIB_INFO$ has to be used. Some BFD quirk? pai/1997-08-05 */ + text_section = bfd_get_section_by_name (objfile->obfd, "$SHLIB_INFO$"); + if (!text_section) + return 0; + /* Get the SOM executable header */ + bfd_get_section_contents (objfile->obfd, text_section, dl_header, 0, 12 * sizeof (int)); + + /* Check header version number for 10.x HP-UX */ + /* Currently we deal only with 10.x systems; on 9.x the version # is 89060912. + FIXME: Change for future HP-UX releases and mods to the SOM executable format */ + if (dl_header[0] != 93092112) + return 0; + + export_list = dl_header[8]; + export_list_size = dl_header[9]; + if (!export_list_size) + return 0; + string_table = dl_header[10]; + string_table_size = dl_header[11]; + if (!string_table_size) + return 0; + + /* Suck in SOM string table */ + string_buffer = (char *) xmalloc (string_table_size); + bfd_get_section_contents (objfile->obfd, text_section, string_buffer, + string_table, string_table_size); + + /* Allocate export list in the psymbol obstack; this has nothing + to do with psymbols, just a matter of convenience. We want the + export list to be freed when the objfile is deallocated */ + objfile->export_list + = (ExportEntry *) obstack_alloc (&objfile->psymbol_obstack, + export_list_size * sizeof (ExportEntry)); + + /* Read in the export entries, a bunch at a time */ + for (j=0, k=0; + j < (export_list_size / SOM_READ_EXPORTS_NUM); + j++) + { + bfd_get_section_contents (objfile->obfd, text_section, buffer, + export_list + j * SOM_READ_EXPORTS_CHUNK_SIZE, + SOM_READ_EXPORTS_CHUNK_SIZE); + for (i=0; i < SOM_READ_EXPORTS_NUM; i++, k++) + { + if (buffer[i].type != (unsigned char) 0) + { + objfile->export_list[k].name + = (char *) obstack_alloc (&objfile->psymbol_obstack, strlen (string_buffer + buffer[i].name) + 1); + strcpy (objfile->export_list[k].name, string_buffer + buffer[i].name); + objfile->export_list[k].address = buffer[i].value; + /* Some day we might want to record the type and other information too */ + } + else /* null type */ + { + objfile->export_list[k].name = NULL; + objfile->export_list[k].address = 0; + } +#if 0 /* DEBUGGING */ + printf ("Export String %d:%d (%d), type %d is %s\n", j, i, k, + (int) buffer[i].type, objfile->export_list[k].name); +#endif + } + } + + /* Get the leftovers */ + if (k < export_list_size) + bfd_get_section_contents (objfile->obfd, text_section, buffer, + export_list + k * sizeof (SomExportEntry), + (export_list_size - k) * sizeof (SomExportEntry)); + for (i=0; k < export_list_size; i++, k++) + { + if (buffer[i].type != (unsigned char) 0) + { + objfile->export_list[k].name + = (char *) obstack_alloc (&objfile->psymbol_obstack, strlen (string_buffer + buffer[i].name) + 1); + strcpy (objfile->export_list[k].name, string_buffer + buffer[i].name); + /* Some day we might want to record the type and other information too */ + objfile->export_list[k].address = buffer[i].value; + } + else + { + objfile->export_list[k].name = NULL; + objfile->export_list[k].address = 0; + } +#if 0 /* DEBUGGING */ + printf ("Export String F:%d (%d), type %d, value %x is %s\n", i, k, + (int) buffer[i].type, buffer[i].value, objfile->export_list[k].name); +#endif + } + + objfile->export_list_size = export_list_size; + free (string_buffer); + return export_list_size; +} + + /* Register that we are able to handle SOM object file formats. */ diff --git a/gdb/somsolib.c b/gdb/somsolib.c index 6c864af..904ddf1 100644 --- a/gdb/somsolib.c +++ b/gdb/somsolib.c @@ -36,8 +36,38 @@ and by Cygnus Support. */ #include "gdb-stabs.h" #include "gdb_stat.h" #include "gdbcmd.h" +#include "assert.h" #include "language.h" +#include <fcntl.h> + +#ifndef O_BINARY +#define O_BINARY 0 +#endif + +/* Uncomment this to turn on some debugging output. + */ + +/* #define SOLIB_DEBUG + */ + +/* Defined in exec.c; used to prevent dangling pointer bug. + */ +extern struct target_ops exec_ops; + +/* This lives in hppa-tdep.c. */ +extern struct unwind_table_entry *find_unwind_entry PARAMS ((CORE_ADDR pc)); + +/* These ought to be defined in some public interface, but aren't. They + define the meaning of the various bits in the distinguished __dld_flags + variable that is declared in every debuggable a.out on HP-UX, and that + is shared between the debugger and the dynamic linker. + */ +#define DLD_FLAGS_MAPPRIVATE 0x1 +#define DLD_FLAGS_HOOKVALID 0x2 +#define DLD_FLAGS_LISTVALID 0x4 +#define DLD_FLAGS_BOR_ENABLE 0x8 + /* TODO: * Most of this code should work for hp300 shared libraries. Does @@ -63,7 +93,9 @@ struct som_solib_mapped_entry /* Version of this library. */ short library_version; - /* Start of text address, link-time text location, end of text address. */ + /* Start of text address, + * link-time text location (length of text area), + * end of text address. */ CORE_ADDR text_addr; CORE_ADDR text_link_addr; CORE_ADDR text_end; @@ -81,6 +113,26 @@ struct som_solib_mapped_entry /* There are other fields, but I don't have information as to what is contained in them. */ + + /* For versions from HPUX-10.30 and up */ + + /* Address in target of offset from thread-local register of + * start of this thread's data. I.e., the first thread-local + * variable in this shared library starts at *(tsd_start_addr) + * from that area pointed to by cr27 (mpsfu_hi). + * + * We do the indirection as soon as we read it, so from then + * on it's the offset itself. + */ + CORE_ADDR tsd_start_addr; + + /* Following this are longwords holding: + * + * ?, ?, ?, ptr to -1, ptr to-1, ptr to lib name (leaf name), + * ptr to __data_start, ptr to __data_end + */ + + }; /* A structure to keep track of all the known shared objects. */ @@ -91,16 +143,287 @@ struct so_list bfd *abfd; struct section_table *sections; struct section_table *sections_end; +/* elz: added this field to store the address in target space (in the + library) of the library descriptor (handle) which we read into + som_solib_mapped_entry structure*/ + CORE_ADDR solib_addr; struct so_list *next; + }; static struct so_list *so_list_head; + +/* This is the cumulative size in bytes of the symbol tables of all + shared objects on the so_list_head list. (When we say size, here + we mean of the information before it is brought into memory and + potentially expanded by GDB.) When adding a new shlib, this value + is compared against the threshold size, held by auto_solib_add + (in megabytes). If adding symbols for the new shlib would cause + the total size to exceed the threshold, then the new shlib's symbols + are not loaded. + */ +static LONGEST som_solib_total_st_size; + +/* When the threshold is reached for any shlib, we refuse to add + symbols for subsequent shlibs, even if those shlibs' symbols would + be small enough to fit under the threshold. (Although this may + result in one, early large shlib preventing the loading of later, + smalller shlibs' symbols, it allows us to issue one informational + message. The alternative, to issue a message for each shlib whose + symbols aren't loaded, could be a big annoyance where the threshold + is exceeded due to a very large number of shlibs.) + */ +static int som_solib_st_size_threshold_exceeded; + +/* These addresses should be filled in by som_solib_create_inferior_hook. + They are also used elsewhere in this module. + */ +typedef struct { + CORE_ADDR address; + struct unwind_table_entry * unwind; +} addr_and_unwind_t; + +/* When adding fields, be sure to clear them in _initialize_som_solib. */ +static struct { + boolean is_valid; + addr_and_unwind_t hook; + addr_and_unwind_t hook_stub; + addr_and_unwind_t load; + addr_and_unwind_t load_stub; + addr_and_unwind_t unload; + addr_and_unwind_t unload2; + addr_and_unwind_t unload_stub; +} dld_cache; + + + static void som_sharedlibrary_info_command PARAMS ((char *, int)); static void som_solib_sharedlibrary_command PARAMS ((char *, int)); -/* Add symbols from shared libraries into the symtab list. */ +static LONGEST +som_solib_sizeof_symbol_table (filename) + char * filename; +{ + bfd * abfd; + int desc; + char * absolute_name; + LONGEST st_size = (LONGEST) 0; + asection * sect; + + /* We believe that filename was handed to us by the dynamic linker, and + is therefore always an absolute path. + */ + desc = openp (getenv ("PATH"), 1, filename, O_RDONLY | O_BINARY, 0, &absolute_name); + if (desc < 0) + { + perror_with_name (filename); + } + filename = absolute_name; + + abfd = bfd_fdopenr (filename, gnutarget, desc); + if (! abfd) + { + close (desc); + make_cleanup (free, filename); + error ("\"%s\": can't open to read symbols: %s.", filename, + bfd_errmsg (bfd_get_error ())); + } + + if (!bfd_check_format (abfd, bfd_object)) /* Reads in section info */ + { + bfd_close (abfd); /* This also closes desc */ + make_cleanup (free, filename); + error ("\"%s\": can't read symbols: %s.", filename, + bfd_errmsg (bfd_get_error ())); + } + + /* Sum the sizes of the various sections that compose debug info. */ + + /* This contains non-DOC information. */ + sect = bfd_get_section_by_name (abfd, "$DEBUG$"); + if (sect) + st_size += (LONGEST) bfd_section_size (abfd, sect); + + /* This contains DOC information. */ + sect = bfd_get_section_by_name (abfd, "$PINFO$"); + if (sect) + st_size += (LONGEST) bfd_section_size (abfd, sect); + + bfd_close (abfd); /* This also closes desc */ + free (filename); + + /* Unfortunately, just summing the sizes of various debug info + sections isn't a very accurate measurement of how much heap + space the debugger will need to hold them. It also doesn't + account for space needed by linker (aka "minimal") symbols. + + Anecdotal evidence suggests that just summing the sizes of + debug-info-related sections understates the heap space needed + to represent it internally by about an order of magnitude. + + Since it's not exactly brain surgery we're doing here, rather + than attempt to more accurately measure the size of a shlib's + symbol table in GDB's heap, we'll just apply a 10x fudge- + factor to the debug info sections' size-sum. No, this doesn't + account for minimal symbols in non-debuggable shlibs. But it + all roughly washes out in the end. + */ + return st_size * (LONGEST) 10; +} + + +static void +som_solib_add_solib_objfile (so, name, from_tty, text_addr) + struct so_list * so; + char * name; + int from_tty; + CORE_ADDR text_addr; +{ + obj_private_data_t *obj_private; + + so->objfile = symbol_file_add (name, from_tty, text_addr, 0, 0, 0, 0, 1); + so->abfd = so->objfile->obfd; + + /* Mark this as a shared library and save private data. + */ + so->objfile->flags |= OBJF_SHARED; + + if( so->objfile->obj_private == NULL ) + { + obj_private = (obj_private_data_t *) + obstack_alloc( &so->objfile->psymbol_obstack, + sizeof( obj_private_data_t )); + obj_private->unwind_info = NULL; + obj_private->so_info = NULL; + so->objfile->obj_private = (PTR) obj_private; + } + + obj_private = (obj_private_data_t *) so->objfile->obj_private; + obj_private->so_info = so; + + if (!bfd_check_format (so->abfd, bfd_object)) + { + error ("\"%s\": not in executable format: %s.", + name, bfd_errmsg (bfd_get_error ())); + } +} + + +static void +som_solib_load_symbols (so, name, from_tty, text_addr, target) + struct so_list * so; + char * name; + int from_tty; + CORE_ADDR text_addr; + struct target_ops * target; +{ + struct section_table * p; + int status; + char buf[4]; + CORE_ADDR presumed_data_start; + +#ifdef SOLIB_DEBUG + printf( "--Adding symbols for shared library \"%s\"\n", name ); +#endif + + som_solib_add_solib_objfile (so, name, from_tty, text_addr); + + /* Now we need to build a section table for this library since + we might be debugging a core file from a dynamically linked + executable in which the libraries were not privately mapped. */ + if (build_section_table (so->abfd, + &so->sections, + &so->sections_end)) + { + error ("Unable to build section table for shared library\n."); + return; + } + + /* Relocate all the sections based on where they got loaded. */ + for (p = so->sections; p < so->sections_end; p++) + { + if (p->the_bfd_section->flags & SEC_CODE) + { + p->addr += ANOFFSET (so->objfile->section_offsets, SECT_OFF_TEXT); + p->endaddr += ANOFFSET (so->objfile->section_offsets, SECT_OFF_TEXT); + } + else if (p->the_bfd_section->flags & SEC_DATA) + { + p->addr += ANOFFSET (so->objfile->section_offsets, SECT_OFF_DATA); + p->endaddr += ANOFFSET (so->objfile->section_offsets, SECT_OFF_DATA); + } + } + + /* Now see if we need to map in the text and data for this shared + library (for example debugging a core file which does not use + private shared libraries.). + + Carefully peek at the first text address in the library. If the + read succeeds, then the libraries were privately mapped and were + included in the core dump file. + + If the peek failed, then the libraries were not privately mapped + and are not in the core file, we'll have to read them in ourselves. */ + status = target_read_memory (text_addr, buf, 4); + if (status != 0) + { + int old, new; + int update_coreops; + int update_execops; + + /* We must update the to_sections field in the core_ops structure + here, otherwise we dereference a potential dangling pointer + for each call to target_read/write_memory within this routine. */ + update_coreops = core_ops.to_sections == target->to_sections; + + /* Ditto exec_ops (this was a bug). + */ + update_execops = exec_ops.to_sections == target->to_sections; + + new = so->sections_end - so->sections; + /* Add sections from the shared library to the core target. */ + if (target->to_sections) + { + old = target->to_sections_end - target->to_sections; + target->to_sections = (struct section_table *) + xrealloc ((char *)target->to_sections, + ((sizeof (struct section_table)) * (old + new))); + } + else + { + old = 0; + target->to_sections = (struct section_table *) + xmalloc ((sizeof (struct section_table)) * new); + } + target->to_sections_end = (target->to_sections + old + new); + + /* Update the to_sections field in the core_ops structure + if needed, ditto exec_ops. */ + if (update_coreops) + { + core_ops.to_sections = target->to_sections; + core_ops.to_sections_end = target->to_sections_end; + } + + if (update_execops) + { + exec_ops.to_sections = target->to_sections; + exec_ops.to_sections_end = target->to_sections_end; + } + + /* Copy over the old data before it gets clobbered. */ + memcpy ((char *)(target->to_sections + old), + so->sections, + ((sizeof (struct section_table)) * new)); + } +} + + +/* Add symbols from shared libraries into the symtab list, unless the + size threshold (specified by auto_solib_add, in megabytes) would + be exceeded. */ void som_solib_add (arg_string, from_tty, target) @@ -115,6 +438,7 @@ som_solib_add (arg_string, from_tty, target) int status; unsigned int dld_flags; char buf[4], *re_err; + int threshold_warning_given = 0; /* First validate our arguments. */ if ((re_err = re_comp (arg_string ? arg_string : ".")) != NULL) @@ -161,15 +485,18 @@ som_solib_add (arg_string, from_tty, target) } dld_flags = extract_unsigned_integer (buf, 4); - /* __dld_list may not be valid. If it's not valid tell the user. */ - if ((dld_flags & 4) == 0) + /* __dld_list may not be valid. If not, then we punt, warning the user if + we were called as a result of the add-symfile command. + */ + if ((dld_flags & DLD_FLAGS_LISTVALID) == 0) { - error ("__dld_list is not valid according to __dld_flags.\n"); + if (from_tty) + error ("__dld_list is not valid according to __dld_flags.\n"); return; } /* If the libraries were not mapped private, warn the user. */ - if ((dld_flags & 1) == 0) + if ((dld_flags & DLD_FLAGS_MAPPRIVATE) == 0) warning ("The shared libraries were not privately mapped; setting a\nbreakpoint in a shared library will not work until you rerun the program.\n"); msymbol = lookup_minimal_symbol ("__dld_list", NULL, NULL); @@ -205,7 +532,7 @@ som_solib_add (arg_string, from_tty, target) /* Using the information in __dld_list is the preferred method to get at shared library information. It doesn't depend on - any functions in /usr/lib/end.o and has a chance of working + any functions in /opt/langtools/lib/end.o and has a chance of working with hpux10 when it is released. */ status = target_read_memory (addr, buf, 4); if (status != 0) @@ -226,6 +553,14 @@ som_solib_add (arg_string, from_tty, target) while (so_list_tail && so_list_tail->next) so_list_tail = so_list_tail->next; +#ifdef SOLIB_DEBUG + printf( "--About to read shared library list data\n" ); +#endif + + /* "addr" will always point to the base of the + * current data entry describing the current + * shared library. + */ while (1) { CORE_ADDR name_addr, text_addr; @@ -233,8 +568,9 @@ som_solib_add (arg_string, from_tty, target) char *name; struct so_list *new_so; struct so_list *so_list = so_list_head; - struct section_table *p; struct stat statbuf; + LONGEST st_size; + int is_main_program; if (addr == 0) break; @@ -280,18 +616,41 @@ som_solib_add (arg_string, from_tty, target) if (status != 0) goto err; - addr = (CORE_ADDR) extract_unsigned_integer (buf, 4); + addr = (CORE_ADDR) extract_unsigned_integer (buf, 4); continue; } /* If we've already loaded this one or it's the main program, skip it. */ - if (so_list || !strcmp (name, symfile_objfile->name)) + is_main_program = (strcmp (name, symfile_objfile->name) == 0); + if (so_list || is_main_program) { + /* This is the "next" pointer in the strcuture. + */ status = target_read_memory (addr + 36, buf, 4); if (status != 0) goto err; addr = (CORE_ADDR) extract_unsigned_integer (buf, 4); + + /* Record the main program's symbol table size. */ + if (is_main_program && !so_list) + { + st_size = som_solib_sizeof_symbol_table (name); + som_solib_total_st_size += st_size; + } + + /* Was this a shlib that we noted but didn't load the symbols for? + If so, were we invoked this time from the command-line, via + a 'sharedlibrary' or 'add-symbol-file' command? If yes to + both, we'd better load the symbols this time. + */ + if (from_tty && so_list && !is_main_program && (so_list->objfile == NULL)) + som_solib_load_symbols (so_list, + name, + from_tty, + so_list->som_solib.text_addr, + target); + continue; } @@ -304,7 +663,6 @@ som_solib_add (arg_string, from_tty, target) text_addr = extract_unsigned_integer (buf, 4); - new_so = (struct so_list *) xmalloc (sizeof (struct so_list)); memset ((char *)new_so, 0, sizeof (struct so_list)); if (so_list_head == NULL) @@ -318,7 +676,10 @@ som_solib_add (arg_string, from_tty, target) so_list_tail = new_so; } - /* Fill in all the entries in GDB's shared library list. */ + /* Fill in all the entries in GDB's shared library list. + */ + + new_so->solib_addr = addr; new_so->som_solib.name = name; status = target_read_memory (addr + 4, buf, 4); if (status != 0) @@ -326,9 +687,16 @@ som_solib_add (arg_string, from_tty, target) new_so->som_solib.struct_version = extract_unsigned_integer (buf + 3, 1); new_so->som_solib.bind_mode = extract_unsigned_integer (buf + 2, 1); + /* Following is "high water mark", highest version number + * seen, rather than plain version number. + */ new_so->som_solib.library_version = extract_unsigned_integer (buf, 2); new_so->som_solib.text_addr = text_addr; + /* Q: What about longword at "addr + 8"? + * A: It's read above, out of order, into "text_addr". + */ + status = target_read_memory (addr + 12, buf, 4); if (status != 0) goto err; @@ -370,96 +738,95 @@ som_solib_add (arg_string, from_tty, target) goto err; new_so->som_solib.next = (void *)extract_unsigned_integer (buf, 4); - addr = (CORE_ADDR)new_so->som_solib.next; - - new_so->objfile = symbol_file_add (name, from_tty, text_addr, 0, 0, 0); - new_so->abfd = new_so->objfile->obfd; - - if (!bfd_check_format (new_so->abfd, bfd_object)) - { - error ("\"%s\": not in executable format: %s.", - name, bfd_errmsg (bfd_get_error ())); - } - - /* Now we need to build a section table for this library since - we might be debugging a core file from a dynamically linked - executable in which the libraries were not privately mapped. */ - if (build_section_table (new_so->abfd, - &new_so->sections, - &new_so->sections_end)) - { - error ("Unable to build section table for shared library\n."); - return; - } - - /* Relocate all the sections based on where they got loaded. */ - for (p = new_so->sections; p < new_so->sections_end; p++) - { - if (p->the_bfd_section->flags & SEC_CODE) - { - p->addr += text_addr - new_so->som_solib.text_link_addr; - p->endaddr += text_addr - new_so->som_solib.text_link_addr; - } - else if (p->the_bfd_section->flags & SEC_DATA) - { - p->addr += new_so->som_solib.data_start; - p->endaddr += new_so->som_solib.data_start; - } - } - /* Now see if we need to map in the text and data for this shared - library (for example debugging a core file which does not use - private shared libraries.). + /* Note that we don't re-set "addr" to the next pointer + * until after we've read the trailing data. + */ - Carefully peek at the first text address in the library. If the - read succeeds, then the libraries were privately mapped and were - included in the core dump file. + status = target_read_memory (addr + 40, buf, 4); + new_so->som_solib.tsd_start_addr = extract_unsigned_integer (buf, 4); + if (status != 0) + goto err; - If the peek failed, then the libraries were not privately mapped - and are not in the core file, we'll have to read them in ourselves. */ - status = target_read_memory (text_addr, buf, 4); + /* Now indirect via that value! + */ + status = target_read_memory (new_so->som_solib.tsd_start_addr, buf, 4); + new_so->som_solib.tsd_start_addr = extract_unsigned_integer (buf, 4); if (status != 0) - { - int old, new; - int update_coreops; + goto err; +#ifdef SOLIB_DEBUG + printf( "\n+ library \"%s\" is described at 0x%x\n", name, addr ); + printf( " 'version' is %d\n", new_so->som_solib.struct_version ); + printf( " 'bind_mode' is %d\n", new_so->som_solib.bind_mode ); + printf( " 'library_version' is %d\n", new_so->som_solib.library_version ); + printf( " 'text_addr' is 0x%x\n", new_so->som_solib.text_addr ); + printf( " 'text_link_addr' is 0x%x\n", new_so->som_solib.text_link_addr ); + printf( " 'text_end' is 0x%x\n", new_so->som_solib.text_end ); + printf( " 'data_start' is 0x%x\n", new_so->som_solib.data_start ); + printf( " 'bss_start' is 0x%x\n", new_so->som_solib.bss_start ); + printf( " 'data_end' is 0x%x\n", new_so->som_solib.data_end ); + printf( " 'got_value' is %x\n", new_so->som_solib.got_value ); + printf( " 'next' is 0x%x\n", new_so->som_solib.next ); + printf( " 'tsd_start_addr' is 0x%x\n", new_so->som_solib.tsd_start_addr ); +#endif + + /* Go on to the next shared library descriptor. + */ + addr = (CORE_ADDR)new_so->som_solib.next; - /* We must update the to_sections field in the core_ops structure - here, otherwise we dereference a potential dangling pointer - for each call to target_read/write_memory within this routine. */ - update_coreops = core_ops.to_sections == target->to_sections; - new = new_so->sections_end - new_so->sections; - /* Add sections from the shared library to the core target. */ - if (target->to_sections) - { - old = target->to_sections_end - target->to_sections; - target->to_sections = (struct section_table *) - xrealloc ((char *)target->to_sections, - ((sizeof (struct section_table)) * (old + new))); - } - else - { - old = 0; - target->to_sections = (struct section_table *) - xmalloc ((sizeof (struct section_table)) * new); - } - target->to_sections_end = (target->to_sections + old + new); - /* Update the to_sections field in the core_ops structure - if needed. */ - if (update_coreops) - { - core_ops.to_sections = target->to_sections; - core_ops.to_sections_end = target->to_sections_end; - } + /* At this point, we have essentially hooked the shlib into the + "info share" command. However, we haven't yet loaded its + symbol table. We must now decide whether we ought to, i.e., + whether doing so would exceed the symbol table size threshold. + + If the threshold has just now been exceeded, then we'll issue + a warning message (which explains how to load symbols manually, + if the user so desires). + + If the threshold has just now or previously been exceeded, + we'll just add the shlib to the list of object files, but won't + actually load its symbols. (This is more useful than it might + sound, for it allows us to e.g., still load and use the shlibs' + unwind information for stack tracebacks.) + */ + + /* Note that we DON'T want to preclude the user from using the + add-symbol-file command! Thus, we only worry about the threshold + when we're invoked for other reasons. + */ + st_size = som_solib_sizeof_symbol_table (name); + som_solib_st_size_threshold_exceeded = + !from_tty && + ((st_size + som_solib_total_st_size) > (auto_solib_add * (LONGEST)1000000)); + + if (som_solib_st_size_threshold_exceeded) + { + if (! threshold_warning_given) + warning ("Symbols for some libraries have not been loaded, because\ndoing so would exceed the size threshold specified by auto-solib-add.\nTo manually load symbols, use the 'sharedlibrary' command.\nTo raise the threshold, set auto-solib-add to a larger value and rerun\nthe program.\n"); + threshold_warning_given = 1; + + /* We'll still make note of this shlib, even if we don't + read its symbols. This allows us to use its unwind + information well enough to know how to e.g., correctly + do a traceback from a PC within the shlib, even if we + can't symbolize those PCs... + */ + som_solib_add_solib_objfile (new_so, name, from_tty, text_addr); + continue; + } - /* Copy over the old data before it gets clobbered. */ - memcpy ((char *)(target->to_sections + old), - new_so->sections, - ((sizeof (struct section_table)) * new)); - } + som_solib_total_st_size += st_size; + + /* This fills in new_so->objfile, among others. */ + som_solib_load_symbols (new_so, name, from_tty, text_addr, target); } +#ifdef SOLIB_DEBUG + printf( "--Done reading shared library data\n" ); +#endif + /* Getting new symbols may change our opinion about what is frameless. */ reinit_frame_cache (); @@ -522,93 +889,84 @@ som_solib_create_inferior_hook() return; have_endo = 0; - /* If __d_pid is present, then put the inferior's pid into __d_pid. hpux9 - requires __d_pid to be set. hpux10 doesn't require __d_pid to be set - and the symbol may not be available. - - Never warn about __d_pid. */ + /* Slam the pid of the process into __d_pid; failing is only a warning! */ msymbol = lookup_minimal_symbol ("__d_pid", NULL, symfile_objfile); - if (msymbol != NULL) + if (msymbol == NULL) { - anaddr = SYMBOL_VALUE_ADDRESS (msymbol); - store_unsigned_integer (buf, 4, inferior_pid); - status = target_write_memory (anaddr, buf, 4); - if (status != 0) - { - warning ("Unable to write __d_pid"); - goto keep_going; - } + warning ("Unable to find __d_pid symbol in object file."); + warning ("Suggest linking with /opt/langtools/lib/end.o."); + warning ("GDB will be unable to track shl_load/shl_unload calls"); + goto keep_going; } - /* If __d_trap_fptr exists, then load whatever's at that address - and put it into __dld_hook. */ - msymbol = lookup_minimal_symbol ("__d_trap_fptr", NULL, symfile_objfile); - if (msymbol != NULL) + anaddr = SYMBOL_VALUE_ADDRESS (msymbol); + store_unsigned_integer (buf, 4, inferior_pid); + status = target_write_memory (anaddr, buf, 4); + if (status != 0) { - anaddr = SYMBOL_VALUE_ADDRESS (msymbol); - status = target_read_memory (anaddr, buf, 4); - anaddr = extract_unsigned_integer (buf, 4); + warning ("Unable to write __d_pid"); + warning ("Suggest linking with /opt/langtools/lib/end.o."); + warning ("GDB will be unable to track shl_load/shl_unload calls"); + goto keep_going; + } - /* If it's a plabel, then get the address of the real function. - Egad. This is just the opposite of how hpux9 and _DLD_HOOK - works. */ - if (anaddr | 0x2) - { - status = target_read_memory (anaddr & ~0x2, buf, 4); - anaddr = extract_unsigned_integer (buf, 4); - } + /* Get the value of _DLD_HOOK (an export stub) and put it in __dld_hook; + This will force the dynamic linker to call __d_trap when significant + events occur. + + Note that the above is the pre-HP-UX 9.0 behaviour. At 9.0 and above, + the dld provides an export stub named "__d_trap" as well as the + function named "__d_trap" itself, but doesn't provide "_DLD_HOOK". + We'll look first for the old flavor and then the new. + */ + msymbol = lookup_minimal_symbol ("_DLD_HOOK", NULL, symfile_objfile); + if (msymbol == NULL) + msymbol = lookup_minimal_symbol ("__d_trap", NULL, symfile_objfile); + if (msymbol == NULL) + { + warning ("Unable to find _DLD_HOOK symbol in object file."); + warning ("Suggest linking with /opt/langtools/lib/end.o."); + warning ("GDB will be unable to track shl_load/shl_unload calls"); + goto keep_going; } - else + anaddr = SYMBOL_VALUE_ADDRESS (msymbol); + dld_cache.hook.address = anaddr; + + /* Grrr, this might not be an export symbol! We have to find the + export stub. */ + ALL_OBJFILES (objfile) { - /* Get the value of _DLD_HOOK (an export stub) and put it in __dld_hook; - This will force the dynamic linker to call __d_trap when significant - events occur. */ - msymbol = lookup_minimal_symbol ("_DLD_HOOK", NULL, symfile_objfile); - if (msymbol == NULL) + struct unwind_table_entry *u; + struct minimal_symbol *msymbol2; + + /* What a crock. */ + msymbol2 = lookup_minimal_symbol_solib_trampoline (SYMBOL_NAME (msymbol), + NULL, objfile); + /* Found a symbol with the right name. */ + if (msymbol2) { - warning ("Unable to find _DLD_HOOK symbol in object file."); - warning ("Suggest linking with /usr/lib/end.o."); - warning ("GDB will be unable to track shl_load/shl_unload calls"); - goto keep_going; + struct unwind_table_entry *u; + /* It must be a shared library trampoline. */ + if (SYMBOL_TYPE (msymbol2) != mst_solib_trampoline) + continue; + + /* It must also be an export stub. */ + u = find_unwind_entry (SYMBOL_VALUE (msymbol2)); + if (!u || u->stub_unwind.stub_type != EXPORT) + continue; + + /* OK. Looks like the correct import stub. */ + anaddr = SYMBOL_VALUE (msymbol2); + dld_cache.hook_stub.address = anaddr; } - anaddr = SYMBOL_VALUE_ADDRESS (msymbol); - - /* Grrr, this might not be an export symbol! We have to find the - export stub. */ - ALL_OBJFILES (objfile) - { - extern struct unwind_table_entry *find_unwind_entry PARAMS ((CORE_ADDR pc)); - - /* What a crock. */ - msymbol - = lookup_minimal_symbol_solib_trampoline (SYMBOL_NAME (msymbol), - NULL, objfile); - /* Found a symbol with the right name. */ - if (msymbol) - { - struct unwind_table_entry *u; - /* It must be a shared library trampoline. */ - if (MSYMBOL_TYPE (msymbol) != mst_solib_trampoline) - continue; - - /* It must also be an export stub. */ - u = find_unwind_entry (SYMBOL_VALUE (msymbol)); - if (!u || u->stub_type != EXPORT) - continue; - - /* OK. Looks like the correct import stub. */ - anaddr = SYMBOL_VALUE (msymbol); - break; - } - } - } + } store_unsigned_integer (buf, 4, anaddr); msymbol = lookup_minimal_symbol ("__dld_hook", NULL, symfile_objfile); if (msymbol == NULL) { warning ("Unable to find __dld_hook symbol in object file."); - warning ("Suggest linking with /usr/lib/end.o."); + warning ("Suggest linking with /opt/langtools/lib/end.o."); warning ("GDB will be unable to track shl_load/shl_unload calls"); goto keep_going; } @@ -621,7 +979,7 @@ som_solib_create_inferior_hook() if (msymbol == NULL) { warning ("Unable to find __dld_d_trap symbol in object file."); - warning ("Suggest linking with /usr/lib/end.o."); + warning ("Suggest linking with /opt/langtools/lib/end.o."); warning ("GDB will be unable to track shl_load/shl_unload calls"); goto keep_going; } @@ -639,34 +997,33 @@ keep_going: if (msymbol == NULL) { error ("Unable to find __dld_flags symbol in object file.\n"); - goto keep_going; - return; } anaddr = SYMBOL_VALUE_ADDRESS (msymbol); + /* Read the current contents. */ status = target_read_memory (anaddr, buf, 4); if (status != 0) { error ("Unable to read __dld_flags\n"); - return; } dld_flags = extract_unsigned_integer (buf, 4); /* Turn on the flags we care about. */ - dld_flags |= (0x5 | (have_endo << 1)); + dld_flags |= DLD_FLAGS_MAPPRIVATE; + if (have_endo) + dld_flags |= DLD_FLAGS_HOOKVALID; store_unsigned_integer (buf, 4, dld_flags); status = target_write_memory (anaddr, buf, 4); if (status != 0) { error ("Unable to write __dld_flags\n"); - return; } /* Now find the address of _start and set a breakpoint there. We still need this code for two reasons: - * Not all sites have /usr/lib/end.o, so it's not always + * Not all sites have /opt/langtools/lib/end.o, so it's not always possible to track the dynamic linker's events. * At this time no events are triggered for shared libraries @@ -676,7 +1033,6 @@ keep_going: if (msymbol == NULL) { error ("Unable to find _start symbol in object file.\n"); - return; } anaddr = SYMBOL_VALUE_ADDRESS (msymbol); @@ -690,7 +1046,6 @@ keep_going: { struct so_list *temp; - free_objfile (so_list_head->objfile); temp = so_list_head; free (so_list_head); so_list_head = temp->next; @@ -698,6 +1053,322 @@ keep_going: clear_symtab_users (); } + +static void +reset_inferior_pid (saved_inferior_pid) + int saved_inferior_pid; +{ + inferior_pid = saved_inferior_pid; +} + + +/* This operation removes the "hook" between GDB and the dynamic linker, + which causes the dld to notify GDB of shared library events. + + After this operation completes, the dld will no longer notify GDB of + shared library events. To resume notifications, GDB must call + som_solib_create_inferior_hook. + + This operation does not remove any knowledge of shared libraries which + GDB may already have been notified of. + */ +void +som_solib_remove_inferior_hook (pid) + int pid; +{ + CORE_ADDR addr; + struct minimal_symbol * msymbol; + int status; + char dld_flags_buffer [TARGET_INT_BIT/TARGET_CHAR_BIT]; + unsigned int dld_flags_value; + int saved_inferior_pid = inferior_pid; + struct cleanup * old_cleanups = make_cleanup (reset_inferior_pid, saved_inferior_pid); + + /* Ensure that we're really operating on the specified process. */ + inferior_pid = pid; + + /* We won't bother to remove the solib breakpoints from this process. + + In fact, on PA64 the breakpoint is hard-coded into the dld callback, + and thus we're not supposed to remove it. + + Rather, we'll merely clear the dld_flags bit that enables callbacks. + */ + msymbol = lookup_minimal_symbol ("__dld_flags", NULL, NULL); + + addr = SYMBOL_VALUE_ADDRESS (msymbol); + status = target_read_memory (addr, dld_flags_buffer, TARGET_INT_BIT/TARGET_CHAR_BIT); + + dld_flags_value = extract_unsigned_integer (dld_flags_buffer, + sizeof (dld_flags_value)); + + dld_flags_value &= ~DLD_FLAGS_HOOKVALID; + store_unsigned_integer (dld_flags_buffer, + sizeof (dld_flags_value), + dld_flags_value); + status = target_write_memory (addr, dld_flags_buffer, TARGET_INT_BIT/TARGET_CHAR_BIT); + + do_cleanups (old_cleanups); +} + + +/* This function creates a breakpoint on the dynamic linker hook, which + is called when e.g., a shl_load or shl_unload call is made. This + breakpoint will only trigger when a shl_load call is made. + + If filename is NULL, then loads of any dll will be caught. Else, + only loads of the file whose pathname is the string contained by + filename will be caught. + + Undefined behaviour is guaranteed if this function is called before + som_solib_create_inferior_hook. + */ +void +som_solib_create_catch_load_hook (pid, tempflag, filename, cond_string) + int pid; + int tempflag; + char * filename; + char * cond_string; +{ + create_solib_load_event_breakpoint ("__d_trap", tempflag, filename, cond_string); +} + +/* This function creates a breakpoint on the dynamic linker hook, which + is called when e.g., a shl_load or shl_unload call is made. This + breakpoint will only trigger when a shl_unload call is made. + + If filename is NULL, then unloads of any dll will be caught. Else, + only unloads of the file whose pathname is the string contained by + filename will be caught. + + Undefined behaviour is guaranteed if this function is called before + som_solib_create_inferior_hook. + */ +void +som_solib_create_catch_unload_hook (pid, tempflag, filename, cond_string) + int pid; + int tempflag; + char * filename; + char * cond_string; +{ + create_solib_unload_event_breakpoint ("__d_trap", tempflag, filename, cond_string); +} + +int +som_solib_have_load_event (pid) + int pid; +{ + CORE_ADDR event_kind; + + event_kind = read_register (ARG0_REGNUM); + return (event_kind == SHL_LOAD); +} + +int +som_solib_have_unload_event (pid) + int pid; +{ + CORE_ADDR event_kind; + + event_kind = read_register (ARG0_REGNUM); + return (event_kind == SHL_UNLOAD); +} + +static char * +som_solib_library_pathname (pid) + int pid; +{ + CORE_ADDR dll_handle_address; + CORE_ADDR dll_pathname_address; + struct som_solib_mapped_entry dll_descriptor; + char * p; + static char dll_pathname [1024]; + + /* Read the descriptor of this newly-loaded library. */ + dll_handle_address = read_register (ARG1_REGNUM); + read_memory (dll_handle_address, (char *) &dll_descriptor, sizeof (dll_descriptor)); + + /* We can find a pointer to the dll's pathname within the descriptor. */ + dll_pathname_address = (CORE_ADDR) dll_descriptor.name; + + /* Read the pathname, one byte at a time. */ + p = dll_pathname; + for (;;) + { + char b; + read_memory (dll_pathname_address++, (char *) &b, 1); + *p++ = b; + if (b == '\0') + break; + } + + return dll_pathname; +} + +char * +som_solib_loaded_library_pathname (pid) + int pid; +{ + if (! som_solib_have_load_event (pid)) + error ("Must have a load event to use this query"); + + return som_solib_library_pathname (pid); +} + +char * +som_solib_unloaded_library_pathname (pid) + int pid; +{ + if (! som_solib_have_unload_event (pid)) + error ("Must have an unload event to use this query"); + + return som_solib_library_pathname (pid); +} + +static void +som_solib_desire_dynamic_linker_symbols () +{ + struct objfile *objfile; + struct unwind_table_entry *u; + struct minimal_symbol * dld_msymbol; + + /* Do we already know the value of these symbols? If so, then + we've no work to do. + + (If you add clauses to this test, be sure to likewise update the + test within the loop.) + */ + if (dld_cache.is_valid) + return; + + ALL_OBJFILES (objfile) + { + dld_msymbol = lookup_minimal_symbol ("shl_load", NULL, objfile); + if (dld_msymbol != NULL) + { + dld_cache.load.address = SYMBOL_VALUE (dld_msymbol); + dld_cache.load.unwind = find_unwind_entry (dld_cache.load.address); + } + + dld_msymbol = lookup_minimal_symbol_solib_trampoline ("shl_load", + NULL, + objfile); + if (dld_msymbol != NULL) + { + if (SYMBOL_TYPE (dld_msymbol) == mst_solib_trampoline) + { + u = find_unwind_entry (SYMBOL_VALUE (dld_msymbol)); + if ((u != NULL) && (u->stub_unwind.stub_type == EXPORT)) + { + dld_cache.load_stub.address = SYMBOL_VALUE (dld_msymbol); + dld_cache.load_stub.unwind = u; + } + } + } + + dld_msymbol = lookup_minimal_symbol ("shl_unload", NULL, objfile); + if (dld_msymbol != NULL) + { + dld_cache.unload.address = SYMBOL_VALUE (dld_msymbol); + dld_cache.unload.unwind = find_unwind_entry (dld_cache.unload.address); + + /* ??rehrauer: I'm not sure exactly what this is, but it appears + that on some HPUX 10.x versions, there's two unwind regions to + cover the body of "shl_unload", the second being 4 bytes past + the end of the first. This is a large hack to handle that + case, but since I don't seem to have any legitimate way to + look for this thing via the symbol table... + */ + if (dld_cache.unload.unwind != NULL) + { + u = find_unwind_entry (dld_cache.unload.unwind->region_end + 4); + if (u != NULL) + { + dld_cache.unload2.address = u->region_start; + dld_cache.unload2.unwind = u; + } + } + } + + dld_msymbol = lookup_minimal_symbol_solib_trampoline ("shl_unload", + NULL, + objfile); + if (dld_msymbol != NULL) + { + if (SYMBOL_TYPE (dld_msymbol) == mst_solib_trampoline) + { + u = find_unwind_entry (SYMBOL_VALUE (dld_msymbol)); + if ((u != NULL) && (u->stub_unwind.stub_type == EXPORT)) + { + dld_cache.unload_stub.address = SYMBOL_VALUE (dld_msymbol); + dld_cache.unload_stub.unwind = u; + } + } + } + + /* Did we find everything we were looking for? If so, stop. */ + if ((dld_cache.load.address != NULL) && (dld_cache.load_stub.address != NULL) + && (dld_cache.unload.address != NULL) && (dld_cache.unload_stub.address != NULL)) + { + dld_cache.is_valid = 1; + break; + } + } + + dld_cache.hook.unwind = find_unwind_entry (dld_cache.hook.address); + dld_cache.hook_stub.unwind = find_unwind_entry (dld_cache.hook_stub.address); + + /* We're prepared not to find some of these symbols, which is why + this function is a "desire" operation, and not a "require". + */ +} + +int +som_solib_in_dynamic_linker (pid, pc) + int pid; + CORE_ADDR pc; +{ + struct unwind_table_entry * u_pc; + + /* Are we in the dld itself? + + ??rehrauer: Large hack -- We'll assume that any address in a + shared text region is the dld's text. This would obviously + fall down if the user attached to a process, whose shlibs + weren't mapped to a (writeable) private region. However, in + that case the debugger probably isn't able to set the fundamental + breakpoint in the dld callback anyways, so this hack should be + safe. + */ + if ((pc & (CORE_ADDR) 0xc0000000) == (CORE_ADDR) 0xc0000000) + return 1; + + /* Cache the address of some symbols that are part of the dynamic + linker, if not already known. + */ + som_solib_desire_dynamic_linker_symbols (); + + /* Are we in the dld callback? Or its export stub? */ + u_pc = find_unwind_entry (pc); + if (u_pc == NULL) + return 0; + + if ((u_pc == dld_cache.hook.unwind) || (u_pc == dld_cache.hook_stub.unwind)) + return 1; + + /* Or the interface of the dld (i.e., "shl_load" or friends)? */ + if ((u_pc == dld_cache.load.unwind) + || (u_pc == dld_cache.unload.unwind) + || (u_pc == dld_cache.unload2.unwind) + || (u_pc == dld_cache.load_stub.unwind) + || (u_pc == dld_cache.unload_stub.unwind)) + return 1; + + /* Apparently this address isn't part of the dld's text. */ + return 0; +} + + /* Return the GOT value for the shared library in which ADDR belongs. If ADDR isn't in any known shared library, return zero. */ @@ -721,6 +1392,34 @@ som_solib_get_got_by_pc (addr) return got_value; } +/* elz: + Return the address of the handle of the shared library + in which ADDR belongs. If + ADDR isn't in any known shared library, return zero. */ +/* this function is used in hppa_fix_call_dummy in hppa-tdep.c*/ + +CORE_ADDR +som_solib_get_solib_by_pc (addr) + CORE_ADDR addr; +{ + struct so_list *so_list = so_list_head; + + while (so_list) + { + if (so_list->som_solib.text_addr <= addr + && so_list->som_solib.text_end > addr) + { + break; + } + so_list = so_list->next; + } + if (so_list) + return so_list->solib_addr; + else + return 0; +} + + int som_solib_section_offsets (objfile, offsets) struct objfile *objfile; @@ -796,7 +1495,10 @@ som_sharedlibrary_info_command (ignore, from_tty) flags = so_list->som_solib.struct_version << 24; flags |= so_list->som_solib.bind_mode << 16; flags |= so_list->som_solib.library_version; - printf_unfiltered ("%s\n", so_list->som_solib.name); + printf_unfiltered ("%s", so_list->som_solib.name); + if (so_list->objfile == NULL) + printf_unfiltered (" (symbols not loaded)"); + printf_unfiltered ("\n"); printf_unfiltered (" %-12s", local_hex_string_custom (flags, "08l")); printf_unfiltered ("%-12s", local_hex_string_custom (so_list->som_solib.text_addr, "08l")); @@ -821,6 +1523,80 @@ som_solib_sharedlibrary_command (args, from_tty) som_solib_add (args, from_tty, (struct target_ops *) 0); } + + +char * +som_solib_address (addr) + CORE_ADDR addr; +{ + struct so_list * so = so_list_head; + + while (so) + { + /* Is this address within this shlib's text range? If so, + return the shlib's name. + */ + if ((addr >= so->som_solib.text_addr) && (addr <= so->som_solib.text_end)) + return so->som_solib.name; + + /* Nope, keep looking... */ + so = so->next; + } + + /* No, we couldn't prove that the address is within a shlib. */ + return NULL; +} + + +void +som_solib_restart () +{ + struct so_list * sl = so_list_head; + + /* Before the shlib info vanishes, use it to disable any breakpoints + that may still be active in those shlibs. + */ + disable_breakpoints_in_shlibs (); + + /* Discard all the shlib descriptors. + */ + while (sl) + { + struct so_list * next_sl = sl->next; + free (sl); + sl = next_sl; + } + so_list_head = NULL; + + som_solib_total_st_size = (LONGEST) 0; + som_solib_st_size_threshold_exceeded = 0; + + dld_cache.is_valid = 0; + + dld_cache.hook.address = 0; + dld_cache.hook.unwind = NULL; + + dld_cache.hook_stub.address = 0; + dld_cache.hook_stub.unwind = NULL; + + dld_cache.load.address = 0; + dld_cache.load.unwind = NULL; + + dld_cache.load_stub.address = 0; + dld_cache.load_stub.unwind = NULL; + + dld_cache.unload.address = 0; + dld_cache.unload.unwind = NULL; + + dld_cache.unload2.address = 0; + dld_cache.unload2.unwind = NULL; + + dld_cache.unload_stub.address = 0; + dld_cache.unload_stub.unwind = NULL; +} + + + void _initialize_som_solib () { @@ -831,12 +1607,34 @@ _initialize_som_solib () add_show_from_set (add_set_cmd ("auto-solib-add", class_support, var_zinteger, (char *) &auto_solib_add, - "Set autoloading of shared library symbols at startup.\n\ + "Set autoloading size threshold (in megabytes) of shared library symbols.\n\ If nonzero, symbols from all shared object libraries will be loaded\n\ automatically when the inferior begins execution or when the dynamic linker\n\ -informs gdb that a new library has been loaded. Otherwise, symbols\n\ -must be loaded manually, using `sharedlibrary'.", +informs gdb that a new library has been loaded, until the symbol table\n\ +of the program and libraries exceeds this threshold.\n\ +Otherwise, symbols must be loaded manually, using `sharedlibrary'.", &setlist), &showlist); + /* ??rehrauer: On HP-UX, the kernel parameter MAXDSIZ limits how much + data space a process can use. We ought to be reading MAXDSIZ and + setting auto_solib_add to some large fraction of that value. If + not that, we maybe ought to be setting it smaller than the default + for MAXDSIZ (that being 64Mb, I believe). However, [1] this threshold + is only crudely approximated rather than actually measured, and [2] + 50 Mbytes is too small for debugging gdb itself. Thus, the arbitrary + 100 figure. + */ + auto_solib_add = 100; /* Megabytes */ + + som_solib_restart (); +} + +/* Get some HPUX-specific data from a shared lib. + */ +CORE_ADDR +so_lib_thread_start_addr( so ) + struct so_list *so; +{ + return so->som_solib.tsd_start_addr; } diff --git a/gdb/somsolib.h b/gdb/somsolib.h index 6fe0200..b969169 100644 --- a/gdb/somsolib.h +++ b/gdb/somsolib.h @@ -49,3 +49,129 @@ som_solib_section_offsets PARAMS ((struct objfile *, struct section_offsets *)); extern void som_solib_create_inferior_hook PARAMS((void)); + +/* Function to be called to remove the connection between debugger and + dynamic linker that was established by SOLIB_CREATE_INFERIOR_HOOK. + (This operation does not remove shared library information from + the debugger, as CLEAR_SOLIB does.) + */ +#define SOLIB_REMOVE_INFERIOR_HOOK(PID) som_solib_remove_inferior_hook(PID) + +extern void +som_solib_remove_inferior_hook PARAMS((int)); + +/* This function is called by the "catch load" command. It allows + the debugger to be notified by the dynamic linker when a specified + library file (or any library file, if filename is NULL) is loaded. + */ +#define SOLIB_CREATE_CATCH_LOAD_HOOK(pid,tempflag, filename,cond_string) \ + som_solib_create_catch_load_hook (pid, tempflag, filename, cond_string) + +extern void +som_solib_create_catch_load_hook PARAMS((int, int, char *, char *)); + +/* This function is called by the "catch unload" command. It allows + the debugger to be notified by the dynamic linker when a specified + library file (or any library file, if filename is NULL) is unloaded. + */ +#define SOLIB_CREATE_CATCH_UNLOAD_HOOK(pid,tempflag,filename, cond_string) \ + som_solib_create_catch_unload_hook (pid, tempflag, filename, cond_string) + +extern void +som_solib_create_catch_unload_hook PARAMS((int, int, char *, char *)); + +/* This function returns TRUE if the dynamic linker has just reported + a load of a library. + + This function must be used only when the inferior has stopped in + the dynamic linker hook, or undefined results are guaranteed. + */ +#define SOLIB_HAVE_LOAD_EVENT(pid) \ + som_solib_have_load_event (pid) + +extern int +som_solib_have_load_event PARAMS((int)); + +/* This function returns a pointer to the string representation of the + pathname of the dynamically-linked library that has just been loaded. + + This function must be used only when SOLIB_HAVE_LOAD_EVENT is TRUE, + or undefined results are guaranteed. + + This string's contents are only valid immediately after the inferior + has stopped in the dynamic linker hook, and becomes invalid as soon + as the inferior is continued. Clients should make a copy of this + string if they wish to continue the inferior and then access the string. + */ +#define SOLIB_LOADED_LIBRARY_PATHNAME(pid) \ + som_solib_loaded_library_pathname (pid) + +extern char * +som_solib_loaded_library_pathname PARAMS((int)); + +/* This function returns TRUE if the dynamic linker has just reported + an unload of a library. + + This function must be used only when the inferior has stopped in + the dynamic linker hook, or undefined results are guaranteed. + */ +#define SOLIB_HAVE_UNLOAD_EVENT(pid) \ + som_solib_have_unload_event (pid) + +extern int +som_solib_have_unload_event PARAMS((int)); + +/* This function returns a pointer to the string representation of the + pathname of the dynamically-linked library that has just been unloaded. + + This function must be used only when SOLIB_HAVE_UNLOAD_EVENT is TRUE, + or undefined results are guaranteed. + + This string's contents are only valid immediately after the inferior + has stopped in the dynamic linker hook, and becomes invalid as soon + as the inferior is continued. Clients should make a copy of this + string if they wish to continue the inferior and then access the string. + */ +#define SOLIB_UNLOADED_LIBRARY_PATHNAME(pid) \ + som_solib_unloaded_library_pathname (pid) + +extern char * +som_solib_unloaded_library_pathname PARAMS((int)); + +/* This function returns TRUE if pc is the address of an instruction that + lies within the dynamic linker (such as the event hook, or the dld + itself). + + This function must be used only when a dynamic linker event has been + caught, and the inferior is being stepped out of the hook, or undefined + results are guaranteed. + */ +#define SOLIB_IN_DYNAMIC_LINKER(pid,pc) \ + som_solib_in_dynamic_linker (pid, pc) + +extern int +som_solib_in_dynamic_linker PARAMS((int, CORE_ADDR)); + +/* This function must be called when the inferior is killed, and the program + restarted. This is not the same as CLEAR_SOLIB, in that it doesn't discard + any symbol tables. + + Presently, this functionality is not implemented. + */ +#define SOLIB_RESTART() \ + som_solib_restart () + +extern void +som_solib_restart PARAMS((void)); + +/* If we can't set a breakpoint, and it's in a shared library, just + disable it. */ + +#define DISABLE_UNSETTABLE_BREAK(addr) (som_solib_address(addr) != NULL) + +extern char * +som_solib_address PARAMS ((CORE_ADDR)); /* somsolib.c */ + +/* If ADDR lies in a shared library, return its name. */ + +#define PC_SOLIB(addr) som_solib_address (addr) diff --git a/gdb/stack.c b/gdb/stack.c index 5338938..685d491 100644 --- a/gdb/stack.c +++ b/gdb/stack.c @@ -18,7 +18,7 @@ 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. */ - +#include <ctype.h> #include "defs.h" #include "gdb_string.h" #include "value.h" @@ -53,13 +53,17 @@ static void return_command PARAMS ((char *, int)); static void down_command PARAMS ((char *, int)); +static void down_silently_base PARAMS ((char *)); + static void down_silently_command PARAMS ((char *, int)); static void up_command PARAMS ((char *, int)); +static void up_silently_base PARAMS ((char *)); + static void up_silently_command PARAMS ((char *, int)); -static void frame_command PARAMS ((char *, int)); +void frame_command PARAMS ((char *, int)); static void select_frame_command PARAMS ((char *, int)); @@ -67,21 +71,31 @@ static void print_frame_arg_vars PARAMS ((struct frame_info *, GDB_FILE *)); static void catch_info PARAMS ((char *, int)); -static void print_frame_label_vars PARAMS ((struct frame_info *, int, +static void args_plus_locals_info PARAMS ((char *, int)); + +static void print_frame_label_vars PARAMS ((struct frame_info *, + int, GDB_FILE *)); -static void print_frame_local_vars PARAMS ((struct frame_info *, GDB_FILE *)); +static void print_frame_local_vars PARAMS ((struct frame_info *, + int, + GDB_FILE *)); static int print_block_frame_labels PARAMS ((struct block *, int *, GDB_FILE *)); static int print_block_frame_locals PARAMS ((struct block *, struct frame_info *, + int, GDB_FILE *)); +static void print_frame_info_base PARAMS ((struct frame_info *, int, int, int)); + +static void print_stack_frame_base PARAMS ((struct frame_info *, int, int)); + static void backtrace_command PARAMS ((char *, int)); -static struct frame_info *parse_frame_specification PARAMS ((char *)); +struct frame_info *parse_frame_specification PARAMS ((char *)); static void frame_info PARAMS ((char *, int)); @@ -116,16 +130,64 @@ struct print_stack_frame_args { int args; }; -static int print_stack_frame_stub PARAMS ((char *)); +static int print_stack_frame_base_stub PARAMS ((char *)); -/* Pass the args the way catch_errors wants them. */ +/* Show and print the frame arguments. + Pass the args the way catch_errors wants them. */ static int -print_stack_frame_stub (args) +show_and_print_stack_frame_stub (args) char *args; { struct print_stack_frame_args *p = (struct print_stack_frame_args *)args; + /* Reversed order of these so tuiDo() doesn't occur + * in the middle of "Breakpoint 1 ... [location]" printing = RT + */ + if (tui_version) + print_frame_info_base (p->fi, p->level, p->source, p->args); print_frame_info (p->fi, p->level, p->source, p->args); + + return 0; +} + +/* Show or print the frame arguments. + Pass the args the way catch_errors wants them. */ +static int +print_stack_frame_stub (args) + char *args; +{ + struct print_stack_frame_args *p = (struct print_stack_frame_args *)args; + + if (tui_version) + print_frame_info (p->fi, p->level, p->source, p->args); + else + print_frame_info_base (p->fi, p->level, p->source, p->args); + return 0; +} + +/* Print a stack frame briefly. FRAME_INFI should be the frame info + and LEVEL should be its level in the stack (or -1 for level not defined). + +/* Pass the args the way catch_errors wants them. */ +static int +print_stack_frame_base_stub (args) + char *args; +{ + struct print_stack_frame_args *p = (struct print_stack_frame_args *)args; + + print_frame_info_base (p->fi, p->level, p->source, p->args); + return 0; +} + +/* print the frame arguments to the terminal. + Pass the args the way catch_errors wants them. */ +static int +print_only_stack_frame_stub (args) + char *args; +{ + struct print_stack_frame_args *p = (struct print_stack_frame_args *)args; + + print_frame_info_base (p->fi, p->level, p->source, p->args); return 0; } @@ -139,6 +201,59 @@ print_stack_frame_stub (args) If SOURCE is 1, print the source line as well. If SOURCE is -1, print ONLY the source line. */ +static void +print_stack_frame_base (fi, level, source) + struct frame_info *fi; + int level; + int source; +{ + struct print_stack_frame_args args; + + args.fi = fi; + args.level = level; + args.source = source; + args.args = 1; + + catch_errors (print_stack_frame_stub, (char *)&args, "", RETURN_MASK_ALL); +} + +/* Show and print a stack frame briefly. FRAME_INFI should be the frame info + and LEVEL should be its level in the stack (or -1 for level not defined). + This prints the level, the function executing, the arguments, + and the file name and line number. + If the pc is not at the beginning of the source line, + the actual pc is printed at the beginning. + + If SOURCE is 1, print the source line as well. + If SOURCE is -1, print ONLY the source line. */ + +void +show_and_print_stack_frame (fi, level, source) + struct frame_info *fi; + int level; + int source; +{ + struct print_stack_frame_args args; + + args.fi = fi; + args.level = level; + args.source = source; + args.args = 1; + + catch_errors (show_and_print_stack_frame_stub, (char *)&args, "", RETURN_MASK_ALL); +} + + +/* Show or print a stack frame briefly. FRAME_INFI should be the frame info + and LEVEL should be its level in the stack (or -1 for level not defined). + This prints the level, the function executing, the arguments, + and the file name and line number. + If the pc is not at the beginning of the source line, + the actual pc is printed at the beginning. + + If SOURCE is 1, print the source line as well. + If SOURCE is -1, print ONLY the source line. */ + void print_stack_frame (fi, level, source) struct frame_info *fi; @@ -155,6 +270,33 @@ print_stack_frame (fi, level, source) catch_errors (print_stack_frame_stub, (char *)&args, "", RETURN_MASK_ALL); } +/* Print a stack frame briefly. FRAME_INFI should be the frame info + and LEVEL should be its level in the stack (or -1 for level not defined). + This prints the level, the function executing, the arguments, + and the file name and line number. + If the pc is not at the beginning of the source line, + the actual pc is printed at the beginning. + + If SOURCE is 1, print the source line as well. + If SOURCE is -1, print ONLY the source line. */ + +void +print_only_stack_frame (fi, level, source) + struct frame_info *fi; + int level; + int source; +{ + struct print_stack_frame_args args; + + args.fi = fi; + args.level = level; + args.source = source; + args.args = 1; + + catch_errors (print_only_stack_frame_stub, + (char *)&args, "", RETURN_MASK_ALL); +} + struct print_args_args { struct symbol *func; struct frame_info *fi; @@ -176,13 +318,20 @@ print_args_stub (args) return 0; } -/* LEVEL is the level of the frame, or -1 if it is the innermost frame - but we don't want to print the level. */ +/* Print information about a frame for frame "fi" at level "level". + * Used in "where" output, also used to emit breakpoint or step messages. + * LEVEL is the level of the frame, or -1 if it is the innermost frame + * but we don't want to print the level. + * The meaning of the SOURCE argument is: + * -1: Print only source line + * 0: Print only location + * 1: Print location and source line + */ -void -print_frame_info (fi, level, source, args) +static void +print_frame_info_base (fi, level, source, args) struct frame_info *fi; - register int level; + int level; int source; int args; { @@ -288,8 +437,31 @@ print_frame_info (fi, level, source, args) } else { + /* I'd like to use SYMBOL_SOURCE_NAME() here, to display + * the demangled name that we already have stored in + * the symbol table, but we stored a version with + * DMGL_PARAMS turned on, and here we don't want + * to display parameters. So call the demangler again, + * with DMGL_ANSI only. RT + * (Yes, I know that printf_symbol_filtered() will + * again try to demangle the name on the fly, but + * the issue is that if cplus_demangle() fails here, + * it'll fail there too. So we want to catch the failure + * ("demangled==NULL" case below) here, while we still + * have our hands on the function symbol.) + */ + char * demangled; funname = SYMBOL_NAME (func); funlang = SYMBOL_LANGUAGE (func); + if (funlang == language_cplus) { + demangled = cplus_demangle (funname, DMGL_ANSI); + if (demangled == NULL) + /* If the demangler fails, try the demangled name + * from the symbol table. This'll have parameters, + * but that's preferable to diplaying a mangled name. + */ + funname = SYMBOL_SOURCE_NAME (func); + } } } else @@ -357,7 +529,7 @@ print_frame_info (fi, level, source, args) } #endif #ifdef PC_SOLIB - if (!funname) + if (!funname || (!sal.symtab || !sal.symtab->filename)) { char *lib = PC_SOLIB (fi->pc); if (lib) @@ -380,14 +552,14 @@ print_frame_info (fi, level, source, args) fi->pc); if (!done) { - if (addressprint && mid_statement) + if (addressprint && mid_statement && !tui_version) { print_address_numeric (fi->pc, 1, gdb_stdout); printf_filtered ("\t"); } if (print_frame_info_listing_hook) print_frame_info_listing_hook (sal.symtab, sal.line, sal.line + 1, 0); - else + else if (!tui_version) print_source_lines (sal.symtab, sal.line, sal.line + 1, 0); } current_source_line = max (sal.line - lines_to_list/2, 1); @@ -399,12 +571,53 @@ print_frame_info (fi, level, source, args) gdb_flush (gdb_stdout); } + + +void +stack_publish_stopped_with_no_frame() +{ + TUIDO(((TuiOpaqueFuncPtr)tuiUpdateOnEnd)); + + return; +} +/* Show or print the frame info. If this is the tui, it will be shown in + the source display */ +void +print_frame_info(fi, level, source, args) + struct frame_info *fi; + register int level; + int source; + int args; +{ + if (!tui_version) + print_frame_info_base(fi, level, source, args); + else + { + if (fi && (frame_in_dummy(fi) || fi->signal_handler_caller)) + print_frame_info_base(fi, level, source, args); + else + { + TUIDO(((TuiOpaqueFuncPtr)tui_vShowFrameInfo, fi)); + } + } +} + +/* Show the frame info. If this is the tui, it will be shown in + the source display otherwise, nothing is done */ +void +show_stack_frame(fi) + struct frame_info *fi; +{ + TUIDO(((TuiOpaqueFuncPtr)tui_vShowFrameInfo, fi)); +} + + /* Read a frame specification in whatever the appropriate format is. Call error() if the specification is in any way invalid (i.e. this function never returns NULL). */ -static struct frame_info * +struct frame_info * parse_frame_specification (frame_exp) char *frame_exp; { @@ -525,7 +738,6 @@ frame_info (addr_exp, from_tty) int from_tty; { struct frame_info *fi; - struct frame_saved_regs fsr; struct symtab_and_line sal; struct symbol *func; struct symtab *s; @@ -549,8 +761,32 @@ frame_info (addr_exp, from_tty) s = find_pc_symtab(fi->pc); if (func) { - funname = SYMBOL_NAME (func); - funlang = SYMBOL_LANGUAGE (func); + /* I'd like to use SYMBOL_SOURCE_NAME() here, to display + * the demangled name that we already have stored in + * the symbol table, but we stored a version with + * DMGL_PARAMS turned on, and here we don't want + * to display parameters. So call the demangler again, + * with DMGL_ANSI only. RT + * (Yes, I know that printf_symbol_filtered() will + * again try to demangle the name on the fly, but + * the issue is that if cplus_demangle() fails here, + * it'll fail there too. So we want to catch the failure + * ("demangled==NULL" case below) here, while we still + * have our hands on the function symbol.) + */ + char * demangled; + funname = SYMBOL_NAME (func); + funlang = SYMBOL_LANGUAGE (func); + if (funlang == language_cplus) + { + demangled = cplus_demangle (funname, DMGL_ANSI); + /* If the demangler fails, try the demangled name + * from the symbol table. This'll have parameters, + * but that's preferable to diplaying a mangled name. + */ + if (demangled == NULL) + funname = SYMBOL_SOURCE_NAME (func); + } } else { @@ -666,36 +902,39 @@ frame_info (addr_exp, from_tty) } } -#if defined (FRAME_FIND_SAVED_REGS) - get_frame_saved_regs (fi, &fsr); - /* The sp is special; what's returned isn't the save address, but - actually the value of the previous frame's sp. */ - printf_filtered (" Previous frame's sp is "); - print_address_numeric (fsr.regs[SP_REGNUM], 1, gdb_stdout); - printf_filtered ("\n"); - count = 0; - numregs = ARCH_NUM_REGS; - for (i = 0; i < numregs; i++) - if (fsr.regs[i] && i != SP_REGNUM) - { - if (count == 0) - puts_filtered (" Saved registers:\n "); - else - puts_filtered (","); - wrap_here (" "); - printf_filtered (" %s at ", REGISTER_NAME (i)); - print_address_numeric (fsr.regs[i], 1, gdb_stdout); - count++; - } - if (count) - puts_filtered ("\n"); -#else /* Have FRAME_FIND_SAVED_REGS. */ - /* We could get some information about saved registers by calling - get_saved_register on each register. Which info goes with which frame - is necessarily lost, however, and I suspect that the users don't care - whether they get the info. */ - puts_filtered ("\n"); -#endif /* Have FRAME_FIND_SAVED_REGS. */ + FRAME_INIT_SAVED_REGS (fi); + if (fi->saved_regs != NULL) + { + /* The sp is special; what's returned isn't the save address, but + actually the value of the previous frame's sp. */ + printf_filtered (" Previous frame's sp is "); + print_address_numeric (fi->saved_regs[SP_REGNUM], 1, gdb_stdout); + printf_filtered ("\n"); + count = 0; + numregs = ARCH_NUM_REGS; + for (i = 0; i < numregs; i++) + if (fi->saved_regs[i] && i != SP_REGNUM) + { + if (count == 0) + puts_filtered (" Saved registers:\n "); + else + puts_filtered (","); + wrap_here (" "); + printf_filtered (" %s at ", REGISTER_NAME (i)); + print_address_numeric (fi->saved_regs[i], 1, gdb_stdout); + count++; + } + if (count) + puts_filtered ("\n"); + } + else + { + /* We could get some information about saved registers by + calling get_saved_register on each register. Which info goes + with which frame is necessarily lost, however, and I suspect + that the users don't care whether they get the info. */ + puts_filtered ("\n"); + } } #if 0 @@ -732,8 +971,9 @@ backtrace_limit_info (arg, from_tty) /* Print briefly all stack frames or just the innermost COUNT frames. */ static void -backtrace_command (count_exp, from_tty) +backtrace_command_1 (count_exp, show_locals, from_tty) char *count_exp; + int show_locals; int from_tty; { struct frame_info *fi; @@ -814,25 +1054,104 @@ backtrace_command (count_exp, from_tty) means further attempts to backtrace would fail (on the other hand, perhaps the code does or could be fixed to make sure the frame->prev field gets set to NULL in that case). */ - print_frame_info (fi, trailing_level + i, 0, 1); + print_frame_info_base (fi, trailing_level + i, 0, 1); + if (show_locals) + print_frame_local_vars(fi, 1, gdb_stdout); } /* If we've stopped before the end, mention that. */ if (fi && from_tty) printf_filtered ("(More stack frames follow...)\n"); } + +static void +backtrace_command (arg, from_tty) + char *arg; + int from_tty; +{ + struct cleanup *old_chain = (struct cleanup *)NULL; + char **argv = (char **)NULL; + int argIndicatingFullTrace = (-1), totArgLen = 0, argc = 0; + char *argPtr = arg; + + if (arg != (char *)NULL) + { + int i; + + argv = buildargv(arg); + old_chain = make_cleanup(freeargv, (char *)argv); + argc = 0; + for (i = 0; (argv[i] != (char *)NULL); i++) + { + int j; + + for (j = 0; (j < strlen(argv[i])); j++) + argv[i][j] = tolower(argv[i][j]); + + if (argIndicatingFullTrace < 0 && subsetCompare(argv[i], "full")) + argIndicatingFullTrace = argc; + else + { + argc++; + totArgLen += strlen(argv[i]); + } + } + totArgLen += argc; + if (argIndicatingFullTrace >= 0) + { + if (totArgLen > 0) + { + argPtr = (char *)xmalloc(totArgLen + 1); + if (!argPtr) + nomem(0); + else + { + memset(argPtr, 0, totArgLen + 1); + for (i = 0; (i < (argc + 1)); i++) + { + if (i != argIndicatingFullTrace) + { + strcat(argPtr, argv[i]); + strcat(argPtr, " "); + } + } + } + } + else + argPtr = (char *)NULL; + } + } + + backtrace_command_1 (argPtr, (argIndicatingFullTrace >= 0), from_tty); + + if (argIndicatingFullTrace >= 0 && totArgLen > 0) + free(argPtr); + + if (old_chain) + do_cleanups(old_chain); +} + +static void +backtrace_full_command (arg, from_tty) + char *arg; + int from_tty; +{ + backtrace_command_1 (arg, 1, from_tty); +} + /* Print the local variables of a block B active in FRAME. Return 1 if any variables were printed; 0 otherwise. */ static int -print_block_frame_locals (b, fi, stream) +print_block_frame_locals (b, fi, num_tabs, stream) struct block *b; register struct frame_info *fi; + int num_tabs; register GDB_FILE *stream; { int nsyms; - register int i; + register int i, j; register struct symbol *sym; register int values_printed = 0; @@ -848,6 +1167,8 @@ print_block_frame_locals (b, fi, stream) case LOC_STATIC: case LOC_BASEREG: values_printed = 1; + for (j = 0; j < num_tabs; j++) + fputs_filtered("\t", stream); fputs_filtered (SYMBOL_SOURCE_NAME (sym), stream); fputs_filtered (" = ", stream); print_variable_value (sym, fi, stream); @@ -913,8 +1234,9 @@ print_block_frame_labels (b, have_default, stream) on the function running in FRAME. */ static void -print_frame_local_vars (fi, stream) +print_frame_local_vars (fi, num_tabs, stream) register struct frame_info *fi; + register int num_tabs; register GDB_FILE *stream; { register struct block *block = get_frame_block (fi); @@ -928,7 +1250,7 @@ print_frame_local_vars (fi, stream) while (block != 0) { - if (print_block_frame_locals (block, fi, stream)) + if (print_block_frame_locals (block, fi, num_tabs, stream)) values_printed = 1; /* After handling the function's top-level block, stop. Don't continue to its superblock, the block of @@ -1024,7 +1346,7 @@ locals_info (args, from_tty) { if (!selected_frame) error ("No frame selected."); - print_frame_local_vars (selected_frame, gdb_stdout); + print_frame_local_vars (selected_frame, 0, gdb_stdout); } static void @@ -1032,9 +1354,29 @@ catch_info (ignore, from_tty) char *ignore; int from_tty; { + struct symtab_and_line * sal; + + /* Check for target support for exception handling */ + sal = target_enable_exception_callback (EX_EVENT_CATCH, 1); + if (sal) + { + /* Currently not handling this */ + /* Ideally, here we should interact with the C++ runtime + system to find the list of active handlers, etc. */ + fprintf_filtered (gdb_stdout, "Info catch not supported with this target/compiler combination.\n"); +#if 0 if (!selected_frame) error ("No frame selected."); - print_frame_label_vars (selected_frame, 0, gdb_stdout); +#endif + } + else + { + /* Assume g++ compiled code -- old v 4.16 behaviour */ + if (!selected_frame) + error ("No frame selected."); + + print_frame_label_vars (selected_frame, 0, gdb_stdout); + } } static void @@ -1112,6 +1454,16 @@ args_info (ignore, from_tty) print_frame_arg_vars (selected_frame, gdb_stdout); } + +static void +args_plus_locals_info(ignore, from_tty) + char *ignore; + int from_tty; +{ + args_info(ignore, from_tty); + locals_info(ignore, from_tty); +} + /* Select frame FI, and note that its stack level is LEVEL. LEVEL may be -1 if an actual level number is not known. */ @@ -1139,9 +1491,48 @@ select_frame (fi, level) && language_mode == language_mode_auto) { set_language(s->language); } + /* elz: this if here fixes the problem with the pc not being displayed + in the tui asm layout, with no debug symbols. The value of s + would be 0 here, and select_source_symtab would abort the + command by calling the 'error' function*/ + if (s) + { + TUIDO(((TuiOpaqueFuncPtr)tui_vSelectSourceSymtab, s)); + } } } + +/* Select frame FI, noting that its stack level is LEVEL. Also print + the stack frame and show the source if this is the tui version. */ +void +select_and_print_frame(fi, level) + struct frame_info *fi; + int level; +{ + select_frame(fi, level); + if (fi) + { + print_stack_frame(fi, level, 1); + TUIDO(((TuiOpaqueFuncPtr)tui_vCheckDataValues, fi)); + } +} + + +/* Select frame FI, noting that its stack level is LEVEL. Be silent if + not the TUI */ +void +select_and_maybe_print_frame(fi, level) + struct frame_info *fi; + int level; +{ + if (!tui_version) + select_frame(fi, level); + else + select_and_print_frame(fi, level); +} + + /* Store the selected frame and its level into *FRAMEP and *LEVELP. If there is no selected frame, *FRAMEP is set to NULL. */ @@ -1249,23 +1640,34 @@ select_frame_command (level_exp, from_tty) With arg, behaves like select_frame and then prints the selected frame. */ -static void +void frame_command (level_exp, from_tty) char *level_exp; int from_tty; { select_frame_command (level_exp, from_tty); - print_stack_frame (selected_frame, selected_frame_level, 1); + show_and_print_stack_frame (selected_frame, selected_frame_level, 1); } +/* The XDB Compatibility command to print the current frame. */ + +void +current_frame_command (level_exp, from_tty) + char *level_exp; + int from_tty; +{ + if (target_has_stack == 0 || selected_frame == 0) + error ("No stack."); + print_only_stack_frame (selected_frame, selected_frame_level, 1); + } + /* Select the frame up one or COUNT stack levels from the previously selected frame, and print it briefly. */ /* ARGSUSED */ static void -up_silently_command (count_exp, from_tty) +up_silently_base (count_exp) char *count_exp; - int from_tty; { register struct frame_info *fi; int count = 1, count1; @@ -1283,12 +1685,22 @@ up_silently_command (count_exp, from_tty) } static void +up_silently_command (count_exp, from_tty) + char *count_exp; + int from_tty; +{ + up_silently_base(count_exp); + if (tui_version) + print_stack_frame (selected_frame, selected_frame_level, 1); +} + +static void up_command (count_exp, from_tty) char *count_exp; int from_tty; { - up_silently_command (count_exp, from_tty); - print_stack_frame (selected_frame, selected_frame_level, 1); + up_silently_base (count_exp); + show_and_print_stack_frame (selected_frame, selected_frame_level, 1); } /* Select the frame down one or COUNT stack levels @@ -1296,9 +1708,8 @@ up_command (count_exp, from_tty) /* ARGSUSED */ static void -down_silently_command (count_exp, from_tty) +down_silently_base (count_exp) char *count_exp; - int from_tty; { register struct frame_info *frame; int count = -1, count1; @@ -1324,14 +1735,24 @@ down_silently_command (count_exp, from_tty) select_frame (frame, selected_frame_level + count - count1); } +/* ARGSUSED */ +static void +down_silently_command (count_exp, from_tty) + char *count_exp; + int from_tty; +{ + down_silently_base (count_exp); + if (tui_version) + print_stack_frame (selected_frame, selected_frame_level, 1); +} static void down_command (count_exp, from_tty) char *count_exp; int from_tty; { - down_silently_command (count_exp, from_tty); - print_stack_frame (selected_frame, selected_frame_level, 1); + down_silently_base (count_exp); + show_and_print_stack_frame (selected_frame, selected_frame_level, 1); } static void @@ -1416,6 +1837,66 @@ return_command (retval_exp, from_tty) select_frame_command ("0", 0); } +/* Sets the scope to input function name, provided that the + function is within the current stack frame */ +typedef struct function_bounds +{ + CORE_ADDR low, high; +}; + +static void +func_command (arg, from_tty) + char *arg; + int from_tty; +{ + if (arg != (char *)NULL) + { + struct frame_info *fp; + int found = 0; + struct symtabs_and_lines sals; + int i; + int level = 1; + struct function_bounds *funcBounds = (struct function_bounds *)NULL; + + fp = parse_frame_specification("0"); + sals = decode_line_spec(arg, 1); + funcBounds = (struct function_bounds *)xmalloc( + sizeof(struct function_bounds) * sals.nelts); + for (i = 0; (i < sals.nelts && !found); i++) + { + if (sals.sals[i].pc == (CORE_ADDR)0 || + find_pc_partial_function(sals.sals[i].pc, + (char **)NULL, + &funcBounds[i].low, + &funcBounds[i].high) == 0) + { + funcBounds[i].low = + funcBounds[i].high = (CORE_ADDR)NULL; + } + } + + do + { + for (i = 0; (i < sals.nelts && !found); i++) + found = (fp->pc >= funcBounds[i].low && + fp->pc < funcBounds[i].high); + if (!found) + { + level = 1; + fp = find_relative_frame(fp, &level); + } + } while (!found && level == 0); + + if (funcBounds) + free(funcBounds); + + if (!found) + printf_filtered("'%s' not within current stack frame.\n", arg); + else if (fp != selected_frame) /* set the stack frame here! */ + select_and_print_frame(fp); + } +} + /* Gets the language of the current frame. */ enum language @@ -1477,6 +1958,12 @@ a command file or a user-defined command."); add_com_alias ("f", "frame", class_stack, 1); + if (xdb_commands) + { + add_com("L", class_stack, current_frame_command, + "Print the current stack frame.\n"); + add_com_alias ("V", "frame", class_stack, 1); + } add_com ("select-frame", class_stack, select_frame_command, "Select a stack frame without printing anything.\n\ An argument specifies the frame to select.\n\ @@ -1484,8 +1971,19 @@ It can be a stack frame number or the address of the frame.\n"); add_com ("backtrace", class_stack, backtrace_command, "Print backtrace of all stack frames, or innermost COUNT frames.\n\ -With a negative argument, print outermost -COUNT frames."); +With a negative argument, print outermost -COUNT frames.\n\ +Use of the 'full' qualifier also prints the values of the local variables.\n"); add_com_alias ("bt", "backtrace", class_stack, 0); + if (xdb_commands) + { + add_com_alias ("t", "backtrace", class_stack, 0); + add_com ("T", class_stack, backtrace_full_command, + "Print backtrace of all stack frames, or innermost COUNT frames \n\ +and the values of the local variables.\n\ +With a negative argument, print outermost -COUNT frames.\n\ +Usage: T <count>\n"); + } + add_com_alias ("where", "backtrace", class_alias, 0); add_info ("stack", backtrace_command, "Backtrace of the stack, or innermost COUNT frames."); @@ -1497,6 +1995,14 @@ With a negative argument, print outermost -COUNT frames."); "Local variables of current stack frame."); add_info ("args", args_info, "Argument variables of current stack frame."); + if (xdb_commands) + add_com("l", class_info, args_plus_locals_info, + "Argument and local variables of current stack frame."); + + if (dbx_commands) + add_com("func", class_stack, func_command, + "Select the stack frame that contains <func>.\nUsage: func <name>\n"); + add_info ("catch", catch_info, "Exceptions that can be caught in the current stack frame."); diff --git a/gdb/symfile.c b/gdb/symfile.c index 6b00ed9..ca864dd 100644 --- a/gdb/symfile.c +++ b/gdb/symfile.c @@ -1,5 +1,5 @@ /* Generic symbol file reading for the GNU debugger, GDB. - Copyright 1990, 1991, 1992, 1993, 1994, 1995, 1996 + Copyright 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1998 Free Software Foundation, Inc. Contributed by Cygnus Support, using pieces from other GDB modules. @@ -52,6 +52,20 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #define O_BINARY 0 #endif +#ifdef HPUXHPPA +extern int hpread_pxdb_check PARAMS ((bfd *, char *)); + +/* Some HP-UX related globals to clear when a new "main" + symbol file is loaded. HP-specific. */ + +extern int hp_som_som_object_present; +extern int hp_cxx_exception_support_initialized; +#define RESET_HP_UX_GLOBALS() do {\ + hp_som_som_object_present = 0; /* indicates HP-compiled code */ \ + hp_cxx_exception_support_initialized = 0; /* must reinitialize exception stuff */ \ + } while (0) +#endif + int (*ui_load_progress_hook) PARAMS ((char *, unsigned long)); void (*pre_add_symbol_hook) PARAMS ((char *)); void (*post_add_symbol_hook) PARAMS ((void)); @@ -95,12 +109,42 @@ static int compare_psymbols PARAMS ((const void *, const void *)); static int compare_symbols PARAMS ((const void *, const void *)); -static bfd *symfile_bfd_open PARAMS ((char *)); +bfd *symfile_bfd_open PARAMS ((char *)); static void find_sym_fns PARAMS ((struct objfile *)); static void decrement_reading_symtab PARAMS ((void *)); +static void overlay_invalidate_all PARAMS ((void)); + +static int overlay_is_mapped PARAMS ((struct obj_section *)); + +void list_overlays_command PARAMS ((char *, int)); + +void map_overlay_command PARAMS ((char *, int)); + +void unmap_overlay_command PARAMS ((char *, int)); + +static void overlay_auto_command PARAMS ((char *, int)); + +static void overlay_manual_command PARAMS ((char *, int)); + +static void overlay_off_command PARAMS ((char *, int)); + +static void overlay_load_command PARAMS ((char *, int)); + +static void overlay_command PARAMS ((char *, int)); + +static void simple_free_overlay_table PARAMS ((void)); + +static void read_target_long_array PARAMS ((CORE_ADDR, unsigned int *, int)); + +static int simple_read_overlay_table PARAMS ((void)); + +static int simple_overlay_update_1 PARAMS ((struct obj_section *)); + +void _initialize_symfile PARAMS ((void)); + /* List of all available sym_fns. On gdb startup, each object file reader calls add_symtab_fns() to register information on each format it is prepared to read. */ @@ -116,15 +160,25 @@ int symbol_reloading = SYMBOL_RELOADING_DEFAULT; int symbol_reloading = 0; #endif -/* If true, then shared library symbols will be added automatically - when the inferior is created, new libraries are loaded, or when - attaching to the inferior. This is almost always what users +/* If non-zero, then on HP-UX (i.e., platforms that use somsolib.c), + this variable is interpreted as a threshhold. If adding a new + library's symbol table to those already known to the debugger would + exceed this threshhold, then the shlib's symbols are not added. + + If non-zero on other platforms, shared library symbols will be added + automatically when the inferior is created, new libraries are loaded, + or when attaching to the inferior. This is almost always what users will want to have happen; but for very large programs, the startup time will be excessive, and so if this is a problem, the user can clear this flag and then add the shared library symbols as needed. Note that there is a potential for confusion, since if the shared library symbols are not loaded, commands like "info fun" will *not* - report all the functions that are actually present. */ + report all the functions that are actually present. + + Note that HP-UX interprets this variable to mean, "threshhold size + in megabytes, where zero means never add". Other platforms interpret + this variable to mean, "always add if non-zero, never add if zero." + */ int auto_solib_add = 1; @@ -186,7 +240,19 @@ compare_psymbols (s1p, s2p) } else { - return (STRCMP (st1 + 2, st2 + 2)); + /* Note: I replaced the STRCMP line (commented out below) + * with a simpler "strcmp()" which compares the 2 strings + * from the beginning. (STRCMP is a macro which first compares + * the initial characters, then falls back on strcmp). + * The reason is that the STRCMP line was tickling a C compiler + * bug on HP-UX 10.30, which is avoided with the simpler + * code. The performance gain from the more complicated code + * is negligible, given that we have already checked the + * initial 2 characters above. I reported the compiler bug, + * and once it is fixed the original line can be put back. RT + */ + /* return ( STRCMP (st1 + 2, st2 + 2)); */ + return ( strcmp (st1, st2)); } } @@ -431,13 +497,13 @@ syms_from_objfile (objfile, addr, mainline, verbo) /* Make sure that partially constructed symbol tables will be cleaned up if an error occurs during symbol reading. */ - old_chain = make_cleanup (free_objfile, objfile); + old_chain = make_cleanup ((make_cleanup_func) free_objfile, objfile); if (mainline) { /* We will modify the main symbol table, make sure that all its users will be cleaned up if an error occurs during symbol reading. */ - make_cleanup (clear_symtab_users, 0); + make_cleanup ((make_cleanup_func) clear_symtab_users, 0); /* Since no error yet, throw away the old symbol table. */ @@ -604,17 +670,25 @@ new_symfile_objfile (objfile, mainline, verbo) as dynamically loaded code. If !mainline, ADDR is the address where the text segment was loaded. + USER_LOADED is TRUE if the add-symbol-file command was how this + symbol file came to be processed. + + IS_SOLIB is TRUE if this symbol file represents a solib, as discovered + by the target's implementation of the solib package. + Upon success, returns a pointer to the objfile that was added. Upon failure, jumps back to command level (never returns). */ struct objfile * -symbol_file_add (name, from_tty, addr, mainline, mapped, readnow) +symbol_file_add (name, from_tty, addr, mainline, mapped, readnow, user_loaded, is_solib) char *name; int from_tty; CORE_ADDR addr; int mainline; int mapped; int readnow; + int user_loaded; + int is_solib; { struct objfile *objfile; struct partial_symtab *psymtab; @@ -625,13 +699,59 @@ symbol_file_add (name, from_tty, addr, mainline, mapped, readnow) abfd = symfile_bfd_open (name); +#if 0 +#ifdef GDB_TARGET_IS_HPPA +{ + /********* TARGET SPECIFIC CODE (HACK!) ********** + * + * The following code is HP-specific. The "right" way of + * doing this is unknown, but we bet would involve a target- + * specific pre-file-load check using a generic mechanism. + */ + /* elz: I moved this code from inside the symfile_bfd_open function + this way we don't have to worry about recursion. + I also have taken out the call to pxdb from within the check + for pxdb processing. Seemed two logically unrelated things, + and I needed a pure checking later on anyway. + */ + + if( hpread_pxdb_needed( abfd )) + { + /* + * This file has not been pre-processed. + * Preprocess now + */ + + if (hpread_call_pxdb(name)) + { + /* The + * call above has used "system" to pre-process + * the on-disk file, so we now need to close + * and re-open the file. + */ + bfd_close (abfd); /* This also closes 'desc', */ + /* hence the recursion. */ + abfd = symfile_bfd_open( name ); + } + else /* we fail miserably without pxdb!!!*/ + /* the warning to the user has already been given + from within the call_pxdb function + */ + error("Command ignored.\n"); + } +} + /*************** END HACK ********************/ +#endif +#endif /* 0 */ + + if ((have_full_symbols () || have_partial_symbols ()) && mainline && from_tty && !query ("Load new symbol table from \"%s\"? ", name)) error ("Not confirmed."); - objfile = allocate_objfile (abfd, mapped); + objfile = allocate_objfile (abfd, mapped, user_loaded, is_solib); /* If the objfile uses a mapped symbol file, and we have a psymtab for it, then skip reading any symbols at this time. */ @@ -658,17 +778,17 @@ symbol_file_add (name, from_tty, addr, mainline, mapped, readnow) performed, or need to read an unmapped symbol table. */ if (from_tty || info_verbose) { - if (pre_add_symbol_hook) - pre_add_symbol_hook (name); - else - { - printf_filtered ("Reading symbols from %s...", name); - wrap_here (""); - gdb_flush (gdb_stdout); - } + if (pre_add_symbol_hook) + pre_add_symbol_hook (name); + else + { + printf_filtered ("Reading symbols from %s...", name); + wrap_here (""); + gdb_flush (gdb_stdout); + } } syms_from_objfile (objfile, addr, mainline, from_tty); - } + } /* We now have at least a partial symbol table. Check to see if the user requested that all symbols be read on initial access via either @@ -742,11 +862,23 @@ symbol_file_command (args, from_tty) symfile_objfile -> name)) error ("Not confirmed."); free_all_objfiles (); + + /* solib descriptors may have handles to objfiles. Since their + storage has just been released, we'd better wipe the solib + descriptors as well. + */ +#if defined(SOLIB_RESTART) + SOLIB_RESTART (); +#endif + symfile_objfile = NULL; if (from_tty) { printf_unfiltered ("No symbol file now.\n"); } +#ifdef HPUXHPPA + RESET_HP_UX_GLOBALS (); +#endif } else { @@ -754,7 +886,7 @@ symbol_file_command (args, from_tty) { nomem (0); } - cleanups = make_cleanup (freeargv, (char *) argv); + cleanups = make_cleanup ((make_cleanup_func) freeargv, (char *) argv); while (*argv != NULL) { if (STREQ (*argv, "-mapped")) @@ -786,11 +918,16 @@ symbol_file_command (args, from_tty) if (text_relocation == (CORE_ADDR)0) return; else if (text_relocation == (CORE_ADDR)-1) - symbol_file_add (name, from_tty, (CORE_ADDR)0, 1, mapped, - readnow); + { + symbol_file_add (name, from_tty, (CORE_ADDR)0, + 1, mapped, readnow, 1, 0); +#ifdef HPUXHPPA + RESET_HP_UX_GLOBALS (); +#endif + } else symbol_file_add (name, from_tty, (CORE_ADDR)text_relocation, - 0, mapped, readnow); + 0, mapped, readnow, 1, 0); /* Getting new symbols may change our opinion about what is frameless. */ @@ -805,6 +942,7 @@ symbol_file_command (args, from_tty) { error ("no symbol file name was specified"); } + TUIDO(((TuiOpaqueFuncPtr)tuiDisplayMainFunction)); do_cleanups (cleanups); } } @@ -847,7 +985,7 @@ set_initial_language () malloc'd` copy of NAME (tilde-expanded and made absolute). In case of trouble, error() is called. */ -static bfd * +bfd * symfile_bfd_open (name) char *name; { @@ -855,6 +993,8 @@ symfile_bfd_open (name) int desc; char *absolute_name; + + name = tilde_expand (name); /* Returns 1st new malloc'd copy */ /* Look down path for it, allocate 2nd new malloc'd copy. */ @@ -898,6 +1038,42 @@ symfile_bfd_open (name) bfd_errmsg (bfd_get_error ())); } +#ifdef HPUXHPPA + { + /********* TARGET SPECIFIC CODE (HACK!) ********** + * + * The following code is HP-specific. The "right" way of + * doing this is unknown, but we bet would involve a target- + * specific pre-file-load check using a generic mechanism. + */ + static prevent_recursion = 0; /* Needed for hack below! HP-specific */ + + if( prevent_recursion == 0 ) { + if( hpread_pxdb_needed (sym_bfd, name)) { + /* + * This file has not been pre-processed. + * Preprocess now + */ + + if (hpread_call_pxdb(name)) + { + /* The + * call above has used "system" to pre-process + * the on-disk file, so we now need to close + * and re-open the file. + */ + bfd_close (sym_bfd); /* This also closes 'desc', */ + /* hence the recursion. */ + prevent_recursion++; + sym_bfd = symfile_bfd_open( name ); + prevent_recursion--; + } + } + } +} + /*************** END HACK ********************/ +#endif + return (sym_bfd); } @@ -1004,7 +1180,7 @@ generic_load (filename, from_tty) /* FIXME: should be checking for errors from bfd_close (for one thing, on error it does not free all the storage associated with the bfd). */ - old_cleanups = make_cleanup (bfd_close, loadfile_bfd); + old_cleanups = make_cleanup ((make_cleanup_func) bfd_close, loadfile_bfd); if (!bfd_check_format (loadfile_bfd, bfd_object)) { @@ -1210,7 +1386,9 @@ add_symbol_file_command (args, from_tty) name, local_hex_string ((unsigned long)text_addr)))) error ("Not confirmed."); - symbol_file_add (name, from_tty, text_addr, 0, mapped, readnow); + symbol_file_add (name, from_tty, text_addr, 0, mapped, readnow, + 1, /* user_loaded */ + 0); /* We'll guess it's ! is_solib */ /* Getting new symbols may change our opinion about what is frameless. */ @@ -1284,9 +1462,10 @@ reread_symbols () /* If we get an error, blow away this objfile (not sure if that is the correct response for things like shared libraries). */ - old_cleanups = make_cleanup (free_objfile, objfile); + old_cleanups = make_cleanup ((make_cleanup_func) free_objfile, + objfile); /* We need to do this whenever any symbols go away. */ - make_cleanup (clear_symtab_users, 0); + make_cleanup ((make_cleanup_func) clear_symtab_users, 0); /* Clean up any state BFD has sitting around. We don't need to close the descriptor but BFD lacks a way of closing the @@ -1375,7 +1554,12 @@ reread_symbols () distinguishing between the main file and additional files in this way seems rather dubious. */ if (objfile == symfile_objfile) - (*objfile->sf->sym_new_init) (objfile); + { + (*objfile->sf->sym_new_init) (objfile); +#ifdef HPUXHPPA + RESET_HP_UX_GLOBALS (); +#endif + } (*objfile->sf->sym_init) (objfile); clear_complaints (1, 1); @@ -1423,35 +1607,152 @@ reread_symbols () } + +typedef struct { + char *ext; + enum language lang; +} filename_language; + +static filename_language * filename_language_table; +static int fl_table_size, fl_table_next; + +static void +add_filename_language (ext, lang) + char *ext; + enum language lang; +{ + if (fl_table_next >= fl_table_size) + { + fl_table_size += 10; + filename_language_table = realloc (filename_language_table, + fl_table_size); + } + + filename_language_table[fl_table_next].ext = strsave (ext); + filename_language_table[fl_table_next].lang = lang; + fl_table_next++; +} + +static char *ext_args; + +static void +set_ext_lang_command (args, from_tty) + char *args; + int from_tty; +{ + int i; + char *cp = ext_args; + enum language lang; + + /* First arg is filename extension, starting with '.' */ + if (*cp != '.') + error ("'%s': Filename extension must begin with '.'", ext_args); + + /* Find end of first arg. */ + while (*cp && !isspace (*cp)) + cp++; + + if (*cp == '\0') + error ("'%s': two arguments required -- filename extension and language", + ext_args); + + /* Null-terminate first arg */ + *cp++ = '\0'; + + /* Find beginning of second arg, which should be a source language. */ + while (*cp && isspace (*cp)) + cp++; + + if (*cp == '\0') + error ("'%s': two arguments required -- filename extension and language", + ext_args); + + /* Lookup the language from among those we know. */ + lang = language_enum (cp); + + /* Now lookup the filename extension: do we already know it? */ + for (i = 0; i < fl_table_next; i++) + if (0 == strcmp (ext_args, filename_language_table[i].ext)) + break; + + if (i >= fl_table_next) + { + /* new file extension */ + add_filename_language (ext_args, lang); + } + else + { + /* redefining a previously known filename extension */ + + /* if (from_tty) */ + /* query ("Really make files of type %s '%s'?", */ + /* ext_args, language_str (lang)); */ + + free (filename_language_table[i].ext); + filename_language_table[i].ext = strsave (ext_args); + filename_language_table[i].lang = lang; + } +} + +static void +info_ext_lang_command (args, from_tty) + char *args; + int from_tty; +{ + int i; + + printf_filtered ("Filename extensions and the languages they represent:"); + printf_filtered ("\n\n"); + for (i = 0; i < fl_table_next; i++) + printf_filtered ("\t%s\t- %s\n", + filename_language_table[i].ext, + language_str (filename_language_table[i].lang)); +} + +static void +init_filename_language_table () +{ + if (fl_table_size == 0) /* protect against repetition */ + { + fl_table_size = 20; + fl_table_next = 0; + filename_language_table = + xmalloc (fl_table_size * sizeof (*filename_language_table)); + add_filename_language (".c", language_c); + add_filename_language (".C", language_cplus); + add_filename_language (".cc", language_cplus); + add_filename_language (".cp", language_cplus); + add_filename_language (".cpp", language_cplus); + add_filename_language (".cxx", language_cplus); + add_filename_language (".c++", language_cplus); + /* start-sanitize-java */ + add_filename_language (".java", language_java); + add_filename_language (".class", language_java); + /* end-sanitize-java */ + add_filename_language (".ch", language_chill); + add_filename_language (".c186", language_chill); + add_filename_language (".c286", language_chill); + add_filename_language (".f", language_fortran); + add_filename_language (".F", language_fortran); + add_filename_language (".s", language_asm); + add_filename_language (".S", language_asm); + } +} + enum language deduce_language_from_filename (filename) char *filename; { - char *c; - - if (0 == filename) - ; /* Get default */ - else if (0 == (c = strrchr (filename, '.'))) - ; /* Get default. */ - else if (STREQ (c, ".c")) - return language_c; - else if (STREQ (c, ".cc") || STREQ (c, ".C") || STREQ (c, ".cxx") - || STREQ (c, ".cpp") || STREQ (c, ".cp") || STREQ (c, ".c++")) - return language_cplus; - /* start-sanitize-java */ - else if (STREQ (c, ".java") || STREQ (c, ".class")) - return language_java; - /* end-sanitize-java */ - else if (STREQ (c, ".ch") || STREQ (c, ".c186") || STREQ (c, ".c286")) - return language_chill; - else if (STREQ (c, ".f") || STREQ (c, ".F")) - return language_fortran; - else if (STREQ (c, ".mod")) - return language_m2; - else if (STREQ (c, ".s") || STREQ (c, ".S")) - return language_asm; + int i; + char *cp; + + if (filename != NULL) + if ((cp = strrchr (filename, '.')) != NULL) + for (i = 0; i < fl_table_next; i++) + if (strcmp (cp, filename_language_table[i].ext) == 0) + return filename_language_table[i].lang; - return language_unknown; /* default */ + return language_unknown; } /* allocate_symtab: @@ -1874,6 +2175,83 @@ add_psymbol_to_list (name, namelength, namespace, class, list, val, coreaddr, OBJSTAT (objfile, n_psyms++); } +/* Add a symbol with a long value to a psymtab. This differs from + * add_psymbol_to_list above in taking both a mangled and a demangled + * name. */ + +void +add_psymbol_with_dem_name_to_list (name, namelength, dem_name, dem_namelength, + namespace, class, list, val, coreaddr, language, objfile) + char *name; + int namelength; + char *dem_name; + int dem_namelength; + namespace_enum namespace; + enum address_class class; + struct psymbol_allocation_list *list; + long val; /* Value as a long */ + CORE_ADDR coreaddr; /* Value as a CORE_ADDR */ + enum language language; + struct objfile *objfile; +{ + register struct partial_symbol *psym; + char *buf = alloca (namelength + 1); + /* psymbol is static so that there will be no uninitialized gaps in the + structure which might contain random data, causing cache misses in + bcache. */ + static struct partial_symbol psymbol; + + /* Create local copy of the partial symbol */ + + memcpy (buf, name, namelength); + buf[namelength] = '\0'; + SYMBOL_NAME (&psymbol) = bcache (buf, namelength + 1, &objfile->psymbol_cache); + + buf = alloca (dem_namelength + 1); + memcpy (buf, dem_name, dem_namelength); + buf[dem_namelength] = '\0'; + + switch (language) + { + case language_c: + case language_cplus: + SYMBOL_CPLUS_DEMANGLED_NAME (&psymbol) = + bcache (buf, dem_namelength + 1, &objfile->psymbol_cache); + break; + case language_chill: + SYMBOL_CHILL_DEMANGLED_NAME (&psymbol) = + bcache (buf, dem_namelength + 1, &objfile->psymbol_cache); + + /* FIXME What should be done for the default case? Ignoring for now. */ + } + + /* val and coreaddr are mutually exclusive, one of them *will* be zero */ + if (val != 0) + { + SYMBOL_VALUE (&psymbol) = val; + } + else + { + SYMBOL_VALUE_ADDRESS (&psymbol) = coreaddr; + } + SYMBOL_SECTION (&psymbol) = 0; + SYMBOL_LANGUAGE (&psymbol) = language; + PSYMBOL_NAMESPACE (&psymbol) = namespace; + PSYMBOL_CLASS (&psymbol) = class; + SYMBOL_INIT_LANGUAGE_SPECIFIC (&psymbol, language); + + /* Stash the partial symbol away in the cache */ + psym = bcache (&psymbol, sizeof (struct partial_symbol), &objfile->psymbol_cache); + + /* Save pointer to partial symbol in psymtab, growing symtab if needed. */ + if (list->next >= list->list + list->size) + { + extend_psymbol_list (list, objfile); + } + *list->next++ = psym; + OBJSTAT (objfile, n_psyms++); +} + /* Initialize storage for partial symbols. */ void @@ -2240,10 +2618,17 @@ list_overlays_command (args, from_tty) lma = bfd_section_lma (objfile->obfd, osect->the_bfd_section); size = bfd_get_section_size_before_reloc (osect->the_bfd_section); name = bfd_section_name (objfile->obfd, osect->the_bfd_section); - printf_filtered ("Section %s, loaded at %08x - %08x, ", - name, lma, lma + size); - printf_filtered ("mapped at %08x - %08x\n", - vma, vma + size); + + printf_filtered ("Section %s, loaded at ", name); + print_address_numeric (lma, 1, stdout); + puts_filtered (" - "); + print_address_numeric (lma + size, 1, stdout); + printf_filtered (", mapped at "); + print_address_numeric (vma, 1, stdout); + puts_filtered (" - "); + print_address_numeric (vma + size, 1, stdout); + puts_filtered ("\n"); + nmapped ++; } if (nmapped == 0) @@ -2367,7 +2752,7 @@ overlay_off_command (args, from_tty) char *args; int from_tty; { - overlay_debugging = 0; + overlay_debugging = 0; if (info_verbose) printf_filtered ("Overlay debugging disabled."); } @@ -2707,4 +3092,16 @@ for access from GDB.", &cmdlist); "Enable automatic overlay debugging.", &overlaylist); add_cmd ("load-target", class_support, overlay_load_command, "Read the overlay mapping state from the target.", &overlaylist); + + /* Filename extension to source language lookup table: */ + init_filename_language_table (); + c = add_set_cmd ("extension-language", class_files, var_string_noescape, + (char *) &ext_args, + "Set mapping between filename extension and source language.\n\ +Usage: set extension-language .foo bar", + &setlist); + c->function.cfunc = set_ext_lang_command; + + add_info ("extensions", info_ext_lang_command, + "All filename extensions associated with a source language."); } diff --git a/gdb/symmisc.c b/gdb/symmisc.c index b008e9e..db13aa8 100644 --- a/gdb/symmisc.c +++ b/gdb/symmisc.c @@ -1,5 +1,5 @@ /* Do various things to symbol tables (other than lookup), for GDB. - Copyright 1986, 1987, 1989, 1991, 1992, 1993, 1994, 1995, 1996 + Copyright 1986, 87, 89, 91, 92, 93, 94, 95, 96, 1998 Free Software Foundation, Inc. This file is part of GDB. @@ -48,23 +48,26 @@ FILE *std_err; /* Prototypes for local functions */ -static void -dump_symtab PARAMS ((struct objfile *, struct symtab *, GDB_FILE *)); +static void dump_symtab PARAMS ((struct objfile *, struct symtab *, + GDB_FILE *)); -static void -dump_psymtab PARAMS ((struct objfile *, struct partial_symtab *, GDB_FILE *)); +static void dump_psymtab PARAMS ((struct objfile *, struct partial_symtab *, + GDB_FILE *)); -static void -dump_msymbols PARAMS ((struct objfile *, GDB_FILE *)); +static void dump_msymbols PARAMS ((struct objfile *, GDB_FILE *)); -static void -dump_objfile PARAMS ((struct objfile *)); +static void dump_objfile PARAMS ((struct objfile *)); -static int -block_depth PARAMS ((struct block *)); +static int block_depth PARAMS ((struct block *)); -static void -print_partial_symbols PARAMS ((struct partial_symbol **, int, char *, GDB_FILE *)); +static void print_partial_symbols PARAMS ((struct partial_symbol **, int, + char *, GDB_FILE *)); + +static int print_symbol PARAMS ((char *)); + +static void free_symtab_block PARAMS ((struct objfile *, struct block *)); + +void _initialize_symmisc PARAMS ((void)); struct print_symbol_args { struct symbol *symbol; @@ -148,6 +151,8 @@ free_symtab (s) mfree (s -> objfile -> md, (PTR) s -> line_charpos); if (s -> fullname != NULL) mfree (s -> objfile -> md, s -> fullname); + if (s -> debugformat != NULL) + mfree (s -> objfile -> md, s -> debugformat); mfree (s -> objfile -> md, (PTR) s); } @@ -180,22 +185,24 @@ print_objfile_statistics () printf_filtered (" Number of \"stab\" symbols read: %d\n", OBJSTAT (objfile, n_stabs)); if (OBJSTAT (objfile, n_minsyms) > 0) - printf_filtered (" Number of \"minimal symbols read: %d\n", + printf_filtered (" Number of \"minimal\" symbols read: %d\n", OBJSTAT (objfile, n_minsyms)); if (OBJSTAT (objfile, n_psyms) > 0) - printf_filtered (" Number of \"partial symbols read: %d\n", + printf_filtered (" Number of \"partial\" symbols read: %d\n", OBJSTAT (objfile, n_psyms)); if (OBJSTAT (objfile, n_syms) > 0) - printf_filtered (" Number of \"full symbols read: %d\n", + printf_filtered (" Number of \"full\" symbols read: %d\n", OBJSTAT (objfile, n_syms)); if (OBJSTAT (objfile, n_types) > 0) - printf_filtered (" Number of \"types defined: %d\n", + printf_filtered (" Number of \"types\" defined: %d\n", OBJSTAT (objfile, n_types)); if (OBJSTAT (objfile, sz_strtab) > 0) printf_filtered (" Space used by a.out string tables: %d\n", OBJSTAT (objfile, sz_strtab)); printf_filtered (" Total memory used for psymbol obstack: %d\n", obstack_memory_used (&objfile -> psymbol_obstack)); + printf_filtered (" Total memory used for psymbol cache: %d\n", + obstack_memory_used (&objfile -> psymbol_cache.cache)); printf_filtered (" Total memory used for symbol obstack: %d\n", obstack_memory_used (&objfile -> symbol_obstack)); printf_filtered (" Total memory used for type obstack: %d\n", @@ -312,8 +319,13 @@ dump_msymbols (objfile, outfile) ms_type = '?'; break; } - fprintf_filtered (outfile, "[%2d] %c %#10lx %s", index, ms_type, - SYMBOL_VALUE_ADDRESS (msymbol), SYMBOL_NAME (msymbol)); + fprintf_filtered (outfile, "[%2d] %c ", index, ms_type); + print_address_numeric (SYMBOL_VALUE_ADDRESS (msymbol), 1, outfile); + fprintf_filtered (outfile, " %s", SYMBOL_NAME (msymbol)); + if (SYMBOL_BFD_SECTION (msymbol)) + fprintf_filtered (outfile, " section %s", + bfd_section_name (objfile->obfd, + SYMBOL_BFD_SECTION (msymbol))); if (SYMBOL_DEMANGLED_NAME (msymbol) != NULL) { fprintf_filtered (outfile, " %s", SYMBOL_DEMANGLED_NAME (msymbol)); @@ -415,10 +427,13 @@ dump_symtab (objfile, symtab, outfile) int depth; fprintf_filtered (outfile, "\nSymtab for file %s\n", symtab->filename); + if (symtab->dirname) + fprintf_filtered (outfile, "Compilation directory is %s\n", + symtab->dirname); fprintf_filtered (outfile, "Read from object file %s (", objfile->name); gdb_print_address (objfile, outfile); fprintf_filtered (outfile, ")\n"); - fprintf_filtered (outfile, "Language: %s\n", language_str (symtab -> language)); + fprintf_filtered (outfile, "Language: %s\n", language_str (symtab->language)); /* First print the line table. */ l = LINETABLE (symtab); @@ -433,53 +448,59 @@ dump_symtab (objfile, symtab, outfile) fprintf_filtered (outfile, "\n"); } } - /* Now print the block info. */ - fprintf_filtered (outfile, "\nBlockvector:\n\n"); - bv = BLOCKVECTOR (symtab); - len = BLOCKVECTOR_NBLOCKS (bv); - for (i = 0; i < len; i++) + /* Now print the block info, but only for primary symtabs since we will + print lots of duplicate info otherwise. */ + if (symtab -> primary) { - b = BLOCKVECTOR_BLOCK (bv, i); - depth = block_depth (b) * 2; - print_spaces (depth, outfile); - fprintf_filtered (outfile, "block #%03d (object ", i); - gdb_print_address (b, outfile); - fprintf_filtered (outfile, ") "); - fprintf_filtered (outfile, "["); - print_address_numeric (BLOCK_START (b), 1, outfile); - fprintf_filtered (outfile, ".."); - print_address_numeric (BLOCK_END (b), 1, outfile); - fprintf_filtered (outfile, "]"); - if (BLOCK_SUPERBLOCK (b)) - { - fprintf_filtered (outfile, " (under "); - gdb_print_address (BLOCK_SUPERBLOCK (b), outfile); - fprintf_filtered (outfile, ")"); - } - if (BLOCK_FUNCTION (b)) + fprintf_filtered (outfile, "\nBlockvector:\n\n"); + bv = BLOCKVECTOR (symtab); + len = BLOCKVECTOR_NBLOCKS (bv); + for (i = 0; i < len; i++) { - fprintf_filtered (outfile, " %s", SYMBOL_NAME (BLOCK_FUNCTION (b))); - if (SYMBOL_DEMANGLED_NAME (BLOCK_FUNCTION (b)) != NULL) + b = BLOCKVECTOR_BLOCK (bv, i); + depth = block_depth (b) * 2; + print_spaces (depth, outfile); + fprintf_filtered (outfile, "block #%03d, object at ", i); + gdb_print_address (b, outfile); + if (BLOCK_SUPERBLOCK (b)) + { + fprintf_filtered (outfile, " under "); + gdb_print_address (BLOCK_SUPERBLOCK (b), outfile); + } + blen = BLOCK_NSYMS (b); + fprintf_filtered (outfile, ", %d syms in ", blen); + print_address_numeric (BLOCK_START (b), 1, outfile); + fprintf_filtered (outfile, ".."); + print_address_numeric (BLOCK_END (b), 1, outfile); + if (BLOCK_FUNCTION (b)) + { + fprintf_filtered (outfile, ", function %s", SYMBOL_NAME (BLOCK_FUNCTION (b))); + if (SYMBOL_DEMANGLED_NAME (BLOCK_FUNCTION (b)) != NULL) + { + fprintf_filtered (outfile, ", %s", + SYMBOL_DEMANGLED_NAME (BLOCK_FUNCTION (b))); + } + } + if (BLOCK_GCC_COMPILED(b)) + fprintf_filtered (outfile, ", compiled with gcc%d", BLOCK_GCC_COMPILED(b)); + fprintf_filtered (outfile, "\n"); + /* Now print each symbol in this block */ + for (j = 0; j < blen; j++) { - fprintf_filtered (outfile, " %s", - SYMBOL_DEMANGLED_NAME (BLOCK_FUNCTION (b))); + struct print_symbol_args s; + s.symbol = BLOCK_SYM (b, j); + s.depth = depth + 1; + s.outfile = outfile; + catch_errors (print_symbol, &s, "Error printing symbol:\n", + RETURN_MASK_ALL); } } - if (BLOCK_GCC_COMPILED(b)) - fprintf_filtered (outfile, " gcc%d compiled", BLOCK_GCC_COMPILED(b)); fprintf_filtered (outfile, "\n"); - blen = BLOCK_NSYMS (b); - for (j = 0; j < blen; j++) - { - struct print_symbol_args s; - s.symbol = BLOCK_SYM (b, j); - s.depth = depth + 1; - s.outfile = outfile; - catch_errors (print_symbol, &s, "Error printing symbol:\n", - RETURN_MASK_ALL); - } } - fprintf_filtered (outfile, "\n"); + else + { + fprintf_filtered (outfile, "\nBlockvector same as previous symtab\n\n"); + } } void @@ -506,7 +527,7 @@ Arguments missing: an output file name and an optional symbol file name"); { nomem (0); } - cleanups = make_cleanup (freeargv, (char *) argv); + cleanups = make_cleanup ((make_cleanup_func) freeargv, (char *) argv); if (argv[0] != NULL) { @@ -524,7 +545,7 @@ Arguments missing: an output file name and an optional symbol file name"); outfile = gdb_fopen (filename, FOPEN_WT); if (outfile == 0) perror_with_name (filename); - make_cleanup (fclose, (char *) outfile); + make_cleanup ((make_cleanup_func) gdb_fclose, (char *) &outfile); immediate_quit++; ALL_SYMTABS (objfile, s) @@ -552,7 +573,12 @@ print_symbol (args) { fprintf_filtered (outfile, "label %s at ", SYMBOL_SOURCE_NAME (symbol)); print_address_numeric (SYMBOL_VALUE_ADDRESS (symbol), 1, outfile); - fprintf_filtered (outfile, "\n"); + if (SYMBOL_BFD_SECTION (symbol)) + fprintf_filtered (outfile, " section %s\n", + bfd_section_name (SYMBOL_BFD_SECTION (symbol)->owner, + SYMBOL_BFD_SECTION (symbol))); + else + fprintf_filtered (outfile, "\n"); return 1; } if (SYMBOL_NAMESPACE (symbol) == STRUCT_NAMESPACE) @@ -592,7 +618,7 @@ print_symbol (args) switch (SYMBOL_CLASS (symbol)) { case LOC_CONST: - fprintf_filtered (outfile, "const %ld (0x%lx),", + fprintf_filtered (outfile, "const %ld (0x%lx)", SYMBOL_VALUE (symbol), SYMBOL_VALUE (symbol)); break; @@ -606,44 +632,53 @@ print_symbol (args) for (i = 0; i < TYPE_LENGTH (type); i++) fprintf_filtered (outfile, " %02x", (unsigned)SYMBOL_VALUE_BYTES (symbol) [i]); - fprintf_filtered (outfile, ","); } break; case LOC_STATIC: fprintf_filtered (outfile, "static at "); print_address_numeric (SYMBOL_VALUE_ADDRESS (symbol), 1,outfile); - fprintf_filtered (outfile, ","); + if (SYMBOL_BFD_SECTION (symbol)) + fprintf_filtered (outfile, " section %s", + bfd_section_name + (SYMBOL_BFD_SECTION (symbol)->owner, + SYMBOL_BFD_SECTION (symbol))); + break; + + case LOC_INDIRECT: + fprintf_filtered (outfile, "extern global at *("); + print_address_numeric (SYMBOL_VALUE_ADDRESS (symbol), 1,outfile); + fprintf_filtered (outfile, "),"); break; case LOC_REGISTER: - fprintf_filtered (outfile, "register %ld,", SYMBOL_VALUE (symbol)); + fprintf_filtered (outfile, "register %ld", SYMBOL_VALUE (symbol)); break; case LOC_ARG: - fprintf_filtered (outfile, "arg at offset 0x%lx,", + fprintf_filtered (outfile, "arg at offset 0x%lx", SYMBOL_VALUE (symbol)); break; case LOC_LOCAL_ARG: - fprintf_filtered (outfile, "arg at offset 0x%lx from fp,", + fprintf_filtered (outfile, "arg at offset 0x%lx from fp", SYMBOL_VALUE (symbol)); break; case LOC_REF_ARG: - fprintf_filtered (outfile, "reference arg at 0x%lx,", SYMBOL_VALUE (symbol)); + fprintf_filtered (outfile, "reference arg at 0x%lx", SYMBOL_VALUE (symbol)); break; case LOC_REGPARM: - fprintf_filtered (outfile, "parameter register %ld,", SYMBOL_VALUE (symbol)); + fprintf_filtered (outfile, "parameter register %ld", SYMBOL_VALUE (symbol)); break; case LOC_REGPARM_ADDR: - fprintf_filtered (outfile, "address parameter register %ld,", SYMBOL_VALUE (symbol)); + fprintf_filtered (outfile, "address parameter register %ld", SYMBOL_VALUE (symbol)); break; case LOC_LOCAL: - fprintf_filtered (outfile, "local at offset 0x%lx,", + fprintf_filtered (outfile, "local at offset 0x%lx", SYMBOL_VALUE (symbol)); break; @@ -653,7 +688,7 @@ print_symbol (args) break; case LOC_BASEREG_ARG: - fprintf_filtered (outfile, "arg at 0x%lx from register %d,", + fprintf_filtered (outfile, "arg at 0x%lx from register %d", SYMBOL_VALUE (symbol), SYMBOL_BASEREG (symbol)); break; @@ -663,16 +698,29 @@ print_symbol (args) case LOC_LABEL: fprintf_filtered (outfile, "label at "); print_address_numeric (SYMBOL_VALUE_ADDRESS (symbol), 1, outfile); + if (SYMBOL_BFD_SECTION (symbol)) + fprintf_filtered (outfile, " section %s", + bfd_section_name + (SYMBOL_BFD_SECTION (symbol)->owner, + SYMBOL_BFD_SECTION (symbol))); break; case LOC_BLOCK: - fprintf_filtered (outfile, "block (object "); + fprintf_filtered (outfile, "block object "); gdb_print_address (SYMBOL_BLOCK_VALUE (symbol), outfile); - fprintf_filtered (outfile, ") starting at "); + fprintf_filtered (outfile, ", "); print_address_numeric (BLOCK_START (SYMBOL_BLOCK_VALUE (symbol)), 1, outfile); - fprintf_filtered (outfile, ","); + fprintf_filtered (outfile, ".."); + print_address_numeric (BLOCK_END (SYMBOL_BLOCK_VALUE (symbol)), + 1, + outfile); + if (SYMBOL_BFD_SECTION (symbol)) + fprintf_filtered (outfile, " section %s", + bfd_section_name + (SYMBOL_BFD_SECTION (symbol)->owner, + SYMBOL_BFD_SECTION (symbol))); break; case LOC_UNRESOLVED: @@ -699,7 +747,7 @@ maintenance_print_psymbols (args, from_tty) int from_tty; { char **argv; - GDB_FILE *outfile; + GDB_FILE *outfile; struct cleanup *cleanups; char *symname = NULL; char *filename = DEV_TTY; @@ -716,7 +764,7 @@ maintenance_print_psymbols (args, from_tty) { nomem (0); } - cleanups = make_cleanup (freeargv, (char *) argv); + cleanups = make_cleanup ((make_cleanup_func) freeargv, (char *) argv); if (argv[0] != NULL) { @@ -734,7 +782,7 @@ maintenance_print_psymbols (args, from_tty) outfile = gdb_fopen (filename, FOPEN_WT); if (outfile == 0) perror_with_name (filename); - make_cleanup (fclose, outfile); + make_cleanup ((make_cleanup_func) gdb_fclose, &outfile); immediate_quit++; ALL_PSYMTABS (objfile, ps) @@ -789,6 +837,9 @@ print_partial_symbols (p, count, what, outfile) case LOC_STATIC: fputs_filtered ("static", outfile); break; + case LOC_INDIRECT: + fputs_filtered ("extern global", outfile); + break; case LOC_REGISTER: fputs_filtered ("register", outfile); break; @@ -833,10 +884,8 @@ print_partial_symbols (p, count, what, outfile) break; } fputs_filtered (", ", outfile); - /* FIXME-32x64: Need to use SYMBOL_VALUE_ADDRESS, etc.; this - could be 32 bits when some of the other fields in the union - are 64. */ - fprintf_filtered (outfile, "0x%lx\n", SYMBOL_VALUE (*p)); + print_address_numeric (SYMBOL_VALUE_ADDRESS (*p), 1, outfile); + fprintf_filtered (outfile, "\n"); p++; } } @@ -863,7 +912,7 @@ maintenance_print_msymbols (args, from_tty) { nomem (0); } - cleanups = make_cleanup (freeargv, argv); + cleanups = make_cleanup ((make_cleanup_func) freeargv, argv); if (argv[0] != NULL) { @@ -881,7 +930,7 @@ maintenance_print_msymbols (args, from_tty) outfile = gdb_fopen (filename, FOPEN_WT); if (outfile == 0) perror_with_name (filename); - make_cleanup (fclose, outfile); + make_cleanup ((make_cleanup_func) gdb_fclose, &outfile); immediate_quit++; ALL_OBJFILES (objfile) diff --git a/gdb/target.c b/gdb/target.c index 736d200..07e62f4 100644 --- a/gdb/target.c +++ b/gdb/target.c @@ -1196,7 +1196,7 @@ generic_mourn_inferior () inferior_pid = 0; attach_flag = 0; - breakpoint_init_inferior (); + breakpoint_init_inferior (inf_exited); registers_changed (); #ifdef CLEAR_DEFERRED_STORES diff --git a/gdb/thread.c b/gdb/thread.c index a2d052a..aff7ef3 100644 --- a/gdb/thread.c +++ b/gdb/thread.c @@ -53,9 +53,25 @@ struct thread_info int trap_expected; int handling_longjmp; int another_trap; -}; + /* This is set TRUE when a catchpoint of a shared library event + triggers. Since we don't wish to leave the inferior in the + solib hook when we report the event, we step the inferior + back to user code before stopping and reporting the event. + */ + int stepping_through_solib_after_catch; + + /* When stepping_through_solib_after_catch is TRUE, this is a + list of the catchpoints that should be reported as triggering + when we finally do stop stepping. + */ + bpstat stepping_through_solib_catchpoints; + /* This is set to TRUE when this thread is in a signal handler + trampoline and we're single-stepping through it */ + int stepping_through_sigtramp; + +}; static struct target_thread_vector *target_thread_functions; @@ -104,6 +120,7 @@ bind_target_thread_vector (vec) { target_thread_functions = vec; } + /* Prototypes for exported functions. */ struct target_thread_vector * @@ -203,6 +220,9 @@ add_thread (pid) tp->handling_longjmp = 0; tp->trap_expected = 0; tp->another_trap = 0; + tp->stepping_through_solib_after_catch = 0; + tp->stepping_through_solib_catchpoints = NULL; + tp->stepping_through_sigtramp = 0; tp->next = thread_list; thread_list = tp; } @@ -301,7 +321,10 @@ void load_infrun_state (pid, prev_pc, prev_func_start, prev_func_name, trap_expected, step_resume_breakpoint, through_sigtramp_breakpoint, step_range_start, step_range_end, step_frame_address, - handling_longjmp, another_trap) + handling_longjmp, another_trap, + stepping_through_solib_after_catch, + stepping_through_solib_catchpoints, + stepping_through_sigtramp) int pid; CORE_ADDR *prev_pc; CORE_ADDR *prev_func_start; @@ -314,6 +337,9 @@ void load_infrun_state (pid, prev_pc, prev_func_start, prev_func_name, CORE_ADDR *step_frame_address; int *handling_longjmp; int *another_trap; + int * stepping_through_solib_after_catch; + bpstat * stepping_through_solib_catchpoints; + int * stepping_through_sigtramp; { struct thread_info *tp; @@ -334,6 +360,9 @@ void load_infrun_state (pid, prev_pc, prev_func_start, prev_func_name, *handling_longjmp = tp->handling_longjmp; *trap_expected = tp->trap_expected; *another_trap = tp->another_trap; + *stepping_through_solib_after_catch = tp->stepping_through_solib_after_catch; + *stepping_through_solib_catchpoints = tp->stepping_through_solib_catchpoints; + *stepping_through_sigtramp = tp->stepping_through_sigtramp; } /* Save infrun state for the thread PID. */ @@ -342,7 +371,10 @@ void save_infrun_state (pid, prev_pc, prev_func_start, prev_func_name, trap_expected, step_resume_breakpoint, through_sigtramp_breakpoint, step_range_start, step_range_end, step_frame_address, - handling_longjmp, another_trap) + handling_longjmp, another_trap, + stepping_through_solib_after_catch, + stepping_through_solib_catchpoints, + stepping_through_sigtramp) int pid; CORE_ADDR prev_pc; CORE_ADDR prev_func_start; @@ -355,6 +387,9 @@ void save_infrun_state (pid, prev_pc, prev_func_start, prev_func_name, CORE_ADDR step_frame_address; int handling_longjmp; int another_trap; + int stepping_through_solib_after_catch; + bpstat stepping_through_solib_catchpoints; + int stepping_through_sigtramp; { struct thread_info *tp; @@ -375,6 +410,9 @@ void save_infrun_state (pid, prev_pc, prev_func_start, prev_func_name, tp->handling_longjmp = handling_longjmp; tp->trap_expected = trap_expected; tp->another_trap = another_trap; + tp->stepping_through_solib_after_catch = stepping_through_solib_after_catch; + tp->stepping_through_solib_catchpoints = stepping_through_solib_catchpoints; + tp->stepping_through_sigtramp = stepping_through_sigtramp; } /* Return true if TP is an active thread. */ @@ -414,7 +452,12 @@ prune_threads () } } -/* Print information about currently known threads */ +/* Print information about currently known threads + * + * Note: this has the drawback that it _really_ switches + * threads, which frees the frame cache. A no-side + * effects info-threads command would be nicer. + */ static void info_threads_command (arg, from_tty) @@ -423,6 +466,9 @@ info_threads_command (arg, from_tty) { struct thread_info *tp; int current_pid = inferior_pid; + struct frame_info *cur_frame; + int saved_frame_level = selected_frame_level; + int counter; /* Avoid coredumps which would happen if we tried to access a NULL selected_frame. */ @@ -440,16 +486,39 @@ info_threads_command (arg, from_tty) else printf_filtered (" "); +#ifdef HPUXHPPA + printf_filtered ("%d %s ", tp->num, target_tid_to_str (tp->pid)); +#else printf_filtered ("%d %s ", tp->num, target_pid_to_str (tp->pid)); - +#endif switch_to_thread (tp->pid); if (selected_frame) - print_stack_frame (selected_frame, -1, 0); + print_only_stack_frame (selected_frame, -1, 0); else printf_filtered ("[No stack.]\n"); } switch_to_thread (current_pid); + + /* Code below copied from "up_silently_base" in "stack.c". + * It restores the frame set by the user before the "info threads" + * command. We have finished the info-threads display by switching + * back to the current thread. That switch has put us at the top + * of the stack (leaf frame). + */ + counter = saved_frame_level; + cur_frame = find_relative_frame(selected_frame, &counter); + if (counter != 0) { + /* Ooops, can't restore, tell user where we are. */ + warning ("Couldn't restore frame in current thread, at frame 0"); + print_stack_frame (selected_frame, -1, 0); + } + else { + select_frame(cur_frame, saved_frame_level); + } + + /* re-show current frame. */ + show_stack_frame(cur_frame); } /* Switch from one thread to another. */ @@ -472,8 +541,10 @@ static void restore_current_thread (pid) int pid; { - if (pid != inferior_pid) + if (pid != inferior_pid) { switch_to_thread (pid); + print_stack_frame( get_current_frame(), 0, -1); + } } /* Apply a GDB command to a list of threads. List syntax is a whitespace @@ -503,8 +574,14 @@ thread_apply_all_command (cmd, from_tty) if (thread_alive (tp)) { switch_to_thread (tp->pid); +#ifdef HPUXHPPA + printf_filtered ("\nThread %d (%s):\n", + tp->num, + target_tid_to_str (inferior_pid)); +#else printf_filtered ("\nThread %d (%s):\n", tp->num, target_pid_to_str (inferior_pid)); +#endif execute_command (cmd, from_tty); } } @@ -567,8 +644,13 @@ thread_apply_command (tidlist, from_tty) else { switch_to_thread (tp->pid); +#ifdef HPUXHPPA + printf_filtered ("\nThread %d (%s):\n", tp->num, + target_tid_to_str (inferior_pid)); +#else printf_filtered ("\nThread %d (%s):\n", tp->num, target_pid_to_str (inferior_pid)); +#endif execute_command (cmd, from_tty); } } @@ -587,9 +669,21 @@ thread_command (tidstr, from_tty) struct thread_info *tp; if (!tidstr) - error ("Please specify a thread ID. Use the \"info threads\" command to\n\ -see the IDs of currently known threads."); - + { + /* Don't generate an error, just say which thread is current. */ + if (target_has_stack) + printf_filtered ("[Current thread is %d (%s)]\n", + pid_to_thread_id(inferior_pid), +#if defined(HPUXHPPA) + target_tid_to_str(inferior_pid) +#else + target_pid_to_str(inferior_pid) +#endif + ); + else + error ("No stack."); + return; + } num = atoi (tidstr); tp = find_thread_id (num); @@ -602,9 +696,18 @@ see the IDs of currently known threads.", num); error ("Thread ID %d has terminated.\n", num); switch_to_thread (tp->pid); + if (context_hook) context_hook (num); - printf_filtered ("[Switching to %s]\n", target_pid_to_str (inferior_pid)); + + printf_filtered ("[Switching to thread %d (%s)]\n", + pid_to_thread_id (inferior_pid), +#if defined(HPUXHPPA) + target_tid_to_str (inferior_pid) +#else + target_pid_to_str (inferior_pid) +#endif + ); print_stack_frame (selected_frame, selected_frame_level, 1); } @@ -633,5 +736,6 @@ The new thread ID must be currently known.", &thread_cmd_list, "thread ", 1, "Apply a command to all threads.", &thread_apply_list); - add_com_alias ("t", "thread", class_run, 1); + if (!xdb_commands) + add_com_alias ("t", "thread", class_run, 1); } @@ -203,6 +203,14 @@ struct cmd_list_element *enablelist; struct cmd_list_element *disablelist; +/* Chain containing all defined toggle subcommands. */ + +struct cmd_list_element *togglelist; + +/* Chain containing all defined stop subcommands. */ + +struct cmd_list_element *stoplist; + /* Chain containing all defined delete subcommands. */ struct cmd_list_element *deletelist; @@ -371,7 +379,7 @@ void (*command_loop_hook) PARAMS ((void)); /* Called instead of fputs for all output. */ -void (*fputs_unfiltered_hook) PARAMS ((const char *linebuffer, FILE *stream)); +void (*fputs_unfiltered_hook) PARAMS ((const char *linebuffer, GDB_FILE *stream)); /* Called when the target says something to the host, which may want to appear in a different window. */ @@ -392,7 +400,7 @@ void (*warning_hook) PARAMS ((const char *, va_list)); /* Called from gdb_flush to flush output. */ -void (*flush_hook) PARAMS ((FILE *stream)); +void (*flush_hook) PARAMS ((GDB_FILE *stream)); /* These three functions support getting lines of text from the user. They are used in sequence. First readline_begin_hook is called with a text @@ -1301,13 +1309,16 @@ command_loop () int stdin_is_tty = ISATTY (stdin); long time_at_cmd_start; #ifdef HAVE_SBRK - long space_at_cmd_start; + long space_at_cmd_start = 0; #endif extern int display_time; extern int display_space; while (instream && !feof (instream)) { +#if defined(TUI) + extern int insert_mode; +#endif if (window_hook && instream == stdin) (*window_hook) (instream, prompt); @@ -1315,8 +1326,22 @@ command_loop () if (instream == stdin && stdin_is_tty) reinitialize_more_filter (); old_chain = make_cleanup ((make_cleanup_func) command_loop_marker, 0); + +#if defined(TUI) + /* A bit of paranoia: I want to make sure the "insert_mode" global + * is clear except when it is being used for command-line editing + * (see tuiIO.c, utils.c); otherwise normal output will + * get messed up in the TUI. So clear it before/after + * the command-line-input call. - RT + */ + insert_mode = 0; +#endif + /* Get a command-line. This calls the readline package. */ command = command_line_input (instream == stdin ? prompt : (char *) NULL, instream == stdin, "prompt"); +#if defined(TUI) + insert_mode = 0; +#endif if (command == 0) return; @@ -2803,6 +2828,7 @@ document_command (comname, from_tty) free_command_lines (&doclines); } +/* Print the GDB banner. */ void print_gdb_version (stream) GDB_FILE *stream; @@ -2871,6 +2897,19 @@ get_prompt () { return prompt; } + +void +set_prompt (s) + char *s; +{ +/* ??rehrauer: I don't know why this fails, since it looks as though + assignments to prompt are wrapped in calls to savestring... + if (prompt != NULL) + free (prompt); +*/ + prompt = savestring (s, strlen (s)); +} + /* If necessary, make the user confirm that we should quit. Return non-zero if we should quit, zero if we shouldn't. */ @@ -2934,6 +2973,16 @@ quit_force (args, from_tty) do_final_cleanups(ALL_CLEANUPS); /* Do any final cleanups before exiting */ +#if defined(TUI) + /* tuiDo((TuiOpaqueFuncPtr)tuiCleanUp); */ + /* The above does not need to be inside a tuiDo(), since + * it is not manipulating the curses screen, but rather, + * it is tearing it down. + */ + if (tui_version) + tuiCleanUp(); +#endif + exit (exit_code); } @@ -3341,6 +3390,8 @@ init_cmd_lists () infolist = NULL; enablelist = NULL; disablelist = NULL; + togglelist = NULL; + stoplist = NULL; deletelist = NULL; enablebreaklist = NULL; setlist = NULL; @@ -3428,7 +3479,8 @@ well documented as user commands.", The commands in this class are those defined by the user.\n\ Use the \"define\" command to define a command.", &cmdlist); add_cmd ("support", class_support, NO_FUNCTION, "Support facilities.", &cmdlist); - add_cmd ("status", class_info, NO_FUNCTION, "Status inquiries.", &cmdlist); + if (!dbx_commands) + add_cmd ("status", class_info, NO_FUNCTION, "Status inquiries.", &cmdlist); add_cmd ("files", class_files, NO_FUNCTION, "Specifying and examining files.", &cmdlist); add_cmd ("breakpoints", class_breakpoint, NO_FUNCTION, "Making program stop at certain points.", &cmdlist); add_cmd ("data", class_vars, NO_FUNCTION, "Examining data.", &cmdlist); diff --git a/gdb/typeprint.c b/gdb/typeprint.c index 144df82..1a4877b 100644 --- a/gdb/typeprint.c +++ b/gdb/typeprint.c @@ -34,6 +34,10 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "gdb_string.h" #include <errno.h> +/* For real-type printing in whatis_exp() */ +extern int objectprint; /* Controls looking up an object's derived type + using what we find in its vtables. */ + static void ptype_command PARAMS ((char *, int)); @@ -74,6 +78,10 @@ whatis_exp (exp, show) struct expression *expr; register value_ptr val; register struct cleanup *old_chain = NULL; + struct type * real_type = NULL; + int full = 0; + int top = -1; + int using_enc = 0; if (exp) { @@ -85,7 +93,16 @@ whatis_exp (exp, show) else val = access_value_history (0); + real_type = value_rtti_type (val, &full, &top, &using_enc); + printf_filtered ("type = "); + + if (real_type && objectprint) + printf_filtered ("/* real type = %s%s */\n", + TYPE_NAME (real_type), + full ? "" : " (incomplete object)"); + /* FIXME: maybe better to use type_print (real_type, "", gdb_stdout, -1); */ + type_print (VALUE_TYPE (val), "", gdb_stdout, show); printf_filtered ("\n"); diff --git a/gdb/utils.c b/gdb/utils.c index 529c902..e03245f 100644 --- a/gdb/utils.c +++ b/gdb/utils.c @@ -53,9 +53,10 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* Prototypes for local functions */ -static void vfprintf_maybe_filtered PARAMS ((FILE *, const char *, va_list, int)); +static void vfprintf_maybe_filtered PARAMS ((GDB_FILE *, const char *, + va_list, int)); -static void fputs_maybe_filtered PARAMS ((const char *, FILE *, int)); +static void fputs_maybe_filtered PARAMS ((const char *, GDB_FILE *, int)); #if defined (USE_MMALLOC) && !defined (NO_MMCHECK) static void malloc_botch PARAMS ((void)); @@ -70,12 +71,19 @@ prompt_for_continue PARAMS ((void)); static void set_width_command PARAMS ((char *, int, struct cmd_list_element *)); +static void +set_width PARAMS ((void)); + /* If this definition isn't overridden by the header files, assume that isatty and fileno exist on this system. */ #ifndef ISATTY #define ISATTY(FP) (isatty (fileno (FP))) #endif +#ifndef GDB_FILE_ISATTY +#define GDB_FILE_ISATTY(GDB_FILE_PTR) (gdb_file_isatty(GDB_FILE_PTR)) +#endif + /* Chain of cleanup actions established with make_cleanup, to be executed if an error happens. */ @@ -132,6 +140,9 @@ char *quit_pre_print; /* String to be printed before warning messages, if any. */ char *warning_pre_print = "\nwarning: "; + +int pagination_enabled = 1; + /* Add a new cleanup to the cleanup_chain, and return the previous chain pointer @@ -969,10 +980,16 @@ mstrsave (md, ptr) void print_spaces (n, file) register int n; - register FILE *file; + register GDB_FILE *file; { - while (n-- > 0) - fputc (' ', file); + if (file->ts_streamtype == astring) { + gdb_file_adjust_strbuf (n, file); + while (n-- > 0) + strcat(file->ts_strbuf, ' '); + } else { + while (n-- > 0) + fputc (' ', file->ts_filestream); + } } /* Print a host address. */ @@ -1052,21 +1069,43 @@ query (va_alist) fputs_unfiltered ("\n", gdb_stdout); #endif /* MPW */ + wrap_here(""); gdb_flush (gdb_stdout); - answer = fgetc (stdin); + +#if defined(TUI) + if (!tui_version || cmdWin == tuiWinWithFocus()) +#endif + answer = fgetc (stdin); +#if defined(TUI) + else + + answer = (unsigned char)tuiBufferGetc(); + +#endif clearerr (stdin); /* in case of C-d */ if (answer == EOF) /* C-d */ { retval = 1; break; } - if (answer != '\n') /* Eat rest of input line, to EOF or newline */ + /* Eat rest of input line, to EOF or newline */ + if ((answer != '\n') || (tui_version && answer != '\r')) do { - ans2 = fgetc (stdin); +#if defined(TUI) + if (!tui_version || cmdWin == tuiWinWithFocus()) +#endif + ans2 = fgetc (stdin); +#if defined(TUI) + else + + ans2 = (unsigned char)tuiBufferGetc(); +#endif clearerr (stdin); } - while (ans2 != EOF && ans2 != '\n'); + while (ans2 != EOF && ans2 != '\n' && ans2 != '\r'); + TUIDO(((TuiOpaqueFuncPtr)tui_vStartNewLines, 1)); + if (answer >= 'a') answer -= 040; if (answer == 'Y') @@ -1178,7 +1217,7 @@ parse_escape (string_ptr) void gdb_printchar (c, stream, quoter) register int c; - FILE *stream; + GDB_FILE *stream; int quoter; { @@ -1283,13 +1322,87 @@ static char *wrap_indent; is not in effect. */ static int wrap_column; -/* ARGSUSED */ -static void -set_width_command (args, from_tty, c) - char *args; - int from_tty; - struct cmd_list_element *c; + +/* Inialize the lines and chars per page */ +void +init_page_info() { +#if defined(TUI) + if (tui_version && m_winPtrNotNull(cmdWin)) + { + lines_per_page = cmdWin->generic.height; + chars_per_line = cmdWin->generic.width; + } + else +#endif + { + /* These defaults will be used if we are unable to get the correct + values from termcap. */ +#if defined(__GO32__) + lines_per_page = ScreenRows(); + chars_per_line = ScreenCols(); +#else + lines_per_page = 24; + chars_per_line = 80; + +#if !defined (MPW) && !defined (_WIN32) + /* No termcap under MPW, although might be cool to do something + by looking at worksheet or console window sizes. */ + /* Initialize the screen height and width from termcap. */ + { + char *termtype = getenv ("TERM"); + + /* Positive means success, nonpositive means failure. */ + int status; + + /* 2048 is large enough for all known terminals, according to the + GNU termcap manual. */ + char term_buffer[2048]; + + if (termtype) + { + status = tgetent (term_buffer, termtype); + if (status > 0) + { + int val; + + val = tgetnum ("li"); + if (val >= 0) + lines_per_page = val; + else + /* The number of lines per page is not mentioned + in the terminal description. This probably means + that paging is not useful (e.g. emacs shell window), + so disable paging. */ + lines_per_page = UINT_MAX; + + val = tgetnum ("co"); + if (val >= 0) + chars_per_line = val; + } + } + } +#endif /* MPW */ + +#if defined(SIGWINCH) && defined(SIGWINCH_HANDLER) + + /* If there is a better way to determine the window size, use it. */ + SIGWINCH_HANDLER (SIGWINCH); +#endif +#endif + /* If the output is not a terminal, don't paginate it. */ + if (!GDB_FILE_ISATTY (gdb_stdout)) + lines_per_page = UINT_MAX; + } /* the command_line_version */ + set_width(); +} + +static void +set_width() +{ + if (chars_per_line == 0) + init_page_info(); + if (!wrap_buffer) { wrap_buffer = (char *) xmalloc (chars_per_line + 2); @@ -1297,7 +1410,17 @@ set_width_command (args, from_tty, c) } else wrap_buffer = (char *) xrealloc (wrap_buffer, chars_per_line + 2); - wrap_pointer = wrap_buffer; /* Start it at the beginning */ + wrap_pointer = wrap_buffer; /* Start it at the beginning */ +} + +/* ARGSUSED */ +static void +set_width_command (args, from_tty, c) + char *args; + int from_tty; + struct cmd_list_element *c; +{ + set_width (); } /* Wait, so the user can read what's on the screen. Prompt the user @@ -1436,18 +1559,100 @@ begin_line () } } +int +gdb_file_isatty (stream) + GDB_FILE *stream; +{ + + if (stream->ts_streamtype == afile) + return (isatty(fileno(stream->ts_filestream))); + else return 0; +} + +GDB_FILE * +gdb_file_init_astring (n) + int n; +{ + GDB_FILE *tmpstream; + + tmpstream = xmalloc (sizeof(GDB_FILE)); + tmpstream->ts_streamtype = astring; + tmpstream->ts_filestream = NULL; + if (n > 0) + { + tmpstream->ts_strbuf = xmalloc ((n + 1)*sizeof(char)); + tmpstream->ts_strbuf[0] = '\0'; + } + else + tmpstream->ts_strbuf = NULL; + tmpstream->ts_buflen = n; + + return tmpstream; +} + +void +gdb_file_deallocate (streamptr) + GDB_FILE **streamptr; +{ + GDB_FILE *tmpstream; + + tmpstream = *streamptr; + if ((tmpstream->ts_streamtype == astring) && + (tmpstream->ts_strbuf != NULL)) + { + free (tmpstream->ts_strbuf); + } + + free (tmpstream); + *streamptr = NULL; +} + +char * +gdb_file_get_strbuf (stream) + GDB_FILE *stream; +{ + return (stream->ts_strbuf); +} + +/* adjust the length of the buffer by the amount necessary + to accomodate appending a string of length N to the buffer contents */ +void +gdb_file_adjust_strbuf (n, stream) + int n; + GDB_FILE *stream; +{ + int non_null_chars; + + non_null_chars = strlen(stream->ts_strbuf); + + if (n > (stream->ts_buflen - non_null_chars - 1)) + { + stream->ts_buflen = n + non_null_chars + 1; + stream->ts_strbuf = xrealloc (stream->ts_strbuf, stream->ts_buflen); + } +} GDB_FILE * gdb_fopen (name, mode) char * name; char * mode; { - return fopen (name, mode); + int gdb_file_size; + GDB_FILE *tmp; + + gdb_file_size = sizeof(GDB_FILE); + tmp = (GDB_FILE *) xmalloc (gdb_file_size); + tmp->ts_streamtype = afile; + tmp->ts_filestream = fopen (name, mode); + tmp->ts_strbuf = NULL; + tmp->ts_buflen = 0; + + return tmp; } void gdb_flush (stream) - FILE *stream; + GDB_FILE *stream; { if (flush_hook && (stream == gdb_stdout @@ -1457,7 +1662,18 @@ gdb_flush (stream) return; } - fflush (stream); + fflush (stream->ts_filestream); +} + +void +gdb_fclose(streamptr) + GDB_FILE **streamptr; +{ + GDB_FILE *tmpstream; + + tmpstream = *streamptr; + fclose (tmpstream->ts_filestream); + gdb_file_deallocate (streamptr); } /* Like fputs but if FILTER is true, pause after every screenful. @@ -1476,7 +1692,7 @@ gdb_flush (stream) static void fputs_maybe_filtered (linebuffer, stream, filter) const char *linebuffer; - FILE *stream; + GDB_FILE *stream; int filter; { const char *lineptr; @@ -1580,7 +1796,7 @@ fputs_maybe_filtered (linebuffer, stream, filter) void fputs_filtered (linebuffer, stream) const char *linebuffer; - FILE *stream; + GDB_FILE *stream; { fputs_maybe_filtered (linebuffer, stream, 1); } @@ -1600,7 +1816,7 @@ putchar_unfiltered (c) int fputc_unfiltered (c, stream) int c; - FILE * stream; + GDB_FILE * stream; { char buf[2]; @@ -1613,7 +1829,7 @@ fputc_unfiltered (c, stream) int fputc_filtered (c, stream) int c; - FILE * stream; + GDB_FILE * stream; { char buf[2]; @@ -1713,7 +1929,7 @@ puts_debug (prefix, string, suffix) static void vfprintf_maybe_filtered (stream, format, args, filter) - FILE *stream; + GDB_FILE *stream; const char *format; va_list args; int filter; @@ -1735,7 +1951,7 @@ vfprintf_maybe_filtered (stream, format, args, filter) void vfprintf_filtered (stream, format, args) - FILE *stream; + GDB_FILE *stream; const char *format; va_list args; { @@ -1744,7 +1960,7 @@ vfprintf_filtered (stream, format, args) void vfprintf_unfiltered (stream, format, args) - FILE *stream; + GDB_FILE *stream; const char *format; va_list args; { @@ -1781,7 +1997,7 @@ vprintf_unfiltered (format, args) /* VARARGS */ void #ifdef ANSI_PROTOTYPES -fprintf_filtered (FILE *stream, const char *format, ...) +fprintf_filtered (GDB_FILE *stream, const char *format, ...) #else fprintf_filtered (va_alist) va_dcl @@ -1791,11 +2007,11 @@ fprintf_filtered (va_alist) #ifdef ANSI_PROTOTYPES va_start (args, format); #else - FILE *stream; + GDB_FILE *stream; char *format; va_start (args); - stream = va_arg (args, FILE *); + stream = va_arg (args, GDB_FILE *); format = va_arg (args, char *); #endif vfprintf_filtered (stream, format, args); @@ -1805,7 +2021,7 @@ fprintf_filtered (va_alist) /* VARARGS */ void #ifdef ANSI_PROTOTYPES -fprintf_unfiltered (FILE *stream, const char *format, ...) +fprintf_unfiltered (GDB_FILE *stream, const char *format, ...) #else fprintf_unfiltered (va_alist) va_dcl @@ -1815,11 +2031,11 @@ fprintf_unfiltered (va_alist) #ifdef ANSI_PROTOTYPES va_start (args, format); #else - FILE *stream; + GDB_FILE *stream; char *format; va_start (args); - stream = va_arg (args, FILE *); + stream = va_arg (args, GDB_FILE *); format = va_arg (args, char *); #endif vfprintf_unfiltered (stream, format, args); @@ -1832,7 +2048,7 @@ fprintf_unfiltered (va_alist) /* VARARGS */ void #ifdef ANSI_PROTOTYPES -fprintfi_filtered (int spaces, FILE *stream, const char *format, ...) +fprintfi_filtered (int spaces, GDB_FILE *stream, const char *format, ...) #else fprintfi_filtered (va_alist) va_dcl @@ -1843,12 +2059,12 @@ fprintfi_filtered (va_alist) va_start (args, format); #else int spaces; - FILE *stream; + GDB_FILE *stream; char *format; va_start (args); spaces = va_arg (args, int); - stream = va_arg (args, FILE *); + stream = va_arg (args, GDB_FILE *); format = va_arg (args, char *); #endif print_spaces_filtered (spaces, stream); @@ -1978,7 +2194,7 @@ n_spaces (n) void print_spaces_filtered (n, stream) int n; - FILE *stream; + GDB_FILE *stream; { fputs_filtered (n_spaces (n), stream); } @@ -1992,7 +2208,7 @@ print_spaces_filtered (n, stream) void fprintf_symbol_filtered (stream, name, lang, arg_mode) - FILE *stream; + GDB_FILE *stream; char *name; enum language lang; int arg_mode; @@ -2072,6 +2288,50 @@ strcmp_iw (string1, string2) } +/* +** subsetCompare() +** Answer whether stringToCompare is a full or partial match to +** templateString. The partial match must be in sequence starting +** at index 0. +*/ +int +#ifdef _STDC__ +subsetCompare( + char *stringToCompare, + char *templateString) +#else +subsetCompare(stringToCompare, templateString) + char *stringToCompare; + char *templateString; +#endif +{ + int match = 0; + + if (templateString != (char *)NULL && stringToCompare != (char *)NULL && + strlen(stringToCompare) <= strlen(templateString)) + match = (strncmp(templateString, + stringToCompare, + strlen(stringToCompare)) == 0); + + return match; +} /* subsetCompare */ + + +void pagination_on_command(arg, from_tty) + char *arg; + int from_tty; +{ + pagination_enabled = 1; +} + +void pagination_off_command(arg, from_tty) + char *arg; + int from_tty; +{ + pagination_enabled = 0; +} + + void initialize_utils () { @@ -2090,62 +2350,10 @@ initialize_utils () "Set number of lines gdb thinks are in a page.", &setlist), &showlist); - /* These defaults will be used if we are unable to get the correct - values from termcap. */ -#if defined(__GO32__) - lines_per_page = ScreenRows(); - chars_per_line = ScreenCols(); -#else - lines_per_page = 24; - chars_per_line = 80; - -#if !defined (MPW) && !defined (_WIN32) - /* No termcap under MPW, although might be cool to do something - by looking at worksheet or console window sizes. */ - /* Initialize the screen height and width from termcap. */ - { - char *termtype = getenv ("TERM"); - - /* Positive means success, nonpositive means failure. */ - int status; - - /* 2048 is large enough for all known terminals, according to the - GNU termcap manual. */ - char term_buffer[2048]; + init_page_info (); - if (termtype) - { - status = tgetent (term_buffer, termtype); - if (status > 0) - { - int val; - - val = tgetnum ("li"); - if (val >= 0) - lines_per_page = val; - else - /* The number of lines per page is not mentioned - in the terminal description. This probably means - that paging is not useful (e.g. emacs shell window), - so disable paging. */ - lines_per_page = UINT_MAX; - - val = tgetnum ("co"); - if (val >= 0) - chars_per_line = val; - } - } - } -#endif /* MPW */ - -#if defined(SIGWINCH) && defined(SIGWINCH_HANDLER) - - /* If there is a better way to determine the window size, use it. */ - SIGWINCH_HANDLER (SIGWINCH); -#endif -#endif /* If the output is not a terminal, don't paginate it. */ - if (!ISATTY (gdb_stdout)) + if (!GDB_FILE_ISATTY (gdb_stdout)) lines_per_page = UINT_MAX; set_width_command ((char *)NULL, 0, c); @@ -2158,6 +2366,19 @@ initialize_utils () &showprintlist); add_show_from_set + (add_set_cmd ("pagination", class_support, + var_boolean, (char *)&pagination_enabled, + "Set state of pagination.", &setlist), + &showlist); + if (xdb_commands) + { + add_com("am", class_support, pagination_on_command, + "Enable pagination"); + add_com("sm", class_support, pagination_off_command, + "Disable pagination"); + } + + add_show_from_set (add_set_cmd ("sevenbit-strings", class_support, var_boolean, (char *)&sevenbit_strings, "Set printing of 8-bit characters in strings as \\nnn.", diff --git a/gdb/win32-nat.c b/gdb/win32-nat.c index 04d8a9e..e0f07ef 100644 --- a/gdb/win32-nat.c +++ b/gdb/win32-nat.c @@ -424,7 +424,7 @@ handle_load_dll (char *dummy) FIXME: Is this the real reason that we need the 0x1000 ? */ printf_unfiltered ("%x:%s", event->lpBaseOfDll, dll_name); - symbol_file_add (dll_name, 0, (int) event->lpBaseOfDll + 0x1000, 0, 0, 0); + symbol_file_add (dll_name, 0, (int) event->lpBaseOfDll + 0x1000, 0, 0, 0, 0, 1); printf_unfiltered ("\n"); out: |