diff options
author | Tristan Gingold <gingold@adacore.com> | 2008-11-27 09:23:01 +0000 |
---|---|---|
committer | Tristan Gingold <gingold@adacore.com> | 2008-11-27 09:23:01 +0000 |
commit | a80b95ba67e2bcdda3a8437c23a8cab7fafd33db (patch) | |
tree | f66a189908a12160982621eda9dd5bf2d0af0a43 | |
parent | 6c7c4ca467457417ac60105e72a8379f3de16b35 (diff) | |
download | gdb-a80b95ba67e2bcdda3a8437c23a8cab7fafd33db.zip gdb-a80b95ba67e2bcdda3a8437c23a8cab7fafd33db.tar.gz gdb-a80b95ba67e2bcdda3a8437c23a8cab7fafd33db.tar.bz2 |
gdb:
2008-11-27 Tristan Gingold <gingold@adacore.com>
* NEWS: Add entry for new native configuration: Darwin.
* configure.host: Add Darwin host.
* configure.tgt: Add Darwin target.
* defs.h (enum gdb_osabi): Add GDB_OSABI_DARWIN.
* osabi.c (gdb_osabi_names): Add name for Darwin abi.
* i386-darwin-nat.c: New file.
* i386-darwin-tdep.c: New file.
* machoread.c: New file.
* darwin-nat-info.c: New file.
* darwin-nat.c: New file.
* darwin-nat.h: New file.
* darwin.defs: New file.
* config/i386/darwin.mh: New file.
gdb/doc:
2008-11-27 Tristan Gingold <gingold@adacore.com>
* gdb.texinfo (Darwin): Document Darwin specific features.
-rw-r--r-- | gdb/ChangeLog | 16 | ||||
-rw-r--r-- | gdb/NEWS | 4 | ||||
-rw-r--r-- | gdb/config/i386/darwin.mh | 10 | ||||
-rw-r--r-- | gdb/configure.host | 2 | ||||
-rw-r--r-- | gdb/configure.tgt | 5 | ||||
-rw-r--r-- | gdb/darwin-nat-info.c | 847 | ||||
-rw-r--r-- | gdb/darwin-nat.c | 1307 | ||||
-rw-r--r-- | gdb/darwin-nat.h | 107 | ||||
-rw-r--r-- | gdb/darwin.defs | 6 | ||||
-rw-r--r-- | gdb/defs.h | 1 | ||||
-rw-r--r-- | gdb/doc/ChangeLog | 4 | ||||
-rw-r--r-- | gdb/doc/gdb.texinfo | 43 | ||||
-rw-r--r-- | gdb/i386-darwin-nat.c | 492 | ||||
-rw-r--r-- | gdb/i386-darwin-tdep.c | 159 | ||||
-rw-r--r-- | gdb/i386-darwin-tdep.h | 33 | ||||
-rw-r--r-- | gdb/machoread.c | 694 | ||||
-rw-r--r-- | gdb/osabi.c | 1 |
17 files changed, 3731 insertions, 0 deletions
diff --git a/gdb/ChangeLog b/gdb/ChangeLog index 84aff02..843c194 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,19 @@ +2008-11-27 Tristan Gingold <gingold@adacore.com> + + * NEWS: Add entry for new native configuration: Darwin. + * configure.host: Add Darwin host. + * configure.tgt: Add Darwin target. + * defs.h (enum gdb_osabi): Add GDB_OSABI_DARWIN. + * osabi.c (gdb_osabi_names): Add name for Darwin abi. + * i386-darwin-nat.c: New file. + * i386-darwin-tdep.c: New file. + * machoread.c: New file. + * darwin-nat-info.c: New file. + * darwin-nat.c: New file. + * darwin-nat.h: New file. + * darwin.defs: New file. + * config/i386/darwin.mh: New file. + 2008-11-26 Tristan Gingold <gingold@adacore.com> * MAINTAINERS: Add myself for write after approval privileges. @@ -172,6 +172,10 @@ macro undef These allow macros to be defined, undefined, and listed interactively. +* New native configurations + +x86/x86_64 Darwin i[34567]86-*-darwin* + * New targets x86 DICOS i[34567]86-*-dicos* diff --git a/gdb/config/i386/darwin.mh b/gdb/config/i386/darwin.mh new file mode 100644 index 0000000..ed49d7d --- /dev/null +++ b/gdb/config/i386/darwin.mh @@ -0,0 +1,10 @@ +# Host: IA86 running Darwin + +NATDEPFILES = fork-child.o machoread.o darwin-nat.o excServer.o \ + i386-darwin-nat.o i386-nat.o amd64-nat.o darwin-nat-info.o + +# Trick so that excServer.c is not the default target! +_all: all + +excServer.c: darwin.defs + /usr/bin/mig -I. $< diff --git a/gdb/configure.host b/gdb/configure.host index 672920d..b9ed7ae 100644 --- a/gdb/configure.host +++ b/gdb/configure.host @@ -62,6 +62,8 @@ esac case "${host}" in +*-*-darwin*) gdb_host=darwin ;; + alpha*-*-osf[3456789]*) gdb_host=alpha-osf3 ;; alpha*-*-linux*) gdb_host=alpha-linux ;; alpha*-*-freebsd* | alpha*-*-kfreebsd*-gnu) diff --git a/gdb/configure.tgt b/gdb/configure.tgt index 95556f8..b9cd21b 100644 --- a/gdb/configure.tgt +++ b/gdb/configure.tgt @@ -145,6 +145,11 @@ hppa*-*-*) gdb_target_obs="hppa-tdep.o" ;; +i[34567]86-*-darwin*) + # Target: Darwin/i386 + gdb_target_obs="amd64-tdep.o i386-tdep.o i387-tdep.o \ + i386-darwin-tdep.o" + ;; i[34567]86-*-dicos*) # Target: DICOS/i386 gdb_target_obs="i386-tdep.o i387-tdep.o \ diff --git a/gdb/darwin-nat-info.c b/gdb/darwin-nat-info.c new file mode 100644 index 0000000..db7cb05 --- /dev/null +++ b/gdb/darwin-nat-info.c @@ -0,0 +1,847 @@ +/* Darwin support for GDB, the GNU debugger. + Copyright 1997, 1998, 1999, 2000, 2001, 2002, 2008 + Free Software Foundation, Inc. + + Contributed by Apple Computer, Inc. + + This file is part of GDB. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +/* The name of the ppc_thread_state structure, and the names of its + members, have been changed for Unix conformance reasons. The easiest + way to have gdb build on systems with the older names and systems + with the newer names is to build this compilation unit with the + non-conformant define below. This doesn't seem to cause the resulting + binary any problems but it seems like it could cause us problems in + the future. It'd be good to remove this at some point when compiling on + Tiger is no longer important. */ + +#include "defs.h" +#include "symtab.h" +#include "gdbtypes.h" +#include "gdbcore.h" +#include "value.h" +#include "gdbcmd.h" +#include "inferior.h" + +#include <sys/param.h> +#include <sys/sysctl.h> + +#include "darwin-nat.h" + +#include <mach/thread_info.h> +#include <mach/thread_act.h> +#include <mach/task.h> +#include <mach/vm_map.h> +#include <mach/mach_port.h> +#include <mach/mach_init.h> +#include <mach/mach_vm.h> + +#define CHECK_ARGS(what, args) do { \ + if ((NULL == args) || ((args[0] != '0') && (args[1] != 'x'))) \ + error("%s must be specified with 0x...", what); \ +} while (0) + +#define PRINT_FIELD(structure, field) \ + printf_unfiltered(_(#field":\t%#lx\n"), (unsigned long) (structure)->field) + +#define PRINT_TV_FIELD(structure, field) \ + printf_unfiltered(_(#field":\t%u.%06u sec\n"), \ + (unsigned) (structure)->field.seconds, \ + (unsigned) (structure)->field.microseconds) + +#define task_self mach_task_self +#define task_by_unix_pid task_for_pid +#define port_name_array_t mach_port_array_t +#define port_type_array_t mach_port_array_t + +static void +info_mach_tasks_command (char *args, int from_tty) +{ + int sysControl[4]; + int count, index; + size_t length; + struct kinfo_proc *procInfo; + + sysControl[0] = CTL_KERN; + sysControl[1] = KERN_PROC; + sysControl[2] = KERN_PROC_ALL; + + sysctl (sysControl, 3, NULL, &length, NULL, 0); + procInfo = (struct kinfo_proc *) xmalloc (length); + sysctl (sysControl, 3, procInfo, &length, NULL, 0); + + count = (length / sizeof (struct kinfo_proc)); + printf_unfiltered (_("%d processes:\n"), count); + for (index = 0; index < count; ++index) + { + kern_return_t result; + mach_port_t taskPort; + + result = + task_by_unix_pid (mach_task_self (), procInfo[index].kp_proc.p_pid, + &taskPort); + if (KERN_SUCCESS == result) + { + printf_unfiltered (_(" %s is %d has task %#x\n"), + procInfo[index].kp_proc.p_comm, + procInfo[index].kp_proc.p_pid, taskPort); + } + else + { + printf_unfiltered (_(" %s is %d unknown task port\n"), + procInfo[index].kp_proc.p_comm, + procInfo[index].kp_proc.p_pid); + } + } + + xfree (procInfo); +} + +static task_t +get_task_from_args (char *args) +{ + task_t task; + char *eptr; + + if (args == NULL || *args == 0) + { + if (darwin_inf->task == TASK_NULL) + printf_unfiltered (_("No inferior running\n")); + return darwin_inf->task; + } + if (strcmp (args, "gdb") == 0) + return mach_task_self (); + task = strtoul (args, &eptr, 0); + if (*eptr) + { + printf_unfiltered (_("cannot parse task id '%s'\n"), args); + return TASK_NULL; + } + return task; +} + +static void +info_mach_task_command (char *args, int from_tty) +{ + union + { + struct task_basic_info basic; + struct task_events_info events; + struct task_thread_times_info thread_times; + } task_info_data; + + kern_return_t result; + unsigned int info_count; + task_t task; + + task = get_task_from_args (args); + if (task == TASK_NULL) + return; + + printf_unfiltered (_("TASK_BASIC_INFO for 0x%x:\n"), task); + info_count = TASK_BASIC_INFO_COUNT; + result = task_info (task, + TASK_BASIC_INFO, + (task_info_t) & task_info_data.basic, &info_count); + MACH_CHECK_ERROR (result); + + PRINT_FIELD (&task_info_data.basic, suspend_count); + PRINT_FIELD (&task_info_data.basic, virtual_size); + PRINT_FIELD (&task_info_data.basic, resident_size); + PRINT_TV_FIELD (&task_info_data.basic, user_time); + PRINT_TV_FIELD (&task_info_data.basic, system_time); + printf_unfiltered (_("\nTASK_EVENTS_INFO:\n")); + info_count = TASK_EVENTS_INFO_COUNT; + result = task_info (task, + TASK_EVENTS_INFO, + (task_info_t) & task_info_data.events, &info_count); + MACH_CHECK_ERROR (result); + + PRINT_FIELD (&task_info_data.events, faults); +#if 0 + PRINT_FIELD (&task_info_data.events, zero_fills); + PRINT_FIELD (&task_info_data.events, reactivations); +#endif + PRINT_FIELD (&task_info_data.events, pageins); + PRINT_FIELD (&task_info_data.events, cow_faults); + PRINT_FIELD (&task_info_data.events, messages_sent); + PRINT_FIELD (&task_info_data.events, messages_received); + printf_unfiltered (_("\nTASK_THREAD_TIMES_INFO:\n")); + info_count = TASK_THREAD_TIMES_INFO_COUNT; + result = task_info (task, + TASK_THREAD_TIMES_INFO, + (task_info_t) & task_info_data.thread_times, + &info_count); + MACH_CHECK_ERROR (result); + PRINT_TV_FIELD (&task_info_data.thread_times, user_time); + PRINT_TV_FIELD (&task_info_data.thread_times, system_time); +} + +static void +info_mach_ports_command (char *args, int from_tty) +{ + port_name_array_t names; + port_type_array_t types; + unsigned int name_count, type_count; + kern_return_t result; + int index; + task_t task; + + task = get_task_from_args (args); + if (task == TASK_NULL) + return; + + result = mach_port_names (task, &names, &name_count, &types, &type_count); + MACH_CHECK_ERROR (result); + + gdb_assert (name_count == type_count); + + printf_unfiltered (_("Ports for task 0x%x:\n"), task); + printf_unfiltered (_("port type\n")); + for (index = 0; index < name_count; ++index) + { + mach_port_t port = names[index]; + unsigned int j; + struct type_descr + { + mach_port_type_t type; + const char *name; + mach_port_right_t right; + }; + static struct type_descr descrs[] = + { + {MACH_PORT_TYPE_SEND, "send", MACH_PORT_RIGHT_SEND}, + {MACH_PORT_TYPE_SEND_ONCE, "send-once", MACH_PORT_RIGHT_SEND_ONCE}, + {MACH_PORT_TYPE_RECEIVE, "receive", MACH_PORT_RIGHT_RECEIVE}, + {MACH_PORT_TYPE_PORT_SET, "port-set", MACH_PORT_RIGHT_PORT_SET}, + {MACH_PORT_TYPE_DEAD_NAME, "dead", MACH_PORT_RIGHT_DEAD_NAME} + }; + + printf_unfiltered (_("%04x: %08x "), port, types[index]); + for (j = 0; j < sizeof(descrs) / sizeof(*descrs); j++) + if (types[index] & descrs[j].type) + { + mach_port_urefs_t ref; + kern_return_t ret; + + printf_unfiltered (_(" %s("), descrs[j].name); + ret = mach_port_get_refs (task, port, descrs[j].right, &ref); + if (ret != KERN_SUCCESS) + printf_unfiltered (_("??")); + else + printf_unfiltered (_("%u"), ref); + printf_unfiltered (_(" refs)")); + } + + if (task == task_self ()) + { + if (port == task_self()) + printf_unfiltered (_(" gdb-task")); + else if (port == darwin_host_self) + printf_unfiltered (_(" host-self")); + else if (port == darwin_not_port) + printf_unfiltered (_(" gdb-notifier")); + else if (port == darwin_ex_port) + printf_unfiltered (_(" gdb-exception")); + else if (port == darwin_port_set) + printf_unfiltered (_(" gdb-port_set")); + else if (darwin_inf && port == darwin_inf->task) + printf_unfiltered (_(" inferior-task")); + else if (darwin_inf && darwin_inf->threads) + { + int k; + thread_t t; + for (k = 0; VEC_iterate(thread_t, darwin_inf->threads, k, t); k++) + if (port == t) + { + printf_unfiltered (_(" inferior-thread for 0x%x"), + darwin_inf->task); + break; + } + } + } + printf_unfiltered (_("\n")); + } + + vm_deallocate (task_self (), (vm_address_t) names, + (name_count * sizeof (mach_port_t))); + vm_deallocate (task_self (), (vm_address_t) types, + (type_count * sizeof (mach_port_type_t))); +} + + +void +darwin_debug_port_info (task_t task, mach_port_t port) +{ + kern_return_t kret; + mach_port_status_t status; + mach_msg_type_number_t len = sizeof (status); + + kret = mach_port_get_attributes + (task, port, MACH_PORT_RECEIVE_STATUS, (mach_port_info_t)&status, &len); + MACH_CHECK_ERROR (kret); + + printf_unfiltered (_("Port 0x%lx in task 0x%lx:\n"), (unsigned long) port, + (unsigned long) task); + printf_unfiltered (_(" port set: 0x%x\n"), status.mps_pset); + printf_unfiltered (_(" seqno: 0x%x\n"), status.mps_seqno); + printf_unfiltered (_(" mscount: 0x%x\n"), status.mps_mscount); + printf_unfiltered (_(" qlimit: 0x%x\n"), status.mps_qlimit); + printf_unfiltered (_(" msgcount: 0x%x\n"), status.mps_msgcount); + printf_unfiltered (_(" sorights: 0x%x\n"), status.mps_sorights); + printf_unfiltered (_(" srights: 0x%x\n"), status.mps_srights); + printf_unfiltered (_(" pdrequest: 0x%x\n"), status.mps_pdrequest); + printf_unfiltered (_(" nsrequest: 0x%x\n"), status.mps_nsrequest); + printf_unfiltered (_(" flags: 0x%x\n"), status.mps_flags); +} + +static void +info_mach_port_command (char *args, int from_tty) +{ + task_t task; + mach_port_t port; + + CHECK_ARGS (_("Task and port"), args); + sscanf (args, "0x%x 0x%x", &task, &port); + + darwin_debug_port_info (task, port); +} + +static void +info_mach_threads_command (char *args, int from_tty) +{ + thread_array_t threads; + unsigned int thread_count; + kern_return_t result; + task_t task; + int i; + + task = get_task_from_args (args); + if (task == TASK_NULL) + return; + + result = task_threads (task, &threads, &thread_count); + MACH_CHECK_ERROR (result); + + printf_unfiltered (_("Threads in task %#x:\n"), task); + for (i = 0; i < thread_count; ++i) + { + printf_unfiltered (_(" %#x\n"), threads[i]); + mach_port_deallocate (task_self (), threads[i]); + } + + vm_deallocate (task_self (), (vm_address_t) threads, + (thread_count * sizeof (thread_t))); +} + +static void +info_mach_thread_command (char *args, int from_tty) +{ + union + { + struct thread_basic_info basic; + } thread_info_data; + + thread_t thread; + kern_return_t result; + unsigned int info_count; + + CHECK_ARGS (_("Thread"), args); + sscanf (args, "0x%x", &thread); + + printf_unfiltered (_("THREAD_BASIC_INFO\n")); + info_count = THREAD_BASIC_INFO_COUNT; + result = thread_info (thread, + THREAD_BASIC_INFO, + (thread_info_t) & thread_info_data.basic, + &info_count); + MACH_CHECK_ERROR (result); + +#if 0 + PRINT_FIELD (&thread_info_data.basic, user_time); + PRINT_FIELD (&thread_info_data.basic, system_time); +#endif + PRINT_FIELD (&thread_info_data.basic, cpu_usage); + PRINT_FIELD (&thread_info_data.basic, run_state); + PRINT_FIELD (&thread_info_data.basic, flags); + PRINT_FIELD (&thread_info_data.basic, suspend_count); + PRINT_FIELD (&thread_info_data.basic, sleep_time); +} + +static const char * +unparse_protection (vm_prot_t p) +{ + switch (p) + { + case VM_PROT_NONE: + return "---"; + case VM_PROT_READ: + return "r--"; + case VM_PROT_WRITE: + return "-w-"; + case VM_PROT_READ | VM_PROT_WRITE: + return "rw-"; + case VM_PROT_EXECUTE: + return "--x"; + case VM_PROT_EXECUTE | VM_PROT_READ: + return "r-x"; + case VM_PROT_EXECUTE | VM_PROT_WRITE: + return "-wx"; + case VM_PROT_EXECUTE | VM_PROT_WRITE | VM_PROT_READ: + return "rwx"; + default: + return "???"; + } +} + +static const char * +unparse_inheritance (vm_inherit_t i) +{ + switch (i) + { + case VM_INHERIT_SHARE: + return _("share"); + case VM_INHERIT_COPY: + return _("copy "); + case VM_INHERIT_NONE: + return _("none "); + default: + return _("??? "); + } +} + +static const char * +unparse_share_mode (unsigned char p) +{ + switch (p) + { + case SM_COW: + return _("cow"); + case SM_PRIVATE: + return _("private"); + case SM_EMPTY: + return _("empty"); + case SM_SHARED: + return _("shared"); + case SM_TRUESHARED: + return _("true-shrd"); + case SM_PRIVATE_ALIASED: + return _("prv-alias"); + case SM_SHARED_ALIASED: + return _("shr-alias"); + default: + return _("???"); + } +} + +static const char * +unparse_user_tag (unsigned int tag) +{ + switch (tag) + { + case 0: + return _("default"); + case VM_MEMORY_MALLOC: + return _("malloc"); + case VM_MEMORY_MALLOC_SMALL: + return _("malloc_small"); + case VM_MEMORY_MALLOC_LARGE: + return _("malloc_large"); + case VM_MEMORY_MALLOC_HUGE: + return _("malloc_huge"); + case VM_MEMORY_SBRK: + return _("sbrk"); + case VM_MEMORY_REALLOC: + return _("realloc"); + case VM_MEMORY_MALLOC_TINY: + return _("malloc_tiny"); + case VM_MEMORY_ANALYSIS_TOOL: + return _("analysis_tool"); + case VM_MEMORY_MACH_MSG: + return _("mach_msg"); + case VM_MEMORY_IOKIT: + return _("iokit"); + case VM_MEMORY_STACK: + return _("stack"); + case VM_MEMORY_GUARD: + return _("guard"); + case VM_MEMORY_SHARED_PMAP: + return _("shared_pmap"); + case VM_MEMORY_DYLIB: + return _("dylib"); + case VM_MEMORY_APPKIT: + return _("appkit"); + case VM_MEMORY_FOUNDATION: + return _("foundation"); + default: + return NULL; + } +} + +static void +darwin_debug_regions (task_t task, mach_vm_address_t address, int max) +{ + kern_return_t kret; + vm_region_basic_info_data_64_t info, prev_info; + mach_vm_address_t prev_address; + mach_vm_size_t size, prev_size; + + mach_port_t object_name; + mach_msg_type_number_t count; + + int nsubregions = 0; + int num_printed = 0; + + count = VM_REGION_BASIC_INFO_COUNT_64; + kret = mach_vm_region (task, &address, &size, VM_REGION_BASIC_INFO_64, + (vm_region_info_t) &info, &count, &object_name); + if (kret != KERN_SUCCESS) + { + printf_filtered (_("No memory regions.")); + return; + } + memcpy (&prev_info, &info, sizeof (vm_region_basic_info_data_64_t)); + prev_address = address; + prev_size = size; + nsubregions = 1; + + for (;;) + { + int print = 0; + int done = 0; + + address = prev_address + prev_size; + + /* Check to see if address space has wrapped around. */ + if (address == 0) + print = done = 1; + + if (!done) + { + count = VM_REGION_BASIC_INFO_COUNT_64; + kret = + mach_vm_region (task, &address, &size, VM_REGION_BASIC_INFO_64, + (vm_region_info_t) &info, &count, &object_name); + if (kret != KERN_SUCCESS) + { + size = 0; + print = done = 1; + } + } + + if (address != prev_address + prev_size) + print = 1; + + if ((info.protection != prev_info.protection) + || (info.max_protection != prev_info.max_protection) + || (info.inheritance != prev_info.inheritance) + || (info.shared != prev_info.reserved) + || (info.reserved != prev_info.reserved)) + print = 1; + + if (print) + { + printf_filtered (_("%s-%s %s/%s %s %s %s"), + paddr(prev_address), + paddr(prev_address + prev_size), + unparse_protection (prev_info.protection), + unparse_protection (prev_info.max_protection), + unparse_inheritance (prev_info.inheritance), + prev_info.shared ? _("shrd") : _("priv"), + prev_info.reserved ? _("reserved") : _("not-rsvd")); + + if (nsubregions > 1) + printf_filtered (_(" (%d sub-rgn)"), nsubregions); + + printf_filtered (_("\n")); + + prev_address = address; + prev_size = size; + memcpy (&prev_info, &info, sizeof (vm_region_basic_info_data_64_t)); + nsubregions = 1; + + num_printed++; + } + else + { + prev_size += size; + nsubregions++; + } + + if ((max > 0) && (num_printed >= max)) + done = 1; + + if (done) + break; + } +} + +static void +darwin_debug_regions_recurse (task_t task) +{ + mach_vm_address_t r_addr; + mach_vm_address_t r_start; + mach_vm_size_t r_size; + natural_t r_depth; + mach_msg_type_number_t r_info_size; + vm_region_submap_short_info_data_64_t r_info; + kern_return_t kret; + int ret; + + r_start = 0; + r_depth = 0; + while (1) + { + const char *tag; + + r_info_size = VM_REGION_SUBMAP_SHORT_INFO_COUNT_64; + r_size = -1; + kret = mach_vm_region_recurse (task, &r_start, &r_size, &r_depth, + (vm_region_recurse_info_t) &r_info, + &r_info_size); + if (kret != KERN_SUCCESS) + break; + printf_filtered (_("%s-%s %s/%s %-5s %-10s %2d %s"), + paddr(r_start), + paddr(r_start + r_size), + unparse_protection (r_info.protection), + unparse_protection (r_info.max_protection), + unparse_inheritance (r_info.inheritance), + unparse_share_mode (r_info.share_mode), + r_depth, + r_info.is_submap ? _("sm ") : _("obj")); + tag = unparse_user_tag (r_info.user_tag); + if (tag) + printf_unfiltered (_(" %s\n"), tag); + else + printf_unfiltered (_(" %u\n"), r_info.user_tag); + if (r_info.is_submap) + r_depth++; + else + r_start += r_size; + } +} + + +static void +darwin_debug_region (task_t task, mach_vm_address_t address) +{ + darwin_debug_regions (task, address, 1); +} + +static void +info_mach_regions_command (char *args, int from_tty) +{ + task_t task; + + task = get_task_from_args (args); + if (task == TASK_NULL) + return; + + darwin_debug_regions (task, 0, -1); +} + +static void +info_mach_regions_recurse_command (char *args, int from_tty) +{ + task_t task; + + task = get_task_from_args (args); + if (task == TASK_NULL) + return; + + darwin_debug_regions_recurse (task); +} + +static void +info_mach_region_command (char *exp, int from_tty) +{ + struct expression *expr; + struct value *val; + mach_vm_address_t address; + + expr = parse_expression (exp); + val = evaluate_expression (expr); + if (TYPE_CODE (value_type (val)) == TYPE_CODE_REF) + { + val = value_ind (val); + } + /* In rvalue contexts, such as this, functions are coerced into + pointers to functions. */ + if (TYPE_CODE (value_type (val)) == TYPE_CODE_FUNC + && VALUE_LVAL (val) == lval_memory) + { + address = VALUE_ADDRESS (val); + } + else + { + address = value_as_address (val); + } + + if ((!darwin_inf) || (darwin_inf->task == TASK_NULL)) + error (_("Inferior not available")); + + darwin_debug_region (darwin_inf->task, address); +} + +static void +disp_exception (const darwin_exception_info *info) +{ + int i; + + printf_filtered (_("%d exceptions:\n"), info->count); + for (i = 0; i < info->count; i++) + { + exception_mask_t mask = info->masks[i]; + + printf_filtered (_("port 0x%04x, behavior: "), info->ports[i]); + switch (info->behaviors[i]) + { + case EXCEPTION_DEFAULT: + printf_unfiltered (_("default")); + break; + case EXCEPTION_STATE: + printf_unfiltered (_("state")); + break; + case EXCEPTION_STATE_IDENTITY: + printf_unfiltered (_("state-identity")); + break; + default: + printf_unfiltered (_("0x%x"), info->behaviors[i]); + } + printf_unfiltered (_(", masks:")); + if (mask & EXC_MASK_BAD_ACCESS) + printf_unfiltered (_(" BAD_ACCESS")); + if (mask & EXC_MASK_BAD_INSTRUCTION) + printf_unfiltered (_(" BAD_INSTRUCTION")); + if (mask & EXC_MASK_ARITHMETIC) + printf_unfiltered (_(" ARITHMETIC")); + if (mask & EXC_MASK_EMULATION) + printf_unfiltered (_(" EMULATION")); + if (mask & EXC_MASK_SOFTWARE) + printf_unfiltered (_(" SOFTWARE")); + if (mask & EXC_MASK_BREAKPOINT) + printf_unfiltered (_(" BREAKPOINT")); + if (mask & EXC_MASK_SYSCALL) + printf_unfiltered (_(" SYSCALL")); + if (mask & EXC_MASK_MACH_SYSCALL) + printf_unfiltered (_(" MACH_SYSCALL")); + if (mask & EXC_MASK_RPC_ALERT) + printf_unfiltered (_(" RPC_ALERT")); + if (mask & EXC_MASK_CRASH) + printf_unfiltered (_(" CRASH")); + printf_unfiltered (_("\n")); + } +} + +static void +info_mach_exceptions_command (char *args, int from_tty) +{ + int i; + task_t task; + kern_return_t kret; + darwin_exception_info info; + + info.count = sizeof (info.ports) / sizeof (info.ports[0]); + + if (args != NULL) + { + if (strcmp (args, "saved") == 0) + { + if (darwin_inf->task == TASK_NULL) + error (_("No inferior running\n")); + disp_exception (&darwin_inf->exception_info); + return; + } + else if (strcmp (args, "host") == 0) + { + /* FIXME: This need a the privilegied host port! */ + kret = host_get_exception_ports + (darwin_host_self, EXC_MASK_ALL, info.masks, + &info.count, info.ports, info.behaviors, info.flavors); + MACH_CHECK_ERROR (kret); + disp_exception (&info); + } + else + error (_("Parameter is saved, host or none")); + } + else + { + if (darwin_inf->task == TASK_NULL) + error (_("No inferior running\n")); + + kret = task_get_exception_ports + (darwin_inf->task, EXC_MASK_ALL, info.masks, + &info.count, info.ports, info.behaviors, info.flavors); + MACH_CHECK_ERROR (kret); + disp_exception (&info); + } +} + +static void +darwin_list_gdb_ports (const char *msg) +{ + mach_port_name_array_t names; + mach_port_type_array_t types; + unsigned int name_count, type_count; + kern_return_t result; + int i; + + result = mach_port_names (mach_task_self (), + &names, &name_count, &types, &type_count); + MACH_CHECK_ERROR (result); + + gdb_assert (name_count == type_count); + + printf_unfiltered (_("Ports for %s:"), msg); + for (i = 0; i < name_count; ++i) + printf_unfiltered (_(" 0x%04x"), names[i]); + printf_unfiltered (_("\n")); + + vm_deallocate (mach_task_self (), (vm_address_t) names, + (name_count * sizeof (mach_port_t))); + vm_deallocate (mach_task_self (), (vm_address_t) types, + (type_count * sizeof (mach_port_type_t))); +} + +void +_initialize_darwin_info_commands (void) +{ + add_info ("mach-tasks", info_mach_tasks_command, + _("Get list of tasks in system.")); + add_info ("mach-ports", info_mach_ports_command, + _("Get list of ports in a task.")); + add_info ("mach-port", info_mach_port_command, + _("Get info on a specific port.")); + add_info ("mach-task", info_mach_task_command, + _("Get info on a specific task.")); + add_info ("mach-threads", info_mach_threads_command, + _("Get list of threads in a task.")); + add_info ("mach-thread", info_mach_thread_command, + _("Get info on a specific thread.")); + + add_info ("mach-regions", info_mach_regions_command, + _("Get information on all mach region for the task.")); + add_info ("mach-regions-rec", info_mach_regions_recurse_command, + _("Get information on all mach sub region for the task.")); + add_info ("mach-region", info_mach_region_command, + _("Get information on mach region at given address.")); + + add_info ("mach-exceptions", info_mach_exceptions_command, + _("Disp mach exceptions.")); +} diff --git a/gdb/darwin-nat.c b/gdb/darwin-nat.c new file mode 100644 index 0000000..de9c715 --- /dev/null +++ b/gdb/darwin-nat.c @@ -0,0 +1,1307 @@ +/* Darwin support for GDB, the GNU debugger. + Copyright (C) 2008 Free Software Foundation, Inc. + + Contributed by AdaCore. + + 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 "top.h" +#include "inferior.h" +#include "target.h" +#include "symfile.h" +#include "symtab.h" +#include "objfiles.h" +#include "gdb.h" +#include "gdbcmd.h" +#include "gdbcore.h" +#include "gdbthread.h" +#include "regcache.h" +#include "event-top.h" +#include "inf-loop.h" +#include "gdb_stat.h" +#include "exceptions.h" +#include "inf-child.h" +#include "value.h" +#include "arch-utils.h" +#include "bfd.h" + +#include <sys/ptrace.h> +#include <sys/signal.h> +#include <machine/setjmp.h> +#include <sys/types.h> +#include <unistd.h> +#include <signal.h> +#include <string.h> +#include <ctype.h> +#include <sys/param.h> +#include <sys/sysctl.h> +#include <sys/proc.h> + +#include <mach/mach_error.h> +#include <mach/mach_vm.h> +#include <mach/mach_init.h> +#include <mach/vm_map.h> +#include <mach/task.h> +#include <mach/mach_port.h> +#include <mach/thread_act.h> +#include <mach/port.h> + +#include "darwin-nat.h" + +/* Quick overview. + Darwin kernel is Mach + BSD derived kernel. Note that they share the + same memory space and are linked together (ie there is no micro-kernel). + + Although ptrace(2) is available on Darwin, it is not complete. We have + to use Mach calls to read and write memory and to modify registers. We + also use Mach to get inferior faults. As we cannot use select(2) or + signals with Mach port (the Mach communication channel), signals are + reported to gdb as an exception. Furthermore we detect death of the + inferior through a Mach notification message. This way we only wait + on Mach ports. + + Some Mach documentation is available for Apple xnu source package or + from the web. */ + + +#define PTRACE(CMD, PID, ADDR, SIG) \ + darwin_ptrace(#CMD, CMD, (PID), (ADDR), (SIG)) + +extern boolean_t exc_server (mach_msg_header_t *in, mach_msg_header_t *out); + +static void darwin_stop (ptid_t); + +static void darwin_resume (ptid_t ptid, int step, + enum target_signal signal); + +static ptid_t darwin_wait (ptid_t ptid, struct target_waitstatus *status); + +static void darwin_mourn_inferior (struct target_ops *ops); + +static int darwin_lookup_task (char *args, task_t * ptask, int *ppid); + +static void darwin_kill_inferior (void); + +static void darwin_ptrace_me (void); + +static void darwin_ptrace_him (int pid); + +static void darwin_create_inferior (struct target_ops *ops, char *exec_file, + char *allargs, char **env, int from_tty); + +static void darwin_files_info (struct target_ops *ops); + +static char *darwin_pid_to_str (ptid_t tpid); + +static int darwin_thread_alive (ptid_t tpid); + +/* Current inferior. */ +darwin_inferior *darwin_inf = NULL; + +/* Target operations for Darwin. */ +static struct target_ops *darwin_ops; + +/* Task identifier of gdb. */ +static task_t gdb_task; + +/* A copy of mach_host_self (). */ +mach_port_t darwin_host_self; + +/* Exception port. */ +mach_port_t darwin_ex_port; + +/* Notification port. */ +mach_port_t darwin_not_port; + +/* Port set. */ +mach_port_t darwin_port_set; + +/* Page size. */ +static vm_size_t mach_page_size; + +/* If Set, catch all mach exceptions (before they are converted to signals + by the kernel). */ +static int enable_mach_exceptions; + +#define PAGE_TRUNC(x) ((x) & ~(mach_page_size - 1)) +#define PAGE_ROUND(x) PAGE_TRUNC((x) + mach_page_size - 1) + +/* Buffer containing received message and to be sent message. */ +static union +{ + mach_msg_header_t hdr; + char data[1024]; +} msgin, msgout; + +/* Current message state. + If the kernel has sent a message it expects a reply and the inferior + can't be killed before. */ +static enum msg_state { NO_MESSAGE, GOT_MESSAGE, REPLY_SENT } msg_state; + +/* Unmarshalled received message. */ +static struct exc_msg +{ + /* Receive port. */ + mach_port_t port; + + /* Thread and task taking the exception. */ + mach_port_t thread_port; + mach_port_t task_port; + + /* Type of the exception. */ + exception_type_t ex_type; + + /* Machine dependent details. */ + mach_msg_type_number_t data_count; + integer_t ex_data[4]; +} exc_msg; + + +/* This controls output of inferior debugging. + 1 = basic exception handling + 2 = task management + 3 = thread management + 4 = pending_event_handler + 6 = most chatty level. */ + +static int darwin_debug_flag = 0; + +static void +inferior_debug (int level, const char *fmt, ...) +{ + va_list ap; + + if (darwin_debug_flag < level) + return; + + va_start (ap, fmt); + printf_unfiltered (_("[%d inferior]: "), getpid ()); + vprintf_unfiltered (fmt, ap); + va_end (ap); +} + +void +mach_check_error (kern_return_t ret, const char *file, + unsigned int line, const char *func) +{ + if (ret == KERN_SUCCESS) + return; + if (func == NULL) + func = _("[UNKNOWN]"); + + error (_("error on line %u of \"%s\" in function \"%s\": %s (0x%lx)\n"), + line, file, func, mach_error_string (ret), (unsigned long) ret); +} + +static const char * +unparse_exception_type (unsigned int i) +{ + static char unknown_exception_buf[32]; + + switch (i) + { + case EXC_BAD_ACCESS: + return "EXC_BAD_ACCESS"; + case EXC_BAD_INSTRUCTION: + return "EXC_BAD_INSTRUCTION"; + case EXC_ARITHMETIC: + return "EXC_ARITHMETIC"; + case EXC_EMULATION: + return "EXC_EMULATION"; + case EXC_SOFTWARE: + return "EXC_SOFTWARE"; + case EXC_BREAKPOINT: + return "EXC_BREAKPOINT"; + case EXC_SYSCALL: + return "EXC_SYSCALL"; + case EXC_MACH_SYSCALL: + return "EXC_MACH_SYSCALL"; + case EXC_RPC_ALERT: + return "EXC_RPC_ALERT"; + case EXC_CRASH: + return "EXC_CRASH"; + default: + snprintf (unknown_exception_buf, 32, _("unknown (%d)"), i); + return unknown_exception_buf; + } +} + +static int +darwin_ptrace (const char *name, + int request, int pid, PTRACE_TYPE_ARG3 arg3, int arg4) +{ + int ret; + + ret = ptrace (request, pid, (caddr_t) arg3, arg4); + + inferior_debug (2, _("ptrace (%s, %d, 0x%x, %d): %d (%s)\n"), + name, pid, arg3, arg4, ret, + (ret != 0) ? strerror (errno) : _("no error")); + return ret; +} + +static int +cmp_thread_t (const void *l, const void *r) +{ + thread_t lt = *(const thread_t *)l; + thread_t lr = *(const thread_t *)r; + return (int)(lr - lt); +} + +static void +darwin_check_new_threads (darwin_inferior *inf) +{ + kern_return_t kret; + unsigned int i; + thread_array_t thread_list; + unsigned int new_nbr; + unsigned int old_nbr; + unsigned int new_ix, old_ix; + VEC (thread_t) *thread_vec; + + /* Get list of threads. */ + kret = task_threads (inf->task, &thread_list, &new_nbr); + MACH_CHECK_ERROR (kret); + if (kret != KERN_SUCCESS) + return; + + if (new_nbr > 1) + qsort (thread_list, new_nbr, sizeof (thread_t), cmp_thread_t); + + thread_vec = VEC_alloc (thread_t, new_nbr); + + if (inf->threads) + old_nbr = VEC_length (thread_t, inf->threads); + else + old_nbr = 0; + + for (new_ix = 0, old_ix = 0; new_ix < new_nbr || old_ix < old_nbr;) + { + thread_t new_id = (new_ix < new_nbr) ? + thread_list[new_ix] : THREAD_NULL; + thread_t old_id = (old_ix < old_nbr) ? + VEC_index (thread_t, inf->threads, old_ix) : THREAD_NULL; + + if (old_id == new_id) + { + /* Thread still exist. */ + VEC_safe_push (thread_t, thread_vec, old_id); + new_ix++; + old_ix++; + + kret = mach_port_deallocate (gdb_task, old_id); + MACH_CHECK_ERROR (kret); + continue; + } + if (new_id < old_id || old_ix == old_nbr) + { + /* A thread was created. */ + struct thread_info *tp; + + tp = add_thread (ptid_build (inf->pid, 0, new_id)); + VEC_safe_push (thread_t, thread_vec, new_id); + new_ix++; + continue; + } + if (new_id > old_id || new_ix == new_nbr) + { + /* A thread was removed. */ + delete_thread (ptid_build (inf->pid, 0, old_id)); + kret = mach_port_deallocate (gdb_task, old_id); + MACH_CHECK_ERROR (kret); + old_ix++; + } + } + + if (inf->threads) + VEC_free (thread_t, inf->threads); + inf->threads = thread_vec; + + kret = vm_deallocate (gdb_task, (vm_address_t) thread_list, + new_nbr * sizeof (int)); + MACH_CHECK_ERROR (kret); +} + +static void +darwin_stop (ptid_t t) +{ + int ret; + + ret = kill (ptid_get_pid (inferior_ptid), SIGINT); +} + +static void +darwin_resume (ptid_t ptid, int step, enum target_signal signal) +{ + struct target_waitstatus status; + int pid; + thread_t thread; + kern_return_t kret; + int res; + + /* minus_one_ptid is RESUME_ALL. */ + if (ptid_equal (ptid, minus_one_ptid)) + ptid = inferior_ptid; + + pid = ptid_get_pid (ptid); + thread = ptid_get_tid (ptid); + + inferior_debug + (2, _("darwin_resume: state=%d, thread=0x%x, step=%d signal=%d\n"), + msg_state, thread, step, signal); + + switch (msg_state) + { + case GOT_MESSAGE: + switch (exc_msg.ex_type) + { + case EXC_SOFTWARE: + if (exc_msg.ex_data[0] == EXC_SOFT_SIGNAL) + { + int nsignal = target_signal_to_host (signal); + res = PTRACE (PT_THUPDATE, pid, + (void *)exc_msg.thread_port, nsignal); + if (res < 0) + printf_unfiltered (_("ptrace THUP: res=%d\n"), res); + } + break; + + default: + break; + } + + if (thread != 0) + { + inferior_debug (2, _("darwin_set_sstep (thread=%x, enable=%d)\n"), + thread, step); + darwin_set_sstep (thread, step); + } + + kret = mach_msg (&msgout.hdr, MACH_SEND_MSG | MACH_SEND_INTERRUPT, + msgout.hdr.msgh_size, 0, + MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE, + MACH_PORT_NULL); + if (kret != 0) + printf_unfiltered (_("mach_msg (reply) ret=%d\n"), kret); + + msg_state = REPLY_SENT; + break; + + case NO_MESSAGE: + if (step) + res = PTRACE (PT_STEP, pid, (caddr_t)1, 0); + else + res = PTRACE (PT_CONTINUE, pid, (caddr_t)1, 0); + break; + + default: + gdb_assert (0); + } +} + +kern_return_t +catch_exception_raise_state + (mach_port_t port, + exception_type_t exception_type, mach_exception_data_t exception_data, + mach_msg_type_number_t data_count, thread_state_flavor_t * state_flavor, + thread_state_t in_state, mach_msg_type_number_t in_state_count, + thread_state_t out_state, mach_msg_type_number_t out_state_count) +{ + return KERN_FAILURE; +} + +kern_return_t +catch_exception_raise_state_identity + (mach_port_t port, mach_port_t thread_port, mach_port_t task_port, + exception_type_t exception_type, mach_exception_data_t exception_data, + mach_msg_type_number_t data_count, thread_state_flavor_t * state_flavor, + thread_state_t in_state, mach_msg_type_number_t in_state_count, + thread_state_t out_state, mach_msg_type_number_t out_state_count) +{ + kern_return_t kret; + + kret = mach_port_deallocate (mach_task_self (), task_port); + MACH_CHECK_ERROR (kret); + kret = mach_port_deallocate (mach_task_self (), thread_port); + MACH_CHECK_ERROR (kret); + + return KERN_FAILURE; +} + +kern_return_t +catch_exception_raise (mach_port_t port, + mach_port_t thread_port, + mach_port_t task_port, + exception_type_t exception_type, + exception_data_t exception_data, + mach_msg_type_number_t data_count) +{ + kern_return_t kret; + int i; + int res; + + /* We got new rights to the task. Get rid of it. */ + kret = mach_port_deallocate (mach_task_self (), task_port); + MACH_CHECK_ERROR (kret); + + inferior_debug + (7, _("catch_exception_raise: exception_type=%d, data_count=%d\n"), + exception_type, data_count); + if (darwin_debug_flag > 7) + { + for (i = 0; i < data_count; i++) + printf_unfiltered (" %08x", exception_data[i]); + printf_unfiltered ("\n"); + } + + /* Save the message. + FIXME: this should be in a per-thread variable. */ + exc_msg.port = port; + exc_msg.thread_port = thread_port; + exc_msg.task_port = task_port; + exc_msg.ex_type = exception_type; + exc_msg.data_count = data_count; + for (i = 0; i < data_count && i < 4; i++) + exc_msg.ex_data[i] = exception_data[i]; + + return KERN_SUCCESS; +} + +static ptid_t +darwin_wait (ptid_t ptid, struct target_waitstatus *status) +{ + kern_return_t kret; + mach_msg_header_t *hdr = &msgin.hdr; + pid_t pid = ptid_get_pid (inferior_ptid); /* FIXME. */ + + gdb_assert (msg_state != GOT_MESSAGE); + + inferior_debug (6, _("darwin_wait: waiting for a message\n")); + + /* Wait for a message. */ + kret = mach_msg (&msgin.hdr, MACH_RCV_MSG | MACH_RCV_INTERRUPT, 0, + sizeof (msgin.data), darwin_port_set, 0, MACH_PORT_NULL); + + if (kret == MACH_RCV_INTERRUPTED) + { + status->kind = TARGET_WAITKIND_IGNORE; + return minus_one_ptid; + } + + if (kret != MACH_MSG_SUCCESS) + { + inferior_debug (1, _("mach_msg: ret=%x\n"), kret); + status->kind = TARGET_WAITKIND_SPURIOUS; + return minus_one_ptid; + } + + /* Debug: display message. */ + if (darwin_debug_flag > 10) + { + const unsigned long *buf = (unsigned long *) hdr; + unsigned int i; + + printf_unfiltered (_(" bits: 0x%x"), hdr->msgh_bits); + printf_unfiltered (_(", size: 0x%x"), hdr->msgh_size); + printf_unfiltered (_(", remote-port: 0x%x"), hdr->msgh_remote_port); + printf_unfiltered (_(", local-port: 0x%x"), hdr->msgh_local_port); + printf_unfiltered (_(", reserved: 0x%x"), hdr->msgh_reserved); + printf_unfiltered (_(", id: 0x%x\n"), hdr->msgh_id); + + if (darwin_debug_flag > 11) + { + printf_unfiltered (_(" data:")); + for (i = 0; i < hdr->msgh_size; i++) + printf_unfiltered (" %08lx", buf[i]); + printf_unfiltered (_("\n")); + } + } + + /* Exception message. */ + if (hdr->msgh_local_port == darwin_ex_port) + { + /* Handle it via the exception server. */ + if (!exc_server (&msgin.hdr, &msgout.hdr)) + { + printf_unfiltered (_("exc_server: unknown message (id=%x)\n"), + hdr->msgh_id); + status->kind = TARGET_WAITKIND_SPURIOUS; + return minus_one_ptid; + } + + status->kind = TARGET_WAITKIND_STOPPED; + + inferior_debug (2, _("darwin_wait: thread=%x, got %s\n"), + exc_msg.thread_port, + unparse_exception_type (exc_msg.ex_type)); + + switch (exc_msg.ex_type) + { + case EXC_BAD_ACCESS: + status->value.sig = TARGET_EXC_BAD_ACCESS; + break; + case EXC_BAD_INSTRUCTION: + status->value.sig = TARGET_EXC_BAD_INSTRUCTION; + break; + case EXC_ARITHMETIC: + status->value.sig = TARGET_EXC_ARITHMETIC; + break; + case EXC_EMULATION: + status->value.sig = TARGET_EXC_EMULATION; + break; + case EXC_SOFTWARE: + if (exc_msg.ex_data[0] == EXC_SOFT_SIGNAL) + { + status->value.sig = target_signal_from_host (exc_msg.ex_data[1]); + inferior_debug (2, _(" (signal %d: %s)\n"), + exc_msg.ex_data[1], + target_signal_to_name (status->value.sig)); + } + else + status->value.sig = TARGET_EXC_SOFTWARE; + break; + case EXC_BREAKPOINT: + /* Many internal GDB routines expect breakpoints to be reported + as TARGET_SIGNAL_TRAP, and will report TARGET_EXC_BREAKPOINT + as a spurious signal. */ + status->value.sig = TARGET_SIGNAL_TRAP; + break; + default: + status->value.sig = TARGET_SIGNAL_UNKNOWN; + break; + } + + msg_state = GOT_MESSAGE; + + return ptid_build (pid, 0, exc_msg.thread_port); + } + else if (hdr->msgh_local_port == darwin_not_port) + { + pid_t res; + int wstatus; + + /* FIXME: translate task port to pid. */ + res = wait4 (pid, &wstatus, 0, NULL); + if (res != pid) + { + printf_unfiltered (_("wait4: res=%x\n"), res); + wstatus = 0; + } + status->kind = TARGET_WAITKIND_EXITED; + status->value.integer = WEXITSTATUS (wstatus); + + inferior_debug (2, _("darwin_wait: pid=%d exit, status=%x\n"), + pid, wstatus); + + msg_state = NO_MESSAGE; + + return ptid; + } + else + { + printf_unfiltered (_("Bad local-port: %x\n"), hdr->msgh_local_port); + status->kind = TARGET_WAITKIND_SPURIOUS; + return minus_one_ptid; + } +} + +static void +darwin_mourn_inferior (struct target_ops *ops) +{ + struct inferior *inf = current_inferior (); + kern_return_t kret; + mach_port_t prev; + int i; + + unpush_target (darwin_ops); + + /* Deallocate threads. */ + if (darwin_inf->threads) + { + int k; + thread_t t; + for (k = 0; VEC_iterate (thread_t, darwin_inf->threads, k, t); k++) + { + kret = mach_port_deallocate (gdb_task, t); + MACH_CHECK_ERROR (kret); + } + VEC_free (thread_t, darwin_inf->threads); + darwin_inf->threads = NULL; + } + + kret = mach_port_request_notification (gdb_task, darwin_inf->task, + MACH_NOTIFY_DEAD_NAME, 0, + darwin_inf->prev_not_port, + MACH_MSG_TYPE_MAKE_SEND_ONCE, + &prev); + /* This can fail if the task is dead. */ + if (kret == KERN_SUCCESS) + { + kret = mach_port_deallocate (gdb_task, prev); + MACH_CHECK_ERROR (kret); + } + + /* Deallocate saved exception ports. */ + for (i = 0; i < darwin_inf->exception_info.count; i++) + { + kret = mach_port_deallocate + (gdb_task, darwin_inf->exception_info.ports[i]); + MACH_CHECK_ERROR (kret); + } + darwin_inf->exception_info.count = 0; + + kret = mach_port_deallocate (gdb_task, darwin_inf->task); + MACH_CHECK_ERROR (kret); + + darwin_inf->task = 0; + darwin_inf->pid = 0; + + generic_mourn_inferior (); +} + +static void +darwin_stop_inferior (darwin_inferior *inf) +{ + struct target_waitstatus wstatus; + ptid_t ptid; + kern_return_t kret; + int status; + int res; + + gdb_assert (inf != NULL); + + kret = task_suspend (inf->task); + MACH_CHECK_ERROR (kret); + + if (msg_state == GOT_MESSAGE) + darwin_resume (inferior_ptid, 0, 0); + + res = kill (inf->pid, SIGSTOP); + if (res != 0) + warning (_("cannot kill: %s\n"), strerror (errno)); + + ptid = darwin_wait (inferior_ptid, &wstatus); + gdb_assert (wstatus.kind = TARGET_WAITKIND_STOPPED); +} + +static void +darwin_kill_inferior (void) +{ + struct target_waitstatus wstatus; + ptid_t ptid; + kern_return_t kret; + int status; + int res; + + gdb_assert (darwin_inf != NULL); + + if (ptid_equal (inferior_ptid, null_ptid)) + return; + + darwin_stop_inferior (darwin_inf); + + res = PTRACE (PT_KILL, darwin_inf->pid, 0, 0); + gdb_assert (res == 0); + + if (msg_state == GOT_MESSAGE) + { + exc_msg.ex_type = 0; + darwin_resume (inferior_ptid, 0, 0); + } + + kret = task_resume (darwin_inf->task); + MACH_CHECK_ERROR (kret); + + ptid = darwin_wait (inferior_ptid, &wstatus); + + /* This double wait seems required... */ + res = waitpid (darwin_inf->pid, &status, 0); + gdb_assert (res == darwin_inf->pid); + + msg_state = NO_MESSAGE; + + target_mourn_inferior (); +} + +/* The child must synchronize with gdb: gdb must set the exception port + before the child call PTRACE_SIGEXC. We use a pipe to achieve this. + FIXME: is there a lighter way ? */ +static int ptrace_fds[2]; + +static void +darwin_ptrace_me (void) +{ + int res; + char c; + + /* Close write end point. */ + close (ptrace_fds[1]); + + /* Wait until gdb is ready. */ + res = read (ptrace_fds[0], &c, 1); + gdb_assert (res == 0); + close (ptrace_fds[0]); + + /* Get rid of privileges. */ + setegid (getgid ()); + + /* Set TRACEME. */ + PTRACE (PT_TRACE_ME, 0, 0, 0); + + /* Redirect signals to exception port. */ + PTRACE (PT_SIGEXC, 0, 0, 0); +} + +/* Dummy function to be sure fork_inferior uses fork(2) and not vfork(2). */ +static void +darwin_pre_ptrace (void) +{ + if (pipe (ptrace_fds) != 0) + { + ptrace_fds[0] = -1; + ptrace_fds[1] = -1; + error (_("unable to create a pipe: %s"), safe_strerror (errno)); + } +} + +static kern_return_t +darwin_save_exception_ports (darwin_inferior *inf) +{ + kern_return_t kret; + + inf->exception_info.count = + sizeof (inf->exception_info.ports) / sizeof (inf->exception_info.ports[0]); + + kret = task_get_exception_ports + (inf->task, EXC_MASK_ALL, inf->exception_info.masks, + &inf->exception_info.count, inf->exception_info.ports, + inf->exception_info.behaviors, inf->exception_info.flavors); + return kret; +} + +static kern_return_t +darwin_restore_exception_ports (darwin_inferior *inf) +{ + int i; + kern_return_t kret; + + for (i = 0; i < inf->exception_info.count; i++) + { + kret = task_set_exception_ports + (inf->task, inf->exception_info.masks[i], inf->exception_info.ports[i], + inf->exception_info.behaviors[i], inf->exception_info.flavors[i]); + if (kret != KERN_SUCCESS) + return kret; + } + + return KERN_SUCCESS; +} + +static void +darwin_attach_pid (int pid) +{ + task_t itask; + kern_return_t kret; + mach_port_t prev_port; + int traps_expected; + exception_mask_t mask; + + kret = task_for_pid (gdb_task, pid, &itask); + if (kret != KERN_SUCCESS) + { + int status; + struct inferior *inf = current_inferior (); + + if (!inf->attach_flag) + { + kill (pid, 9); + waitpid (pid, &status, 0); + } + + error (_("Unable to find Mach task port for process-id %d: %s (0x%lx).\n" + " (please check gdb is setgid procmod)"), + pid, mach_error_string (kret), (unsigned long) kret); + } + + inferior_debug (2, _("inferior task: 0x%08x, pid: %d\n"), itask, pid); + + if (darwin_ex_port == MACH_PORT_NULL) + { + /* Create a port to get exceptions. */ + kret = mach_port_allocate (gdb_task, MACH_PORT_RIGHT_RECEIVE, + &darwin_ex_port); + gdb_assert (kret == KERN_SUCCESS); + + kret = mach_port_insert_right (gdb_task, darwin_ex_port, darwin_ex_port, + MACH_MSG_TYPE_MAKE_SEND); + gdb_assert (kret == KERN_SUCCESS); + + /* Create a port set and put ex_port in it. */ + kret = mach_port_allocate (gdb_task, MACH_PORT_RIGHT_PORT_SET, + &darwin_port_set); + gdb_assert (kret == KERN_SUCCESS); + + kret = mach_port_move_member (gdb_task, darwin_ex_port, darwin_port_set); + gdb_assert (kret == KERN_SUCCESS); + + /* Create a port to be notified when the child task terminates. */ + kret = mach_port_allocate (gdb_task, MACH_PORT_RIGHT_RECEIVE, + &darwin_not_port); + gdb_assert (kret == KERN_SUCCESS); + + kret = mach_port_insert_right (gdb_task, darwin_not_port, darwin_not_port, + MACH_MSG_TYPE_MAKE_SEND); + gdb_assert (kret == KERN_SUCCESS); + + kret = mach_port_move_member (gdb_task, darwin_not_port, darwin_port_set); + gdb_assert (kret == KERN_SUCCESS); + } + + kret = mach_port_request_notification (gdb_task, itask, + MACH_NOTIFY_DEAD_NAME, 0, + darwin_not_port, + MACH_MSG_TYPE_MAKE_SEND_ONCE, + &darwin_inf->prev_not_port); + gdb_assert (kret == KERN_SUCCESS); + + darwin_inf->task = itask; + darwin_inf->pid = pid; + + kret = darwin_save_exception_ports (darwin_inf); + gdb_assert (kret == KERN_SUCCESS); + + /* Set exception port. */ + if (enable_mach_exceptions) + mask = EXC_MASK_ALL; + else + mask = EXC_MASK_SOFTWARE; + kret = task_set_exception_ports + (itask, mask, darwin_ex_port, EXCEPTION_DEFAULT, THREAD_STATE_NONE); + gdb_assert (kret == KERN_SUCCESS); + + push_target (darwin_ops); +} + +static void +darwin_init_thread_list (darwin_inferior *inf) +{ + thread_t thread; + + darwin_check_new_threads (inf); + + gdb_assert (inf->threads && VEC_length (thread_t, inf->threads) > 0); + thread = VEC_index (thread_t, inf->threads, 0); + inferior_ptid = ptid_build (inf->pid, 0, thread); +} + +static void +darwin_ptrace_him (int pid) +{ + task_t itask; + kern_return_t kret; + mach_port_t prev_port; + int traps_expected; + + darwin_attach_pid (pid); + + /* Let's the child run. */ + close (ptrace_fds[0]); + close (ptrace_fds[1]); + + /* fork_inferior automatically add a thread - but it uses a wrong tid. */ + delete_thread_silent (inferior_ptid); + darwin_init_thread_list (darwin_inf); + + startup_inferior (START_INFERIOR_TRAPS_EXPECTED); +} + +static void +darwin_create_inferior (struct target_ops *ops, char *exec_file, + char *allargs, char **env, int from_tty) +{ + /* Do the hard work. */ + fork_inferior (exec_file, allargs, env, darwin_ptrace_me, darwin_ptrace_him, + darwin_pre_ptrace, NULL); + + /* Return now in case of error. */ + if (ptid_equal (inferior_ptid, null_ptid)) + return; +} + + +/* Attach to process PID, then initialize for debugging it + and wait for the trace-trap that results from attaching. */ +static void +darwin_attach (struct target_ops *ops, char *args, int from_tty) +{ + pid_t pid; + pid_t pid2; + int wstatus; + int res; + struct inferior *inf; + + gdb_assert (msg_state == NO_MESSAGE); + + if (!args) + error_no_arg (_("process-id to attach")); + + pid = atoi (args); + + if (pid == getpid ()) /* Trying to masturbate? */ + error (_("I refuse to debug myself!")); + + if (from_tty) + printf_unfiltered (_("Attaching to pid %d\n"), pid); + + res = PTRACE (PT_ATTACHEXC, pid, 0, 0); + if (res != 0) + error (_("Unable to attach to process-id %d: %s (%d)"), + pid, strerror (errno), errno); + + inf = add_inferior (pid); + inf->attach_flag = 1; + + darwin_attach_pid (pid); + + pid2 = wait4 (pid, &wstatus, WUNTRACED, NULL); + gdb_assert (pid2 == pid); + inferior_debug (1, _("darwin_attach: wait4 pid=%d, status=0x%x\n"), + pid2, wstatus); + + + darwin_init_thread_list (darwin_inf); + + darwin_check_osabi (darwin_inf, ptid_get_tid (inferior_ptid)); + + /* Looks strange, but the kernel doesn't stop the process... + (Bug in Leopard ?) + Do it manually. */ + /* FIXME: doesn't look to work with multi-threads!! */ + kill (pid, SIGSTOP); +} + +/* Take a program previously attached to and detaches it. + The program resumes execution and will no longer stop + on signals, etc. We'd better not have left any breakpoints + in the program or it'll die when it hits one. For this + to work, it may be necessary for the process to have been + previously attached. It *might* work if the program was + started via fork. */ +static void +darwin_detach (struct target_ops *ops, char *args, int from_tty) +{ + kern_return_t kret; + int res; + + if (from_tty) + { + char *exec_file = get_exec_file (0); + if (exec_file == 0) + exec_file = ""; + printf_unfiltered (_("Detaching from program: %s, %d\n"), exec_file, + ptid_get_pid (inferior_ptid)); + gdb_flush (gdb_stdout); + } + + darwin_stop_inferior (darwin_inf); + + kret = darwin_restore_exception_ports (darwin_inf); + MACH_CHECK_ERROR (kret); + + if (msg_state == GOT_MESSAGE) + { + exc_msg.ex_type = 0; + darwin_resume (inferior_ptid, 0, 0); + } + + kret = task_resume (darwin_inf->task); + gdb_assert (kret == KERN_SUCCESS); + + res = PTRACE (PT_DETACH, darwin_inf->pid, 0, 0); + if (res != 0) + printf_unfiltered (_("Unable to detach from process-id %d: %s (%d)"), + darwin_inf->pid, strerror (errno), errno); + + msg_state = NO_MESSAGE; + + darwin_mourn_inferior (ops); +} + +static void +darwin_files_info (struct target_ops *ops) +{ + gdb_assert (darwin_inf != NULL); +} + +static char * +darwin_pid_to_str (ptid_t ptid) +{ + static char buf[128]; + + snprintf (buf, sizeof (buf), + _("process %d gdb-thread 0x%lx"), + (unsigned) ptid_get_pid (ptid), + (unsigned long) ptid_get_tid (ptid)); + return buf; +} + +static int +darwin_thread_alive (ptid_t ptid) +{ + return 1; +} + +/* If RDADDR is not NULL, read inferior task's LEN bytes from ADDR and + copy it to RDADDR in gdb's address space. + If WRADDR is not NULL, write gdb's LEN bytes from WRADDR and copy it + to ADDR in inferior task's address space. + Return 0 on failure; number of bytes read / writen otherwise. */ +static int +darwin_read_write_inferior (task_t task, CORE_ADDR addr, + char *rdaddr, const char *wraddr, int length) +{ + kern_return_t err; + mach_vm_address_t offset = addr & (mach_page_size - 1); + mach_vm_address_t low_address = (mach_vm_address_t) (addr - offset); + mach_vm_size_t aligned_length = (mach_vm_size_t) PAGE_ROUND (offset + length); + pointer_t copied; + int copy_count; + mach_vm_size_t remaining_length; + mach_vm_address_t region_address; + mach_vm_size_t region_length; + + inferior_debug (8, _("darwin_read_write_inferior(%s, len=%d)\n"), + core_addr_to_string (addr), length); + + /* Get memory from inferior with page aligned addresses */ + err = mach_vm_read (task, low_address, aligned_length, + &copied, ©_count); + if (err != KERN_SUCCESS) + { + warning (_("darwin_read_write_inferior: vm_read failed: %s"), + mach_error_string (err)); + return 0; + } + + if (rdaddr != NULL) + memcpy (rdaddr, (char *)copied + offset, length); + + if (wraddr == NULL) + goto out; + + memcpy ((char *)copied + offset, wraddr, length); + + /* Do writes atomically. + First check for holes and unwritable memory. */ + for (region_address = low_address, remaining_length = aligned_length; + region_address < low_address + aligned_length; + region_address += region_length, remaining_length -= region_length) + { + vm_region_basic_info_data_64_t info; + mach_port_t object_name; + mach_vm_address_t old_address = region_address; + mach_msg_type_number_t count; + + region_length = remaining_length; + count = VM_REGION_BASIC_INFO_COUNT_64; + err = mach_vm_region (task, ®ion_address, ®ion_length, + VM_REGION_BASIC_INFO_64, + (vm_region_info_t) &info, &count, &object_name); + + if (err != KERN_SUCCESS) + { + warning (_("darwin_write_inferior: mach_vm_region failed: %s"), + mach_error_string (err)); + goto out; + } + + /* Check for holes in memory */ + if (region_address > old_address) + { + warning (_("No memory at %s (vs %s+0x%x). Nothing written"), + core_addr_to_string (old_address), + core_addr_to_string (region_address), + (unsigned)region_length); + length = 0; + goto out; + } + + if (!(info.max_protection & VM_PROT_WRITE)) + { + warning (_("Memory at address %s is unwritable. Nothing written"), + core_addr_to_string (old_address)); + length = 0; + goto out; + } + + if (!(info.protection & VM_PROT_WRITE)) + { + err = mach_vm_protect (task, old_address, region_length, + FALSE, info.protection | VM_PROT_WRITE); + if (err != KERN_SUCCESS) + { + warning + (_("darwin_read_write_inferior: mach_vm_protect failed: %s"), + mach_error_string (err)); + length = 0; + goto out; + } + } + } + + err = mach_vm_write (task, low_address, copied, aligned_length); + + if (err != KERN_SUCCESS) + { + warning (_("darwin_read_write_inferior: mach_vm_write failed: %s"), + mach_error_string (err)); + length = 0; + } +out: + mach_vm_deallocate (mach_task_self (), copied, copy_count); + return length; +} + + +/* Return 0 on failure, number of bytes handled otherwise. TARGET + is ignored. */ +static int +darwin_xfer_memory (CORE_ADDR memaddr, gdb_byte *myaddr, int len, int write, + struct mem_attrib *attrib, struct target_ops *target) +{ + task_t task = darwin_inf->task; + + if (task == MACH_PORT_NULL) + return 0; + + inferior_debug (8, _("darwin_xfer_memory(%s, %d, %c)\n"), + core_addr_to_string (memaddr), len, write ? 'w' : 'r'); + + if (write) + return darwin_read_write_inferior (task, memaddr, NULL, myaddr, len); + else + return darwin_read_write_inferior (task, memaddr, myaddr, NULL, len); +} + +static LONGEST +darwin_xfer_partial (struct target_ops *ops, + enum target_object object, const char *annex, + gdb_byte *readbuf, const gdb_byte *writebuf, + ULONGEST offset, LONGEST len) +{ + inferior_debug (8, _("darwin_xfer_partial(%s, %d, rbuf=%p, wbuf=%p)\n"), + core_addr_to_string (offset), (int)len, readbuf, writebuf); + + if (object != TARGET_OBJECT_MEMORY) + return -1; + + return darwin_read_write_inferior (darwin_inf->task, offset, + readbuf, writebuf, len); +} + +static void +set_enable_mach_exceptions (char *args, int from_tty, + struct cmd_list_element *c) +{ + if (darwin_inf && darwin_inf->task != TASK_NULL) + { + exception_mask_t mask; + kern_return_t kret; + + if (enable_mach_exceptions) + mask = EXC_MASK_ALL; + else + { + darwin_restore_exception_ports (darwin_inf); + mask = EXC_MASK_SOFTWARE; + } + kret = task_set_exception_ports (darwin_inf->task, mask, darwin_ex_port, + EXCEPTION_DEFAULT, THREAD_STATE_NONE); + MACH_CHECK_ERROR (kret); + } +} + +void +_initialize_darwin_inferior () +{ + kern_return_t kret; + + gdb_assert (darwin_inf == NULL); + + gdb_task = mach_task_self (); + darwin_host_self = mach_host_self (); + + /* Read page size. */ + kret = host_page_size (darwin_host_self, &mach_page_size); + if (kret != KERN_SUCCESS) + { + mach_page_size = 0x1000; + MACH_CHECK_ERROR (kret); + } + + darwin_inf = (struct darwin_inferior *) + xmalloc (sizeof (struct darwin_inferior)); + + memset (darwin_inf, 0, sizeof (*darwin_inf)); + + darwin_ops = inf_child_target (); + + darwin_ops->to_shortname = "darwin-child"; + darwin_ops->to_longname = _("Darwin child process"); + darwin_ops->to_doc = + _("Darwin child process (started by the \"run\" command)."); + darwin_ops->to_create_inferior = darwin_create_inferior; + darwin_ops->to_attach = darwin_attach; + darwin_ops->to_attach_no_wait = 0; + darwin_ops->to_detach = darwin_detach; + darwin_ops->to_files_info = darwin_files_info; + darwin_ops->to_wait = darwin_wait; + darwin_ops->to_mourn_inferior = darwin_mourn_inferior; + darwin_ops->to_kill = darwin_kill_inferior; + darwin_ops->to_stop = darwin_stop; + darwin_ops->to_resume = darwin_resume; + darwin_ops->to_thread_alive = darwin_thread_alive; + darwin_ops->to_pid_to_str = darwin_pid_to_str; + darwin_ops->to_load = NULL; + darwin_ops->deprecated_xfer_memory = darwin_xfer_memory; + darwin_ops->to_xfer_partial = darwin_xfer_partial; + darwin_ops->to_has_thread_control = tc_schedlock /*| tc_switch */; + + darwin_complete_target (darwin_ops); + + add_target (darwin_ops); + + inferior_debug (2, _("GDB task: 0x%lx, pid: %d\n"), mach_task_self (), + getpid ()); + + add_setshow_zinteger_cmd ("darwin", class_obscure, + &darwin_debug_flag, _("\ +Set if printing inferior communication debugging statements."), _("\ +Show if printing inferior communication debugging statements."), NULL, + NULL, NULL, + &setdebuglist, &showdebuglist); + + add_setshow_boolean_cmd ("mach-exceptions", class_support, + &enable_mach_exceptions, _("\ +Set if mach exceptions are caught."), _("\ +Show if mach exceptions are caught."), _("\ +When this mode is on, all low level exceptions are reported before being\n\ +reported by the kernel."), + &set_enable_mach_exceptions, NULL, + &setlist, &showlist); +} diff --git a/gdb/darwin-nat.h b/gdb/darwin-nat.h new file mode 100644 index 0000000..43e48dd --- /dev/null +++ b/gdb/darwin-nat.h @@ -0,0 +1,107 @@ +/* Common things used by the various darwin files + Copyright (C) 1995, 1996, 1997, 1999, 2000, 2007, 2008 + Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 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/>. +*/ + +#ifndef __DARWIN_NAT_H__ +#define __DARWIN_NAT_H__ + +#include <mach/mach.h> +#include "gdb_assert.h" + +/* Define the threads vector type. */ +DEF_VEC_I (thread_t); + +/* Describe the mach exception handling state for a task. This state is saved + before being changed and restored when a process is detached. + For more information on these fields see task_get_exception_ports manual + page. */ +struct darwin_exception_info +{ + /* Exceptions handled by the port. */ + exception_mask_t masks[EXC_TYPES_COUNT]; + + /* Ports receiving exception messages. */ + mach_port_t ports[EXC_TYPES_COUNT]; + + /* Type of messages sent. */ + exception_behavior_t behaviors[EXC_TYPES_COUNT]; + + /* Type of state to be sent. */ + thread_state_flavor_t flavors[EXC_TYPES_COUNT]; + + /* Number of elements set. */ + mach_msg_type_number_t count; +}; +typedef struct darwin_exception_info darwin_exception_info; + +/* Describe an inferior. */ +struct darwin_inferior +{ + /* Inferior PID. */ + int pid; + + /* Corresponding task port. */ + task_t task; + + /* Previous port for request notification on task. */ + mach_port_t prev_not_port; + + /* Initial exception handling. */ + darwin_exception_info exception_info; + + /* Sorted vector of known threads. */ + VEC(thread_t) *threads; +}; +typedef struct darwin_inferior darwin_inferior; + +/* Current inferior. */ +extern darwin_inferior *darwin_inf; + +/* Exception port. */ +extern mach_port_t darwin_ex_port; + +/* Notification port. */ +extern mach_port_t darwin_not_port; + +/* Port set. */ +extern mach_port_t darwin_port_set; + +/* A copy of mach_host_self (). */ +extern mach_port_t darwin_host_self; + +/* ASSERT_FUNCTION is defined in gdb_assert.h (or not). */ +#ifdef ASSERT_FUNCTION +#define MACH_CHECK_ERROR(ret) \ + mach_check_error (ret, __FILE__, __LINE__, ASSERT_FUNCTION) +#else +#define MACH_CHECK_ERROR(ret) \ + mach_check_error (ret, __FILE__, __LINE__, "??") +#endif + +extern void mach_check_error (kern_return_t ret, const char *file, + unsigned int line, const char *func); + +void darwin_set_sstep (thread_t thread, int enable); + +/* This one is called in darwin-nat.c, but needs to be provided by the + platform specific nat code. It allows each platform to add platform specific + stuff to the darwin_ops. */ +extern void darwin_complete_target (struct target_ops *target); + +void darwin_check_osabi (darwin_inferior *inf, thread_t thread); + +#endif /* __DARWIN_NAT_H__ */ diff --git a/gdb/darwin.defs b/gdb/darwin.defs new file mode 100644 index 0000000..d7b5f0d --- /dev/null +++ b/gdb/darwin.defs @@ -0,0 +1,6 @@ +#include "config.h" +#ifdef HAVE_64_BIT_MACH_EXCEPTIONS +#import <mach/mach_exc.defs> +#else +#import <mach/exc.defs> +#endif @@ -957,6 +957,7 @@ enum gdb_osabi GDB_OSABI_CYGWIN, GDB_OSABI_AIX, GDB_OSABI_DICOS, + GDB_OSABI_DARWIN, GDB_OSABI_INVALID /* keep this last */ }; diff --git a/gdb/doc/ChangeLog b/gdb/doc/ChangeLog index 350831b..9f4ec6d 100644 --- a/gdb/doc/ChangeLog +++ b/gdb/doc/ChangeLog @@ -1,3 +1,7 @@ +2008-11-27 Tristan Gingold <gingold@adacore.com> + + * gdb.texinfo (Darwin): Document Darwin specific features. + 2008-11-25 Jan Kratochvil <jan.kratochvil@redhat.com> * gdbint.texinfo (Target Conditionals): Extend the diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo index 3c54c43..2a86017 100644 --- a/gdb/doc/gdb.texinfo +++ b/gdb/doc/gdb.texinfo @@ -14675,6 +14675,7 @@ configurations. * Cygwin Native:: Features specific to the Cygwin port * Hurd Native:: Features specific to @sc{gnu} Hurd * Neutrino:: Features specific to QNX Neutrino +* Darwin:: Features specific to Darwin @end menu @node HP-UX @@ -15456,6 +15457,48 @@ Neutrino support. Show the current state of QNX Neutrino messages. @end table +@node Darwin +@subsection Darwin +@cindex Darwin + +@value{GDBN} provides the following commands specific to the Darwin target: + +@table @code +@item set debug darwin @var{num} +@kindex set debug darwin +When set to a non zero value, enables debugging messages specific to +the Darwin support. Higher values produce more verbose output. + +@item show debug darwin +@kindex show debug darwin +Show the current state of Darwin messages. + +@item set debug mach-o @var{num} +@kindex set debug mach-o +When set to a non zero value, enables debugging messages while +@value{GDBN} is reading Darwin object files. (@dfn{Mach-O} is the +file format used on Darwin for object and executable files.) Higher +values produce more verbose output. This is a command to diagnose +problems internal to @value{GDBN} and should not be needed in normal +usage. + +@item show debug mach-o +@kindex show debug mach-o +Show the current state of Mach-O file messages. + +@item set mach-exceptions on +@itemx set mach-exceptions off +@kindex set mach-exceptions +On Darwin, faults are first reported as a Mach exception and are then +mapped to a Posix signal. Use this command to turn on trapping of +Mach exceptions in the inferior. This might be sometimes useful to +better understand the cause of a fault. The default is off. + +@item show mach-exceptions +@kindex show mach-exceptions +Show the current state of exceptions trapping. +@end table + @node Embedded OS @section Embedded Operating Systems diff --git a/gdb/i386-darwin-nat.c b/gdb/i386-darwin-nat.c new file mode 100644 index 0000000..0305173 --- /dev/null +++ b/gdb/i386-darwin-nat.c @@ -0,0 +1,492 @@ +/* Darwin support for GDB, the GNU debugger. + Copyright 1997, 1998, 1999, 2000, 2001, 2002, 2008 + Free Software Foundation, Inc. + + Contributed by Apple Computer, 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 "frame.h" +#include "inferior.h" +#include "target.h" +#include "symfile.h" +#include "symtab.h" +#include "objfiles.h" +#include "gdbcmd.h" +#include "regcache.h" +#include "gdb_assert.h" +#include "i386-tdep.h" +#include "amd64-nat.h" +#include "i387-tdep.h" +#include "gdbarch.h" +#include "arch-utils.h" + +#include "darwin-nat.h" +#include "i386-darwin-tdep.h" + +/* Read register values from the inferior process. + If REGNO is -1, do this for all registers. + Otherwise, REGNO specifies which register (so we can save time). */ +static void +i386_darwin_fetch_inferior_registers (struct regcache *regcache, int regno) +{ + thread_t current_thread = ptid_get_tid (inferior_ptid); + int fetched = 0; + struct gdbarch *gdbarch = get_regcache_arch (regcache); + + if (gdbarch_ptr_bit (gdbarch) == 64) + { + if (regno == -1 || amd64_native_gregset_supplies_p (gdbarch, regno)) + { + x86_thread_state_t gp_regs; + unsigned int gp_count = x86_THREAD_STATE_COUNT; + kern_return_t ret; + + ret = thread_get_state + (current_thread, x86_THREAD_STATE, (thread_state_t) & gp_regs, + &gp_count); + if (ret != KERN_SUCCESS) + { + printf_unfiltered (_("Error calling thread_get_state for GP registers for thread 0x%ulx"), current_thread); + MACH_CHECK_ERROR (ret); + } + amd64_supply_native_gregset (regcache, &gp_regs.uts, -1); + fetched++; + } + + if (regno == -1 || !amd64_native_gregset_supplies_p (gdbarch, regno)) + { + x86_float_state_t fp_regs; + unsigned int fp_count = x86_FLOAT_STATE_COUNT; + kern_return_t ret; + + ret = thread_get_state + (current_thread, x86_FLOAT_STATE, (thread_state_t) & fp_regs, + &fp_count); + if (ret != KERN_SUCCESS) + { + printf_unfiltered (_("Error calling thread_get_state for float registers for thread 0x%ulx"), current_thread); + MACH_CHECK_ERROR (ret); + } + i387_supply_fxsave (regcache, -1, &fp_regs.ufs.fs64); + fetched++; + } + } + else + { + if (regno == -1 || regno < I386_NUM_GREGS) + { + i386_thread_state_t gp_regs; + unsigned int gp_count = i386_THREAD_STATE_COUNT; + kern_return_t ret; + int i; + + ret = thread_get_state + (current_thread, i386_THREAD_STATE, (thread_state_t) & gp_regs, + &gp_count); + if (ret != KERN_SUCCESS) + { + printf_unfiltered (_("Error calling thread_get_state for GP registers for thread 0x%ulx"), current_thread); + MACH_CHECK_ERROR (ret); + } + for (i = 0; i < I386_NUM_GREGS; i++) + regcache_raw_supply + (regcache, i, + (char *)&gp_regs + i386_darwin_thread_state_reg_offset[i]); + + fetched++; + } + + if (regno == -1 + || (regno >= I386_ST0_REGNUM && regno < I386_SSE_NUM_REGS)) + { + i386_float_state_t fp_regs; + unsigned int fp_count = i386_FLOAT_STATE_COUNT; + kern_return_t ret; + + ret = thread_get_state + (current_thread, i386_FLOAT_STATE, (thread_state_t) & fp_regs, + &fp_count); + if (ret != KERN_SUCCESS) + { + printf_unfiltered (_("Error calling thread_get_state for float registers for thread 0x%ulx"), current_thread); + MACH_CHECK_ERROR (ret); + } + i387_supply_fxsave (regcache, -1, &fp_regs.__fpu_fcw); + fetched++; + } + } + + if (! fetched) + { + warning (_("unknown register %d"), regno); + regcache_raw_supply (regcache, regno, NULL); + } +} + +/* Store our register values back into the inferior. + If REGNO is -1, do this for all registers. + Otherwise, REGNO specifies which register (so we can save time). */ + +static void +i386_darwin_store_inferior_registers (struct regcache *regcache, int regno) +{ + thread_t current_thread = ptid_get_tid (inferior_ptid); + struct gdbarch *gdbarch = get_regcache_arch (regcache); + + if (gdbarch_ptr_bit (gdbarch) == 64) + { + if (regno == -1 || amd64_native_gregset_supplies_p (gdbarch, regno)) + { + x86_thread_state_t gp_regs; + kern_return_t ret; + unsigned int gp_count = x86_THREAD_STATE_COUNT; + + ret = thread_get_state + (current_thread, x86_THREAD_STATE, (thread_state_t) &gp_regs, + &gp_count); + MACH_CHECK_ERROR (ret); + gdb_assert (gp_regs.tsh.flavor == x86_THREAD_STATE64); + gdb_assert (gp_regs.tsh.count == x86_THREAD_STATE64_COUNT); + + amd64_collect_native_gregset (regcache, &gp_regs.uts, regno); + + ret = thread_set_state (current_thread, x86_THREAD_STATE, + (thread_state_t) &gp_regs, + x86_THREAD_STATE_COUNT); + MACH_CHECK_ERROR (ret); + } + + if (regno == -1 || !amd64_native_gregset_supplies_p (gdbarch, regno)) + { + x86_float_state_t fp_regs; + kern_return_t ret; + unsigned int fp_count = x86_FLOAT_STATE_COUNT; + + ret = thread_get_state + (current_thread, x86_FLOAT_STATE, (thread_state_t) & fp_regs, + &fp_count); + MACH_CHECK_ERROR (ret); + gdb_assert (fp_regs.fsh.flavor == x86_FLOAT_STATE64); + gdb_assert (fp_regs.fsh.count == x86_FLOAT_STATE64_COUNT); + + i387_collect_fxsave (regcache, regno, &fp_regs.ufs.fs64.__fpu_fcw); + + ret = thread_set_state (current_thread, x86_FLOAT_STATE, + (thread_state_t) & fp_regs, + x86_FLOAT_STATE_COUNT); + MACH_CHECK_ERROR (ret); + } + } + else + { + if (regno == -1 || regno < I386_NUM_GREGS) + { + i386_thread_state_t gp_regs; + kern_return_t ret; + unsigned int gp_count = i386_THREAD_STATE_COUNT; + int i; + + ret = thread_get_state + (current_thread, i386_THREAD_STATE, (thread_state_t) & gp_regs, + &gp_count); + MACH_CHECK_ERROR (ret); + + for (i = 0; i < I386_NUM_GREGS; i++) + if (regno == -1 || regno == i) + regcache_raw_collect + (regcache, i, + (char *)&gp_regs + i386_darwin_thread_state_reg_offset[i]); + + ret = thread_set_state (current_thread, i386_THREAD_STATE, + (thread_state_t) & gp_regs, + i386_THREAD_STATE_COUNT); + MACH_CHECK_ERROR (ret); + } + + if (regno == -1 + || (regno >= I386_ST0_REGNUM && regno < I386_SSE_NUM_REGS)) + { + i386_float_state_t fp_regs; + unsigned int fp_count = i386_FLOAT_STATE_COUNT; + kern_return_t ret; + + ret = thread_get_state + (current_thread, i386_FLOAT_STATE, (thread_state_t) & fp_regs, + &fp_count); + MACH_CHECK_ERROR (ret); + + i387_collect_fxsave (regcache, regno, &fp_regs.__fpu_fcw); + + ret = thread_set_state (current_thread, i386_FLOAT_STATE, + (thread_state_t) & fp_regs, + i386_FLOAT_STATE_COUNT); + MACH_CHECK_ERROR (ret); + } + } +} + + +/* Support for debug registers, boosted mostly from i386-linux-nat.c. */ + +#ifndef DR_FIRSTADDR +#define DR_FIRSTADDR 0 +#endif + +#ifndef DR_LASTADDR +#define DR_LASTADDR 3 +#endif + +#ifndef DR_STATUS +#define DR_STATUS 6 +#endif + +#ifndef DR_CONTROL +#define DR_CONTROL 7 +#endif + + +static void +i386_darwin_dr_set (int regnum, uint32_t value) +{ + int current_pid; + thread_t current_thread; + x86_debug_state_t dr_regs; + kern_return_t ret; + unsigned int dr_count = x86_DEBUG_STATE_COUNT; + + gdb_assert (regnum >= 0 && regnum <= DR_CONTROL); + + current_thread = ptid_get_tid (inferior_ptid); + + dr_regs.dsh.flavor = x86_DEBUG_STATE32; + dr_regs.dsh.count = x86_DEBUG_STATE32_COUNT; + dr_count = x86_DEBUG_STATE_COUNT; + ret = thread_get_state (current_thread, x86_DEBUG_STATE, + (thread_state_t) &dr_regs, &dr_count); + + if (ret != KERN_SUCCESS) + { + printf_unfiltered (_("Error reading debug registers thread 0x%x via thread_get_state\n"), (int) current_thread); + MACH_CHECK_ERROR (ret); + } + + switch (regnum) + { + case 0: + dr_regs.uds.ds32.__dr0 = value; + break; + case 1: + dr_regs.uds.ds32.__dr1 = value; + break; + case 2: + dr_regs.uds.ds32.__dr2 = value; + break; + case 3: + dr_regs.uds.ds32.__dr3 = value; + break; + case 4: + dr_regs.uds.ds32.__dr4 = value; + break; + case 5: + dr_regs.uds.ds32.__dr5 = value; + break; + case 6: + dr_regs.uds.ds32.__dr6 = value; + break; + case 7: + dr_regs.uds.ds32.__dr7 = value; + break; + } + + ret = thread_set_state (current_thread, x86_DEBUG_STATE, + (thread_state_t) &dr_regs, dr_count); + + if (ret != KERN_SUCCESS) + { + printf_unfiltered (_("Error writing debug registers thread 0x%x via thread_get_state\n"), (int) current_thread); + MACH_CHECK_ERROR (ret); + } +} + +static uint32_t +i386_darwin_dr_get (int regnum) +{ + thread_t current_thread; + x86_debug_state_t dr_regs; + kern_return_t ret; + unsigned int dr_count = x86_DEBUG_STATE_COUNT; + + gdb_assert (regnum >= 0 && regnum <= DR_CONTROL); + + current_thread = ptid_get_tid (inferior_ptid); + + dr_regs.dsh.flavor = x86_DEBUG_STATE32; + dr_regs.dsh.count = x86_DEBUG_STATE32_COUNT; + dr_count = x86_DEBUG_STATE_COUNT; + ret = thread_get_state (current_thread, x86_DEBUG_STATE, + (thread_state_t) &dr_regs, &dr_count); + + if (ret != KERN_SUCCESS) + { + printf_unfiltered (_("Error reading debug registers thread 0x%x via thread_get_state\n"), (int) current_thread); + MACH_CHECK_ERROR (ret); + } + + switch (regnum) + { + case 0: + return dr_regs.uds.ds32.__dr0; + case 1: + return dr_regs.uds.ds32.__dr1; + case 2: + return dr_regs.uds.ds32.__dr2; + case 3: + return dr_regs.uds.ds32.__dr3; + case 4: + return dr_regs.uds.ds32.__dr4; + case 5: + return dr_regs.uds.ds32.__dr5; + case 6: + return dr_regs.uds.ds32.__dr6; + case 7: + return dr_regs.uds.ds32.__dr7; + default: + return -1; + } +} + +void +i386_darwin_dr_set_control (unsigned long control) +{ + i386_darwin_dr_set (DR_CONTROL, control); +} + +void +i386_darwin_dr_set_addr (int regnum, CORE_ADDR addr) +{ + gdb_assert (regnum >= 0 && regnum <= DR_LASTADDR - DR_FIRSTADDR); + + i386_darwin_dr_set (DR_FIRSTADDR + regnum, addr); +} + +void +i386_darwin_dr_reset_addr (int regnum) +{ + gdb_assert (regnum >= 0 && regnum <= DR_LASTADDR - DR_FIRSTADDR); + + i386_darwin_dr_set (DR_FIRSTADDR + regnum, 0L); +} + +unsigned long +i386_darwin_dr_get_status (void) +{ + return i386_darwin_dr_get (DR_STATUS); +} + +void +darwin_check_osabi (darwin_inferior *inf, thread_t thread) +{ + if (gdbarch_osabi (current_gdbarch) == GDB_OSABI_UNKNOWN) + { + /* Attaching to a process. Let's figure out what kind it is. */ + x86_thread_state_t gp_regs; + struct gdbarch_info info; + unsigned int gp_count = x86_THREAD_STATE_COUNT; + kern_return_t ret; + + ret = thread_get_state (thread, x86_THREAD_STATE, + (thread_state_t) &gp_regs, &gp_count); + if (ret != KERN_SUCCESS) + { + MACH_CHECK_ERROR (ret); + return; + } + + gdbarch_info_init (&info); + gdbarch_info_fill (&info); + info.byte_order = gdbarch_byte_order (current_gdbarch); + info.osabi = GDB_OSABI_DARWIN; + if (gp_regs.tsh.flavor == x86_THREAD_STATE64) + info.bfd_arch_info = bfd_lookup_arch (bfd_arch_i386, + bfd_mach_x86_64); + else + info.bfd_arch_info = bfd_lookup_arch (bfd_arch_i386, + bfd_mach_i386_i386); + gdbarch_update_p (info); + } +} + +#define X86_EFLAGS_T 0x100UL + +void +darwin_set_sstep (thread_t thread, int enable) +{ + x86_thread_state_t regs; + unsigned int count = x86_THREAD_STATE_COUNT; + kern_return_t kret; + + kret = thread_get_state (thread, x86_THREAD_STATE, + (thread_state_t) ®s, &count); + if (kret != KERN_SUCCESS) + { + printf_unfiltered (_("darwin_set_sstep: error %x, thread=%x\n"), + kret, thread); + return; + } + switch (regs.tsh.flavor) + { + case x86_THREAD_STATE32: + { + __uint32_t bit = enable ? X86_EFLAGS_T : 0; + + if ((regs.uts.ts32.__eflags & X86_EFLAGS_T) == bit) + return; + regs.uts.ts32.__eflags = (regs.uts.ts32.__eflags & ~X86_EFLAGS_T) | bit; + kret = thread_set_state (thread, x86_THREAD_STATE, + (thread_state_t) ®s, count); + MACH_CHECK_ERROR (kret); + } + break; + case x86_THREAD_STATE64: + { + __uint64_t bit = enable ? X86_EFLAGS_T : 0; + + if ((regs.uts.ts64.__rflags & X86_EFLAGS_T) == bit) + return; + regs.uts.ts64.__rflags = (regs.uts.ts64.__rflags & ~X86_EFLAGS_T) | bit; + kret = thread_set_state (thread, x86_THREAD_STATE, + (thread_state_t) ®s, count); + MACH_CHECK_ERROR (kret); + } + break; + default: + error (_("darwin_set_sstep: unknown flavour: %d\n"), regs.tsh.flavor); + } +} + +void +darwin_complete_target (struct target_ops *target) +{ + amd64_native_gregset64_reg_offset = amd64_darwin_thread_state_reg_offset; + amd64_native_gregset64_num_regs = amd64_darwin_thread_state_num_regs; + amd64_native_gregset32_reg_offset = i386_darwin_thread_state_reg_offset; + amd64_native_gregset32_num_regs = i386_darwin_thread_state_num_regs; + + target->to_fetch_registers = i386_darwin_fetch_inferior_registers; + target->to_store_registers = i386_darwin_store_inferior_registers; +} diff --git a/gdb/i386-darwin-tdep.c b/gdb/i386-darwin-tdep.c new file mode 100644 index 0000000..522fe63 --- /dev/null +++ b/gdb/i386-darwin-tdep.c @@ -0,0 +1,159 @@ +/* Darwin support for GDB, the GNU debugger. + Copyright 1997, 1998, 1999, 2000, 2001, 2002, 2005, 2008 + Free Software Foundation, Inc. + + Contributed by Apple Computer, 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 "frame.h" +#include "inferior.h" +#include "gdbcore.h" +#include "target.h" +#include "floatformat.h" +#include "symtab.h" +#include "regcache.h" +#include "libbfd.h" +#include "objfiles.h" + +#include "i387-tdep.h" +#include "i386-tdep.h" +#include "amd64-tdep.h" +#include "osabi.h" +#include "ui-out.h" +#include "symtab.h" +#include "frame.h" +#include "gdb_assert.h" +#include "i386-darwin-tdep.h" + +/* Offsets into the struct i386_thread_state where we'll find the saved regs. + From <mach/i386/thread_status.h> and i386-tdep.h. */ +int i386_darwin_thread_state_reg_offset[] = +{ + 0 * 4, /* EAX */ + 2 * 4, /* ECX */ + 3 * 4, /* EDX */ + 1 * 4, /* EBX */ + 7 * 4, /* ESP */ + 6 * 4, /* EBP */ + 5 * 4, /* ESI */ + 4 * 4, /* EDI */ + 10 * 4, /* EIP */ + 9 * 4, /* EFLAGS */ + 11 * 4, /* CS */ + 8, /* SS */ + 12 * 4, /* DS */ + 13 * 4, /* ES */ + 14 * 4, /* FS */ + 15 * 4 /* GS */ +}; + +const int i386_darwin_thread_state_num_regs = + ARRAY_SIZE (i386_darwin_thread_state_reg_offset); + +/* Offsets into the struct x86_thread_state64 where we'll find the saved regs. + From <mach/i386/thread_status.h> and amd64-tdep.h. */ +int amd64_darwin_thread_state_reg_offset[] = +{ + 0 * 8, /* %rax */ + 1 * 8, /* %rbx */ + 2 * 8, /* %rcx */ + 3 * 8, /* %rdx */ + 5 * 8, /* %rsi */ + 4 * 8, /* %rdi */ + 6 * 8, /* %rbp */ + 7 * 8, /* %rsp */ + 8 * 8, /* %r8 ... */ + 9 * 8, + 10 * 8, + 11 * 8, + 12 * 8, + 13 * 8, + 14 * 8, + 15 * 8, /* ... %r15 */ + 16 * 8, /* %rip */ + 17 * 8, /* %rflags */ + 18 * 8, /* %cs */ + -1, /* %ss */ + -1, /* %ds */ + -1, /* %es */ + 19 * 8, /* %fs */ + 20 * 8 /* %gs */ +}; + +const int amd64_darwin_thread_state_num_regs = + ARRAY_SIZE (amd64_darwin_thread_state_reg_offset); + +static void +i386_darwin_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) +{ + struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch); + + /* We support the SSE registers. */ + tdep->num_xmm_regs = I386_NUM_XREGS - 1; + set_gdbarch_num_regs (gdbarch, I386_SSE_NUM_REGS); + + tdep->struct_return = reg_struct_return; + + tdep->sigcontext_addr = NULL; + tdep->sc_reg_offset = i386_darwin_thread_state_reg_offset; + tdep->sc_num_regs = 16; + + tdep->jb_pc_offset = 20; +} + +static void +x86_darwin_init_abi_64 (struct gdbarch_info info, struct gdbarch *gdbarch) +{ + struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch); + + amd64_init_abi (info, gdbarch); + + tdep->struct_return = reg_struct_return; + + /* We don't do signals yet. */ + tdep->sigcontext_addr = NULL; + tdep->sc_reg_offset = amd64_darwin_thread_state_reg_offset; + tdep->sc_num_regs = ARRAY_SIZE (amd64_darwin_thread_state_reg_offset); + + tdep->jb_pc_offset = 148; +} + +static enum gdb_osabi +i386_mach_o_osabi_sniffer (bfd *abfd) +{ + if (!bfd_check_format (abfd, bfd_object)) + return GDB_OSABI_UNKNOWN; + + if (bfd_get_arch (abfd) == bfd_arch_i386) + return GDB_OSABI_DARWIN; + + return GDB_OSABI_UNKNOWN; +} + +void +_initialize_i386_darwin_tdep (void) +{ + gdbarch_register_osabi_sniffer (bfd_arch_unknown, bfd_target_mach_o_flavour, + i386_mach_o_osabi_sniffer); + + gdbarch_register_osabi (bfd_arch_i386, bfd_mach_i386_i386, + GDB_OSABI_DARWIN, i386_darwin_init_abi); + + gdbarch_register_osabi (bfd_arch_i386, bfd_mach_x86_64, + GDB_OSABI_DARWIN, x86_darwin_init_abi_64); +} diff --git a/gdb/i386-darwin-tdep.h b/gdb/i386-darwin-tdep.h new file mode 100644 index 0000000..209ca12 --- /dev/null +++ b/gdb/i386-darwin-tdep.h @@ -0,0 +1,33 @@ +/* Target-dependent code for Darwin x86. + + Copyright (C) 2008 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/>. */ +#ifndef __I386_DARWIN_TDEP_H__ +#define __I386_DARWIN_TDEP_H__ + +/* Mapping between the general-purpose registers in Darwin x86 thread_state + struct and GDB's register cache layout. */ +extern int i386_darwin_thread_state_reg_offset[]; +extern const int i386_darwin_thread_state_num_regs; + +/* Mapping between the general-purpose registers in Darwin x86-64 thread + state and GDB's register cache layout. + Indexed by amd64_regnum. */ +extern int amd64_darwin_thread_state_reg_offset[]; +extern const int amd64_darwin_thread_state_num_regs; + +#endif /* __I386_DARWIN_TDEP_H__ */ diff --git a/gdb/machoread.c b/gdb/machoread.c new file mode 100644 index 0000000..f15495f --- /dev/null +++ b/gdb/machoread.c @@ -0,0 +1,694 @@ +/* Darwin support for GDB, the GNU debugger. + Copyright (C) 2008 Free Software Foundation, Inc. + + Contributed by AdaCore. + + 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 "symtab.h" +#include "gdbtypes.h" +#include "bfd.h" +#include "symfile.h" +#include "objfiles.h" +#include "buildsym.h" +#include "gdbcmd.h" +#include "gdbcore.h" +#include "mach-o.h" +#include "gdb_assert.h" +#include "aout/stab_gnu.h" +#include "vec.h" + +#include <string.h> + +/* If non-zero displays debugging message. */ +static int mach_o_debug_level = 0; + +static void +macho_new_init (struct objfile *objfile) +{ +} + +static void +macho_symfile_init (struct objfile *objfile) +{ + objfile->flags |= OBJF_REORDERED; + init_entry_point_info (objfile); +} + +/* Dwarf debugging information are never in the final executable. They stay + in object files and the executable contains the list of object files read + during the link. + Each time an oso (other source) is found in the executable, the reader + creates such a structure. They are read after the processing of the + executable. +*/ +typedef struct oso_el +{ + /* Object file name. */ + const char *name; + + /* Associated time stamp. */ + unsigned long mtime; + + /* Number of sections. This is the length of SYMBOLS and OFFSETS array. */ + int num_sections; + + /* Each seaction of the object file is represented by a symbol and its + offset. */ + asymbol **symbols; + bfd_vma *offsets; +} +oso_el; + +/* Vector of object files to be read after the executable. */ +DEF_VEC_O (oso_el); +static VEC (oso_el) *oso_vector; + +/* Add a new OSO to the vector. */ +static void +macho_add_oso (const asymbol *oso_sym, int nbr_sections, + asymbol **symbols, bfd_vma *offsets) +{ + oso_el el; + + el.name = oso_sym->name; + el.mtime = oso_sym->value; + el.num_sections = nbr_sections; + el.symbols = symbols; + el.offsets = offsets; + VEC_safe_push (oso_el, oso_vector, &el); +} + +/* Build the minimal symbol table from SYMBOL_TABLE of length + NUMBER_OF_SYMBOLS for OBJFILE. + Read OSO files at the end. */ +static void +macho_symtab_read (struct objfile *objfile, + long number_of_symbols, asymbol **symbol_table) +{ + struct gdbarch *gdbarch = get_objfile_arch (objfile); + long storage_needed; + asymbol *sym; + long i, j; + CORE_ADDR offset; + enum minimal_symbol_type ms_type; + unsigned int nbr_sections = bfd_count_sections (objfile->obfd); + asymbol **first_symbol = NULL; + bfd_vma *first_offset = NULL; + const asymbol *oso_file = NULL; + + for (i = 0; i < number_of_symbols; i++) + { + sym = symbol_table[i]; + offset = ANOFFSET (objfile->section_offsets, sym->section->index); + + if (sym->flags & BSF_DEBUGGING) + { + unsigned char type = BFD_MACH_O_SYM_NTYPE(sym); + bfd_vma addr; + + switch (type) + { + case N_SO: + if ((sym->name == NULL || sym->name[0] == 0) + && oso_file != NULL) + { + macho_add_oso (oso_file, nbr_sections, + first_symbol, first_offset); + first_symbol = NULL; + first_offset = NULL; + oso_file = NULL; + } + break; + case N_FUN: + case N_STSYM: + if (sym->name == NULL || sym->name[0] == '\0') + break; + /* Fall through. */ + case N_BNSYM: + gdb_assert (oso_file != NULL); + addr = sym->value + + bfd_get_section_vma (sym->section->bfd, sym->section); + if (addr != 0 + && first_symbol[sym->section->index] == NULL) + { + first_symbol[sym->section->index] = sym; + first_offset[sym->section->index] = addr + offset; + } + break; + case N_GSYM: + gdb_assert (oso_file != NULL); + if (first_symbol[sym->section->index] == NULL) + first_symbol[sym->section->index] = sym; + break; + case N_OSO: + gdb_assert (oso_file == NULL); + first_symbol = (asymbol **)xmalloc (nbr_sections + * sizeof (asymbol *)); + first_offset = (bfd_vma *)xmalloc (nbr_sections + * sizeof (bfd_vma)); + for (j = 0; j < nbr_sections; j++) + first_symbol[j] = NULL; + oso_file = sym; + break; + } + continue; + } + + if (sym->name == NULL || *sym->name == '\0') + { + /* Skip names that don't exist (shouldn't happen), or names + that are null strings (may happen). */ + continue; + } + + if (sym->flags & (BSF_GLOBAL | BSF_LOCAL | BSF_WEAK)) + { + struct minimal_symbol *msym; + CORE_ADDR symaddr; + + /* Bfd symbols are section relative. */ + symaddr = sym->value + sym->section->vma; + + /* Select global/local/weak symbols. Note that bfd puts abs + symbols in their own section, so all symbols we are + interested in will have a section. */ + /* Relocate all non-absolute and non-TLS symbols by the + section offset. */ + if (sym->section != &bfd_abs_section + && !(sym->section->flags & SEC_THREAD_LOCAL)) + symaddr += offset; + + if (sym->section == &bfd_abs_section) + ms_type = mst_abs; + else if (sym->section->flags & SEC_CODE) + { + if (sym->flags & (BSF_GLOBAL | BSF_WEAK)) + ms_type = mst_text; + else + ms_type = mst_file_text; + } + else if (sym->section->flags & SEC_ALLOC) + { + if (sym->flags & (BSF_GLOBAL | BSF_WEAK)) + { + if (sym->section->flags & SEC_LOAD) + ms_type = mst_data; + else + ms_type = mst_bss; + } + else if (sym->flags & BSF_LOCAL) + { + /* Not a special stabs-in-elf symbol, do regular + symbol processing. */ + if (sym->section->flags & SEC_LOAD) + ms_type = mst_file_data; + else + ms_type = mst_file_bss; + } + else + ms_type = mst_unknown; + } + else + continue; /* Skip this symbol. */ + + gdb_assert (sym->section->index < nbr_sections); + if (oso_file != NULL + && first_symbol[sym->section->index] == NULL) + { + first_symbol[sym->section->index] = sym; + first_offset[sym->section->index] = symaddr; + } + + msym = prim_record_minimal_symbol_and_info + (sym->name, symaddr, ms_type, sym->section->index, + sym->section, objfile); + } + } + + if (oso_file != NULL) + macho_add_oso (oso_file, nbr_sections, first_symbol, first_offset); +} + +/* If NAME describes an archive member (ie: ARCHIVE '(' MEMBER ')'), + returns the length of the archive name. + Returns -1 otherwise. */ +static int +get_archive_prefix_len (const char *name) +{ + char *lparen; + int name_len = strlen (name); + + if (name_len == 0 || name[name_len - 1] != ')') + return -1; + + lparen = strrchr (name, '('); + if (lparen == NULL || lparen == name) + return -1; + return lparen - name; +} + +/* Read symbols from the vector of oso files. */ +static void +macho_oso_symfile (struct objfile *main_objfile) +{ + int ix; + VEC (oso_el) *vec; + oso_el *oso; + char leading_char; + + /* TODO: Sort them, group library search. */ + + vec = oso_vector; + oso_vector = NULL; + + leading_char = bfd_get_symbol_leading_char (main_objfile->obfd); + + for (ix = 0; VEC_iterate (oso_el, vec, ix, oso); ix++) + { + struct section_addr_info *addrs; + int pfx_len; + int len; + int i; + oso_el el; + + if (mach_o_debug_level > 0) + printf_unfiltered (_("Loading symbols from oso: %s\n"), oso->name); + + /* Compute addr length. */ + len = 0; + for (i = 0; i < oso->num_sections; i++) + if (oso->symbols[i] != NULL) + len++; + + addrs = alloc_section_addr_info (len); + + len = 0; + for (i = 0; i < oso->num_sections; i++) + if (oso->symbols[i] != NULL) + { + if (oso->offsets[i]) + addrs->other[len].addr = oso->offsets[i]; + else + { + struct minimal_symbol *msym; + const char *name = oso->symbols[i]->name; + + if (name[0] == leading_char) + ++name; + + if (mach_o_debug_level > 3) + printf_unfiltered (_("resolv sec %s with %s\n"), + oso->symbols[i]->section->name, + oso->symbols[i]->name); + msym = lookup_minimal_symbol (name, NULL, main_objfile); + if (msym == NULL) + { + warning (_("can't find symbol '%s' in minsymtab"), + oso->symbols[i]->name); + addrs->other[len].addr = 0; + } + else + addrs->other[len].addr = SYMBOL_VALUE_ADDRESS (msym); + } + addrs->other[len].name = (char *)oso->symbols[i]->section->name; + len++; + } + + if (mach_o_debug_level > 1) + { + int j; + for (j = 0; j < addrs->num_sections; j++) + printf_unfiltered + (_(" %s: %s\n"), + core_addr_to_string (addrs->other[j].addr), + addrs->other[j].name); + } + + /* Check if this is a library name. */ + pfx_len = get_archive_prefix_len (oso->name); + if (pfx_len > 0) + { + bfd *archive_bfd; + bfd *member_bfd; + char *archive_name = (char *) alloca (pfx_len + 1); + int member_len; + + member_len = strlen (oso->name + pfx_len + 1) - 1; + memcpy (archive_name, oso->name, pfx_len); + archive_name[pfx_len] = '\0'; + + /* Open the archive and check the format. */ + archive_bfd = bfd_openr (archive_name, gnutarget); + if (archive_bfd == NULL) + { + warning (_("Could not open OSO archive file \"%s\""), + archive_name); + continue; + } + if (!bfd_check_format (archive_bfd, bfd_archive)) + { + warning (_("OSO archive file \"%s\" not an archive."), + archive_name); + bfd_close (archive_bfd); + continue; + } + member_bfd = bfd_openr_next_archived_file (archive_bfd, NULL); + + if (member_bfd == NULL) + { + warning (_("Could not read archive members out of " + "OSO archive \"%s\""), archive_name); + bfd_close (archive_bfd); + continue; + } + + while (member_bfd != NULL) + { + bfd *prev = member_bfd; + const char *member_name = member_bfd->filename; + if (strlen (member_name) == member_len + && !memcmp (member_name, oso->name + pfx_len + 1, member_len)) + break; + member_bfd = bfd_openr_next_archived_file + (archive_bfd, member_bfd); + bfd_close (prev); + } + if (member_bfd == NULL) + { + warning (_("Could not find specified archive member " + "for OSO name \"%s\""), oso->name); + bfd_close (archive_bfd); + continue; + } + + bfd_set_cacheable (member_bfd, 1); + + if (!bfd_check_format (member_bfd, bfd_object)) + { + warning (_("`%s': can't read symbols: %s."), oso->name, + bfd_errmsg (bfd_get_error ())); + bfd_close (member_bfd); + } + else + symbol_file_add_from_bfd (member_bfd, 0, addrs, 0, 0); + } + else + { + bfd *abfd; + + abfd = bfd_openr (oso->name, gnutarget); + if (!abfd) + { + warning (_("`%s': can't open to read symbols: %s."), oso->name, + bfd_errmsg (bfd_get_error ())); + continue; + } + bfd_set_cacheable (abfd, 1); + + if (!bfd_check_format (abfd, bfd_object)) + { + bfd_close (abfd); + warning (_("`%s': can't read symbols: %s."), oso->name, + bfd_errmsg (bfd_get_error ())); + continue; + } + + symbol_file_add_from_bfd (abfd, 0, addrs, 0, 0); + } + xfree (oso->symbols); + xfree (oso->offsets); + } + VEC_free (oso_el, vec); +} + +/* DSYM (debug symbols) files contain the debug info of an executable. + This is a separate file created by dsymutil(1) and is similar to debug + link feature on ELF. + DSYM files are located in a subdirectory. Append DSYM_SUFFIX to the + executable name and the executable base name to get the DSYM file name. */ +#define DSYM_SUFFIX ".dSYM/Contents/Resources/DWARF/" + +/* Check if a dsym file exists for OBJFILE. If so, returns a bfd for it. + Return NULL if no valid dsym file is found. */ +static bfd * +macho_check_dsym (struct objfile *objfile) +{ + size_t name_len = strlen (objfile->name); + size_t dsym_len = strlen (DSYM_SUFFIX); + const char *base_name = lbasename (objfile->name); + size_t base_len = strlen (base_name); + char *dsym_filename = alloca (name_len + dsym_len + base_len + 1); + bfd *dsym_bfd; + asection *sect; + bfd_byte main_uuid[16]; + bfd_byte dsym_uuid[16]; + + strcpy (dsym_filename, objfile->name); + strcpy (dsym_filename + name_len, DSYM_SUFFIX); + strcpy (dsym_filename + name_len + dsym_len, base_name); + + if (access (dsym_filename, R_OK) != 0) + return NULL; + + sect = bfd_get_section_by_name (objfile->obfd, "LC_UUID"); + if (sect == NULL) + { + warning (_("can't find UUID in %s"), objfile->name); + return NULL; + } + if (!bfd_get_section_contents (objfile->obfd, sect, main_uuid, + 0, sizeof (main_uuid))) + { + warning (_("can't read UUID in %s"), objfile->name); + return NULL; + } + + dsym_filename = xstrdup (dsym_filename); + dsym_bfd = bfd_openr (dsym_filename, gnutarget); + if (dsym_bfd == NULL) + { + warning (_("can't open dsym file %s"), dsym_filename); + xfree (dsym_filename); + return NULL; + } + + if (!bfd_check_format (dsym_bfd, bfd_object)) + { + bfd_close (dsym_bfd); + warning (_("bad dsym file format: %s"), bfd_errmsg (bfd_get_error ())); + xfree (dsym_filename); + return NULL; + } + + sect = bfd_get_section_by_name (dsym_bfd, "LC_UUID"); + if (sect == NULL) + { + warning (_("can't find UUID in %s"), dsym_filename); + bfd_close (dsym_bfd); + xfree (dsym_filename); + return NULL; + } + if (!bfd_get_section_contents (dsym_bfd, sect, dsym_uuid, + 0, sizeof (dsym_uuid))) + { + warning (_("can't read UUID in %s"), dsym_filename); + bfd_close (dsym_bfd); + xfree (dsym_filename); + return NULL; + } + if (memcmp (dsym_uuid, main_uuid, sizeof (main_uuid))) + { + warning (_("dsym file UUID doesn't match the one in %s"), objfile->name); + bfd_close (dsym_bfd); + xfree (dsym_filename); + return NULL; + } + return dsym_bfd; + +} + +static void +macho_symfile_read (struct objfile *objfile, int mainline) +{ + bfd *abfd = objfile->obfd; + struct cleanup *back_to; + CORE_ADDR offset; + long storage_needed; + bfd *dsym_bfd; + + init_minimal_symbol_collection (); + back_to = make_cleanup_discard_minimal_symbols (); + + /* Get symbols from the symbol table only if the file is an executable. + The symbol table of object files is not relocated and is expected to + be in the executable. */ + if (bfd_get_file_flags (abfd) & EXEC_P) + { + /* Process the normal symbol table first. */ + storage_needed = bfd_get_symtab_upper_bound (objfile->obfd); + if (storage_needed < 0) + error (_("Can't read symbols from %s: %s"), + bfd_get_filename (objfile->obfd), + bfd_errmsg (bfd_get_error ())); + + if (storage_needed > 0) + { + asymbol **symbol_table; + long symcount; + + symbol_table = (asymbol **) xmalloc (storage_needed); + make_cleanup (xfree, symbol_table); + symcount = bfd_canonicalize_symtab (objfile->obfd, symbol_table); + + if (symcount < 0) + error (_("Can't read symbols from %s: %s"), + bfd_get_filename (objfile->obfd), + bfd_errmsg (bfd_get_error ())); + + macho_symtab_read (objfile, symcount, symbol_table); + } + + install_minimal_symbols (objfile); + + /* Check for DSYM file. */ + dsym_bfd = macho_check_dsym (objfile); + if (dsym_bfd != NULL) + { + int ix; + oso_el *oso; + + if (mach_o_debug_level > 0) + printf_unfiltered (_("dsym file found\n")); + + /* Remove oso. They won't be used. */ + for (ix = 0; VEC_iterate (oso_el, oso_vector, ix, oso); ix++) + { + xfree (oso->symbols); + xfree (oso->offsets); + } + VEC_free (oso_el, oso_vector); + oso_vector = NULL; + + /* Now recurse: read dwarf from dsym. */ + symbol_file_add_from_bfd (dsym_bfd, 0, NULL, 0, 0); + + /* Don't try to read dwarf2 from main file. */ + return; + } + } + + if (dwarf2_has_info (objfile)) + { + /* DWARF 2 sections */ + dwarf2_build_psymtabs (objfile, mainline); + } + + /* FIXME: kettenis/20030504: This still needs to be integrated with + dwarf2read.c in a better way. */ + dwarf2_build_frame_info (objfile); + + /* Then the oso. */ + if (oso_vector != NULL) + macho_oso_symfile (objfile); +} + +static void +macho_symfile_finish (struct objfile *objfile) +{ +} + +static void +macho_symfile_offsets (struct objfile *objfile, + struct section_addr_info *addrs) +{ + unsigned int i; + unsigned int num_sections; + struct obj_section *osect; + + /* Allocate section_offsets. */ + objfile->num_sections = bfd_count_sections (objfile->obfd); + objfile->section_offsets = (struct section_offsets *) + obstack_alloc (&objfile->objfile_obstack, + SIZEOF_N_SECTION_OFFSETS (objfile->num_sections)); + memset (objfile->section_offsets, 0, + SIZEOF_N_SECTION_OFFSETS (objfile->num_sections)); + + /* This code is run when we first add the objfile with + symfile_add_with_addrs_or_offsets, when "addrs" not "offsets" are + passed in. The place in symfile.c where the addrs are applied + depends on the addrs having section names. But in the dyld code + we build an anonymous array of addrs, so that code is a no-op. + Because of that, we have to apply the addrs to the sections here. + N.B. if an objfile slides after we've already created it, then it + goes through objfile_relocate. */ + + for (i = 0; i < addrs->num_sections; i++) + { + if (addrs->other[i].name == NULL) + continue; + + ALL_OBJFILE_OSECTIONS (objfile, osect) + { + const char *bfd_sect_name = osect->the_bfd_section->name; + + if (strcmp (bfd_sect_name, addrs->other[i].name) == 0) + { + obj_section_offset (osect) = addrs->other[i].addr; + break; + } + } + } + + objfile->sect_index_text = 0; + + ALL_OBJFILE_OSECTIONS (objfile, osect) + { + const char *bfd_sect_name = osect->the_bfd_section->name; + int sect_index = osect->the_bfd_section->index; + + if (strcmp (bfd_sect_name, "LC_SEGMENT.__TEXT") == 0) + objfile->sect_index_text = sect_index; + else if (strcmp (bfd_sect_name, "LC_SEGMENT.__TEXT.__text") == 0) + objfile->sect_index_text = sect_index; + } +} + +static struct sym_fns macho_sym_fns = { + bfd_target_mach_o_flavour, + + macho_new_init, /* sym_new_init: init anything gbl to entire symtab */ + macho_symfile_init, /* sym_init: read initial info, setup for sym_read() */ + macho_symfile_read, /* sym_read: read a symbol file into symtab */ + macho_symfile_finish, /* sym_finish: finished with file, cleanup */ + macho_symfile_offsets, /* sym_offsets: xlate external to internal form */ + NULL /* next: pointer to next struct sym_fns */ +}; + +void +_initialize_machoread () +{ + add_symtab_fns (&macho_sym_fns); + + add_setshow_zinteger_cmd ("mach-o", class_obscure, + &mach_o_debug_level, _("\ +Set if printing Mach-O symbols processing."), _("\ +Show if printing Mach-O symbols processing."), NULL, + NULL, NULL, + &setdebuglist, &showdebuglist); +} diff --git a/gdb/osabi.c b/gdb/osabi.c index 64f0910..988e388 100644 --- a/gdb/osabi.c +++ b/gdb/osabi.c @@ -73,6 +73,7 @@ static const char * const gdb_osabi_names[] = "Cygwin", "AIX", "DICOS", + "Darwin", "<invalid>" }; |