macosx-nat-mutils.c [plain text]
#include "defs.h"
#include "inferior.h"
#include "symtab.h"
#include "symfile.h"
#include "objfiles.h"
#include "target.h"
#include "terminal.h"
#include "gdbcmd.h"
#include "regcache.h"
#include "value.h"
#include "exceptions.h"
#include "cli-out.h"
#include "macosx-nat-mutils.h"
#include "macosx-nat-inferior.h"
#include "macosx-nat-inferior-debug.h"
#include <mach-o/nlist.h>
#include <mach-o/dyld_debug.h>
#include <mach/mach_error.h>
#include <sys/ptrace.h>
#include <sys/signal.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <limits.h>
#include <unistd.h>
#include <AvailabilityMacros.h>
#define MACH64 (MAC_OS_X_VERSION_MAX_ALLOWED >= 1040)
#if MACH64
#include <mach/mach_vm.h>
#else
#define mach_vm_size_t vm_size_t
#define mach_vm_address_t vm_address_t
#define mach_vm_read vm_read
#define mach_vm_write vm_write
#define mach_vm_region vm_region
#define mach_vm_protect vm_protect
#define VM_REGION_BASIC_INFO_COUNT_64 VM_REGION_BASIC_INFO_COUNT
#define VM_REGION_BASIC_INFO_64 VM_REGION_BASIC_INFO
#endif
#define MAX_INSTRUCTION_CACHE_WARNINGS 0
#define MINUS_INT_MIN (((unsigned int) (- (INT_MAX + INT_MIN))) + INT_MAX)
static FILE *mutils_stderr = NULL;
static int mutils_debugflag = 0;
extern macosx_inferior_status *macosx_status;
void
mutils_debug (const char *fmt, ...)
{
va_list ap;
if (mutils_debugflag)
{
va_start (ap, fmt);
fprintf (mutils_stderr, "[%d mutils]: ", getpid ());
vfprintf (mutils_stderr, fmt, ap);
va_end (ap);
fflush (mutils_stderr);
}
}
unsigned int
child_get_pagesize ()
{
kern_return_t status;
static int result = -1;
if (result == -1)
{
status = host_page_size (mach_host_self (), &result);
if (status != KERN_SUCCESS)
result = -1;
MACH_CHECK_ERROR (status);
}
return result;
}
static int
mach_xfer_memory_remainder (CORE_ADDR memaddr, gdb_byte *myaddr,
int len, int write,
struct mem_attrib *attrib,
struct target_ops *target)
{
unsigned int pagesize = child_get_pagesize ();
vm_offset_t mempointer;
mach_msg_type_number_t memcopied;
CORE_ADDR pageaddr = memaddr - (memaddr % pagesize);
kern_return_t kret;
CHECK_FATAL (((memaddr + len - 1) - ((memaddr + len - 1) % pagesize))
== pageaddr);
kret = mach_vm_read (macosx_status->task, pageaddr, pagesize,
&mempointer, &memcopied);
if (kret != KERN_SUCCESS)
{
mutils_debug
("Unable to read page for region at 0x%s with length %lu from inferior: %s (0x%lx)\n",
paddr_nz (pageaddr), (unsigned long) len,
MACH_ERROR_STRING (kret), kret);
return 0;
}
if (memcopied != pagesize)
{
kret = vm_deallocate (mach_task_self (), mempointer, memcopied);
if (kret != KERN_SUCCESS)
{
warning
("Unable to deallocate memory used by failed read from inferior: %s (0x%lx)",
MACH_ERROR_STRING (kret), (unsigned long) kret);
}
mutils_debug
("Unable to read region at 0x%s with length %lu from inferior: "
"vm_read returned %lu bytes instead of %lu\n",
paddr_nz (pageaddr), (unsigned long) pagesize,
(unsigned long) memcopied, (unsigned long) pagesize);
return 0;
}
if (!write)
{
memcpy (myaddr, ((unsigned char *) 0) + mempointer
+ (memaddr - pageaddr), len);
}
else
{
vm_machine_attribute_val_t flush = MATTR_VAL_CACHE_FLUSH;
memcpy (((unsigned char *) 0) + mempointer
+ (memaddr - pageaddr), myaddr, len);
kret = vm_machine_attribute (mach_task_self (), mempointer,
pagesize, MATTR_CACHE, &flush);
if (kret != KERN_SUCCESS)
{
mutils_debug
("Unable to flush GDB's address space after memcpy prior to vm_write: %s (0x%lx)\n",
MACH_ERROR_STRING (kret), kret);
}
kret =
mach_vm_write (macosx_status->task, pageaddr, (pointer_t) mempointer,
pagesize);
if (kret != KERN_SUCCESS)
{
mutils_debug
("Unable to write region at 0x%s with length %lu to inferior: %s (0x%lx)\n",
paddr_nz (memaddr), (unsigned long) len,
MACH_ERROR_STRING (kret), kret);
return 0;
}
}
kret = vm_deallocate (mach_task_self (), mempointer, memcopied);
if (kret != KERN_SUCCESS)
{
warning
("Unable to deallocate memory used to read from inferior: %s (0x%ulx)",
MACH_ERROR_STRING (kret), kret);
return 0;
}
return len;
}
static int
mach_xfer_memory_block (CORE_ADDR memaddr, gdb_byte *myaddr,
int len, int write,
struct mem_attrib *attrib, struct target_ops *target)
{
unsigned int pagesize = child_get_pagesize ();
vm_offset_t mempointer;
mach_msg_type_number_t memcopied;
kern_return_t kret;
CHECK_FATAL ((memaddr % pagesize) == 0);
CHECK_FATAL ((len % pagesize) == 0);
if (!write)
{
kret =
mach_vm_read (macosx_status->task, memaddr, len, &mempointer,
&memcopied);
if (kret != KERN_SUCCESS)
{
mutils_debug
("Unable to read region at 0x%s with length %lu from inferior: %s (0x%lx)\n",
paddr_nz (memaddr), (unsigned long) len,
MACH_ERROR_STRING (kret), kret);
return 0;
}
if (memcopied != len)
{
kret = vm_deallocate (mach_task_self (), mempointer, memcopied);
if (kret != KERN_SUCCESS)
{
warning
("Unable to deallocate memory used by failed read from inferior: %s (0x%ux)",
MACH_ERROR_STRING (kret), kret);
}
mutils_debug
("Unable to read region at 0x%s with length %lu from inferior: "
"vm_read returned %lu bytes instead of %lu\n",
paddr_nz (memaddr), (unsigned long) len,
(unsigned long) memcopied, (unsigned long) len);
return 0;
}
memcpy (myaddr, ((unsigned char *) 0) + mempointer, len);
kret = vm_deallocate (mach_task_self (), mempointer, memcopied);
if (kret != KERN_SUCCESS)
{
warning
("Unable to deallocate memory used by read from inferior: %s (0x%ulx)",
MACH_ERROR_STRING (kret), kret);
return 0;
}
}
else
{
kret =
mach_vm_write (macosx_status->task, memaddr, (pointer_t) myaddr, len);
if (kret != KERN_SUCCESS)
{
mutils_debug
("Unable to write region at 0x%s with length %lu from inferior: %s (0x%lx)\n",
paddr_nz (memaddr), (unsigned long) len,
MACH_ERROR_STRING (kret), kret);
return 0;
}
}
return len;
}
int
mach_xfer_memory (CORE_ADDR memaddr, gdb_byte *myaddr,
int len, int write,
struct mem_attrib *attrib, struct target_ops *target)
{
mach_vm_address_t r_start;
mach_vm_address_t r_end;
mach_vm_size_t r_size;
mach_port_t r_object_name;
vm_region_basic_info_data_64_t r_data;
mach_msg_type_number_t r_info_size;
CORE_ADDR cur_memaddr;
gdb_byte *cur_myaddr;
int cur_len;
unsigned int pagesize = child_get_pagesize ();
vm_machine_attribute_val_t flush = MATTR_VAL_CACHE_FLUSH;
kern_return_t kret;
int ret;
r_start = memaddr;
if (r_start != memaddr)
{
errno = EINVAL;
return 0;
}
if (len == 0)
{
return 0;
}
CHECK_FATAL (myaddr != NULL);
errno = 0;
{
r_start = memaddr;
r_info_size = VM_REGION_BASIC_INFO_COUNT_64;
kret = mach_vm_region (macosx_status->task, &r_start, &r_size,
VM_REGION_BASIC_INFO_64,
(vm_region_info_t) &r_data, &r_info_size,
&r_object_name);
if (kret != KERN_SUCCESS)
{
return 0;
}
if (r_start > memaddr)
{
if ((r_start - memaddr) <= MINUS_INT_MIN)
{
mutils_debug
("First available address near 0x%s is at 0x%s; returning\n",
paddr_nz (memaddr), paddr_nz (r_start));
return -(r_start - memaddr);
}
else
{
mutils_debug ("First available address near 0x%s is at 0x%s "
"(too far; returning 0)\n",
paddr_nz (memaddr), paddr_nz (r_start));
return 0;
}
}
}
cur_memaddr = memaddr;
cur_myaddr = myaddr;
cur_len = len;
while (cur_len > 0)
{
r_start = cur_memaddr;
r_info_size = VM_REGION_BASIC_INFO_COUNT_64;
kret = mach_vm_region (macosx_status->task, &r_start, &r_size,
VM_REGION_BASIC_INFO_64,
(vm_region_info_t) & r_data, &r_info_size,
&r_object_name);
if (kret != KERN_SUCCESS)
{
mutils_debug
("Unable to read region information for memory at 0x%s: %s (0x%lx)\n",
paddr_nz (cur_memaddr), MACH_ERROR_STRING (kret), kret);
break;
}
if (r_start > cur_memaddr)
{
mutils_debug
("Next available region for address at 0x%s is 0x%s\n",
paddr_nz (cur_memaddr), paddr_nz (r_start));
break;
}
if (write)
{
kret = mach_vm_protect (macosx_status->task, r_start, r_size, 0,
VM_PROT_READ | VM_PROT_WRITE);
if (kret != KERN_SUCCESS)
{
kret = mach_vm_protect (macosx_status->task, r_start, r_size, 0,
VM_PROT_COPY | VM_PROT_READ | VM_PROT_WRITE);
}
if (kret != KERN_SUCCESS)
{
mutils_debug
("Unable to add write access to region at 0x%s: %s (0x%lx)",
paddr_nz (r_start), MACH_ERROR_STRING (kret), kret);
break;
}
}
r_end = r_start + r_size;
CHECK_FATAL (r_start <= cur_memaddr);
CHECK_FATAL (r_end >= cur_memaddr);
CHECK_FATAL ((r_start % pagesize) == 0);
CHECK_FATAL ((r_end % pagesize) == 0);
CHECK_FATAL (r_end >= (r_start + pagesize));
if ((cur_memaddr % pagesize) != 0)
{
int max_len = pagesize - (cur_memaddr % pagesize);
int op_len = cur_len;
if (op_len > max_len)
{
op_len = max_len;
}
ret = mach_xfer_memory_remainder (cur_memaddr, cur_myaddr, op_len,
write, attrib, target);
}
else if (cur_len >= pagesize)
{
int max_len = r_end - cur_memaddr;
int op_len = cur_len;
if (op_len > max_len)
{
op_len = max_len;
}
op_len -= (op_len % pagesize);
ret = mach_xfer_memory_block (cur_memaddr, cur_myaddr, op_len,
write, attrib, target);
}
else
{
ret = mach_xfer_memory_remainder (cur_memaddr, cur_myaddr, cur_len,
write, attrib, target);
}
cur_memaddr += ret;
cur_myaddr += ret;
cur_len -= ret;
if (write)
{
kret = vm_machine_attribute (macosx_status->task, r_start, r_size,
MATTR_CACHE, &flush);
if (kret != KERN_SUCCESS)
{
static int nwarn = 0;
nwarn++;
if (nwarn <= MAX_INSTRUCTION_CACHE_WARNINGS)
{
warning
("Unable to flush data/instruction cache for region at 0x%s: %s",
paddr_nz (r_start), MACH_ERROR_STRING (ret));
}
if (nwarn == MAX_INSTRUCTION_CACHE_WARNINGS)
{
warning
("Support for flushing the data/instruction cache on this machine appears broken");
warning ("No further warning messages will be given.");
}
break;
}
kret = mach_vm_protect (macosx_status->task, r_start, r_size, 0,
r_data.protection);
if (kret != KERN_SUCCESS)
{
warning
("Unable to restore original permissions for region at 0x%s",
paddr_nz (r_start));
break;
}
}
if (ret == 0)
{
break;
}
}
return len - cur_len;
}
LONGEST
mach_xfer_partial (struct target_ops *ops,
enum target_object object, const char *annex,
gdb_byte *readbuf, const gdb_byte *writebuf,
ULONGEST offset, LONGEST len)
{
switch (object)
{
case TARGET_OBJECT_MEMORY:
{
ssize_t nbytes = len;
if (readbuf)
nbytes = mach_xfer_memory (offset, readbuf, nbytes, 0, NULL, ops);
if (writebuf && nbytes > 0)
nbytes = mach_xfer_memory (offset, writebuf, nbytes, 1, NULL, ops);
return nbytes;
}
default:
return -1;
}
}
int
macosx_port_valid (mach_port_t port)
{
mach_port_type_t ptype;
kern_return_t ret;
ret = mach_port_type (mach_task_self (), port, &ptype);
return (ret == KERN_SUCCESS);
}
int
macosx_task_valid (task_t task)
{
kern_return_t ret;
struct task_basic_info info;
unsigned int info_count = TASK_BASIC_INFO_COUNT;
ret = task_info (task, TASK_BASIC_INFO, (task_info_t) & info, &info_count);
return (ret == KERN_SUCCESS);
}
int
macosx_thread_valid (task_t task, thread_t thread)
{
thread_array_t thread_list;
unsigned int thread_count;
kern_return_t kret;
unsigned int found = 0;
unsigned int i;
CHECK_FATAL (task != TASK_NULL);
kret = task_threads (task, &thread_list, &thread_count);
if ((kret == KERN_INVALID_ARGUMENT)
|| (kret == MACH_SEND_INVALID_RIGHT) || (kret == MACH_RCV_INVALID_NAME))
{
return 0;
}
MACH_CHECK_ERROR (kret);
for (i = 0; i < thread_count; i++)
{
if (thread_list[i] == thread)
{
found = 1;
}
}
kret = vm_deallocate (mach_task_self (), (vm_address_t) thread_list,
(vm_size_t) (thread_count * sizeof (thread_t)));
MACH_CHECK_ERROR (kret);
if (!found)
{
mutils_debug ("thread 0x%lx no longer valid for task 0x%lx\n",
(unsigned long) thread, (unsigned long) task);
}
return found;
}
int
macosx_pid_valid (int pid)
{
int ret;
ret = kill (pid, 0);
mutils_debug ("kill (%d, 0) : ret = %d, errno = %d (%s)\n", pid,
ret, errno, strerror (errno));
return ((ret == 0) || ((errno != ESRCH) && (errno != ECHILD)));
}
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);
}
void
mach_warn_error (kern_return_t ret, const char *file,
unsigned int line, const char *func)
{
if (ret == KERN_SUCCESS)
{
return;
}
if (func == NULL)
{
func = "[UNKNOWN]";
}
warning ("error on line %u of \"%s\" in function \"%s\": %s (0x%ux)",
line, file, func, MACH_ERROR_STRING (ret), ret);
}
thread_t
macosx_primary_thread_of_task (task_t task)
{
thread_array_t thread_list;
unsigned int thread_count;
thread_t tret = THREAD_NULL;
kern_return_t ret;
CHECK_FATAL (task != TASK_NULL);
ret = task_threads (task, &thread_list, &thread_count);
MACH_CHECK_ERROR (ret);
tret = thread_list[0];
ret = vm_deallocate (mach_task_self (), (vm_address_t) thread_list,
(vm_size_t) (thread_count * sizeof (thread_t)));
MACH_CHECK_ERROR (ret);
return tret;
}
kern_return_t
macosx_msg_receive (mach_msg_header_t * msgin, size_t msg_size,
unsigned long timeout, mach_port_t port)
{
kern_return_t kret;
mach_msg_option_t options;
mutils_debug ("macosx_msg_receive: waiting for message\n");
options = MACH_RCV_MSG;
if (timeout > 0)
{
options |= MACH_RCV_TIMEOUT;
}
kret = mach_msg (msgin, options, 0, msg_size, port,
timeout, MACH_PORT_NULL);
if (mutils_debugflag)
{
if (kret == KERN_SUCCESS)
{
macosx_debug_message (msgin);
}
else
{
mutils_debug ("macosx_msg_receive: returning %s (0x%lx)\n",
MACH_ERROR_STRING (kret), kret);
}
}
return kret;
}
CORE_ADDR
allocate_space_in_inferior_mach (int len)
{
kern_return_t kret;
vm_address_t address;
kret = vm_allocate (macosx_status->task, &address, len, TRUE);
if (kret != KERN_SUCCESS)
error ("No memory available to program: call to vm_allocate failed");
return address;
}
struct macosx_alloc_data
{
CORE_ADDR addr;
int len;
};
static int
macosx_allocate_space_in_inferior_helper (struct ui_out *ui_out, void *args)
{
struct macosx_alloc_data *alloc = (struct macosx_alloc_data *) args;
alloc->addr = allocate_space_in_inferior_malloc (alloc->len);
return 0;
}
CORE_ADDR
macosx_allocate_space_in_inferior (int len)
{
int ret;
struct macosx_alloc_data alloc;
struct ui_file *saved_gdb_stderr;
struct ui_out *null_uiout = NULL;
struct cleanup *cleanups;
null_uiout = cli_out_new (gdb_null);
if (null_uiout == NULL)
error ("Unable to allocate memory: unable to allocate null uiout.");
cleanups = make_cleanup_ui_out_delete (null_uiout);
saved_gdb_stderr = gdb_stderr;
gdb_stderr = gdb_null;
alloc.len = len;
alloc.addr = 0;
ret = catch_exceptions (null_uiout, macosx_allocate_space_in_inferior_helper,
&alloc, RETURN_MASK_ALL);
do_cleanups (cleanups);
gdb_stderr = saved_gdb_stderr;
if (ret >= 0)
return alloc.addr;
alloc.addr = allocate_space_in_inferior_mach (len);
return alloc.addr;
}
void
_initialize_macosx_mutils ()
{
mutils_stderr = fdopen (fileno (stderr), "w+");
add_setshow_boolean_cmd ("mutils", class_obscure,
&mutils_debugflag, _("\
Set if printing inferior memory debugging statements."), _("\
Show if printing inferior memory debugging statements."), NULL,
NULL, NULL,
&setdebuglist, &showdebuglist);
}