macosx-nat-utils.c [plain text]
#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 "environ.h"
#include "event-top.h"
#include "event-loop.h"
#include "inf-loop.h"
#include "gdb_stat.h"
#include "gdb_assert.h"
#include "exceptions.h"
#include "checkpoint.h"
#include "objc-lang.h"
#include "bfd.h"
#include "macosx-nat-inferior-debug.h"
#include <unistd.h>
#include <signal.h>
#include <string.h>
#include <ctype.h>
#include <CoreFoundation/CFURLAccess.h>
#include <CoreFoundation/CFPropertyList.h>
#include "macosx-nat-utils.h"
#include "macosx-nat-dyld.h"
static const char *make_info_plist_path (const char *bundle,
const char *bundle_suffix,
const char *plist_bundle_path);
char *
macosx_filename_in_bundle (const char *filename, int mainline)
{
const char *info_plist_filename = NULL;
char *full_pathname = NULL;
const void *plist = NULL;
int shallow_bundle = 0;
if (!mainline)
return NULL;
info_plist_filename = make_info_plist_path (filename, ".app", "Info.plist");
if (info_plist_filename)
plist = macosx_parse_plist (info_plist_filename);
if (plist == NULL)
{
info_plist_filename = make_info_plist_path (filename, ".xpc", "Info.plist");
if (info_plist_filename)
plist = macosx_parse_plist (info_plist_filename);
}
if (plist != NULL)
{
shallow_bundle = 1;
}
else
{
xfree ((char *) info_plist_filename);
info_plist_filename = make_info_plist_path (filename, ".app",
"Contents/Info.plist");
if (info_plist_filename)
plist = macosx_parse_plist (info_plist_filename);
if (plist == NULL)
{
info_plist_filename = make_info_plist_path (filename, ".xpc", "Contents/Info.plist");
if (info_plist_filename)
plist = macosx_parse_plist (info_plist_filename);
}
}
if (plist != NULL)
{
const char *bundle_exe_from_plist;
bundle_exe_from_plist = macosx_get_plist_posix_value (plist,
"CFBundleExecutable");
macosx_free_plist (&plist);
if (bundle_exe_from_plist != NULL)
{
int info_plist_dir_len = strlen (info_plist_filename) -
strlen ("Info.plist");
int full_pathname_length = info_plist_dir_len +
strlen (bundle_exe_from_plist) + 1;
if (!shallow_bundle)
full_pathname_length += strlen ("MacOS/");
full_pathname = xmalloc (full_pathname_length);
if (full_pathname)
{
memcpy (full_pathname, info_plist_filename, info_plist_dir_len);
full_pathname[info_plist_dir_len] = '\0';
if (!shallow_bundle)
strcat (full_pathname, "MacOS/");
strcat (full_pathname, bundle_exe_from_plist);
gdb_assert ( strlen(full_pathname) + 1 == full_pathname_length );
}
xfree ((char *) bundle_exe_from_plist);
}
}
if (info_plist_filename)
xfree ((char *) info_plist_filename);
return full_pathname;
}
static const char *
make_info_plist_path (const char *bundle, const char *bundle_suffix,
const char *plist_bundle_path)
{
char plist_path[PATH_MAX];
char plist_realpath[PATH_MAX];
char *bundle_suffix_pos = NULL;
char *t = NULL;
int bundle_suffix_len = strlen (bundle_suffix);
for (t = strstr (bundle, bundle_suffix); t != NULL;
t = strstr (t+1, bundle_suffix))
bundle_suffix_pos = t;
if (bundle_suffix_pos != NULL && bundle_suffix_pos > bundle)
{
int bundle_dir_len = (bundle_suffix_pos - bundle) + bundle_suffix_len;
int info_plist_len = bundle_dir_len + 1 + strlen (plist_bundle_path) + 1;
if (info_plist_len < PATH_MAX)
{
memcpy (plist_path, bundle, bundle_dir_len);
plist_path[bundle_dir_len] = '/';
plist_path[bundle_dir_len+1] = '\0';
strcat (plist_path, plist_bundle_path);
gdb_assert ( strlen(plist_path) + 1 == info_plist_len );
if (realpath (plist_path, plist_realpath) == NULL)
return xstrdup (plist_path);
else
return xstrdup (plist_realpath);
}
}
return NULL;
}
const void *
macosx_parse_plist (const char *path)
{
CFPropertyListRef plist = NULL;
const char url_header[] = "file://";
char *url_text = NULL;
CFURLRef url = NULL;
CFAllocatorRef cf_alloc = kCFAllocatorDefault;
size_t url_text_len = (sizeof (url_header) - 1) + strlen (path) + 1;
url_text = xmalloc (url_text_len);
strcpy (url_text, url_header);
strcat (url_text, path);
url = CFURLCreateWithBytes (cf_alloc, (const UInt8 *)url_text,
url_text_len, kCFStringEncodingUTF8, NULL);
if (url)
{
CFDataRef data = NULL;
if (CFURLCreateDataAndPropertiesFromResource (cf_alloc, url, &data,
NULL, NULL,NULL)
&& data != NULL)
{
plist = CFPropertyListCreateFromXMLData (cf_alloc, data,
kCFPropertyListImmutable,
NULL);
CFRelease (data);
if (plist != NULL)
{
if (CFGetTypeID (plist) != CFDictionaryGetTypeID ())
{
CFRelease (plist);
plist = NULL;
}
}
}
CFRelease (url);
}
xfree (url_text);
return plist;
}
const char *
macosx_get_plist_posix_value (const void *plist, const char* key)
{
char *value = NULL;
if (plist == NULL)
return NULL;
CFStringRef cf_key = CFStringCreateWithCString (kCFAllocatorDefault, key,
kCFStringEncodingUTF8);
CFStringRef cf_value = CFDictionaryGetValue ((CFDictionaryRef) plist, cf_key);
if (cf_value != NULL && CFGetTypeID (cf_value) == CFStringGetTypeID ())
{
CFIndex max_value_len = CFStringGetMaximumSizeOfFileSystemRepresentation
(cf_value);
if (max_value_len > 0)
{
value = (char *) xmalloc (max_value_len + 1);
if (value)
{
if (!CFStringGetFileSystemRepresentation (cf_value, value,
max_value_len))
{
xfree (value);
value = NULL;
}
}
}
}
if (cf_key)
CFRelease (cf_key);
return value;
}
const char *
macosx_get_plist_string_value (const void *plist, const char* key)
{
char *value = NULL;
if (plist == NULL)
return NULL;
CFStringRef cf_key = CFStringCreateWithCString (kCFAllocatorDefault, key,
kCFStringEncodingUTF8);
CFStringRef cf_value = CFDictionaryGetValue ((CFDictionaryRef) plist, cf_key);
if (cf_value != NULL && CFGetTypeID (cf_value) == CFStringGetTypeID ())
{
CFIndex max_value_len = CFStringGetLength (cf_value);
max_value_len = CFStringGetMaximumSizeForEncoding (max_value_len,
kCFStringEncodingUTF8);
if (max_value_len > 0)
{
value = xmalloc (max_value_len + 1);
if (value)
{
if (!CFStringGetCString (cf_value, value, max_value_len,
kCFStringEncodingUTF8))
{
xfree (value);
value = NULL;
}
}
}
}
if (cf_key)
CFRelease (cf_key);
return value;
}
void
macosx_free_plist (const void **plist)
{
if (*plist != NULL)
{
CFRelease ((CFPropertyListRef)*plist);
*plist = NULL;
}
}
void
macosx_print_extra_stop_info (int code, CORE_ADDR address)
{
ui_out_text (uiout, "Reason: ");
switch (code)
{
case KERN_PROTECTION_FAILURE:
ui_out_field_string (uiout, "access-reason", "KERN_PROTECTION_FAILURE");
break;
case KERN_INVALID_ADDRESS:
ui_out_field_string (uiout, "access-reason", "KERN_INVALID_ADDRESS");
break;
#if defined (TARGET_ARM)
case 0x101:
ui_out_field_string (uiout, "access-reason", "EXC_ARM_DA_ALIGN");
break;
case 0x102:
ui_out_field_string (uiout, "access-reason", "EXC_ARM_DA_DEBUG");
break;
#endif
default:
ui_out_field_int (uiout, "access-reason", code);
}
ui_out_text (uiout, " at address: ");
ui_out_field_core_addr (uiout, "address", address);
ui_out_text (uiout, "\n");
}
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);
}
static int malloc_unsafe_flag = -1;
static void
do_reset_malloc_unsafe_flag (void *unused)
{
malloc_unsafe_flag = -1;
}
static int
macosx_check_malloc_is_unsafe ()
{
static struct cached_value *malloc_check_fn = NULL;
struct cleanup *scheduler_cleanup;
struct value *tmp_value = NULL;
struct gdb_exception e;
int success;
if (malloc_unsafe_flag != -1)
return malloc_unsafe_flag;
if (malloc_check_fn == NULL)
{
if (lookup_minimal_symbol("malloc_gdb_po_unsafe", 0, 0))
{
struct type *func_type;
func_type = builtin_type_int;
func_type = lookup_function_type (func_type);
func_type = lookup_pointer_type (func_type);
malloc_check_fn = create_cached_function ("malloc_gdb_po_unsafe",
func_type);
}
else
return -1;
}
if (debug_handcall_setup)
printf_unfiltered ("Overriding debugger mode to call malloc check function.\n");
scheduler_cleanup = make_cleanup_set_restore_scheduler_locking_mode
(scheduler_locking_on);
make_cleanup_set_restore_debugger_mode (NULL, 0);
make_cleanup_set_restore_unwind_on_signal (1);
TRY_CATCH (e, RETURN_MASK_ALL)
{
tmp_value = call_function_by_hand (lookup_cached_function (malloc_check_fn),
0, NULL);
}
do_cleanups (scheduler_cleanup);
if (e.reason != NO_ERROR)
return 1;
success = value_as_long (tmp_value);
if (success == 0 || success == 1)
{
malloc_unsafe_flag = success;
make_hand_call_cleanup (do_reset_malloc_unsafe_flag, 0);
return success;
}
else
{
warning ("Got unexpected value from malloc_gdb_po_unsafe: %d.", success);
return 1;
}
return -1;
}
enum {
MALLOC_SUBSYSTEM_INDEX = 0,
LOADER_SUBSYSTEM_INDEX = 1,
OBJC_SUBSYSTEM_INDEX = 2,
SPINLOCK_SUBSYSTEM_INDEX = 3,
LAST_SUBSYSTEM_INDEX = 4,
};
static char *macosx_unsafe_regexes[] = {"(^(m|c|re|v)?alloca*)|(::[^ ]*allocator)|(^szone_)",
"(^dlopen)|(^__dyld)|(^dyld)|(NSBundle load)|"
"(NSBundle unload)|(CFBundleLoad)|(CFBundleUnload)",
"(_class_lookup)|(^objc_lookUpClass)|(^look_up_class)",
"(^__spin_lock)|(^pthread_mutex_lock)|(^pthread_mutex_unlock)|(^__spin_unlock)"};
int
macosx_check_safe_call (int which, enum check_which_threads thread_mode)
{
int retval = 1;
regex_t unsafe_patterns[LAST_SUBSYSTEM_INDEX];
int num_unsafe_patterns = 0;
int depth = 0;
static regex_t macosx_unsafe_patterns[LAST_SUBSYSTEM_INDEX];
static int patterns_initialized = 0;
if (!patterns_initialized)
{
int i;
patterns_initialized = 1;
for (i = 0; i < LAST_SUBSYSTEM_INDEX; i++)
{
int err_code;
err_code = regcomp (&(macosx_unsafe_patterns[i]),
macosx_unsafe_regexes[i],
REG_EXTENDED|REG_NOSUB);
if (err_code != 0)
{
char err_str[512];
regerror (err_code, &(macosx_unsafe_patterns[i]),
err_str, 512);
internal_error (__FILE__, __LINE__,
"Couldn't compile unsafe call pattern %s, error %s",
macosx_unsafe_regexes[i], err_str);
}
}
}
if (which & MALLOC_SUBSYSTEM)
{
int malloc_unsafe;
if (macosx_get_malloc_inited () == 0)
{
ui_out_text (uiout, "Unsafe to run code: ");
ui_out_field_string (uiout, "problem", "malloc library is not initialized yet");
ui_out_text (uiout, ".\n");
return 0;
}
if (thread_mode == CHECK_CURRENT_THREAD
|| (thread_mode == CHECK_SCHEDULER_VALUE && !scheduler_lock_on_p ()))
malloc_unsafe = -1;
else
malloc_unsafe = macosx_check_malloc_is_unsafe ();
if (malloc_unsafe == 1)
{
ui_out_text (uiout, "Unsafe to run code: ");
ui_out_field_string (uiout, "problem", "malloc zone lock is held for some zone.");
ui_out_text (uiout, ".\n");
return 0;
}
else if (malloc_unsafe == -1)
{
unsafe_patterns[num_unsafe_patterns]
= macosx_unsafe_patterns[MALLOC_SUBSYSTEM_INDEX];
num_unsafe_patterns++;
if (depth < 5)
depth = 5;
}
}
if (which & OBJC_SUBSYSTEM)
{
struct cleanup *runtime_cleanup;
enum objc_debugger_mode_result objc_retval = objc_debugger_mode_unknown;
if (thread_mode == CHECK_ALL_THREADS
|| (thread_mode == CHECK_SCHEDULER_VALUE && scheduler_lock_on_p ()))
{
objc_retval = make_cleanup_set_restore_debugger_mode (&runtime_cleanup, 1);
do_cleanups (runtime_cleanup);
if (objc_retval == objc_debugger_mode_success)
{
return 1;
}
}
if (thread_mode == CHECK_CURRENT_THREAD
|| (thread_mode == CHECK_SCHEDULER_VALUE && !scheduler_lock_on_p ())
|| objc_retval == objc_debugger_mode_fail_objc_api_unavailable)
{
if (new_objc_runtime_internals ())
{
struct objfile *libobjc_objfile;
libobjc_objfile = find_libobjc_objfile ();
if (libobjc_objfile != NULL)
{
struct frame_info *fi;
fi = get_current_frame ();
if (!fi)
{
warning ("Cancelling operation - can't find base frame of "
"the current thread to determine whether it is safe.");
return 0;
}
while (frame_relative_level (fi) < 5)
{
struct obj_section *obj_sect = find_pc_section (get_frame_pc (fi));
if (obj_sect == NULL || obj_sect->objfile == libobjc_objfile)
{
warning ("Cancelling call - objc code on the current "
"thread's stack makes this unsafe.");
return 0;
}
fi = get_prev_frame (fi);
if (fi == NULL)
break;
}
}
}
else
{
unsafe_patterns[num_unsafe_patterns]
= macosx_unsafe_patterns[OBJC_SUBSYSTEM_INDEX];
num_unsafe_patterns++;
if (depth < 5)
depth = 5;
}
}
else
{
ui_out_text (uiout, "Unsafe to run code: ");
ui_out_field_string (uiout, "problem", "objc runtime lock is held");
ui_out_text (uiout, ".\n");
return 0;
}
}
if (which & LOADER_SUBSYSTEM)
{
struct minimal_symbol *dyld_lock_p;
int got_it_easy = 0;
dyld_lock_p = lookup_minimal_symbol ("_dyld_global_lock_held", 0, 0);
if (dyld_lock_p != NULL)
{
ULONGEST locked;
if (safe_read_memory_unsigned_integer (SYMBOL_VALUE_ADDRESS (dyld_lock_p),
4, &locked))
{
got_it_easy = 1;
if (locked == 1)
return 0;
}
}
if (!got_it_easy)
{
unsafe_patterns[num_unsafe_patterns]
= macosx_unsafe_patterns[LOADER_SUBSYSTEM_INDEX];
num_unsafe_patterns++;
if (depth < 5)
depth = 5;
}
}
if (which & SPINLOCK_SUBSYSTEM)
{
unsafe_patterns[num_unsafe_patterns]
= macosx_unsafe_patterns[SPINLOCK_SUBSYSTEM_INDEX];
num_unsafe_patterns++;
if (depth < 1)
depth = 1;
}
if (num_unsafe_patterns > 0)
{
retval = check_safe_call (unsafe_patterns, num_unsafe_patterns, depth,
thread_mode);
}
return retval;
}
#ifndef RTLD_LAZY
#define RTLD_LAZY 0x1
#define RTLD_NOW 0x2
#define RTLD_LOCAL 0x4
#define RTLD_GLOBAL 0x8
#if !defined(_POSIX_C_SOURCE) || defined(_DARWIN_C_SOURCE)
#define RTLD_NOLOAD 0x10
#define RTLD_NODELETE 0x80
#define RTLD_FIRST 0x100
#endif
#endif
static struct cached_value *dlerror_function;
struct value *
macosx_load_dylib (char *name, char *flags)
{
struct cleanup *debugger_mode_cleanup;
struct cleanup *sched_cleanup;
static struct cached_value *dlopen_function = NULL;
struct value *arg_val[2];
struct value *ret_val;
int int_flags;
enum objc_debugger_mode_result objc_retval;
if (!macosx_check_safe_call (LOADER_SUBSYSTEM, CHECK_ALL_THREADS))
error ("Cannot call into the loader at present, it is locked.");
if (dlopen_function == NULL)
{
if (lookup_minimal_symbol ("dlopen", 0, 0))
{
dlopen_function = create_cached_function ("dlopen",
builtin_type_voidptrfuncptr);
}
}
if (dlopen_function == NULL)
error ("Can't find dlopen function, so it is not possible to load shared libraries.");
if (dlerror_function == NULL)
{
if (lookup_minimal_symbol ("dlerror", 0, 0))
{
dlerror_function = create_cached_function ("dlerror",
builtin_type_voidptrfuncptr);
}
}
int_flags = 0;
if (flags != NULL)
{
if (strstr (flags, "RTLD_LAZY") != NULL)
int_flags |= RTLD_LAZY;
if (strstr (flags, "RTLD_NOW") != NULL)
int_flags |= RTLD_NOW;
if (strstr (flags, "RTLD_LOCAL") != NULL)
int_flags |= RTLD_LOCAL;
if (strstr (flags, "RTLD_GLOBAL") != NULL)
int_flags |= RTLD_GLOBAL;
if (strstr (flags, "RTLD_NOLOAD") != NULL)
int_flags |= RTLD_NOLOAD;
if (strstr (flags, "RTLD_NODELETE") != NULL)
int_flags |= RTLD_NODELETE;
if (strstr (flags, "RTLD_FIRST") != NULL)
int_flags |= RTLD_FIRST;
}
if (int_flags == 0)
int_flags = RTLD_GLOBAL|RTLD_NOW;
arg_val[1] = value_from_longest (builtin_type_int, int_flags);
do_hand_call_cleanups (ALL_CLEANUPS);
sched_cleanup = make_cleanup_set_restore_scheduler_locking_mode (scheduler_locking_on);
objc_retval = make_cleanup_set_restore_debugger_mode (&debugger_mode_cleanup, -1);
if (objc_retval == objc_debugger_mode_fail_objc_api_unavailable)
if (target_check_safe_call (OBJC_SUBSYSTEM, CHECK_SCHEDULER_VALUE))
objc_retval = objc_debugger_mode_success;
if (objc_retval != objc_debugger_mode_success)
error ("Not safe to call dlopen at this time.");
arg_val[0] = value_coerce_array (value_string (name, strlen (name) + 1));
ret_val = call_function_by_hand (lookup_cached_function (dlopen_function),
2, arg_val);
do_cleanups (debugger_mode_cleanup);
do_cleanups (sched_cleanup);
do_hand_call_cleanups (ALL_CLEANUPS);
if (ret_val != NULL)
{
CORE_ADDR dlopen_token;
dlopen_token = value_as_address (ret_val);
if (dlopen_token == 0)
{
char *error_str;
int error_str_len;
int read_error;
CORE_ADDR error_addr;
struct cleanup *scheduler_cleanup;
if (dlerror_function == NULL)
error ("dlopen got an error, but dlerror isn't available to report the error.");
scheduler_cleanup =
make_cleanup_set_restore_scheduler_locking_mode (scheduler_locking_on);
ret_val = call_function_by_hand (lookup_cached_function (dlerror_function),
0, NULL);
error_addr = value_as_address (ret_val);
error_str_len = target_read_string (error_addr, &error_str, INT_MAX,
&read_error);
if (read_error == 0)
{
make_cleanup (xfree, error_str);
error ("Error calling dlopen for: \"%s\": \"%s\"", name, error_str);
}
else
error ("Error calling dlopen for \"%s\", could not fetch error string.",
name);
}
else
{
ui_out_field_core_addr (uiout, "handle", value_as_address (ret_val));
if (info_verbose)
printf_unfiltered("Return token was: %s.\n", paddr_nz (value_as_address (ret_val)));
}
}
else if (info_verbose)
printf_unfiltered("Return value was NULL.\n");
return ret_val;
}