5b6d1e4fa4
This commit adds multi-target support to GDB. What this means is that with this commit, GDB can now be connected to different targets at the same time. E.g., you can debug a live native process and a core dump at the same time, connect to multiple gdbservers, etc. Actually, the word "target" is overloaded in gdb. We already have a target stack, with pushes several target_ops instances on top of one another. We also have "info target" already, which means something completely different to what this patch does. So from here on, I'll be using the "target connections" term, to mean an open process_stratum target, pushed on a target stack. This patch makes gdb have multiple target stacks, and multiple process_stratum targets open simultaneously. The user-visible changes / commands will also use this terminology, but of course it's all open to debate. User-interface-wise, not that much changes. The main difference is that each inferior may have its own target connection. A target connection (e.g., a target extended-remote connection) may support debugging multiple processes, just as before. Say you're debugging against gdbserver in extended-remote mode, and you do "add-inferior" to prepare to spawn a new process, like: (gdb) target extended-remote :9999 ... (gdb) start ... (gdb) add-inferior Added inferior 2 (gdb) inferior 2 [Switching to inferior 2 [<null>] (<noexec>)] (gdb) file a.out ... (gdb) start ... At this point, you have two inferiors connected to the same gdbserver. With this commit, GDB will maintain a target stack per inferior, instead of a global target stack. To preserve the behavior above, by default, "add-inferior" makes the new inferior inherit a copy of the target stack of the current inferior. Same across a fork - the child inherits a copy of the target stack of the parent. While the target stacks are copied, the targets themselves are not. Instead, target_ops is made a refcounted_object, which means that target_ops instances are refcounted, which each inferior counting for a reference. What if you want to create an inferior and connect it to some _other_ target? For that, this commit introduces a new "add-inferior -no-connection" option that makes the new inferior not share the current inferior's target. So you could do: (gdb) target extended-remote :9999 Remote debugging using :9999 ... (gdb) add-inferior -no-connection [New inferior 2] Added inferior 2 (gdb) inferior 2 [Switching to inferior 2 [<null>] (<noexec>)] (gdb) info inferiors Num Description Executable 1 process 18401 target:/home/pedro/tmp/main * 2 <null> (gdb) tar extended-remote :10000 Remote debugging using :10000 ... (gdb) info inferiors Num Description Executable 1 process 18401 target:/home/pedro/tmp/main * 2 process 18450 target:/home/pedro/tmp/main (gdb) A following patch will extended "info inferiors" to include a column indicating which connection an inferior is bound to, along with a couple other UI tweaks. Other than that, debugging is the same as before. Users interact with inferiors and threads as before. The only difference is that inferiors may be bound to processes running in different machines. That's pretty much all there is to it in terms of noticeable UI changes. On to implementation. Since we can be connected to different systems at the same time, a ptid_t is no longer a unique identifier. Instead a thread can be identified by a pair of ptid_t and 'process_stratum_target *', the later being the instance of the process_stratum target that owns the process/thread. Note that process_stratum_target inherits from target_ops, and all process_stratum targets inherit from process_stratum_target. In earlier patches, many places in gdb were converted to refer to threads by thread_info pointer instead of ptid_t, but there are still places in gdb where we start with a pid/tid and need to find the corresponding inferior or thread_info objects. So you'll see in the patch many places adding a process_stratum_target parameter to functions that used to take only a ptid_t. Since each inferior has its own target stack now, we can always find the process_stratum target for an inferior. That is done via a inf->process_target() convenience method. Since each inferior has its own target stack, we need to handle the "beneath" calls when servicing target calls. The solution I settled with is just to make sure to switch the current inferior to the inferior you want before making a target call. Not relying on global context is just not feasible in current GDB. Fortunately, there aren't that many places that need to do that, because generally most code that calls target methods already has the current context pointing to the right inferior/thread. Note, to emphasize -- there's no method to "switch to this target stack". Instead, you switch the current inferior, and that implicitly switches the target stack. In some spots, we need to iterate over all inferiors so that we reach all target stacks. Native targets are still singletons. There's always only a single instance of such targets. Remote targets however, we'll have one instance per remote connection. The exec target is still a singleton. There's only one instance. I did not see the point of instanciating more than one exec_target object. After vfork, we need to make sure to push the exec target on the new inferior. See exec_on_vfork. For type safety, functions that need a {target, ptid} pair to identify a thread, take a process_stratum_target pointer for target parameter instead of target_ops *. Some shared code in gdb/nat/ also need to gain a target pointer parameter. This poses an issue, since gdbserver doesn't have process_stratum_target, only target_ops. To fix this, this commit renames gdbserver's target_ops to process_stratum_target. I think this makes sense. There's no concept of target stack in gdbserver, and gdbserver's target_ops really implements a process_stratum-like target. The thread and inferior iterator functions also gain process_stratum_target parameters. These are used to be able to iterate over threads and inferiors of a given target. Following usual conventions, if the target pointer is null, then we iterate over threads and inferiors of all targets. I tried converting "add-inferior" to the gdb::option framework, as a preparatory patch, but that stumbled on the fact that gdb::option does not support file options yet, for "add-inferior -exec". I have a WIP patchset that adds that, but it's not a trivial patch, mainly due to need to integrate readline's filename completion, so I deferred that to some other time. In infrun.c/infcmd.c, the main change is that we need to poll events out of all targets. See do_target_wait. Right after collecting an event, we switch the current inferior to an inferior bound to the target that reported the event, so that target methods can be used while handling the event. This makes most of the code transparent to multi-targets. See fetch_inferior_event. infrun.c:stop_all_threads is interesting -- in this function we need to stop all threads of all targets. What the function does is send an asynchronous stop request to all threads, and then synchronously waits for events, with target_wait, rinse repeat, until all it finds are stopped threads. Now that we have multiple targets, it's not efficient to synchronously block in target_wait waiting for events out of one target. Instead, we implement a mini event loop, with interruptible_select, select'ing on one file descriptor per target. For this to work, we need to be able to ask the target for a waitable file descriptor. Such file descriptors already exist, they are the descriptors registered in the main event loop with add_file_handler, inside the target_async implementations. This commit adds a new target_async_wait_fd target method that just returns the file descriptor in question. See wait_one / stop_all_threads in infrun.c. The 'threads_executing' global is made a per-target variable. Since it is only relevant to process_stratum_target targets, this is where it is put, instead of in target_ops. You'll notice that remote.c includes some FIXME notes. These refer to the fact that the global arrays that hold data for the remote packets supported are still globals. For example, if we connect to two different servers/stubs, then each might support different remote protocol features. They might even be different architectures, like e.g., one ARM baremetal stub, and a x86 gdbserver, to debug a host/controller scenario as a single program. That isn't going to work correctly today, because of said globals. I'm leaving fixing that for another pass, since it does not appear to be trivial, and I'd rather land the base work first. It's already useful to be able to debug multiple instances of the same server (e.g., a distributed cluster, where you have full control over the servers installed), so I think as is it's already reasonable incremental progress. Current limitations: - You can only resume more that one target at the same time if all targets support asynchronous debugging, and support non-stop mode. It should be possible to support mixed all-stop + non-stop backends, but that is left for another time. This means that currently in order to do multi-target with gdbserver you need to issue "maint set target-non-stop on". I would like to make that mode be the default, but we're not there yet. Note that I'm talking about how the target backend works, only. User-visible all-stop mode works just fine. - As explained above, connecting to different remote servers at the same time is likely to produce bad results if they don't support the exact set of RSP features. FreeBSD updates courtesy of John Baldwin. gdb/ChangeLog: 2020-01-10 Pedro Alves <palves@redhat.com> John Baldwin <jhb@FreeBSD.org> * aarch64-linux-nat.c (aarch64_linux_nat_target::thread_architecture): Adjust. * ada-tasks.c (print_ada_task_info): Adjust find_thread_ptid call. (task_command_1): Likewise. * aix-thread.c (sync_threadlists, aix_thread_target::resume) (aix_thread_target::wait, aix_thread_target::fetch_registers) (aix_thread_target::store_registers) (aix_thread_target::thread_alive): Adjust. * amd64-fbsd-tdep.c: Include "inferior.h". (amd64fbsd_get_thread_local_address): Pass down target. * amd64-linux-nat.c (ps_get_thread_area): Use ps_prochandle thread's gdbarch instead of target_gdbarch. * break-catch-sig.c (signal_catchpoint_print_it): Adjust call to get_last_target_status. * break-catch-syscall.c (print_it_catch_syscall): Likewise. * breakpoint.c (breakpoints_should_be_inserted_now): Consider all inferiors. (update_inserted_breakpoint_locations): Skip if inferiors with no execution. (update_global_location_list): When handling moribund locations, find representative inferior for location's pspace, and use thread count of its process_stratum target. * bsd-kvm.c (bsd_kvm_target_open): Pass target down. * bsd-uthread.c (bsd_uthread_target::wait): Use as_process_stratum_target and adjust thread_change_ptid and add_thread calls. (bsd_uthread_target::update_thread_list): Use as_process_stratum_target and adjust find_thread_ptid, thread_change_ptid and add_thread calls. * btrace.c (maint_btrace_packet_history_cmd): Adjust find_thread_ptid call. * corelow.c (add_to_thread_list): Adjust add_thread call. (core_target_open): Adjust add_thread_silent and thread_count calls. (core_target::pid_to_str): Adjust find_inferior_ptid call. * ctf.c (ctf_target_open): Adjust add_thread_silent call. * event-top.c (async_disconnect): Pop targets from all inferiors. * exec.c (add_target_sections): Push exec target on all inferiors sharing the program space. (remove_target_sections): Remove the exec target from all inferiors sharing the program space. (exec_on_vfork): New. * exec.h (exec_on_vfork): Declare. * fbsd-nat.c (fbsd_add_threads): Add fbsd_nat_target parameter. Pass it down. (fbsd_nat_target::update_thread_list): Adjust. (fbsd_nat_target::resume): Adjust. (fbsd_handle_debug_trap): Add fbsd_nat_target parameter. Pass it down. (fbsd_nat_target::wait, fbsd_nat_target::post_attach): Adjust. * fbsd-tdep.c (fbsd_corefile_thread): Adjust get_thread_arch_regcache call. * fork-child.c (gdb_startup_inferior): Pass target down to startup_inferior and set_executing. * gdbthread.h (struct process_stratum_target): Forward declare. (add_thread, add_thread_silent, add_thread_with_info) (in_thread_list): Add process_stratum_target parameter. (find_thread_ptid(inferior*, ptid_t)): New overload. (find_thread_ptid, thread_change_ptid): Add process_stratum_target parameter. (all_threads()): Delete overload. (all_threads, all_non_exited_threads): Add process_stratum_target parameter. (all_threads_safe): Use brace initialization. (thread_count): Add process_stratum_target parameter. (set_resumed, set_running, set_stop_requested, set_executing) (threads_are_executing, finish_thread_state): Add process_stratum_target parameter. (switch_to_thread): Use is_current_thread. * i386-fbsd-tdep.c: Include "inferior.h". (i386fbsd_get_thread_local_address): Pass down target. * i386-linux-nat.c (i386_linux_nat_target::low_resume): Adjust. * inf-child.c (inf_child_target::maybe_unpush_target): Remove have_inferiors check. * inf-ptrace.c (inf_ptrace_target::create_inferior) (inf_ptrace_target::attach): Adjust. * infcall.c (run_inferior_call): Adjust. * infcmd.c (run_command_1): Pass target to scoped_finish_thread_state. (proceed_thread_callback): Skip inferiors with no execution. (continue_command): Rename 'all_threads' local to avoid hiding 'all_threads' function. Adjust get_last_target_status call. (prepare_one_step): Adjust set_running call. (signal_command): Use user_visible_resume_target. Compare thread pointers instead of inferior_ptid. (info_program_command): Adjust to pass down target. (attach_command): Mark target's 'thread_executing' flag. (stop_current_target_threads_ns): New, factored out from ... (interrupt_target_1): ... this. Switch inferior before making target calls. * inferior-iter.h (struct all_inferiors_iterator, struct all_inferiors_range) (struct all_inferiors_safe_range) (struct all_non_exited_inferiors_range): Filter on process_stratum_target too. Remove explicit. * inferior.c (inferior::inferior): Push dummy target on target stack. (find_inferior_pid, find_inferior_ptid, number_of_live_inferiors): Add process_stratum_target parameter, and pass it down. (have_live_inferiors): Adjust. (switch_to_inferior_and_push_target): New. (add_inferior_command, clone_inferior_command): Handle "-no-connection" parameter. Use switch_to_inferior_and_push_target. (_initialize_inferior): Mention "-no-connection" option in the help of "add-inferior" and "clone-inferior" commands. * inferior.h: Include "process-stratum-target.h". (interrupt_target_1): Use bool. (struct inferior) <push_target, unpush_target, target_is_pushed, find_target_beneath, top_target, process_target, target_at, m_stack>: New. (discard_all_inferiors): Delete. (find_inferior_pid, find_inferior_ptid, number_of_live_inferiors) (all_inferiors, all_non_exited_inferiors): Add process_stratum_target parameter. * infrun.c: Include "gdb_select.h" and <unordered_map>. (target_last_proc_target): New global. (follow_fork_inferior): Push target on new inferior. Pass target to add_thread_silent. Call exec_on_vfork. Handle target's reference count. (follow_fork): Adjust get_last_target_status call. Also consider target. (follow_exec): Push target on new inferior. (struct execution_control_state) <target>: New field. (user_visible_resume_target): New. (do_target_resume): Call target_async. (resume_1): Set target's threads_executing flag. Consider resume target. (commit_resume_all_targets): New. (proceed): Also consider resume target. Skip threads of inferiors with no execution. Commit resumtion in all targets. (start_remote): Pass current inferior to wait_for_inferior. (infrun_thread_stop_requested): Consider target as well. Pass thread_info pointer to clear_inline_frame_state instead of ptid. (infrun_thread_thread_exit): Consider target as well. (random_pending_event_thread): New inferior parameter. Use it. (do_target_wait): Rename to ... (do_target_wait_1): ... this. Add inferior parameter, and pass it down. (threads_are_resumed_pending_p, do_target_wait): New. (prepare_for_detach): Adjust calls. (wait_for_inferior): New inferior parameter. Handle it. Use do_target_wait_1 instead of do_target_wait. (fetch_inferior_event): Adjust. Switch to representative inferior. Pass target down. (set_last_target_status): Add process_stratum_target parameter. Save target in global. (get_last_target_status): Add process_stratum_target parameter and handle it. (nullify_last_target_wait_ptid): Clear 'target_last_proc_target'. (context_switch): Check inferior_ptid == null_ptid before calling inferior_thread(). (get_inferior_stop_soon): Pass down target. (wait_one): Rename to ... (poll_one_curr_target): ... this. (struct wait_one_event): New. (wait_one): New. (stop_all_threads): Adjust. (handle_no_resumed, handle_inferior_event): Adjust to consider the event's target. (switch_back_to_stepped_thread): Also consider target. (print_stop_event): Update. (normal_stop): Update. Also consider the resume target. * infrun.h (wait_for_inferior): Remove declaration. (user_visible_resume_target): New declaration. (get_last_target_status, set_last_target_status): New process_stratum_target parameter. * inline-frame.c (clear_inline_frame_state(ptid_t)): Add process_stratum_target parameter, and use it. (clear_inline_frame_state (thread_info*)): New. * inline-frame.c (clear_inline_frame_state(ptid_t)): Add process_stratum_target parameter. (clear_inline_frame_state (thread_info*)): Declare. * linux-fork.c (delete_checkpoint_command): Pass target down to find_thread_ptid. (checkpoint_command): Adjust. * linux-nat.c (linux_nat_target::follow_fork): Switch to thread instead of just tweaking inferior_ptid. (linux_nat_switch_fork): Pass target down to thread_change_ptid. (exit_lwp): Pass target down to find_thread_ptid. (attach_proc_task_lwp_callback): Pass target down to add_thread/set_running/set_executing. (linux_nat_target::attach): Pass target down to thread_change_ptid. (get_detach_signal): Pass target down to find_thread_ptid. Consider last target status's target. (linux_resume_one_lwp_throw, resume_lwp) (linux_handle_syscall_trap, linux_handle_extended_wait, wait_lwp) (stop_wait_callback, save_stop_reason, linux_nat_filter_event) (linux_nat_wait_1, resume_stopped_resumed_lwps): Pass target down. (linux_nat_target::async_wait_fd): New. (linux_nat_stop_lwp, linux_nat_target::thread_address_space): Pass target down. * linux-nat.h (linux_nat_target::async_wait_fd): Declare. * linux-tdep.c (get_thread_arch_regcache): Pass target down. * linux-thread-db.c (struct thread_db_info::process_target): New field. (add_thread_db_info): Save target. (get_thread_db_info): New process_stratum_target parameter. Also match target. (delete_thread_db_info): New process_stratum_target parameter. Also match target. (thread_from_lwp): Adjust to pass down target. (thread_db_notice_clone): Pass down target. (check_thread_db_callback): Pass down target. (try_thread_db_load_1): Always push the thread_db target. (try_thread_db_load, record_thread): Pass target down. (thread_db_target::detach): Pass target down. Always unpush the thread_db target. (thread_db_target::wait, thread_db_target::mourn_inferior): Pass target down. Always unpush the thread_db target. (find_new_threads_callback, thread_db_find_new_threads_2) (thread_db_target::update_thread_list): Pass target down. (thread_db_target::pid_to_str): Pass current inferior down. (thread_db_target::get_thread_local_address): Pass target down. (thread_db_target::resume, maintenance_check_libthread_db): Pass target down. * nto-procfs.c (nto_procfs_target::update_thread_list): Adjust. * procfs.c (procfs_target::procfs_init_inferior): Declare. (proc_set_current_signal, do_attach, procfs_target::wait): Adjust. (procfs_init_inferior): Rename to ... (procfs_target::procfs_init_inferior): ... this and adjust. (procfs_target::create_inferior, procfs_notice_thread) (procfs_do_thread_registers): Adjust. * ppc-fbsd-tdep.c: Include "inferior.h". (ppcfbsd_get_thread_local_address): Pass down target. * proc-service.c (ps_xfer_memory): Switch current inferior and program space as well. (get_ps_regcache): Pass target down. * process-stratum-target.c (process_stratum_target::thread_address_space) (process_stratum_target::thread_architecture): Pass target down. * process-stratum-target.h (process_stratum_target::threads_executing): New field. (as_process_stratum_target): New. * ravenscar-thread.c (ravenscar_thread_target::update_inferior_ptid): Pass target down. (ravenscar_thread_target::wait, ravenscar_add_thread): Pass target down. * record-btrace.c (record_btrace_target::info_record): Adjust. (record_btrace_target::record_method) (record_btrace_target::record_is_replaying) (record_btrace_target::fetch_registers) (get_thread_current_frame_id, record_btrace_target::resume) (record_btrace_target::wait, record_btrace_target::stop): Pass target down. * record-full.c (record_full_wait_1): Switch to event thread. Pass target down. * regcache.c (regcache::regcache) (get_thread_arch_aspace_regcache, get_thread_arch_regcache): Add process_stratum_target parameter and handle it. (current_thread_target): New global. (get_thread_regcache): Add process_stratum_target parameter and handle it. Switch inferior before calling target method. (get_thread_regcache): Pass target down. (get_thread_regcache_for_ptid): Pass target down. (registers_changed_ptid): Add process_stratum_target parameter and handle it. (registers_changed_thread, registers_changed): Pass target down. (test_get_thread_arch_aspace_regcache): New. (current_regcache_test): Define a couple local test_target_ops instances and use them for testing. (readwrite_regcache): Pass process_stratum_target parameter. (cooked_read_test, cooked_write_test): Pass mock_target down. * regcache.h (get_thread_regcache, get_thread_arch_regcache) (get_thread_arch_aspace_regcache): Add process_stratum_target parameter. (regcache::target): New method. (regcache::regcache, regcache::get_thread_arch_aspace_regcache) (regcache::registers_changed_ptid): Add process_stratum_target parameter. (regcache::m_target): New field. (registers_changed_ptid): Add process_stratum_target parameter. * remote.c (remote_state::supports_vCont_probed): New field. (remote_target::async_wait_fd): New method. (remote_unpush_and_throw): Add remote_target parameter. (get_current_remote_target): Adjust. (remote_target::remote_add_inferior): Push target. (remote_target::remote_add_thread) (remote_target::remote_notice_new_inferior) (get_remote_thread_info): Pass target down. (remote_target::update_thread_list): Skip threads of inferiors bound to other targets. (remote_target::close): Don't discard inferiors. (remote_target::add_current_inferior_and_thread) (remote_target::process_initial_stop_replies) (remote_target::start_remote) (remote_target::remote_serial_quit_handler): Pass down target. (remote_target::remote_unpush_target): New remote_target parameter. Unpush the target from all inferiors. (remote_target::remote_unpush_and_throw): New remote_target parameter. Pass it down. (remote_target::open_1): Check whether the current inferior has execution instead of checking whether any inferior is live. Pass target down. (remote_target::remote_detach_1): Pass down target. Use remote_unpush_target. (extended_remote_target::attach): Pass down target. (remote_target::remote_vcont_probe): Set supports_vCont_probed. (remote_target::append_resumption): Pass down target. (remote_target::append_pending_thread_resumptions) (remote_target::remote_resume_with_hc, remote_target::resume) (remote_target::commit_resume): Pass down target. (remote_target::remote_stop_ns): Check supports_vCont_probed. (remote_target::interrupt_query) (remote_target::remove_new_fork_children) (remote_target::check_pending_events_prevent_wildcard_vcont) (remote_target::remote_parse_stop_reply) (remote_target::process_stop_reply): Pass down target. (first_remote_resumed_thread): New remote_target parameter. Pass it down. (remote_target::wait_as): Pass down target. (unpush_and_perror): New remote_target parameter. Pass it down. (remote_target::readchar, remote_target::remote_serial_write) (remote_target::getpkt_or_notif_sane_1) (remote_target::kill_new_fork_children, remote_target::kill): Pass down target. (remote_target::mourn_inferior): Pass down target. Use remote_unpush_target. (remote_target::core_of_thread) (remote_target::remote_btrace_maybe_reopen): Pass down target. (remote_target::pid_to_exec_file) (remote_target::thread_handle_to_thread_info): Pass down target. (remote_target::async_wait_fd): New. * riscv-fbsd-tdep.c: Include "inferior.h". (riscv_fbsd_get_thread_local_address): Pass down target. * sol2-tdep.c (sol2_core_pid_to_str): Pass down target. * sol-thread.c (sol_thread_target::wait, ps_lgetregs, ps_lsetregs) (ps_lgetfpregs, ps_lsetfpregs, sol_update_thread_list_callback): Adjust. * solib-spu.c (spu_skip_standalone_loader): Pass down target. * solib-svr4.c (enable_break): Pass down target. * spu-multiarch.c (parse_spufs_run): Pass down target. * spu-tdep.c (spu2ppu_sniffer): Pass down target. * target-delegates.c: Regenerate. * target.c (g_target_stack): Delete. (current_top_target): Return the current inferior's top target. (target_has_execution_1): Refer to the passed-in inferior's top target. (target_supports_terminal_ours): Check whether the initial inferior was already created. (decref_target): New. (target_stack::push): Incref/decref the target. (push_target, push_target, unpush_target): Adjust. (target_stack::unpush): Defref target. (target_is_pushed): Return bool. Adjust to refer to the current inferior's target stack. (dispose_inferior): Delete, and inline parts ... (target_preopen): ... here. Only dispose of the current inferior. (target_detach): Hold strong target reference while detaching. Pass target down. (target_thread_name): Add assertion. (target_resume): Pass down target. (target_ops::beneath, find_target_at): Adjust to refer to the current inferior's target stack. (get_dummy_target): New. (target_pass_ctrlc): Pass the Ctrl-C to the first inferior that has a thread running. (initialize_targets): Rename to ... (_initialize_target): ... this. * target.h: Include "gdbsupport/refcounted-object.h". (struct target_ops): Inherit refcounted_object. (target_ops::shortname, target_ops::longname): Make const. (target_ops::async_wait_fd): New method. (decref_target): Declare. (struct target_ops_ref_policy): New. (target_ops_ref): New typedef. (get_dummy_target): Declare function. (target_is_pushed): Return bool. * thread-iter.c (all_matching_threads_iterator::m_inf_matches) (all_matching_threads_iterator::all_matching_threads_iterator): Handle filter target. * thread-iter.h (struct all_matching_threads_iterator, struct all_matching_threads_range, class all_non_exited_threads_range): Filter by target too. Remove explicit. * thread.c (threads_executing): Delete. (inferior_thread): Pass down current inferior. (clear_thread_inferior_resources): Pass down thread pointer instead of ptid_t. (add_thread_silent, add_thread_with_info, add_thread): Add process_stratum_target parameter. Use it for thread and inferior searches. (is_current_thread): New. (thread_info::deletable): Use it. (find_thread_ptid, thread_count, in_thread_list) (thread_change_ptid, set_resumed, set_running): New process_stratum_target parameter. Pass it down. (set_executing): New process_stratum_target parameter. Pass it down. Adjust reference to 'threads_executing'. (threads_are_executing): New process_stratum_target parameter. Adjust reference to 'threads_executing'. (set_stop_requested, finish_thread_state): New process_stratum_target parameter. Pass it down. (switch_to_thread): Also match inferior. (switch_to_thread): New process_stratum_target parameter. Pass it down. (update_threads_executing): Reimplement. * top.c (quit_force): Pop targets from all inferior. (gdb_init): Don't call initialize_targets. * windows-nat.c (windows_nat_target) <get_windows_debug_event>: Declare. (windows_add_thread, windows_delete_thread): Adjust. (get_windows_debug_event): Rename to ... (windows_nat_target::get_windows_debug_event): ... this. Adjust. * tracefile-tfile.c (tfile_target_open): Pass down target. * gdbsupport/common-gdbthread.h (struct process_stratum_target): Forward declare. (switch_to_thread): Add process_stratum_target parameter. * mi/mi-interp.c (mi_on_resume_1): Add process_stratum_target parameter. Use it. (mi_on_resume): Pass target down. * nat/fork-inferior.c (startup_inferior): Add process_stratum_target parameter. Pass it down. * nat/fork-inferior.h (startup_inferior): Add process_stratum_target parameter. * python/py-threadevent.c (py_get_event_thread): Pass target down. gdb/gdbserver/ChangeLog: 2020-01-10 Pedro Alves <palves@redhat.com> * fork-child.c (post_fork_inferior): Pass target down to startup_inferior. * inferiors.c (switch_to_thread): Add process_stratum_target parameter. * lynx-low.c (lynx_target_ops): Now a process_stratum_target. * nto-low.c (nto_target_ops): Now a process_stratum_target. * linux-low.c (linux_target_ops): Now a process_stratum_target. * remote-utils.c (prepare_resume_reply): Pass the target to switch_to_thread. * target.c (the_target): Now a process_stratum_target. (done_accessing_memory): Pass the target to switch_to_thread. (set_target_ops): Ajust to use process_stratum_target. * target.h (struct target_ops): Rename to ... (struct process_stratum_target): ... this. (the_target, set_target_ops): Adjust. (prepare_to_access_memory): Adjust comment. * win32-low.c (child_xfer_memory): Adjust to use process_stratum_target. (win32_target_ops): Now a process_stratum_target.
2523 lines
76 KiB
C
2523 lines
76 KiB
C
/* Target-dependent code for GNU/Linux, architecture independent.
|
|
|
|
Copyright (C) 2009-2020 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 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
|
|
|
#include "defs.h"
|
|
#include "gdbtypes.h"
|
|
#include "linux-tdep.h"
|
|
#include "auxv.h"
|
|
#include "target.h"
|
|
#include "gdbthread.h"
|
|
#include "gdbcore.h"
|
|
#include "regcache.h"
|
|
#include "regset.h"
|
|
#include "elf/common.h"
|
|
#include "elf-bfd.h" /* for elfcore_write_* */
|
|
#include "inferior.h"
|
|
#include "cli/cli-utils.h"
|
|
#include "arch-utils.h"
|
|
#include "gdb_obstack.h"
|
|
#include "observable.h"
|
|
#include "objfiles.h"
|
|
#include "infcall.h"
|
|
#include "gdbcmd.h"
|
|
#include "gdb_regex.h"
|
|
#include "gdbsupport/enum-flags.h"
|
|
#include "gdbsupport/gdb_optional.h"
|
|
|
|
#include <ctype.h>
|
|
|
|
/* This enum represents the values that the user can choose when
|
|
informing the Linux kernel about which memory mappings will be
|
|
dumped in a corefile. They are described in the file
|
|
Documentation/filesystems/proc.txt, inside the Linux kernel
|
|
tree. */
|
|
|
|
enum filter_flag
|
|
{
|
|
COREFILTER_ANON_PRIVATE = 1 << 0,
|
|
COREFILTER_ANON_SHARED = 1 << 1,
|
|
COREFILTER_MAPPED_PRIVATE = 1 << 2,
|
|
COREFILTER_MAPPED_SHARED = 1 << 3,
|
|
COREFILTER_ELF_HEADERS = 1 << 4,
|
|
COREFILTER_HUGETLB_PRIVATE = 1 << 5,
|
|
COREFILTER_HUGETLB_SHARED = 1 << 6,
|
|
};
|
|
DEF_ENUM_FLAGS_TYPE (enum filter_flag, filter_flags);
|
|
|
|
/* This struct is used to map flags found in the "VmFlags:" field (in
|
|
the /proc/<PID>/smaps file). */
|
|
|
|
struct smaps_vmflags
|
|
{
|
|
/* Zero if this structure has not been initialized yet. It
|
|
probably means that the Linux kernel being used does not emit
|
|
the "VmFlags:" field on "/proc/PID/smaps". */
|
|
|
|
unsigned int initialized_p : 1;
|
|
|
|
/* Memory mapped I/O area (VM_IO, "io"). */
|
|
|
|
unsigned int io_page : 1;
|
|
|
|
/* Area uses huge TLB pages (VM_HUGETLB, "ht"). */
|
|
|
|
unsigned int uses_huge_tlb : 1;
|
|
|
|
/* Do not include this memory region on the coredump (VM_DONTDUMP, "dd"). */
|
|
|
|
unsigned int exclude_coredump : 1;
|
|
|
|
/* Is this a MAP_SHARED mapping (VM_SHARED, "sh"). */
|
|
|
|
unsigned int shared_mapping : 1;
|
|
};
|
|
|
|
/* Whether to take the /proc/PID/coredump_filter into account when
|
|
generating a corefile. */
|
|
|
|
static bool use_coredump_filter = true;
|
|
|
|
/* Whether the value of smaps_vmflags->exclude_coredump should be
|
|
ignored, including mappings marked with the VM_DONTDUMP flag in
|
|
the dump. */
|
|
static bool dump_excluded_mappings = false;
|
|
|
|
/* This enum represents the signals' numbers on a generic architecture
|
|
running the Linux kernel. The definition of "generic" comes from
|
|
the file <include/uapi/asm-generic/signal.h>, from the Linux kernel
|
|
tree, which is the "de facto" implementation of signal numbers to
|
|
be used by new architecture ports.
|
|
|
|
For those architectures which have differences between the generic
|
|
standard (e.g., Alpha), we define the different signals (and *only*
|
|
those) in the specific target-dependent file (e.g.,
|
|
alpha-linux-tdep.c, for Alpha). Please refer to the architecture's
|
|
tdep file for more information.
|
|
|
|
ARM deserves a special mention here. On the file
|
|
<arch/arm/include/uapi/asm/signal.h>, it defines only one different
|
|
(and ARM-only) signal, which is SIGSWI, with the same number as
|
|
SIGRTMIN. This signal is used only for a very specific target,
|
|
called ArthurOS (from RISCOS). Therefore, we do not handle it on
|
|
the ARM-tdep file, and we can safely use the generic signal handler
|
|
here for ARM targets.
|
|
|
|
As stated above, this enum is derived from
|
|
<include/uapi/asm-generic/signal.h>, from the Linux kernel
|
|
tree. */
|
|
|
|
enum
|
|
{
|
|
LINUX_SIGHUP = 1,
|
|
LINUX_SIGINT = 2,
|
|
LINUX_SIGQUIT = 3,
|
|
LINUX_SIGILL = 4,
|
|
LINUX_SIGTRAP = 5,
|
|
LINUX_SIGABRT = 6,
|
|
LINUX_SIGIOT = 6,
|
|
LINUX_SIGBUS = 7,
|
|
LINUX_SIGFPE = 8,
|
|
LINUX_SIGKILL = 9,
|
|
LINUX_SIGUSR1 = 10,
|
|
LINUX_SIGSEGV = 11,
|
|
LINUX_SIGUSR2 = 12,
|
|
LINUX_SIGPIPE = 13,
|
|
LINUX_SIGALRM = 14,
|
|
LINUX_SIGTERM = 15,
|
|
LINUX_SIGSTKFLT = 16,
|
|
LINUX_SIGCHLD = 17,
|
|
LINUX_SIGCONT = 18,
|
|
LINUX_SIGSTOP = 19,
|
|
LINUX_SIGTSTP = 20,
|
|
LINUX_SIGTTIN = 21,
|
|
LINUX_SIGTTOU = 22,
|
|
LINUX_SIGURG = 23,
|
|
LINUX_SIGXCPU = 24,
|
|
LINUX_SIGXFSZ = 25,
|
|
LINUX_SIGVTALRM = 26,
|
|
LINUX_SIGPROF = 27,
|
|
LINUX_SIGWINCH = 28,
|
|
LINUX_SIGIO = 29,
|
|
LINUX_SIGPOLL = LINUX_SIGIO,
|
|
LINUX_SIGPWR = 30,
|
|
LINUX_SIGSYS = 31,
|
|
LINUX_SIGUNUSED = 31,
|
|
|
|
LINUX_SIGRTMIN = 32,
|
|
LINUX_SIGRTMAX = 64,
|
|
};
|
|
|
|
static struct gdbarch_data *linux_gdbarch_data_handle;
|
|
|
|
struct linux_gdbarch_data
|
|
{
|
|
struct type *siginfo_type;
|
|
};
|
|
|
|
static void *
|
|
init_linux_gdbarch_data (struct gdbarch *gdbarch)
|
|
{
|
|
return GDBARCH_OBSTACK_ZALLOC (gdbarch, struct linux_gdbarch_data);
|
|
}
|
|
|
|
static struct linux_gdbarch_data *
|
|
get_linux_gdbarch_data (struct gdbarch *gdbarch)
|
|
{
|
|
return ((struct linux_gdbarch_data *)
|
|
gdbarch_data (gdbarch, linux_gdbarch_data_handle));
|
|
}
|
|
|
|
/* Linux-specific cached data. This is used by GDB for caching
|
|
purposes for each inferior. This helps reduce the overhead of
|
|
transfering data from a remote target to the local host. */
|
|
struct linux_info
|
|
{
|
|
/* Cache of the inferior's vsyscall/vDSO mapping range. Only valid
|
|
if VSYSCALL_RANGE_P is positive. This is cached because getting
|
|
at this info requires an auxv lookup (which is itself cached),
|
|
and looking through the inferior's mappings (which change
|
|
throughout execution and therefore cannot be cached). */
|
|
struct mem_range vsyscall_range {};
|
|
|
|
/* Zero if we haven't tried looking up the vsyscall's range before
|
|
yet. Positive if we tried looking it up, and found it. Negative
|
|
if we tried looking it up but failed. */
|
|
int vsyscall_range_p = 0;
|
|
};
|
|
|
|
/* Per-inferior data key. */
|
|
static const struct inferior_key<linux_info> linux_inferior_data;
|
|
|
|
/* Frees whatever allocated space there is to be freed and sets INF's
|
|
linux cache data pointer to NULL. */
|
|
|
|
static void
|
|
invalidate_linux_cache_inf (struct inferior *inf)
|
|
{
|
|
linux_inferior_data.clear (inf);
|
|
}
|
|
|
|
/* Fetch the linux cache info for INF. This function always returns a
|
|
valid INFO pointer. */
|
|
|
|
static struct linux_info *
|
|
get_linux_inferior_data (void)
|
|
{
|
|
struct linux_info *info;
|
|
struct inferior *inf = current_inferior ();
|
|
|
|
info = linux_inferior_data.get (inf);
|
|
if (info == NULL)
|
|
info = linux_inferior_data.emplace (inf);
|
|
|
|
return info;
|
|
}
|
|
|
|
/* See linux-tdep.h. */
|
|
|
|
struct type *
|
|
linux_get_siginfo_type_with_fields (struct gdbarch *gdbarch,
|
|
linux_siginfo_extra_fields extra_fields)
|
|
{
|
|
struct linux_gdbarch_data *linux_gdbarch_data;
|
|
struct type *int_type, *uint_type, *long_type, *void_ptr_type, *short_type;
|
|
struct type *uid_type, *pid_type;
|
|
struct type *sigval_type, *clock_type;
|
|
struct type *siginfo_type, *sifields_type;
|
|
struct type *type;
|
|
|
|
linux_gdbarch_data = get_linux_gdbarch_data (gdbarch);
|
|
if (linux_gdbarch_data->siginfo_type != NULL)
|
|
return linux_gdbarch_data->siginfo_type;
|
|
|
|
int_type = arch_integer_type (gdbarch, gdbarch_int_bit (gdbarch),
|
|
0, "int");
|
|
uint_type = arch_integer_type (gdbarch, gdbarch_int_bit (gdbarch),
|
|
1, "unsigned int");
|
|
long_type = arch_integer_type (gdbarch, gdbarch_long_bit (gdbarch),
|
|
0, "long");
|
|
short_type = arch_integer_type (gdbarch, gdbarch_long_bit (gdbarch),
|
|
0, "short");
|
|
void_ptr_type = lookup_pointer_type (builtin_type (gdbarch)->builtin_void);
|
|
|
|
/* sival_t */
|
|
sigval_type = arch_composite_type (gdbarch, NULL, TYPE_CODE_UNION);
|
|
TYPE_NAME (sigval_type) = xstrdup ("sigval_t");
|
|
append_composite_type_field (sigval_type, "sival_int", int_type);
|
|
append_composite_type_field (sigval_type, "sival_ptr", void_ptr_type);
|
|
|
|
/* __pid_t */
|
|
pid_type = arch_type (gdbarch, TYPE_CODE_TYPEDEF,
|
|
TYPE_LENGTH (int_type) * TARGET_CHAR_BIT, "__pid_t");
|
|
TYPE_TARGET_TYPE (pid_type) = int_type;
|
|
TYPE_TARGET_STUB (pid_type) = 1;
|
|
|
|
/* __uid_t */
|
|
uid_type = arch_type (gdbarch, TYPE_CODE_TYPEDEF,
|
|
TYPE_LENGTH (uint_type) * TARGET_CHAR_BIT, "__uid_t");
|
|
TYPE_TARGET_TYPE (uid_type) = uint_type;
|
|
TYPE_TARGET_STUB (uid_type) = 1;
|
|
|
|
/* __clock_t */
|
|
clock_type = arch_type (gdbarch, TYPE_CODE_TYPEDEF,
|
|
TYPE_LENGTH (long_type) * TARGET_CHAR_BIT,
|
|
"__clock_t");
|
|
TYPE_TARGET_TYPE (clock_type) = long_type;
|
|
TYPE_TARGET_STUB (clock_type) = 1;
|
|
|
|
/* _sifields */
|
|
sifields_type = arch_composite_type (gdbarch, NULL, TYPE_CODE_UNION);
|
|
|
|
{
|
|
const int si_max_size = 128;
|
|
int si_pad_size;
|
|
int size_of_int = gdbarch_int_bit (gdbarch) / HOST_CHAR_BIT;
|
|
|
|
/* _pad */
|
|
if (gdbarch_ptr_bit (gdbarch) == 64)
|
|
si_pad_size = (si_max_size / size_of_int) - 4;
|
|
else
|
|
si_pad_size = (si_max_size / size_of_int) - 3;
|
|
append_composite_type_field (sifields_type, "_pad",
|
|
init_vector_type (int_type, si_pad_size));
|
|
}
|
|
|
|
/* _kill */
|
|
type = arch_composite_type (gdbarch, NULL, TYPE_CODE_STRUCT);
|
|
append_composite_type_field (type, "si_pid", pid_type);
|
|
append_composite_type_field (type, "si_uid", uid_type);
|
|
append_composite_type_field (sifields_type, "_kill", type);
|
|
|
|
/* _timer */
|
|
type = arch_composite_type (gdbarch, NULL, TYPE_CODE_STRUCT);
|
|
append_composite_type_field (type, "si_tid", int_type);
|
|
append_composite_type_field (type, "si_overrun", int_type);
|
|
append_composite_type_field (type, "si_sigval", sigval_type);
|
|
append_composite_type_field (sifields_type, "_timer", type);
|
|
|
|
/* _rt */
|
|
type = arch_composite_type (gdbarch, NULL, TYPE_CODE_STRUCT);
|
|
append_composite_type_field (type, "si_pid", pid_type);
|
|
append_composite_type_field (type, "si_uid", uid_type);
|
|
append_composite_type_field (type, "si_sigval", sigval_type);
|
|
append_composite_type_field (sifields_type, "_rt", type);
|
|
|
|
/* _sigchld */
|
|
type = arch_composite_type (gdbarch, NULL, TYPE_CODE_STRUCT);
|
|
append_composite_type_field (type, "si_pid", pid_type);
|
|
append_composite_type_field (type, "si_uid", uid_type);
|
|
append_composite_type_field (type, "si_status", int_type);
|
|
append_composite_type_field (type, "si_utime", clock_type);
|
|
append_composite_type_field (type, "si_stime", clock_type);
|
|
append_composite_type_field (sifields_type, "_sigchld", type);
|
|
|
|
/* _sigfault */
|
|
type = arch_composite_type (gdbarch, NULL, TYPE_CODE_STRUCT);
|
|
append_composite_type_field (type, "si_addr", void_ptr_type);
|
|
|
|
/* Additional bound fields for _sigfault in case they were requested. */
|
|
if ((extra_fields & LINUX_SIGINFO_FIELD_ADDR_BND) != 0)
|
|
{
|
|
struct type *sigfault_bnd_fields;
|
|
|
|
append_composite_type_field (type, "_addr_lsb", short_type);
|
|
sigfault_bnd_fields = arch_composite_type (gdbarch, NULL, TYPE_CODE_STRUCT);
|
|
append_composite_type_field (sigfault_bnd_fields, "_lower", void_ptr_type);
|
|
append_composite_type_field (sigfault_bnd_fields, "_upper", void_ptr_type);
|
|
append_composite_type_field (type, "_addr_bnd", sigfault_bnd_fields);
|
|
}
|
|
append_composite_type_field (sifields_type, "_sigfault", type);
|
|
|
|
/* _sigpoll */
|
|
type = arch_composite_type (gdbarch, NULL, TYPE_CODE_STRUCT);
|
|
append_composite_type_field (type, "si_band", long_type);
|
|
append_composite_type_field (type, "si_fd", int_type);
|
|
append_composite_type_field (sifields_type, "_sigpoll", type);
|
|
|
|
/* struct siginfo */
|
|
siginfo_type = arch_composite_type (gdbarch, NULL, TYPE_CODE_STRUCT);
|
|
TYPE_NAME (siginfo_type) = xstrdup ("siginfo");
|
|
append_composite_type_field (siginfo_type, "si_signo", int_type);
|
|
append_composite_type_field (siginfo_type, "si_errno", int_type);
|
|
append_composite_type_field (siginfo_type, "si_code", int_type);
|
|
append_composite_type_field_aligned (siginfo_type,
|
|
"_sifields", sifields_type,
|
|
TYPE_LENGTH (long_type));
|
|
|
|
linux_gdbarch_data->siginfo_type = siginfo_type;
|
|
|
|
return siginfo_type;
|
|
}
|
|
|
|
/* This function is suitable for architectures that don't
|
|
extend/override the standard siginfo structure. */
|
|
|
|
static struct type *
|
|
linux_get_siginfo_type (struct gdbarch *gdbarch)
|
|
{
|
|
return linux_get_siginfo_type_with_fields (gdbarch, 0);
|
|
}
|
|
|
|
/* Return true if the target is running on uClinux instead of normal
|
|
Linux kernel. */
|
|
|
|
int
|
|
linux_is_uclinux (void)
|
|
{
|
|
CORE_ADDR dummy;
|
|
|
|
return (target_auxv_search (current_top_target (), AT_NULL, &dummy) > 0
|
|
&& target_auxv_search (current_top_target (), AT_PAGESZ, &dummy) == 0);
|
|
}
|
|
|
|
static int
|
|
linux_has_shared_address_space (struct gdbarch *gdbarch)
|
|
{
|
|
return linux_is_uclinux ();
|
|
}
|
|
|
|
/* This is how we want PTIDs from core files to be printed. */
|
|
|
|
static std::string
|
|
linux_core_pid_to_str (struct gdbarch *gdbarch, ptid_t ptid)
|
|
{
|
|
if (ptid.lwp () != 0)
|
|
return string_printf ("LWP %ld", ptid.lwp ());
|
|
|
|
return normal_pid_to_str (ptid);
|
|
}
|
|
|
|
/* Service function for corefiles and info proc. */
|
|
|
|
static void
|
|
read_mapping (const char *line,
|
|
ULONGEST *addr, ULONGEST *endaddr,
|
|
const char **permissions, size_t *permissions_len,
|
|
ULONGEST *offset,
|
|
const char **device, size_t *device_len,
|
|
ULONGEST *inode,
|
|
const char **filename)
|
|
{
|
|
const char *p = line;
|
|
|
|
*addr = strtoulst (p, &p, 16);
|
|
if (*p == '-')
|
|
p++;
|
|
*endaddr = strtoulst (p, &p, 16);
|
|
|
|
p = skip_spaces (p);
|
|
*permissions = p;
|
|
while (*p && !isspace (*p))
|
|
p++;
|
|
*permissions_len = p - *permissions;
|
|
|
|
*offset = strtoulst (p, &p, 16);
|
|
|
|
p = skip_spaces (p);
|
|
*device = p;
|
|
while (*p && !isspace (*p))
|
|
p++;
|
|
*device_len = p - *device;
|
|
|
|
*inode = strtoulst (p, &p, 10);
|
|
|
|
p = skip_spaces (p);
|
|
*filename = p;
|
|
}
|
|
|
|
/* Helper function to decode the "VmFlags" field in /proc/PID/smaps.
|
|
|
|
This function was based on the documentation found on
|
|
<Documentation/filesystems/proc.txt>, on the Linux kernel.
|
|
|
|
Linux kernels before commit
|
|
834f82e2aa9a8ede94b17b656329f850c1471514 (3.10) do not have this
|
|
field on smaps. */
|
|
|
|
static void
|
|
decode_vmflags (char *p, struct smaps_vmflags *v)
|
|
{
|
|
char *saveptr = NULL;
|
|
const char *s;
|
|
|
|
v->initialized_p = 1;
|
|
p = skip_to_space (p);
|
|
p = skip_spaces (p);
|
|
|
|
for (s = strtok_r (p, " ", &saveptr);
|
|
s != NULL;
|
|
s = strtok_r (NULL, " ", &saveptr))
|
|
{
|
|
if (strcmp (s, "io") == 0)
|
|
v->io_page = 1;
|
|
else if (strcmp (s, "ht") == 0)
|
|
v->uses_huge_tlb = 1;
|
|
else if (strcmp (s, "dd") == 0)
|
|
v->exclude_coredump = 1;
|
|
else if (strcmp (s, "sh") == 0)
|
|
v->shared_mapping = 1;
|
|
}
|
|
}
|
|
|
|
/* Regexes used by mapping_is_anonymous_p. Put in a structure because
|
|
they're initialized lazily. */
|
|
|
|
struct mapping_regexes
|
|
{
|
|
/* Matches "/dev/zero" filenames (with or without the "(deleted)"
|
|
string in the end). We know for sure, based on the Linux kernel
|
|
code, that memory mappings whose associated filename is
|
|
"/dev/zero" are guaranteed to be MAP_ANONYMOUS. */
|
|
compiled_regex dev_zero
|
|
{"^/dev/zero\\( (deleted)\\)\\?$", REG_NOSUB,
|
|
_("Could not compile regex to match /dev/zero filename")};
|
|
|
|
/* Matches "/SYSV%08x" filenames (with or without the "(deleted)"
|
|
string in the end). These filenames refer to shared memory
|
|
(shmem), and memory mappings associated with them are
|
|
MAP_ANONYMOUS as well. */
|
|
compiled_regex shmem_file
|
|
{"^/\\?SYSV[0-9a-fA-F]\\{8\\}\\( (deleted)\\)\\?$", REG_NOSUB,
|
|
_("Could not compile regex to match shmem filenames")};
|
|
|
|
/* A heuristic we use to try to mimic the Linux kernel's 'n_link ==
|
|
0' code, which is responsible to decide if it is dealing with a
|
|
'MAP_SHARED | MAP_ANONYMOUS' mapping. In other words, if
|
|
FILE_DELETED matches, it does not necessarily mean that we are
|
|
dealing with an anonymous shared mapping. However, there is no
|
|
easy way to detect this currently, so this is the best
|
|
approximation we have.
|
|
|
|
As a result, GDB will dump readonly pages of deleted executables
|
|
when using the default value of coredump_filter (0x33), while the
|
|
Linux kernel will not dump those pages. But we can live with
|
|
that. */
|
|
compiled_regex file_deleted
|
|
{" (deleted)$", REG_NOSUB,
|
|
_("Could not compile regex to match '<file> (deleted)'")};
|
|
};
|
|
|
|
/* Return 1 if the memory mapping is anonymous, 0 otherwise.
|
|
|
|
FILENAME is the name of the file present in the first line of the
|
|
memory mapping, in the "/proc/PID/smaps" output. For example, if
|
|
the first line is:
|
|
|
|
7fd0ca877000-7fd0d0da0000 r--p 00000000 fd:02 2100770 /path/to/file
|
|
|
|
Then FILENAME will be "/path/to/file". */
|
|
|
|
static int
|
|
mapping_is_anonymous_p (const char *filename)
|
|
{
|
|
static gdb::optional<mapping_regexes> regexes;
|
|
static int init_regex_p = 0;
|
|
|
|
if (!init_regex_p)
|
|
{
|
|
/* Let's be pessimistic and assume there will be an error while
|
|
compiling the regex'es. */
|
|
init_regex_p = -1;
|
|
|
|
regexes.emplace ();
|
|
|
|
/* If we reached this point, then everything succeeded. */
|
|
init_regex_p = 1;
|
|
}
|
|
|
|
if (init_regex_p == -1)
|
|
{
|
|
const char deleted[] = " (deleted)";
|
|
size_t del_len = sizeof (deleted) - 1;
|
|
size_t filename_len = strlen (filename);
|
|
|
|
/* There was an error while compiling the regex'es above. In
|
|
order to try to give some reliable information to the caller,
|
|
we just try to find the string " (deleted)" in the filename.
|
|
If we managed to find it, then we assume the mapping is
|
|
anonymous. */
|
|
return (filename_len >= del_len
|
|
&& strcmp (filename + filename_len - del_len, deleted) == 0);
|
|
}
|
|
|
|
if (*filename == '\0'
|
|
|| regexes->dev_zero.exec (filename, 0, NULL, 0) == 0
|
|
|| regexes->shmem_file.exec (filename, 0, NULL, 0) == 0
|
|
|| regexes->file_deleted.exec (filename, 0, NULL, 0) == 0)
|
|
return 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Return 0 if the memory mapping (which is related to FILTERFLAGS, V,
|
|
MAYBE_PRIVATE_P, MAPPING_ANONYMOUS_P, ADDR and OFFSET) should not
|
|
be dumped, or greater than 0 if it should.
|
|
|
|
In a nutshell, this is the logic that we follow in order to decide
|
|
if a mapping should be dumped or not.
|
|
|
|
- If the mapping is associated to a file whose name ends with
|
|
" (deleted)", or if the file is "/dev/zero", or if it is
|
|
"/SYSV%08x" (shared memory), or if there is no file associated
|
|
with it, or if the AnonHugePages: or the Anonymous: fields in the
|
|
/proc/PID/smaps have contents, then GDB considers this mapping to
|
|
be anonymous. Otherwise, GDB considers this mapping to be a
|
|
file-backed mapping (because there will be a file associated with
|
|
it).
|
|
|
|
It is worth mentioning that, from all those checks described
|
|
above, the most fragile is the one to see if the file name ends
|
|
with " (deleted)". This does not necessarily mean that the
|
|
mapping is anonymous, because the deleted file associated with
|
|
the mapping may have been a hard link to another file, for
|
|
example. The Linux kernel checks to see if "i_nlink == 0", but
|
|
GDB cannot easily (and normally) do this check (iff running as
|
|
root, it could find the mapping in /proc/PID/map_files/ and
|
|
determine whether there still are other hard links to the
|
|
inode/file). Therefore, we made a compromise here, and we assume
|
|
that if the file name ends with " (deleted)", then the mapping is
|
|
indeed anonymous. FWIW, this is something the Linux kernel could
|
|
do better: expose this information in a more direct way.
|
|
|
|
- If we see the flag "sh" in the "VmFlags:" field (in
|
|
/proc/PID/smaps), then certainly the memory mapping is shared
|
|
(VM_SHARED). If we have access to the VmFlags, and we don't see
|
|
the "sh" there, then certainly the mapping is private. However,
|
|
Linux kernels before commit
|
|
834f82e2aa9a8ede94b17b656329f850c1471514 (3.10) do not have the
|
|
"VmFlags:" field; in that case, we use another heuristic: if we
|
|
see 'p' in the permission flags, then we assume that the mapping
|
|
is private, even though the presence of the 's' flag there would
|
|
mean VM_MAYSHARE, which means the mapping could still be private.
|
|
This should work OK enough, however.
|
|
|
|
- Even if, at the end, we decided that we should not dump the
|
|
mapping, we still have to check if it is something like an ELF
|
|
header (of a DSO or an executable, for example). If it is, and
|
|
if the user is interested in dump it, then we should dump it. */
|
|
|
|
static int
|
|
dump_mapping_p (filter_flags filterflags, const struct smaps_vmflags *v,
|
|
int maybe_private_p, int mapping_anon_p, int mapping_file_p,
|
|
const char *filename, ULONGEST addr, ULONGEST offset)
|
|
{
|
|
/* Initially, we trust in what we received from our caller. This
|
|
value may not be very precise (i.e., it was probably gathered
|
|
from the permission line in the /proc/PID/smaps list, which
|
|
actually refers to VM_MAYSHARE, and not VM_SHARED), but it is
|
|
what we have until we take a look at the "VmFlags:" field
|
|
(assuming that the version of the Linux kernel being used
|
|
supports it, of course). */
|
|
int private_p = maybe_private_p;
|
|
int dump_p;
|
|
|
|
/* We always dump vDSO and vsyscall mappings, because it's likely that
|
|
there'll be no file to read the contents from at core load time.
|
|
The kernel does the same. */
|
|
if (strcmp ("[vdso]", filename) == 0
|
|
|| strcmp ("[vsyscall]", filename) == 0)
|
|
return 1;
|
|
|
|
if (v->initialized_p)
|
|
{
|
|
/* We never dump I/O mappings. */
|
|
if (v->io_page)
|
|
return 0;
|
|
|
|
/* Check if we should exclude this mapping. */
|
|
if (!dump_excluded_mappings && v->exclude_coredump)
|
|
return 0;
|
|
|
|
/* Update our notion of whether this mapping is shared or
|
|
private based on a trustworthy value. */
|
|
private_p = !v->shared_mapping;
|
|
|
|
/* HugeTLB checking. */
|
|
if (v->uses_huge_tlb)
|
|
{
|
|
if ((private_p && (filterflags & COREFILTER_HUGETLB_PRIVATE))
|
|
|| (!private_p && (filterflags & COREFILTER_HUGETLB_SHARED)))
|
|
return 1;
|
|
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
if (private_p)
|
|
{
|
|
if (mapping_anon_p && mapping_file_p)
|
|
{
|
|
/* This is a special situation. It can happen when we see a
|
|
mapping that is file-backed, but that contains anonymous
|
|
pages. */
|
|
dump_p = ((filterflags & COREFILTER_ANON_PRIVATE) != 0
|
|
|| (filterflags & COREFILTER_MAPPED_PRIVATE) != 0);
|
|
}
|
|
else if (mapping_anon_p)
|
|
dump_p = (filterflags & COREFILTER_ANON_PRIVATE) != 0;
|
|
else
|
|
dump_p = (filterflags & COREFILTER_MAPPED_PRIVATE) != 0;
|
|
}
|
|
else
|
|
{
|
|
if (mapping_anon_p && mapping_file_p)
|
|
{
|
|
/* This is a special situation. It can happen when we see a
|
|
mapping that is file-backed, but that contains anonymous
|
|
pages. */
|
|
dump_p = ((filterflags & COREFILTER_ANON_SHARED) != 0
|
|
|| (filterflags & COREFILTER_MAPPED_SHARED) != 0);
|
|
}
|
|
else if (mapping_anon_p)
|
|
dump_p = (filterflags & COREFILTER_ANON_SHARED) != 0;
|
|
else
|
|
dump_p = (filterflags & COREFILTER_MAPPED_SHARED) != 0;
|
|
}
|
|
|
|
/* Even if we decided that we shouldn't dump this mapping, we still
|
|
have to check whether (a) the user wants us to dump mappings
|
|
containing an ELF header, and (b) the mapping in question
|
|
contains an ELF header. If (a) and (b) are true, then we should
|
|
dump this mapping.
|
|
|
|
A mapping contains an ELF header if it is a private mapping, its
|
|
offset is zero, and its first word is ELFMAG. */
|
|
if (!dump_p && private_p && offset == 0
|
|
&& (filterflags & COREFILTER_ELF_HEADERS) != 0)
|
|
{
|
|
/* Let's check if we have an ELF header. */
|
|
gdb::unique_xmalloc_ptr<char> header;
|
|
int errcode;
|
|
|
|
/* Useful define specifying the size of the ELF magical
|
|
header. */
|
|
#ifndef SELFMAG
|
|
#define SELFMAG 4
|
|
#endif
|
|
|
|
/* Read the first SELFMAG bytes and check if it is ELFMAG. */
|
|
if (target_read_string (addr, &header, SELFMAG, &errcode) == SELFMAG
|
|
&& errcode == 0)
|
|
{
|
|
const char *h = header.get ();
|
|
|
|
/* The EI_MAG* and ELFMAG* constants come from
|
|
<elf/common.h>. */
|
|
if (h[EI_MAG0] == ELFMAG0 && h[EI_MAG1] == ELFMAG1
|
|
&& h[EI_MAG2] == ELFMAG2 && h[EI_MAG3] == ELFMAG3)
|
|
{
|
|
/* This mapping contains an ELF header, so we
|
|
should dump it. */
|
|
dump_p = 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
return dump_p;
|
|
}
|
|
|
|
/* Implement the "info proc" command. */
|
|
|
|
static void
|
|
linux_info_proc (struct gdbarch *gdbarch, const char *args,
|
|
enum info_proc_what what)
|
|
{
|
|
/* A long is used for pid instead of an int to avoid a loss of precision
|
|
compiler warning from the output of strtoul. */
|
|
long pid;
|
|
int cmdline_f = (what == IP_MINIMAL || what == IP_CMDLINE || what == IP_ALL);
|
|
int cwd_f = (what == IP_MINIMAL || what == IP_CWD || what == IP_ALL);
|
|
int exe_f = (what == IP_MINIMAL || what == IP_EXE || what == IP_ALL);
|
|
int mappings_f = (what == IP_MAPPINGS || what == IP_ALL);
|
|
int status_f = (what == IP_STATUS || what == IP_ALL);
|
|
int stat_f = (what == IP_STAT || what == IP_ALL);
|
|
char filename[100];
|
|
int target_errno;
|
|
|
|
if (args && isdigit (args[0]))
|
|
{
|
|
char *tem;
|
|
|
|
pid = strtoul (args, &tem, 10);
|
|
args = tem;
|
|
}
|
|
else
|
|
{
|
|
if (!target_has_execution)
|
|
error (_("No current process: you must name one."));
|
|
if (current_inferior ()->fake_pid_p)
|
|
error (_("Can't determine the current process's PID: you must name one."));
|
|
|
|
pid = current_inferior ()->pid;
|
|
}
|
|
|
|
args = skip_spaces (args);
|
|
if (args && args[0])
|
|
error (_("Too many parameters: %s"), args);
|
|
|
|
printf_filtered (_("process %ld\n"), pid);
|
|
if (cmdline_f)
|
|
{
|
|
xsnprintf (filename, sizeof filename, "/proc/%ld/cmdline", pid);
|
|
gdb_byte *buffer;
|
|
ssize_t len = target_fileio_read_alloc (NULL, filename, &buffer);
|
|
|
|
if (len > 0)
|
|
{
|
|
gdb::unique_xmalloc_ptr<char> cmdline ((char *) buffer);
|
|
ssize_t pos;
|
|
|
|
for (pos = 0; pos < len - 1; pos++)
|
|
{
|
|
if (buffer[pos] == '\0')
|
|
buffer[pos] = ' ';
|
|
}
|
|
buffer[len - 1] = '\0';
|
|
printf_filtered ("cmdline = '%s'\n", buffer);
|
|
}
|
|
else
|
|
warning (_("unable to open /proc file '%s'"), filename);
|
|
}
|
|
if (cwd_f)
|
|
{
|
|
xsnprintf (filename, sizeof filename, "/proc/%ld/cwd", pid);
|
|
gdb::optional<std::string> contents
|
|
= target_fileio_readlink (NULL, filename, &target_errno);
|
|
if (contents.has_value ())
|
|
printf_filtered ("cwd = '%s'\n", contents->c_str ());
|
|
else
|
|
warning (_("unable to read link '%s'"), filename);
|
|
}
|
|
if (exe_f)
|
|
{
|
|
xsnprintf (filename, sizeof filename, "/proc/%ld/exe", pid);
|
|
gdb::optional<std::string> contents
|
|
= target_fileio_readlink (NULL, filename, &target_errno);
|
|
if (contents.has_value ())
|
|
printf_filtered ("exe = '%s'\n", contents->c_str ());
|
|
else
|
|
warning (_("unable to read link '%s'"), filename);
|
|
}
|
|
if (mappings_f)
|
|
{
|
|
xsnprintf (filename, sizeof filename, "/proc/%ld/maps", pid);
|
|
gdb::unique_xmalloc_ptr<char> map
|
|
= target_fileio_read_stralloc (NULL, filename);
|
|
if (map != NULL)
|
|
{
|
|
char *line;
|
|
|
|
printf_filtered (_("Mapped address spaces:\n\n"));
|
|
if (gdbarch_addr_bit (gdbarch) == 32)
|
|
{
|
|
printf_filtered ("\t%10s %10s %10s %10s %s\n",
|
|
"Start Addr",
|
|
" End Addr",
|
|
" Size", " Offset", "objfile");
|
|
}
|
|
else
|
|
{
|
|
printf_filtered (" %18s %18s %10s %10s %s\n",
|
|
"Start Addr",
|
|
" End Addr",
|
|
" Size", " Offset", "objfile");
|
|
}
|
|
|
|
char *saveptr;
|
|
for (line = strtok_r (map.get (), "\n", &saveptr);
|
|
line;
|
|
line = strtok_r (NULL, "\n", &saveptr))
|
|
{
|
|
ULONGEST addr, endaddr, offset, inode;
|
|
const char *permissions, *device, *mapping_filename;
|
|
size_t permissions_len, device_len;
|
|
|
|
read_mapping (line, &addr, &endaddr,
|
|
&permissions, &permissions_len,
|
|
&offset, &device, &device_len,
|
|
&inode, &mapping_filename);
|
|
|
|
if (gdbarch_addr_bit (gdbarch) == 32)
|
|
{
|
|
printf_filtered ("\t%10s %10s %10s %10s %s\n",
|
|
paddress (gdbarch, addr),
|
|
paddress (gdbarch, endaddr),
|
|
hex_string (endaddr - addr),
|
|
hex_string (offset),
|
|
*mapping_filename ? mapping_filename : "");
|
|
}
|
|
else
|
|
{
|
|
printf_filtered (" %18s %18s %10s %10s %s\n",
|
|
paddress (gdbarch, addr),
|
|
paddress (gdbarch, endaddr),
|
|
hex_string (endaddr - addr),
|
|
hex_string (offset),
|
|
*mapping_filename ? mapping_filename : "");
|
|
}
|
|
}
|
|
}
|
|
else
|
|
warning (_("unable to open /proc file '%s'"), filename);
|
|
}
|
|
if (status_f)
|
|
{
|
|
xsnprintf (filename, sizeof filename, "/proc/%ld/status", pid);
|
|
gdb::unique_xmalloc_ptr<char> status
|
|
= target_fileio_read_stralloc (NULL, filename);
|
|
if (status)
|
|
puts_filtered (status.get ());
|
|
else
|
|
warning (_("unable to open /proc file '%s'"), filename);
|
|
}
|
|
if (stat_f)
|
|
{
|
|
xsnprintf (filename, sizeof filename, "/proc/%ld/stat", pid);
|
|
gdb::unique_xmalloc_ptr<char> statstr
|
|
= target_fileio_read_stralloc (NULL, filename);
|
|
if (statstr)
|
|
{
|
|
const char *p = statstr.get ();
|
|
|
|
printf_filtered (_("Process: %s\n"),
|
|
pulongest (strtoulst (p, &p, 10)));
|
|
|
|
p = skip_spaces (p);
|
|
if (*p == '(')
|
|
{
|
|
/* ps command also relies on no trailing fields
|
|
ever contain ')'. */
|
|
const char *ep = strrchr (p, ')');
|
|
if (ep != NULL)
|
|
{
|
|
printf_filtered ("Exec file: %.*s\n",
|
|
(int) (ep - p - 1), p + 1);
|
|
p = ep + 1;
|
|
}
|
|
}
|
|
|
|
p = skip_spaces (p);
|
|
if (*p)
|
|
printf_filtered (_("State: %c\n"), *p++);
|
|
|
|
if (*p)
|
|
printf_filtered (_("Parent process: %s\n"),
|
|
pulongest (strtoulst (p, &p, 10)));
|
|
if (*p)
|
|
printf_filtered (_("Process group: %s\n"),
|
|
pulongest (strtoulst (p, &p, 10)));
|
|
if (*p)
|
|
printf_filtered (_("Session id: %s\n"),
|
|
pulongest (strtoulst (p, &p, 10)));
|
|
if (*p)
|
|
printf_filtered (_("TTY: %s\n"),
|
|
pulongest (strtoulst (p, &p, 10)));
|
|
if (*p)
|
|
printf_filtered (_("TTY owner process group: %s\n"),
|
|
pulongest (strtoulst (p, &p, 10)));
|
|
|
|
if (*p)
|
|
printf_filtered (_("Flags: %s\n"),
|
|
hex_string (strtoulst (p, &p, 10)));
|
|
if (*p)
|
|
printf_filtered (_("Minor faults (no memory page): %s\n"),
|
|
pulongest (strtoulst (p, &p, 10)));
|
|
if (*p)
|
|
printf_filtered (_("Minor faults, children: %s\n"),
|
|
pulongest (strtoulst (p, &p, 10)));
|
|
if (*p)
|
|
printf_filtered (_("Major faults (memory page faults): %s\n"),
|
|
pulongest (strtoulst (p, &p, 10)));
|
|
if (*p)
|
|
printf_filtered (_("Major faults, children: %s\n"),
|
|
pulongest (strtoulst (p, &p, 10)));
|
|
if (*p)
|
|
printf_filtered (_("utime: %s\n"),
|
|
pulongest (strtoulst (p, &p, 10)));
|
|
if (*p)
|
|
printf_filtered (_("stime: %s\n"),
|
|
pulongest (strtoulst (p, &p, 10)));
|
|
if (*p)
|
|
printf_filtered (_("utime, children: %s\n"),
|
|
pulongest (strtoulst (p, &p, 10)));
|
|
if (*p)
|
|
printf_filtered (_("stime, children: %s\n"),
|
|
pulongest (strtoulst (p, &p, 10)));
|
|
if (*p)
|
|
printf_filtered (_("jiffies remaining in current "
|
|
"time slice: %s\n"),
|
|
pulongest (strtoulst (p, &p, 10)));
|
|
if (*p)
|
|
printf_filtered (_("'nice' value: %s\n"),
|
|
pulongest (strtoulst (p, &p, 10)));
|
|
if (*p)
|
|
printf_filtered (_("jiffies until next timeout: %s\n"),
|
|
pulongest (strtoulst (p, &p, 10)));
|
|
if (*p)
|
|
printf_filtered (_("jiffies until next SIGALRM: %s\n"),
|
|
pulongest (strtoulst (p, &p, 10)));
|
|
if (*p)
|
|
printf_filtered (_("start time (jiffies since "
|
|
"system boot): %s\n"),
|
|
pulongest (strtoulst (p, &p, 10)));
|
|
if (*p)
|
|
printf_filtered (_("Virtual memory size: %s\n"),
|
|
pulongest (strtoulst (p, &p, 10)));
|
|
if (*p)
|
|
printf_filtered (_("Resident set size: %s\n"),
|
|
pulongest (strtoulst (p, &p, 10)));
|
|
if (*p)
|
|
printf_filtered (_("rlim: %s\n"),
|
|
pulongest (strtoulst (p, &p, 10)));
|
|
if (*p)
|
|
printf_filtered (_("Start of text: %s\n"),
|
|
hex_string (strtoulst (p, &p, 10)));
|
|
if (*p)
|
|
printf_filtered (_("End of text: %s\n"),
|
|
hex_string (strtoulst (p, &p, 10)));
|
|
if (*p)
|
|
printf_filtered (_("Start of stack: %s\n"),
|
|
hex_string (strtoulst (p, &p, 10)));
|
|
#if 0 /* Don't know how architecture-dependent the rest is...
|
|
Anyway the signal bitmap info is available from "status". */
|
|
if (*p)
|
|
printf_filtered (_("Kernel stack pointer: %s\n"),
|
|
hex_string (strtoulst (p, &p, 10)));
|
|
if (*p)
|
|
printf_filtered (_("Kernel instr pointer: %s\n"),
|
|
hex_string (strtoulst (p, &p, 10)));
|
|
if (*p)
|
|
printf_filtered (_("Pending signals bitmap: %s\n"),
|
|
hex_string (strtoulst (p, &p, 10)));
|
|
if (*p)
|
|
printf_filtered (_("Blocked signals bitmap: %s\n"),
|
|
hex_string (strtoulst (p, &p, 10)));
|
|
if (*p)
|
|
printf_filtered (_("Ignored signals bitmap: %s\n"),
|
|
hex_string (strtoulst (p, &p, 10)));
|
|
if (*p)
|
|
printf_filtered (_("Catched signals bitmap: %s\n"),
|
|
hex_string (strtoulst (p, &p, 10)));
|
|
if (*p)
|
|
printf_filtered (_("wchan (system call): %s\n"),
|
|
hex_string (strtoulst (p, &p, 10)));
|
|
#endif
|
|
}
|
|
else
|
|
warning (_("unable to open /proc file '%s'"), filename);
|
|
}
|
|
}
|
|
|
|
/* Implement "info proc mappings" for a corefile. */
|
|
|
|
static void
|
|
linux_core_info_proc_mappings (struct gdbarch *gdbarch, const char *args)
|
|
{
|
|
asection *section;
|
|
ULONGEST count, page_size;
|
|
unsigned char *descdata, *filenames, *descend;
|
|
size_t note_size;
|
|
unsigned int addr_size_bits, addr_size;
|
|
struct gdbarch *core_gdbarch = gdbarch_from_bfd (core_bfd);
|
|
/* We assume this for reading 64-bit core files. */
|
|
gdb_static_assert (sizeof (ULONGEST) >= 8);
|
|
|
|
section = bfd_get_section_by_name (core_bfd, ".note.linuxcore.file");
|
|
if (section == NULL)
|
|
{
|
|
warning (_("unable to find mappings in core file"));
|
|
return;
|
|
}
|
|
|
|
addr_size_bits = gdbarch_addr_bit (core_gdbarch);
|
|
addr_size = addr_size_bits / 8;
|
|
note_size = bfd_section_size (section);
|
|
|
|
if (note_size < 2 * addr_size)
|
|
error (_("malformed core note - too short for header"));
|
|
|
|
gdb::def_vector<unsigned char> contents (note_size);
|
|
if (!bfd_get_section_contents (core_bfd, section, contents.data (),
|
|
0, note_size))
|
|
error (_("could not get core note contents"));
|
|
|
|
descdata = contents.data ();
|
|
descend = descdata + note_size;
|
|
|
|
if (descdata[note_size - 1] != '\0')
|
|
error (_("malformed note - does not end with \\0"));
|
|
|
|
count = bfd_get (addr_size_bits, core_bfd, descdata);
|
|
descdata += addr_size;
|
|
|
|
page_size = bfd_get (addr_size_bits, core_bfd, descdata);
|
|
descdata += addr_size;
|
|
|
|
if (note_size < 2 * addr_size + count * 3 * addr_size)
|
|
error (_("malformed note - too short for supplied file count"));
|
|
|
|
printf_filtered (_("Mapped address spaces:\n\n"));
|
|
if (gdbarch_addr_bit (gdbarch) == 32)
|
|
{
|
|
printf_filtered ("\t%10s %10s %10s %10s %s\n",
|
|
"Start Addr",
|
|
" End Addr",
|
|
" Size", " Offset", "objfile");
|
|
}
|
|
else
|
|
{
|
|
printf_filtered (" %18s %18s %10s %10s %s\n",
|
|
"Start Addr",
|
|
" End Addr",
|
|
" Size", " Offset", "objfile");
|
|
}
|
|
|
|
filenames = descdata + count * 3 * addr_size;
|
|
while (--count > 0)
|
|
{
|
|
ULONGEST start, end, file_ofs;
|
|
|
|
if (filenames == descend)
|
|
error (_("malformed note - filenames end too early"));
|
|
|
|
start = bfd_get (addr_size_bits, core_bfd, descdata);
|
|
descdata += addr_size;
|
|
end = bfd_get (addr_size_bits, core_bfd, descdata);
|
|
descdata += addr_size;
|
|
file_ofs = bfd_get (addr_size_bits, core_bfd, descdata);
|
|
descdata += addr_size;
|
|
|
|
file_ofs *= page_size;
|
|
|
|
if (gdbarch_addr_bit (gdbarch) == 32)
|
|
printf_filtered ("\t%10s %10s %10s %10s %s\n",
|
|
paddress (gdbarch, start),
|
|
paddress (gdbarch, end),
|
|
hex_string (end - start),
|
|
hex_string (file_ofs),
|
|
filenames);
|
|
else
|
|
printf_filtered (" %18s %18s %10s %10s %s\n",
|
|
paddress (gdbarch, start),
|
|
paddress (gdbarch, end),
|
|
hex_string (end - start),
|
|
hex_string (file_ofs),
|
|
filenames);
|
|
|
|
filenames += 1 + strlen ((char *) filenames);
|
|
}
|
|
}
|
|
|
|
/* Implement "info proc" for a corefile. */
|
|
|
|
static void
|
|
linux_core_info_proc (struct gdbarch *gdbarch, const char *args,
|
|
enum info_proc_what what)
|
|
{
|
|
int exe_f = (what == IP_MINIMAL || what == IP_EXE || what == IP_ALL);
|
|
int mappings_f = (what == IP_MAPPINGS || what == IP_ALL);
|
|
|
|
if (exe_f)
|
|
{
|
|
const char *exe;
|
|
|
|
exe = bfd_core_file_failing_command (core_bfd);
|
|
if (exe != NULL)
|
|
printf_filtered ("exe = '%s'\n", exe);
|
|
else
|
|
warning (_("unable to find command name in core file"));
|
|
}
|
|
|
|
if (mappings_f)
|
|
linux_core_info_proc_mappings (gdbarch, args);
|
|
|
|
if (!exe_f && !mappings_f)
|
|
error (_("unable to handle request"));
|
|
}
|
|
|
|
/* Read siginfo data from the core, if possible. Returns -1 on
|
|
failure. Otherwise, returns the number of bytes read. READBUF,
|
|
OFFSET, and LEN are all as specified by the to_xfer_partial
|
|
interface. */
|
|
|
|
static LONGEST
|
|
linux_core_xfer_siginfo (struct gdbarch *gdbarch, gdb_byte *readbuf,
|
|
ULONGEST offset, ULONGEST len)
|
|
{
|
|
thread_section_name section_name (".note.linuxcore.siginfo", inferior_ptid);
|
|
asection *section = bfd_get_section_by_name (core_bfd, section_name.c_str ());
|
|
if (section == NULL)
|
|
return -1;
|
|
|
|
if (!bfd_get_section_contents (core_bfd, section, readbuf, offset, len))
|
|
return -1;
|
|
|
|
return len;
|
|
}
|
|
|
|
typedef int linux_find_memory_region_ftype (ULONGEST vaddr, ULONGEST size,
|
|
ULONGEST offset, ULONGEST inode,
|
|
int read, int write,
|
|
int exec, int modified,
|
|
const char *filename,
|
|
void *data);
|
|
|
|
/* List memory regions in the inferior for a corefile. */
|
|
|
|
static int
|
|
linux_find_memory_regions_full (struct gdbarch *gdbarch,
|
|
linux_find_memory_region_ftype *func,
|
|
void *obfd)
|
|
{
|
|
char mapsfilename[100];
|
|
char coredumpfilter_name[100];
|
|
pid_t pid;
|
|
/* Default dump behavior of coredump_filter (0x33), according to
|
|
Documentation/filesystems/proc.txt from the Linux kernel
|
|
tree. */
|
|
filter_flags filterflags = (COREFILTER_ANON_PRIVATE
|
|
| COREFILTER_ANON_SHARED
|
|
| COREFILTER_ELF_HEADERS
|
|
| COREFILTER_HUGETLB_PRIVATE);
|
|
|
|
/* We need to know the real target PID to access /proc. */
|
|
if (current_inferior ()->fake_pid_p)
|
|
return 1;
|
|
|
|
pid = current_inferior ()->pid;
|
|
|
|
if (use_coredump_filter)
|
|
{
|
|
xsnprintf (coredumpfilter_name, sizeof (coredumpfilter_name),
|
|
"/proc/%d/coredump_filter", pid);
|
|
gdb::unique_xmalloc_ptr<char> coredumpfilterdata
|
|
= target_fileio_read_stralloc (NULL, coredumpfilter_name);
|
|
if (coredumpfilterdata != NULL)
|
|
{
|
|
unsigned int flags;
|
|
|
|
sscanf (coredumpfilterdata.get (), "%x", &flags);
|
|
filterflags = (enum filter_flag) flags;
|
|
}
|
|
}
|
|
|
|
xsnprintf (mapsfilename, sizeof mapsfilename, "/proc/%d/smaps", pid);
|
|
gdb::unique_xmalloc_ptr<char> data
|
|
= target_fileio_read_stralloc (NULL, mapsfilename);
|
|
if (data == NULL)
|
|
{
|
|
/* Older Linux kernels did not support /proc/PID/smaps. */
|
|
xsnprintf (mapsfilename, sizeof mapsfilename, "/proc/%d/maps", pid);
|
|
data = target_fileio_read_stralloc (NULL, mapsfilename);
|
|
}
|
|
|
|
if (data != NULL)
|
|
{
|
|
char *line, *t;
|
|
|
|
line = strtok_r (data.get (), "\n", &t);
|
|
while (line != NULL)
|
|
{
|
|
ULONGEST addr, endaddr, offset, inode;
|
|
const char *permissions, *device, *filename;
|
|
struct smaps_vmflags v;
|
|
size_t permissions_len, device_len;
|
|
int read, write, exec, priv;
|
|
int has_anonymous = 0;
|
|
int should_dump_p = 0;
|
|
int mapping_anon_p;
|
|
int mapping_file_p;
|
|
|
|
memset (&v, 0, sizeof (v));
|
|
read_mapping (line, &addr, &endaddr, &permissions, &permissions_len,
|
|
&offset, &device, &device_len, &inode, &filename);
|
|
mapping_anon_p = mapping_is_anonymous_p (filename);
|
|
/* If the mapping is not anonymous, then we can consider it
|
|
to be file-backed. These two states (anonymous or
|
|
file-backed) seem to be exclusive, but they can actually
|
|
coexist. For example, if a file-backed mapping has
|
|
"Anonymous:" pages (see more below), then the Linux
|
|
kernel will dump this mapping when the user specified
|
|
that she only wants anonymous mappings in the corefile
|
|
(*even* when she explicitly disabled the dumping of
|
|
file-backed mappings). */
|
|
mapping_file_p = !mapping_anon_p;
|
|
|
|
/* Decode permissions. */
|
|
read = (memchr (permissions, 'r', permissions_len) != 0);
|
|
write = (memchr (permissions, 'w', permissions_len) != 0);
|
|
exec = (memchr (permissions, 'x', permissions_len) != 0);
|
|
/* 'private' here actually means VM_MAYSHARE, and not
|
|
VM_SHARED. In order to know if a mapping is really
|
|
private or not, we must check the flag "sh" in the
|
|
VmFlags field. This is done by decode_vmflags. However,
|
|
if we are using a Linux kernel released before the commit
|
|
834f82e2aa9a8ede94b17b656329f850c1471514 (3.10), we will
|
|
not have the VmFlags there. In this case, there is
|
|
really no way to know if we are dealing with VM_SHARED,
|
|
so we just assume that VM_MAYSHARE is enough. */
|
|
priv = memchr (permissions, 'p', permissions_len) != 0;
|
|
|
|
/* Try to detect if region should be dumped by parsing smaps
|
|
counters. */
|
|
for (line = strtok_r (NULL, "\n", &t);
|
|
line != NULL && line[0] >= 'A' && line[0] <= 'Z';
|
|
line = strtok_r (NULL, "\n", &t))
|
|
{
|
|
char keyword[64 + 1];
|
|
|
|
if (sscanf (line, "%64s", keyword) != 1)
|
|
{
|
|
warning (_("Error parsing {s,}maps file '%s'"), mapsfilename);
|
|
break;
|
|
}
|
|
|
|
if (strcmp (keyword, "Anonymous:") == 0)
|
|
{
|
|
/* Older Linux kernels did not support the
|
|
"Anonymous:" counter. Check it here. */
|
|
has_anonymous = 1;
|
|
}
|
|
else if (strcmp (keyword, "VmFlags:") == 0)
|
|
decode_vmflags (line, &v);
|
|
|
|
if (strcmp (keyword, "AnonHugePages:") == 0
|
|
|| strcmp (keyword, "Anonymous:") == 0)
|
|
{
|
|
unsigned long number;
|
|
|
|
if (sscanf (line, "%*s%lu", &number) != 1)
|
|
{
|
|
warning (_("Error parsing {s,}maps file '%s' number"),
|
|
mapsfilename);
|
|
break;
|
|
}
|
|
if (number > 0)
|
|
{
|
|
/* Even if we are dealing with a file-backed
|
|
mapping, if it contains anonymous pages we
|
|
consider it to be *also* an anonymous
|
|
mapping, because this is what the Linux
|
|
kernel does:
|
|
|
|
// Dump segments that have been written to.
|
|
if (vma->anon_vma && FILTER(ANON_PRIVATE))
|
|
goto whole;
|
|
|
|
Note that if the mapping is already marked as
|
|
file-backed (i.e., mapping_file_p is
|
|
non-zero), then this is a special case, and
|
|
this mapping will be dumped either when the
|
|
user wants to dump file-backed *or* anonymous
|
|
mappings. */
|
|
mapping_anon_p = 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (has_anonymous)
|
|
should_dump_p = dump_mapping_p (filterflags, &v, priv,
|
|
mapping_anon_p, mapping_file_p,
|
|
filename, addr, offset);
|
|
else
|
|
{
|
|
/* Older Linux kernels did not support the "Anonymous:" counter.
|
|
If it is missing, we can't be sure - dump all the pages. */
|
|
should_dump_p = 1;
|
|
}
|
|
|
|
/* Invoke the callback function to create the corefile segment. */
|
|
if (should_dump_p)
|
|
func (addr, endaddr - addr, offset, inode,
|
|
read, write, exec, 1, /* MODIFIED is true because we
|
|
want to dump the mapping. */
|
|
filename, obfd);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
/* A structure for passing information through
|
|
linux_find_memory_regions_full. */
|
|
|
|
struct linux_find_memory_regions_data
|
|
{
|
|
/* The original callback. */
|
|
|
|
find_memory_region_ftype func;
|
|
|
|
/* The original datum. */
|
|
|
|
void *obfd;
|
|
};
|
|
|
|
/* A callback for linux_find_memory_regions that converts between the
|
|
"full"-style callback and find_memory_region_ftype. */
|
|
|
|
static int
|
|
linux_find_memory_regions_thunk (ULONGEST vaddr, ULONGEST size,
|
|
ULONGEST offset, ULONGEST inode,
|
|
int read, int write, int exec, int modified,
|
|
const char *filename, void *arg)
|
|
{
|
|
struct linux_find_memory_regions_data *data
|
|
= (struct linux_find_memory_regions_data *) arg;
|
|
|
|
return data->func (vaddr, size, read, write, exec, modified, data->obfd);
|
|
}
|
|
|
|
/* A variant of linux_find_memory_regions_full that is suitable as the
|
|
gdbarch find_memory_regions method. */
|
|
|
|
static int
|
|
linux_find_memory_regions (struct gdbarch *gdbarch,
|
|
find_memory_region_ftype func, void *obfd)
|
|
{
|
|
struct linux_find_memory_regions_data data;
|
|
|
|
data.func = func;
|
|
data.obfd = obfd;
|
|
|
|
return linux_find_memory_regions_full (gdbarch,
|
|
linux_find_memory_regions_thunk,
|
|
&data);
|
|
}
|
|
|
|
/* Determine which signal stopped execution. */
|
|
|
|
static int
|
|
find_signalled_thread (struct thread_info *info, void *data)
|
|
{
|
|
if (info->suspend.stop_signal != GDB_SIGNAL_0
|
|
&& info->ptid.pid () == inferior_ptid.pid ())
|
|
return 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* This is used to pass information from
|
|
linux_make_mappings_corefile_notes through
|
|
linux_find_memory_regions_full. */
|
|
|
|
struct linux_make_mappings_data
|
|
{
|
|
/* Number of files mapped. */
|
|
ULONGEST file_count;
|
|
|
|
/* The obstack for the main part of the data. */
|
|
struct obstack *data_obstack;
|
|
|
|
/* The filename obstack. */
|
|
struct obstack *filename_obstack;
|
|
|
|
/* The architecture's "long" type. */
|
|
struct type *long_type;
|
|
};
|
|
|
|
static linux_find_memory_region_ftype linux_make_mappings_callback;
|
|
|
|
/* A callback for linux_find_memory_regions_full that updates the
|
|
mappings data for linux_make_mappings_corefile_notes. */
|
|
|
|
static int
|
|
linux_make_mappings_callback (ULONGEST vaddr, ULONGEST size,
|
|
ULONGEST offset, ULONGEST inode,
|
|
int read, int write, int exec, int modified,
|
|
const char *filename, void *data)
|
|
{
|
|
struct linux_make_mappings_data *map_data
|
|
= (struct linux_make_mappings_data *) data;
|
|
gdb_byte buf[sizeof (ULONGEST)];
|
|
|
|
if (*filename == '\0' || inode == 0)
|
|
return 0;
|
|
|
|
++map_data->file_count;
|
|
|
|
pack_long (buf, map_data->long_type, vaddr);
|
|
obstack_grow (map_data->data_obstack, buf, TYPE_LENGTH (map_data->long_type));
|
|
pack_long (buf, map_data->long_type, vaddr + size);
|
|
obstack_grow (map_data->data_obstack, buf, TYPE_LENGTH (map_data->long_type));
|
|
pack_long (buf, map_data->long_type, offset);
|
|
obstack_grow (map_data->data_obstack, buf, TYPE_LENGTH (map_data->long_type));
|
|
|
|
obstack_grow_str0 (map_data->filename_obstack, filename);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Write the file mapping data to the core file, if possible. OBFD is
|
|
the output BFD. NOTE_DATA is the current note data, and NOTE_SIZE
|
|
is a pointer to the note size. Returns the new NOTE_DATA and
|
|
updates NOTE_SIZE. */
|
|
|
|
static char *
|
|
linux_make_mappings_corefile_notes (struct gdbarch *gdbarch, bfd *obfd,
|
|
char *note_data, int *note_size)
|
|
{
|
|
struct linux_make_mappings_data mapping_data;
|
|
struct type *long_type
|
|
= arch_integer_type (gdbarch, gdbarch_long_bit (gdbarch), 0, "long");
|
|
gdb_byte buf[sizeof (ULONGEST)];
|
|
|
|
auto_obstack data_obstack, filename_obstack;
|
|
|
|
mapping_data.file_count = 0;
|
|
mapping_data.data_obstack = &data_obstack;
|
|
mapping_data.filename_obstack = &filename_obstack;
|
|
mapping_data.long_type = long_type;
|
|
|
|
/* Reserve space for the count. */
|
|
obstack_blank (&data_obstack, TYPE_LENGTH (long_type));
|
|
/* We always write the page size as 1 since we have no good way to
|
|
determine the correct value. */
|
|
pack_long (buf, long_type, 1);
|
|
obstack_grow (&data_obstack, buf, TYPE_LENGTH (long_type));
|
|
|
|
linux_find_memory_regions_full (gdbarch, linux_make_mappings_callback,
|
|
&mapping_data);
|
|
|
|
if (mapping_data.file_count != 0)
|
|
{
|
|
/* Write the count to the obstack. */
|
|
pack_long ((gdb_byte *) obstack_base (&data_obstack),
|
|
long_type, mapping_data.file_count);
|
|
|
|
/* Copy the filenames to the data obstack. */
|
|
int size = obstack_object_size (&filename_obstack);
|
|
obstack_grow (&data_obstack, obstack_base (&filename_obstack),
|
|
size);
|
|
|
|
note_data = elfcore_write_note (obfd, note_data, note_size,
|
|
"CORE", NT_FILE,
|
|
obstack_base (&data_obstack),
|
|
obstack_object_size (&data_obstack));
|
|
}
|
|
|
|
return note_data;
|
|
}
|
|
|
|
/* Structure for passing information from
|
|
linux_collect_thread_registers via an iterator to
|
|
linux_collect_regset_section_cb. */
|
|
|
|
struct linux_collect_regset_section_cb_data
|
|
{
|
|
struct gdbarch *gdbarch;
|
|
const struct regcache *regcache;
|
|
bfd *obfd;
|
|
char *note_data;
|
|
int *note_size;
|
|
unsigned long lwp;
|
|
enum gdb_signal stop_signal;
|
|
int abort_iteration;
|
|
};
|
|
|
|
/* Callback for iterate_over_regset_sections that records a single
|
|
regset in the corefile note section. */
|
|
|
|
static void
|
|
linux_collect_regset_section_cb (const char *sect_name, int supply_size,
|
|
int collect_size, const struct regset *regset,
|
|
const char *human_name, void *cb_data)
|
|
{
|
|
struct linux_collect_regset_section_cb_data *data
|
|
= (struct linux_collect_regset_section_cb_data *) cb_data;
|
|
bool variable_size_section = (regset != NULL
|
|
&& regset->flags & REGSET_VARIABLE_SIZE);
|
|
|
|
if (!variable_size_section)
|
|
gdb_assert (supply_size == collect_size);
|
|
|
|
if (data->abort_iteration)
|
|
return;
|
|
|
|
gdb_assert (regset && regset->collect_regset);
|
|
|
|
/* This is intentionally zero-initialized by using std::vector, so
|
|
that any padding bytes in the core file will show as 0. */
|
|
std::vector<gdb_byte> buf (collect_size);
|
|
|
|
regset->collect_regset (regset, data->regcache, -1, buf.data (),
|
|
collect_size);
|
|
|
|
/* PRSTATUS still needs to be treated specially. */
|
|
if (strcmp (sect_name, ".reg") == 0)
|
|
data->note_data = (char *) elfcore_write_prstatus
|
|
(data->obfd, data->note_data, data->note_size, data->lwp,
|
|
gdb_signal_to_host (data->stop_signal), buf.data ());
|
|
else
|
|
data->note_data = (char *) elfcore_write_register_note
|
|
(data->obfd, data->note_data, data->note_size,
|
|
sect_name, buf.data (), collect_size);
|
|
|
|
if (data->note_data == NULL)
|
|
data->abort_iteration = 1;
|
|
}
|
|
|
|
/* Records the thread's register state for the corefile note
|
|
section. */
|
|
|
|
static char *
|
|
linux_collect_thread_registers (const struct regcache *regcache,
|
|
ptid_t ptid, bfd *obfd,
|
|
char *note_data, int *note_size,
|
|
enum gdb_signal stop_signal)
|
|
{
|
|
struct gdbarch *gdbarch = regcache->arch ();
|
|
struct linux_collect_regset_section_cb_data data;
|
|
|
|
data.gdbarch = gdbarch;
|
|
data.regcache = regcache;
|
|
data.obfd = obfd;
|
|
data.note_data = note_data;
|
|
data.note_size = note_size;
|
|
data.stop_signal = stop_signal;
|
|
data.abort_iteration = 0;
|
|
|
|
/* For remote targets the LWP may not be available, so use the TID. */
|
|
data.lwp = ptid.lwp ();
|
|
if (!data.lwp)
|
|
data.lwp = ptid.tid ();
|
|
|
|
gdbarch_iterate_over_regset_sections (gdbarch,
|
|
linux_collect_regset_section_cb,
|
|
&data, regcache);
|
|
return data.note_data;
|
|
}
|
|
|
|
/* Fetch the siginfo data for the specified thread, if it exists. If
|
|
there is no data, or we could not read it, return an empty
|
|
buffer. */
|
|
|
|
static gdb::byte_vector
|
|
linux_get_siginfo_data (thread_info *thread, struct gdbarch *gdbarch)
|
|
{
|
|
struct type *siginfo_type;
|
|
LONGEST bytes_read;
|
|
|
|
if (!gdbarch_get_siginfo_type_p (gdbarch))
|
|
return gdb::byte_vector ();
|
|
|
|
scoped_restore save_inferior_ptid = make_scoped_restore (&inferior_ptid);
|
|
inferior_ptid = thread->ptid;
|
|
|
|
siginfo_type = gdbarch_get_siginfo_type (gdbarch);
|
|
|
|
gdb::byte_vector buf (TYPE_LENGTH (siginfo_type));
|
|
|
|
bytes_read = target_read (current_top_target (), TARGET_OBJECT_SIGNAL_INFO, NULL,
|
|
buf.data (), 0, TYPE_LENGTH (siginfo_type));
|
|
if (bytes_read != TYPE_LENGTH (siginfo_type))
|
|
buf.clear ();
|
|
|
|
return buf;
|
|
}
|
|
|
|
struct linux_corefile_thread_data
|
|
{
|
|
struct gdbarch *gdbarch;
|
|
bfd *obfd;
|
|
char *note_data;
|
|
int *note_size;
|
|
enum gdb_signal stop_signal;
|
|
};
|
|
|
|
/* Records the thread's register state for the corefile note
|
|
section. */
|
|
|
|
static void
|
|
linux_corefile_thread (struct thread_info *info,
|
|
struct linux_corefile_thread_data *args)
|
|
{
|
|
struct regcache *regcache;
|
|
|
|
regcache = get_thread_arch_regcache (info->inf->process_target (),
|
|
info->ptid, args->gdbarch);
|
|
|
|
target_fetch_registers (regcache, -1);
|
|
gdb::byte_vector siginfo_data = linux_get_siginfo_data (info, args->gdbarch);
|
|
|
|
args->note_data = linux_collect_thread_registers
|
|
(regcache, info->ptid, args->obfd, args->note_data,
|
|
args->note_size, args->stop_signal);
|
|
|
|
/* Don't return anything if we got no register information above,
|
|
such a core file is useless. */
|
|
if (args->note_data != NULL)
|
|
if (!siginfo_data.empty ())
|
|
args->note_data = elfcore_write_note (args->obfd,
|
|
args->note_data,
|
|
args->note_size,
|
|
"CORE", NT_SIGINFO,
|
|
siginfo_data.data (),
|
|
siginfo_data.size ());
|
|
}
|
|
|
|
/* Fill the PRPSINFO structure with information about the process being
|
|
debugged. Returns 1 in case of success, 0 for failures. Please note that
|
|
even if the structure cannot be entirely filled (e.g., GDB was unable to
|
|
gather information about the process UID/GID), this function will still
|
|
return 1 since some information was already recorded. It will only return
|
|
0 iff nothing can be gathered. */
|
|
|
|
static int
|
|
linux_fill_prpsinfo (struct elf_internal_linux_prpsinfo *p)
|
|
{
|
|
/* The filename which we will use to obtain some info about the process.
|
|
We will basically use this to store the `/proc/PID/FILENAME' file. */
|
|
char filename[100];
|
|
/* The basename of the executable. */
|
|
const char *basename;
|
|
const char *infargs;
|
|
/* Temporary buffer. */
|
|
char *tmpstr;
|
|
/* The valid states of a process, according to the Linux kernel. */
|
|
const char valid_states[] = "RSDTZW";
|
|
/* The program state. */
|
|
const char *prog_state;
|
|
/* The state of the process. */
|
|
char pr_sname;
|
|
/* The PID of the program which generated the corefile. */
|
|
pid_t pid;
|
|
/* Process flags. */
|
|
unsigned int pr_flag;
|
|
/* Process nice value. */
|
|
long pr_nice;
|
|
/* The number of fields read by `sscanf'. */
|
|
int n_fields = 0;
|
|
|
|
gdb_assert (p != NULL);
|
|
|
|
/* Obtaining PID and filename. */
|
|
pid = inferior_ptid.pid ();
|
|
xsnprintf (filename, sizeof (filename), "/proc/%d/cmdline", (int) pid);
|
|
/* The full name of the program which generated the corefile. */
|
|
gdb::unique_xmalloc_ptr<char> fname
|
|
= target_fileio_read_stralloc (NULL, filename);
|
|
|
|
if (fname == NULL || fname.get ()[0] == '\0')
|
|
{
|
|
/* No program name was read, so we won't be able to retrieve more
|
|
information about the process. */
|
|
return 0;
|
|
}
|
|
|
|
memset (p, 0, sizeof (*p));
|
|
|
|
/* Defining the PID. */
|
|
p->pr_pid = pid;
|
|
|
|
/* Copying the program name. Only the basename matters. */
|
|
basename = lbasename (fname.get ());
|
|
strncpy (p->pr_fname, basename, sizeof (p->pr_fname));
|
|
p->pr_fname[sizeof (p->pr_fname) - 1] = '\0';
|
|
|
|
infargs = get_inferior_args ();
|
|
|
|
/* The arguments of the program. */
|
|
std::string psargs = fname.get ();
|
|
if (infargs != NULL)
|
|
psargs = psargs + " " + infargs;
|
|
|
|
strncpy (p->pr_psargs, psargs.c_str (), sizeof (p->pr_psargs));
|
|
p->pr_psargs[sizeof (p->pr_psargs) - 1] = '\0';
|
|
|
|
xsnprintf (filename, sizeof (filename), "/proc/%d/stat", (int) pid);
|
|
/* The contents of `/proc/PID/stat'. */
|
|
gdb::unique_xmalloc_ptr<char> proc_stat_contents
|
|
= target_fileio_read_stralloc (NULL, filename);
|
|
char *proc_stat = proc_stat_contents.get ();
|
|
|
|
if (proc_stat == NULL || *proc_stat == '\0')
|
|
{
|
|
/* Despite being unable to read more information about the
|
|
process, we return 1 here because at least we have its
|
|
command line, PID and arguments. */
|
|
return 1;
|
|
}
|
|
|
|
/* Ok, we have the stats. It's time to do a little parsing of the
|
|
contents of the buffer, so that we end up reading what we want.
|
|
|
|
The following parsing mechanism is strongly based on the
|
|
information generated by the `fs/proc/array.c' file, present in
|
|
the Linux kernel tree. More details about how the information is
|
|
displayed can be obtained by seeing the manpage of proc(5),
|
|
specifically under the entry of `/proc/[pid]/stat'. */
|
|
|
|
/* Getting rid of the PID, since we already have it. */
|
|
while (isdigit (*proc_stat))
|
|
++proc_stat;
|
|
|
|
proc_stat = skip_spaces (proc_stat);
|
|
|
|
/* ps command also relies on no trailing fields ever contain ')'. */
|
|
proc_stat = strrchr (proc_stat, ')');
|
|
if (proc_stat == NULL)
|
|
return 1;
|
|
proc_stat++;
|
|
|
|
proc_stat = skip_spaces (proc_stat);
|
|
|
|
n_fields = sscanf (proc_stat,
|
|
"%c" /* Process state. */
|
|
"%d%d%d" /* Parent PID, group ID, session ID. */
|
|
"%*d%*d" /* tty_nr, tpgid (not used). */
|
|
"%u" /* Flags. */
|
|
"%*s%*s%*s%*s" /* minflt, cminflt, majflt,
|
|
cmajflt (not used). */
|
|
"%*s%*s%*s%*s" /* utime, stime, cutime,
|
|
cstime (not used). */
|
|
"%*s" /* Priority (not used). */
|
|
"%ld", /* Nice. */
|
|
&pr_sname,
|
|
&p->pr_ppid, &p->pr_pgrp, &p->pr_sid,
|
|
&pr_flag,
|
|
&pr_nice);
|
|
|
|
if (n_fields != 6)
|
|
{
|
|
/* Again, we couldn't read the complementary information about
|
|
the process state. However, we already have minimal
|
|
information, so we just return 1 here. */
|
|
return 1;
|
|
}
|
|
|
|
/* Filling the structure fields. */
|
|
prog_state = strchr (valid_states, pr_sname);
|
|
if (prog_state != NULL)
|
|
p->pr_state = prog_state - valid_states;
|
|
else
|
|
{
|
|
/* Zero means "Running". */
|
|
p->pr_state = 0;
|
|
}
|
|
|
|
p->pr_sname = p->pr_state > 5 ? '.' : pr_sname;
|
|
p->pr_zomb = p->pr_sname == 'Z';
|
|
p->pr_nice = pr_nice;
|
|
p->pr_flag = pr_flag;
|
|
|
|
/* Finally, obtaining the UID and GID. For that, we read and parse the
|
|
contents of the `/proc/PID/status' file. */
|
|
xsnprintf (filename, sizeof (filename), "/proc/%d/status", (int) pid);
|
|
/* The contents of `/proc/PID/status'. */
|
|
gdb::unique_xmalloc_ptr<char> proc_status_contents
|
|
= target_fileio_read_stralloc (NULL, filename);
|
|
char *proc_status = proc_status_contents.get ();
|
|
|
|
if (proc_status == NULL || *proc_status == '\0')
|
|
{
|
|
/* Returning 1 since we already have a bunch of information. */
|
|
return 1;
|
|
}
|
|
|
|
/* Extracting the UID. */
|
|
tmpstr = strstr (proc_status, "Uid:");
|
|
if (tmpstr != NULL)
|
|
{
|
|
/* Advancing the pointer to the beginning of the UID. */
|
|
tmpstr += sizeof ("Uid:");
|
|
while (*tmpstr != '\0' && !isdigit (*tmpstr))
|
|
++tmpstr;
|
|
|
|
if (isdigit (*tmpstr))
|
|
p->pr_uid = strtol (tmpstr, &tmpstr, 10);
|
|
}
|
|
|
|
/* Extracting the GID. */
|
|
tmpstr = strstr (proc_status, "Gid:");
|
|
if (tmpstr != NULL)
|
|
{
|
|
/* Advancing the pointer to the beginning of the GID. */
|
|
tmpstr += sizeof ("Gid:");
|
|
while (*tmpstr != '\0' && !isdigit (*tmpstr))
|
|
++tmpstr;
|
|
|
|
if (isdigit (*tmpstr))
|
|
p->pr_gid = strtol (tmpstr, &tmpstr, 10);
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
/* Build the note section for a corefile, and return it in a malloc
|
|
buffer. */
|
|
|
|
static char *
|
|
linux_make_corefile_notes (struct gdbarch *gdbarch, bfd *obfd, int *note_size)
|
|
{
|
|
struct linux_corefile_thread_data thread_args;
|
|
struct elf_internal_linux_prpsinfo prpsinfo;
|
|
char *note_data = NULL;
|
|
struct thread_info *curr_thr, *signalled_thr;
|
|
|
|
if (! gdbarch_iterate_over_regset_sections_p (gdbarch))
|
|
return NULL;
|
|
|
|
if (linux_fill_prpsinfo (&prpsinfo))
|
|
{
|
|
if (gdbarch_ptr_bit (gdbarch) == 64)
|
|
note_data = elfcore_write_linux_prpsinfo64 (obfd,
|
|
note_data, note_size,
|
|
&prpsinfo);
|
|
else
|
|
note_data = elfcore_write_linux_prpsinfo32 (obfd,
|
|
note_data, note_size,
|
|
&prpsinfo);
|
|
}
|
|
|
|
/* Thread register information. */
|
|
try
|
|
{
|
|
update_thread_list ();
|
|
}
|
|
catch (const gdb_exception_error &e)
|
|
{
|
|
exception_print (gdb_stderr, e);
|
|
}
|
|
|
|
/* Like the kernel, prefer dumping the signalled thread first.
|
|
"First thread" is what tools use to infer the signalled thread.
|
|
In case there's more than one signalled thread, prefer the
|
|
current thread, if it is signalled. */
|
|
curr_thr = inferior_thread ();
|
|
if (curr_thr->suspend.stop_signal != GDB_SIGNAL_0)
|
|
signalled_thr = curr_thr;
|
|
else
|
|
{
|
|
signalled_thr = iterate_over_threads (find_signalled_thread, NULL);
|
|
if (signalled_thr == NULL)
|
|
signalled_thr = curr_thr;
|
|
}
|
|
|
|
thread_args.gdbarch = gdbarch;
|
|
thread_args.obfd = obfd;
|
|
thread_args.note_data = note_data;
|
|
thread_args.note_size = note_size;
|
|
thread_args.stop_signal = signalled_thr->suspend.stop_signal;
|
|
|
|
linux_corefile_thread (signalled_thr, &thread_args);
|
|
for (thread_info *thr : current_inferior ()->non_exited_threads ())
|
|
{
|
|
if (thr == signalled_thr)
|
|
continue;
|
|
|
|
linux_corefile_thread (thr, &thread_args);
|
|
}
|
|
|
|
note_data = thread_args.note_data;
|
|
if (!note_data)
|
|
return NULL;
|
|
|
|
/* Auxillary vector. */
|
|
gdb::optional<gdb::byte_vector> auxv =
|
|
target_read_alloc (current_top_target (), TARGET_OBJECT_AUXV, NULL);
|
|
if (auxv && !auxv->empty ())
|
|
{
|
|
note_data = elfcore_write_note (obfd, note_data, note_size,
|
|
"CORE", NT_AUXV, auxv->data (),
|
|
auxv->size ());
|
|
|
|
if (!note_data)
|
|
return NULL;
|
|
}
|
|
|
|
/* File mappings. */
|
|
note_data = linux_make_mappings_corefile_notes (gdbarch, obfd,
|
|
note_data, note_size);
|
|
|
|
return note_data;
|
|
}
|
|
|
|
/* Implementation of `gdbarch_gdb_signal_from_target', as defined in
|
|
gdbarch.h. This function is not static because it is exported to
|
|
other -tdep files. */
|
|
|
|
enum gdb_signal
|
|
linux_gdb_signal_from_target (struct gdbarch *gdbarch, int signal)
|
|
{
|
|
switch (signal)
|
|
{
|
|
case 0:
|
|
return GDB_SIGNAL_0;
|
|
|
|
case LINUX_SIGHUP:
|
|
return GDB_SIGNAL_HUP;
|
|
|
|
case LINUX_SIGINT:
|
|
return GDB_SIGNAL_INT;
|
|
|
|
case LINUX_SIGQUIT:
|
|
return GDB_SIGNAL_QUIT;
|
|
|
|
case LINUX_SIGILL:
|
|
return GDB_SIGNAL_ILL;
|
|
|
|
case LINUX_SIGTRAP:
|
|
return GDB_SIGNAL_TRAP;
|
|
|
|
case LINUX_SIGABRT:
|
|
return GDB_SIGNAL_ABRT;
|
|
|
|
case LINUX_SIGBUS:
|
|
return GDB_SIGNAL_BUS;
|
|
|
|
case LINUX_SIGFPE:
|
|
return GDB_SIGNAL_FPE;
|
|
|
|
case LINUX_SIGKILL:
|
|
return GDB_SIGNAL_KILL;
|
|
|
|
case LINUX_SIGUSR1:
|
|
return GDB_SIGNAL_USR1;
|
|
|
|
case LINUX_SIGSEGV:
|
|
return GDB_SIGNAL_SEGV;
|
|
|
|
case LINUX_SIGUSR2:
|
|
return GDB_SIGNAL_USR2;
|
|
|
|
case LINUX_SIGPIPE:
|
|
return GDB_SIGNAL_PIPE;
|
|
|
|
case LINUX_SIGALRM:
|
|
return GDB_SIGNAL_ALRM;
|
|
|
|
case LINUX_SIGTERM:
|
|
return GDB_SIGNAL_TERM;
|
|
|
|
case LINUX_SIGCHLD:
|
|
return GDB_SIGNAL_CHLD;
|
|
|
|
case LINUX_SIGCONT:
|
|
return GDB_SIGNAL_CONT;
|
|
|
|
case LINUX_SIGSTOP:
|
|
return GDB_SIGNAL_STOP;
|
|
|
|
case LINUX_SIGTSTP:
|
|
return GDB_SIGNAL_TSTP;
|
|
|
|
case LINUX_SIGTTIN:
|
|
return GDB_SIGNAL_TTIN;
|
|
|
|
case LINUX_SIGTTOU:
|
|
return GDB_SIGNAL_TTOU;
|
|
|
|
case LINUX_SIGURG:
|
|
return GDB_SIGNAL_URG;
|
|
|
|
case LINUX_SIGXCPU:
|
|
return GDB_SIGNAL_XCPU;
|
|
|
|
case LINUX_SIGXFSZ:
|
|
return GDB_SIGNAL_XFSZ;
|
|
|
|
case LINUX_SIGVTALRM:
|
|
return GDB_SIGNAL_VTALRM;
|
|
|
|
case LINUX_SIGPROF:
|
|
return GDB_SIGNAL_PROF;
|
|
|
|
case LINUX_SIGWINCH:
|
|
return GDB_SIGNAL_WINCH;
|
|
|
|
/* No way to differentiate between SIGIO and SIGPOLL.
|
|
Therefore, we just handle the first one. */
|
|
case LINUX_SIGIO:
|
|
return GDB_SIGNAL_IO;
|
|
|
|
case LINUX_SIGPWR:
|
|
return GDB_SIGNAL_PWR;
|
|
|
|
case LINUX_SIGSYS:
|
|
return GDB_SIGNAL_SYS;
|
|
|
|
/* SIGRTMIN and SIGRTMAX are not continuous in <gdb/signals.def>,
|
|
therefore we have to handle them here. */
|
|
case LINUX_SIGRTMIN:
|
|
return GDB_SIGNAL_REALTIME_32;
|
|
|
|
case LINUX_SIGRTMAX:
|
|
return GDB_SIGNAL_REALTIME_64;
|
|
}
|
|
|
|
if (signal >= LINUX_SIGRTMIN + 1 && signal <= LINUX_SIGRTMAX - 1)
|
|
{
|
|
int offset = signal - LINUX_SIGRTMIN + 1;
|
|
|
|
return (enum gdb_signal) ((int) GDB_SIGNAL_REALTIME_33 + offset);
|
|
}
|
|
|
|
return GDB_SIGNAL_UNKNOWN;
|
|
}
|
|
|
|
/* Implementation of `gdbarch_gdb_signal_to_target', as defined in
|
|
gdbarch.h. This function is not static because it is exported to
|
|
other -tdep files. */
|
|
|
|
int
|
|
linux_gdb_signal_to_target (struct gdbarch *gdbarch,
|
|
enum gdb_signal signal)
|
|
{
|
|
switch (signal)
|
|
{
|
|
case GDB_SIGNAL_0:
|
|
return 0;
|
|
|
|
case GDB_SIGNAL_HUP:
|
|
return LINUX_SIGHUP;
|
|
|
|
case GDB_SIGNAL_INT:
|
|
return LINUX_SIGINT;
|
|
|
|
case GDB_SIGNAL_QUIT:
|
|
return LINUX_SIGQUIT;
|
|
|
|
case GDB_SIGNAL_ILL:
|
|
return LINUX_SIGILL;
|
|
|
|
case GDB_SIGNAL_TRAP:
|
|
return LINUX_SIGTRAP;
|
|
|
|
case GDB_SIGNAL_ABRT:
|
|
return LINUX_SIGABRT;
|
|
|
|
case GDB_SIGNAL_FPE:
|
|
return LINUX_SIGFPE;
|
|
|
|
case GDB_SIGNAL_KILL:
|
|
return LINUX_SIGKILL;
|
|
|
|
case GDB_SIGNAL_BUS:
|
|
return LINUX_SIGBUS;
|
|
|
|
case GDB_SIGNAL_SEGV:
|
|
return LINUX_SIGSEGV;
|
|
|
|
case GDB_SIGNAL_SYS:
|
|
return LINUX_SIGSYS;
|
|
|
|
case GDB_SIGNAL_PIPE:
|
|
return LINUX_SIGPIPE;
|
|
|
|
case GDB_SIGNAL_ALRM:
|
|
return LINUX_SIGALRM;
|
|
|
|
case GDB_SIGNAL_TERM:
|
|
return LINUX_SIGTERM;
|
|
|
|
case GDB_SIGNAL_URG:
|
|
return LINUX_SIGURG;
|
|
|
|
case GDB_SIGNAL_STOP:
|
|
return LINUX_SIGSTOP;
|
|
|
|
case GDB_SIGNAL_TSTP:
|
|
return LINUX_SIGTSTP;
|
|
|
|
case GDB_SIGNAL_CONT:
|
|
return LINUX_SIGCONT;
|
|
|
|
case GDB_SIGNAL_CHLD:
|
|
return LINUX_SIGCHLD;
|
|
|
|
case GDB_SIGNAL_TTIN:
|
|
return LINUX_SIGTTIN;
|
|
|
|
case GDB_SIGNAL_TTOU:
|
|
return LINUX_SIGTTOU;
|
|
|
|
case GDB_SIGNAL_IO:
|
|
return LINUX_SIGIO;
|
|
|
|
case GDB_SIGNAL_XCPU:
|
|
return LINUX_SIGXCPU;
|
|
|
|
case GDB_SIGNAL_XFSZ:
|
|
return LINUX_SIGXFSZ;
|
|
|
|
case GDB_SIGNAL_VTALRM:
|
|
return LINUX_SIGVTALRM;
|
|
|
|
case GDB_SIGNAL_PROF:
|
|
return LINUX_SIGPROF;
|
|
|
|
case GDB_SIGNAL_WINCH:
|
|
return LINUX_SIGWINCH;
|
|
|
|
case GDB_SIGNAL_USR1:
|
|
return LINUX_SIGUSR1;
|
|
|
|
case GDB_SIGNAL_USR2:
|
|
return LINUX_SIGUSR2;
|
|
|
|
case GDB_SIGNAL_PWR:
|
|
return LINUX_SIGPWR;
|
|
|
|
case GDB_SIGNAL_POLL:
|
|
return LINUX_SIGPOLL;
|
|
|
|
/* GDB_SIGNAL_REALTIME_32 is not continuous in <gdb/signals.def>,
|
|
therefore we have to handle it here. */
|
|
case GDB_SIGNAL_REALTIME_32:
|
|
return LINUX_SIGRTMIN;
|
|
|
|
/* Same comment applies to _64. */
|
|
case GDB_SIGNAL_REALTIME_64:
|
|
return LINUX_SIGRTMAX;
|
|
}
|
|
|
|
/* GDB_SIGNAL_REALTIME_33 to _64 are continuous. */
|
|
if (signal >= GDB_SIGNAL_REALTIME_33
|
|
&& signal <= GDB_SIGNAL_REALTIME_63)
|
|
{
|
|
int offset = signal - GDB_SIGNAL_REALTIME_33;
|
|
|
|
return LINUX_SIGRTMIN + 1 + offset;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
/* Helper for linux_vsyscall_range that does the real work of finding
|
|
the vsyscall's address range. */
|
|
|
|
static int
|
|
linux_vsyscall_range_raw (struct gdbarch *gdbarch, struct mem_range *range)
|
|
{
|
|
char filename[100];
|
|
long pid;
|
|
|
|
if (target_auxv_search (current_top_target (), AT_SYSINFO_EHDR, &range->start) <= 0)
|
|
return 0;
|
|
|
|
/* It doesn't make sense to access the host's /proc when debugging a
|
|
core file. Instead, look for the PT_LOAD segment that matches
|
|
the vDSO. */
|
|
if (!target_has_execution)
|
|
{
|
|
long phdrs_size;
|
|
int num_phdrs, i;
|
|
|
|
phdrs_size = bfd_get_elf_phdr_upper_bound (core_bfd);
|
|
if (phdrs_size == -1)
|
|
return 0;
|
|
|
|
gdb::unique_xmalloc_ptr<Elf_Internal_Phdr>
|
|
phdrs ((Elf_Internal_Phdr *) xmalloc (phdrs_size));
|
|
num_phdrs = bfd_get_elf_phdrs (core_bfd, phdrs.get ());
|
|
if (num_phdrs == -1)
|
|
return 0;
|
|
|
|
for (i = 0; i < num_phdrs; i++)
|
|
if (phdrs.get ()[i].p_type == PT_LOAD
|
|
&& phdrs.get ()[i].p_vaddr == range->start)
|
|
{
|
|
range->length = phdrs.get ()[i].p_memsz;
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* We need to know the real target PID to access /proc. */
|
|
if (current_inferior ()->fake_pid_p)
|
|
return 0;
|
|
|
|
pid = current_inferior ()->pid;
|
|
|
|
/* Note that reading /proc/PID/task/PID/maps (1) is much faster than
|
|
reading /proc/PID/maps (2). The later identifies thread stacks
|
|
in the output, which requires scanning every thread in the thread
|
|
group to check whether a VMA is actually a thread's stack. With
|
|
Linux 4.4 on an Intel i7-4810MQ @ 2.80GHz, with an inferior with
|
|
a few thousand threads, (1) takes a few miliseconds, while (2)
|
|
takes several seconds. Also note that "smaps", what we read for
|
|
determining core dump mappings, is even slower than "maps". */
|
|
xsnprintf (filename, sizeof filename, "/proc/%ld/task/%ld/maps", pid, pid);
|
|
gdb::unique_xmalloc_ptr<char> data
|
|
= target_fileio_read_stralloc (NULL, filename);
|
|
if (data != NULL)
|
|
{
|
|
char *line;
|
|
char *saveptr = NULL;
|
|
|
|
for (line = strtok_r (data.get (), "\n", &saveptr);
|
|
line != NULL;
|
|
line = strtok_r (NULL, "\n", &saveptr))
|
|
{
|
|
ULONGEST addr, endaddr;
|
|
const char *p = line;
|
|
|
|
addr = strtoulst (p, &p, 16);
|
|
if (addr == range->start)
|
|
{
|
|
if (*p == '-')
|
|
p++;
|
|
endaddr = strtoulst (p, &p, 16);
|
|
range->length = endaddr - addr;
|
|
return 1;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
warning (_("unable to open /proc file '%s'"), filename);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Implementation of the "vsyscall_range" gdbarch hook. Handles
|
|
caching, and defers the real work to linux_vsyscall_range_raw. */
|
|
|
|
static int
|
|
linux_vsyscall_range (struct gdbarch *gdbarch, struct mem_range *range)
|
|
{
|
|
struct linux_info *info = get_linux_inferior_data ();
|
|
|
|
if (info->vsyscall_range_p == 0)
|
|
{
|
|
if (linux_vsyscall_range_raw (gdbarch, &info->vsyscall_range))
|
|
info->vsyscall_range_p = 1;
|
|
else
|
|
info->vsyscall_range_p = -1;
|
|
}
|
|
|
|
if (info->vsyscall_range_p < 0)
|
|
return 0;
|
|
|
|
*range = info->vsyscall_range;
|
|
return 1;
|
|
}
|
|
|
|
/* Symbols for linux_infcall_mmap's ARG_FLAGS; their Linux MAP_* system
|
|
definitions would be dependent on compilation host. */
|
|
#define GDB_MMAP_MAP_PRIVATE 0x02 /* Changes are private. */
|
|
#define GDB_MMAP_MAP_ANONYMOUS 0x20 /* Don't use a file. */
|
|
|
|
/* See gdbarch.sh 'infcall_mmap'. */
|
|
|
|
static CORE_ADDR
|
|
linux_infcall_mmap (CORE_ADDR size, unsigned prot)
|
|
{
|
|
struct objfile *objf;
|
|
/* Do there still exist any Linux systems without "mmap64"?
|
|
"mmap" uses 64-bit off_t on x86_64 and 32-bit off_t on i386 and x32. */
|
|
struct value *mmap_val = find_function_in_inferior ("mmap64", &objf);
|
|
struct value *addr_val;
|
|
struct gdbarch *gdbarch = get_objfile_arch (objf);
|
|
CORE_ADDR retval;
|
|
enum
|
|
{
|
|
ARG_ADDR, ARG_LENGTH, ARG_PROT, ARG_FLAGS, ARG_FD, ARG_OFFSET, ARG_LAST
|
|
};
|
|
struct value *arg[ARG_LAST];
|
|
|
|
arg[ARG_ADDR] = value_from_pointer (builtin_type (gdbarch)->builtin_data_ptr,
|
|
0);
|
|
/* Assuming sizeof (unsigned long) == sizeof (size_t). */
|
|
arg[ARG_LENGTH] = value_from_ulongest
|
|
(builtin_type (gdbarch)->builtin_unsigned_long, size);
|
|
gdb_assert ((prot & ~(GDB_MMAP_PROT_READ | GDB_MMAP_PROT_WRITE
|
|
| GDB_MMAP_PROT_EXEC))
|
|
== 0);
|
|
arg[ARG_PROT] = value_from_longest (builtin_type (gdbarch)->builtin_int, prot);
|
|
arg[ARG_FLAGS] = value_from_longest (builtin_type (gdbarch)->builtin_int,
|
|
GDB_MMAP_MAP_PRIVATE
|
|
| GDB_MMAP_MAP_ANONYMOUS);
|
|
arg[ARG_FD] = value_from_longest (builtin_type (gdbarch)->builtin_int, -1);
|
|
arg[ARG_OFFSET] = value_from_longest (builtin_type (gdbarch)->builtin_int64,
|
|
0);
|
|
addr_val = call_function_by_hand (mmap_val, NULL, arg);
|
|
retval = value_as_address (addr_val);
|
|
if (retval == (CORE_ADDR) -1)
|
|
error (_("Failed inferior mmap call for %s bytes, errno is changed."),
|
|
pulongest (size));
|
|
return retval;
|
|
}
|
|
|
|
/* See gdbarch.sh 'infcall_munmap'. */
|
|
|
|
static void
|
|
linux_infcall_munmap (CORE_ADDR addr, CORE_ADDR size)
|
|
{
|
|
struct objfile *objf;
|
|
struct value *munmap_val = find_function_in_inferior ("munmap", &objf);
|
|
struct value *retval_val;
|
|
struct gdbarch *gdbarch = get_objfile_arch (objf);
|
|
LONGEST retval;
|
|
enum
|
|
{
|
|
ARG_ADDR, ARG_LENGTH, ARG_LAST
|
|
};
|
|
struct value *arg[ARG_LAST];
|
|
|
|
arg[ARG_ADDR] = value_from_pointer (builtin_type (gdbarch)->builtin_data_ptr,
|
|
addr);
|
|
/* Assuming sizeof (unsigned long) == sizeof (size_t). */
|
|
arg[ARG_LENGTH] = value_from_ulongest
|
|
(builtin_type (gdbarch)->builtin_unsigned_long, size);
|
|
retval_val = call_function_by_hand (munmap_val, NULL, arg);
|
|
retval = value_as_long (retval_val);
|
|
if (retval != 0)
|
|
warning (_("Failed inferior munmap call at %s for %s bytes, "
|
|
"errno is changed."),
|
|
hex_string (addr), pulongest (size));
|
|
}
|
|
|
|
/* See linux-tdep.h. */
|
|
|
|
CORE_ADDR
|
|
linux_displaced_step_location (struct gdbarch *gdbarch)
|
|
{
|
|
CORE_ADDR addr;
|
|
int bp_len;
|
|
|
|
/* Determine entry point from target auxiliary vector. This avoids
|
|
the need for symbols. Also, when debugging a stand-alone SPU
|
|
executable, entry_point_address () will point to an SPU
|
|
local-store address and is thus not usable as displaced stepping
|
|
location. The auxiliary vector gets us the PowerPC-side entry
|
|
point address instead. */
|
|
if (target_auxv_search (current_top_target (), AT_ENTRY, &addr) <= 0)
|
|
throw_error (NOT_SUPPORTED_ERROR,
|
|
_("Cannot find AT_ENTRY auxiliary vector entry."));
|
|
|
|
/* Make certain that the address points at real code, and not a
|
|
function descriptor. */
|
|
addr = gdbarch_convert_from_func_ptr_addr (gdbarch, addr,
|
|
current_top_target ());
|
|
|
|
/* Inferior calls also use the entry point as a breakpoint location.
|
|
We don't want displaced stepping to interfere with those
|
|
breakpoints, so leave space. */
|
|
gdbarch_breakpoint_from_pc (gdbarch, &addr, &bp_len);
|
|
addr += bp_len * 2;
|
|
|
|
return addr;
|
|
}
|
|
|
|
/* See linux-tdep.h. */
|
|
|
|
CORE_ADDR
|
|
linux_get_hwcap (struct target_ops *target)
|
|
{
|
|
CORE_ADDR field;
|
|
if (target_auxv_search (target, AT_HWCAP, &field) != 1)
|
|
return 0;
|
|
return field;
|
|
}
|
|
|
|
/* See linux-tdep.h. */
|
|
|
|
CORE_ADDR
|
|
linux_get_hwcap2 (struct target_ops *target)
|
|
{
|
|
CORE_ADDR field;
|
|
if (target_auxv_search (target, AT_HWCAP2, &field) != 1)
|
|
return 0;
|
|
return field;
|
|
}
|
|
|
|
/* Display whether the gcore command is using the
|
|
/proc/PID/coredump_filter file. */
|
|
|
|
static void
|
|
show_use_coredump_filter (struct ui_file *file, int from_tty,
|
|
struct cmd_list_element *c, const char *value)
|
|
{
|
|
fprintf_filtered (file, _("Use of /proc/PID/coredump_filter file to generate"
|
|
" corefiles is %s.\n"), value);
|
|
}
|
|
|
|
/* Display whether the gcore command is dumping mappings marked with
|
|
the VM_DONTDUMP flag. */
|
|
|
|
static void
|
|
show_dump_excluded_mappings (struct ui_file *file, int from_tty,
|
|
struct cmd_list_element *c, const char *value)
|
|
{
|
|
fprintf_filtered (file, _("Dumping of mappings marked with the VM_DONTDUMP"
|
|
" flag is %s.\n"), value);
|
|
}
|
|
|
|
/* To be called from the various GDB_OSABI_LINUX handlers for the
|
|
various GNU/Linux architectures and machine types. */
|
|
|
|
void
|
|
linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
|
|
{
|
|
set_gdbarch_core_pid_to_str (gdbarch, linux_core_pid_to_str);
|
|
set_gdbarch_info_proc (gdbarch, linux_info_proc);
|
|
set_gdbarch_core_info_proc (gdbarch, linux_core_info_proc);
|
|
set_gdbarch_core_xfer_siginfo (gdbarch, linux_core_xfer_siginfo);
|
|
set_gdbarch_find_memory_regions (gdbarch, linux_find_memory_regions);
|
|
set_gdbarch_make_corefile_notes (gdbarch, linux_make_corefile_notes);
|
|
set_gdbarch_has_shared_address_space (gdbarch,
|
|
linux_has_shared_address_space);
|
|
set_gdbarch_gdb_signal_from_target (gdbarch,
|
|
linux_gdb_signal_from_target);
|
|
set_gdbarch_gdb_signal_to_target (gdbarch,
|
|
linux_gdb_signal_to_target);
|
|
set_gdbarch_vsyscall_range (gdbarch, linux_vsyscall_range);
|
|
set_gdbarch_infcall_mmap (gdbarch, linux_infcall_mmap);
|
|
set_gdbarch_infcall_munmap (gdbarch, linux_infcall_munmap);
|
|
set_gdbarch_get_siginfo_type (gdbarch, linux_get_siginfo_type);
|
|
}
|
|
|
|
void
|
|
_initialize_linux_tdep (void)
|
|
{
|
|
linux_gdbarch_data_handle =
|
|
gdbarch_data_register_post_init (init_linux_gdbarch_data);
|
|
|
|
/* Observers used to invalidate the cache when needed. */
|
|
gdb::observers::inferior_exit.attach (invalidate_linux_cache_inf);
|
|
gdb::observers::inferior_appeared.attach (invalidate_linux_cache_inf);
|
|
|
|
add_setshow_boolean_cmd ("use-coredump-filter", class_files,
|
|
&use_coredump_filter, _("\
|
|
Set whether gcore should consider /proc/PID/coredump_filter."),
|
|
_("\
|
|
Show whether gcore should consider /proc/PID/coredump_filter."),
|
|
_("\
|
|
Use this command to set whether gcore should consider the contents\n\
|
|
of /proc/PID/coredump_filter when generating the corefile. For more information\n\
|
|
about this file, refer to the manpage of core(5)."),
|
|
NULL, show_use_coredump_filter,
|
|
&setlist, &showlist);
|
|
|
|
add_setshow_boolean_cmd ("dump-excluded-mappings", class_files,
|
|
&dump_excluded_mappings, _("\
|
|
Set whether gcore should dump mappings marked with the VM_DONTDUMP flag."),
|
|
_("\
|
|
Show whether gcore should dump mappings marked with the VM_DONTDUMP flag."),
|
|
_("\
|
|
Use this command to set whether gcore should dump mappings marked with the\n\
|
|
VM_DONTDUMP flag (\"dd\" in /proc/PID/smaps) when generating the corefile. For\n\
|
|
more information about this file, refer to the manpage of proc(5) and core(5)."),
|
|
NULL, show_dump_excluded_mappings,
|
|
&setlist, &showlist);
|
|
}
|