#include "defs.h"
#include "inferior.h"
#include "target.h"
#include "symfile.h"
#include "symtab.h"
#include "objfiles.h"
#include "gdbcmd.h"
#include "language.h"
#include "block.h"
#include "libaout.h"
#include "aout/aout64.h"
#include "complaints.h"
#include "mach-o.h"
#include "objc-lang.h"
#include "macosx-tdep.h"
#include "regcache.h"
#include "source.h"
#include "completer.h"
#include "exceptions.h"
#include "gdbcmd.h"
#include "gdbcore.h"
#include "exec.h"
#include <dirent.h>
#include <libgen.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/param.h>
#include <mach/machine.h>
#include <mach/kmod.h>
#include <CoreFoundation/CoreFoundation.h>
#include <CoreFoundation/CFPropertyList.h>
#include "readline/tilde.h"
#include "arch-utils.h"
#include "macosx-nat-utils.h"
#include "x86-shared-tdep.h"
#include "osabi.h"
#include <mach-o/loader.h>
#include "macosx-nat-dyld.h" // for target_read_mach_header()
#if defined(TARGET_POWERPC)
#include "ppc-tdep.h"
#elif defined (TARGET_I386)
#include "amd64-tdep.h"
#include "i386-tdep.h"
#elif defined (TARGET_ARM)
#include "arm-tdep.h"
#else
#error "Unrecognized target architecture."
#endif
#include "gdbarch.h"
int disable_aslr_flag = 1;
CORE_ADDR kernel_slide = INVALID_ADDRESS;
static char *find_info_plist_filename_from_bundle_name (const char *bundle,
const char *bundle_suffix);
#if USE_DEBUG_SYMBOLS_FRAMEWORK
extern CFArrayRef DBGCopyMatchingUUIDsForURL (CFURLRef path,
int cpuType,
int cpuSubtype);
extern CFURLRef DBGCopyDSYMURLForUUID (CFUUIDRef uuid);
extern CFDictionaryRef DBGCopyDSYMPropertyLists (CFURLRef dsym_url);
#endif
static void get_uuid_t_for_uuidref (CFUUIDRef uuid_in, uuid_t *uuid_out);
static const char dsym_extension[] = ".dSYM";
static const char dsym_bundle_subdir[] = "Contents/Resources/DWARF/";
static const int dsym_extension_len = (sizeof (dsym_extension) - 1);
static const int dsym_bundle_subdir_len = (sizeof (dsym_bundle_subdir) - 1);
static int dsym_locate_enabled = 1;
static int kaslr_memory_search_enabled = 1;
#define APPLE_DSYM_EXT_AND_SUBDIRECTORY ".dSYM/Contents/Resources/DWARF/"
int
actually_do_stack_frame_prologue (unsigned int count_limit,
unsigned int print_start,
unsigned int print_end,
unsigned int wordsize,
unsigned int *count,
struct frame_info **out_fi,
void (print_fun) (struct ui_out * uiout, int *frame_num,
CORE_ADDR pc, CORE_ADDR fp));
enum gdb_osabi osabi_seen_in_attached_dyld = GDB_OSABI_UNKNOWN;
#if 0
struct deprecated_complaint unknown_macho_symtype_complaint =
{ "unknown Mach-O symbol type %s", 0, 0 };
struct deprecated_complaint unknown_macho_section_complaint =
{ "unknown Mach-O section value %s (assuming DATA)", 0, 0 };
struct deprecated_complaint unsupported_indirect_symtype_complaint =
{ "unsupported Mach-O symbol type %s (indirect)", 0, 0 };
#endif
#define BFD_GETB16(addr) ((addr[0] << 8) | addr[1])
#define BFD_GETB32(addr) ((((((uint32_t) addr[0] << 8) | addr[1]) << 8) | addr[2]) << 8 | addr[3])
#define BFD_GETB64(addr) ((((((((((uint64_t) addr[0] << 8) | addr[1]) << 8) | addr[2]) << 8 | addr[3]) << 8 | addr[4]) << 8 | addr[5]) << 8 | addr[6]) << 8 | addr[7])
#define BFD_GETL16(addr) ((addr[1] << 8) | addr[0])
#define BFD_GETL32(addr) ((((((uint32_t) addr[3] << 8) | addr[2]) << 8) | addr[1]) << 8 | addr[0])
#define BFD_GETL64(addr) ((((((((((uint64_t) addr[7] << 8) | addr[6]) << 8) | addr[5]) << 8 | addr[4]) << 8 | addr[3]) << 8 | addr[2]) << 8 | addr[1]) << 8 | addr[0])
unsigned char macosx_symbol_types[256];
static unsigned char
macosx_symbol_type_base (macho_type)
unsigned char macho_type;
{
unsigned char mtype = macho_type;
unsigned char ntype = 0;
if (macho_type & BFD_MACH_O_N_STAB)
{
return macho_type;
}
if (mtype & BFD_MACH_O_N_PEXT)
{
mtype &= ~BFD_MACH_O_N_PEXT;
ntype |= N_EXT;
}
if (mtype & BFD_MACH_O_N_EXT)
{
mtype &= ~BFD_MACH_O_N_EXT;
ntype |= N_EXT;
}
switch (mtype & BFD_MACH_O_N_TYPE)
{
case BFD_MACH_O_N_SECT:
break;
case BFD_MACH_O_N_PBUD:
ntype |= N_UNDF;
break;
case BFD_MACH_O_N_ABS:
ntype |= N_ABS;
break;
case BFD_MACH_O_N_UNDF:
ntype |= N_UNDF;
break;
case BFD_MACH_O_N_INDR:
return macho_type;
default:
return macho_type;
}
mtype &= ~BFD_MACH_O_N_TYPE;
CHECK_FATAL (mtype == 0);
return ntype;
}
static void
macosx_symbol_types_init ()
{
unsigned int i;
for (i = 0; i < 256; i++)
{
macosx_symbol_types[i] = macosx_symbol_type_base (i);
}
}
static unsigned char
macosx_symbol_type (macho_type, macho_sect, abfd)
unsigned char macho_type;
unsigned char macho_sect;
bfd *abfd;
{
unsigned char ntype = macosx_symbol_types[macho_type];
if ((macho_type & BFD_MACH_O_N_TYPE) == BFD_MACH_O_N_SECT)
{
if (macho_sect == 1)
{
ntype |= N_TEXT;
}
else if ((macho_sect > 0)
&& (macho_sect <= abfd->tdata.mach_o_data->nsects))
{
const bfd_mach_o_section *sect =
abfd->tdata.mach_o_data->sections[macho_sect - 1];
if (sect == NULL)
{
}
else if ((sect->segname != NULL)
&& (strcmp (sect->segname, "__DATA") == 0))
{
if ((sect->sectname != NULL)
&& (strcmp (sect->sectname, "__bss") == 0))
ntype |= N_BSS;
else
ntype |= N_DATA;
}
else if ((sect->segname != NULL)
&& (strcmp (sect->segname, "__TEXT") == 0))
{
ntype |= N_TEXT;
}
else
{
ntype |= N_DATA;
}
}
else
{
ntype |= N_DATA;
}
}
return ntype;
}
void
macosx_internalize_symbol (in, sect_p, ext, abfd)
struct internal_nlist *in;
int *sect_p;
struct external_nlist *ext;
bfd *abfd;
{
int symwide = (bfd_mach_o_version (abfd) > 1);
if (bfd_header_big_endian (abfd))
{
in->n_strx = BFD_GETB32 (ext->e_strx);
in->n_desc = BFD_GETB16 (ext->e_desc);
if (symwide)
in->n_value = BFD_GETB64 (ext->e_value);
else
in->n_value = BFD_GETB32 (ext->e_value);
}
else if (bfd_header_little_endian (abfd))
{
in->n_strx = BFD_GETL32 (ext->e_strx);
in->n_desc = BFD_GETL16 (ext->e_desc);
if (symwide)
in->n_value = BFD_GETL64 (ext->e_value);
else
in->n_value = BFD_GETL32 (ext->e_value);
}
else
{
error ("unable to internalize symbol (unknown endianness)");
}
if ((ext->e_type[0] & BFD_MACH_O_N_TYPE) == BFD_MACH_O_N_SECT)
*sect_p = 1;
else
*sect_p = 0;
in->n_type = macosx_symbol_type (ext->e_type[0], ext->e_other[0], abfd);
in->n_other = ext->e_other[0];
}
CORE_ADDR
dyld_symbol_stub_function_address (CORE_ADDR pc, const char **name)
{
struct symbol *sym = NULL;
struct minimal_symbol *msym = NULL;
const char *lname = NULL;
lname = dyld_symbol_stub_function_name (pc);
if (name)
*name = lname;
if (lname == NULL)
return 0;
sym = lookup_symbol_global (lname, lname, VAR_DOMAIN, 0);
if ((sym == NULL) && (lname[0] == '_'))
sym = lookup_symbol_global (lname + 1, lname + 1, VAR_DOMAIN, 0);
if (sym != NULL && SYMBOL_BLOCK_VALUE (sym) != NULL)
return BLOCK_LOWEST_PC (SYMBOL_BLOCK_VALUE (sym));
msym = lookup_minimal_symbol (lname, NULL, NULL);
if ((msym == 0) && (lname[0] == '_'))
msym = lookup_minimal_symbol (lname + 1, NULL, NULL);
if (msym != NULL)
return SYMBOL_VALUE_ADDRESS (msym);
return 0;
}
const char *
dyld_symbol_stub_function_name (CORE_ADDR pc)
{
struct minimal_symbol *msymbol = NULL;
const char *DYLD_PREFIX = "dyld_stub_";
msymbol = lookup_minimal_symbol_by_pc (pc);
if (msymbol == NULL)
return NULL;
if (SYMBOL_VALUE_ADDRESS (msymbol) != pc)
return NULL;
if (strncmp
(SYMBOL_LINKAGE_NAME (msymbol), DYLD_PREFIX, strlen (DYLD_PREFIX)) != 0)
return NULL;
return SYMBOL_LINKAGE_NAME (msymbol) + strlen (DYLD_PREFIX);
}
CORE_ADDR
macosx_skip_trampoline_code (CORE_ADDR pc)
{
CORE_ADDR newpc;
newpc = dyld_symbol_stub_function_address (pc, NULL);
if (newpc != 0)
return newpc;
#if defined (TARGET_I386)
newpc = x86_cxx_virtual_override_thunk_trampline (pc);
if (newpc != 0)
return newpc;
#endif
newpc = decode_fix_and_continue_trampoline (pc);
if (newpc != 0)
return newpc;
return 0;
}
int
macosx_record_symbols_from_sect_p (bfd *abfd, unsigned char macho_type,
unsigned char macho_sect)
{
const bfd_mach_o_section *sect;
if (macho_sect <= 0 || macho_sect > abfd->tdata.mach_o_data->nsects)
{
warning ("Bad symbol - type is N_SECT but section is %d in file '%s'", macho_sect, abfd->filename);
return 0;
}
sect = abfd->tdata.mach_o_data->sections[macho_sect - 1];
if ((sect->flags & BFD_MACH_O_SECTION_TYPE_MASK) ==
BFD_MACH_O_S_SYMBOL_STUBS)
return 0;
else
return 1;
}
int
macosx_in_solib_return_trampoline (CORE_ADDR pc, char *name)
{
return 0;
}
int
macosx_in_solib_call_trampoline (CORE_ADDR pc, char *name)
{
if (macosx_skip_trampoline_code (pc) != 0)
{
return 1;
}
return 0;
}
static void
info_trampoline_command (char *exp, int from_tty)
{
struct expression *expr;
struct value *val;
CORE_ADDR address;
CORE_ADDR trampoline;
CORE_ADDR objc;
expr = parse_expression (exp);
val = evaluate_expression (expr);
if (TYPE_CODE (value_type (val)) == TYPE_CODE_REF)
val = value_ind (val);
if ((TYPE_CODE (value_type (val)) == TYPE_CODE_FUNC)
&& (VALUE_LVAL (val) == lval_memory))
address = VALUE_ADDRESS (val);
else
address = value_as_address (val);
trampoline = macosx_skip_trampoline_code (address);
find_objc_msgcall (trampoline, &objc);
fprintf_filtered
(gdb_stderr, "Function at 0x%s becomes 0x%s becomes 0x%s\n",
paddr_nz (address), paddr_nz (trampoline), paddr_nz (objc));
}
struct sal_chain
{
struct sal_chain *next;
struct symtab_and_line sal;
};
int
macosx_enable_exception_callback (enum exception_event_kind kind, int enable)
{
return 1;
}
struct symtabs_and_lines *
macosx_find_exception_catchpoints (enum exception_event_kind kind,
struct objfile *restrict_objfile)
{
struct symtabs_and_lines *return_sals;
char *symbol_name;
struct objfile *objfile;
struct minimal_symbol *msymbol;
unsigned int hash;
struct sal_chain *sal_chain = 0;
switch (kind)
{
case EX_EVENT_THROW:
symbol_name = "__cxa_throw";
break;
case EX_EVENT_CATCH:
symbol_name = "__cxa_begin_catch";
break;
default:
error ("We currently only handle \"throw\" and \"catch\"");
}
hash = msymbol_hash (symbol_name) % MINIMAL_SYMBOL_HASH_SIZE;
ALL_OBJFILES (objfile)
{
for (msymbol = objfile->msymbol_hash[hash];
msymbol != NULL; msymbol = msymbol->hash_next)
if (MSYMBOL_TYPE (msymbol) == mst_text
&& (strcmp_iw (SYMBOL_LINKAGE_NAME (msymbol), symbol_name) == 0))
{
CORE_ADDR catchpoint_address;
CORE_ADDR past_prologue;
struct sal_chain *next
= (struct sal_chain *) alloca (sizeof (struct sal_chain));
next->next = sal_chain;
init_sal (&next->sal);
next->sal.symtab = NULL;
catchpoint_address = SYMBOL_VALUE_ADDRESS (msymbol);
past_prologue = SKIP_PROLOGUE (catchpoint_address);
next->sal.pc = past_prologue;
next->sal.line = 0;
next->sal.end = past_prologue;
sal_chain = next;
}
}
if (sal_chain)
{
int index = 0;
struct sal_chain *temp;
for (temp = sal_chain; temp != NULL; temp = temp->next)
index++;
return_sals = (struct symtabs_and_lines *)
xmalloc (sizeof (struct symtabs_and_lines));
return_sals->nelts = index;
return_sals->sals =
(struct symtab_and_line *) xmalloc (index *
sizeof (struct symtab_and_line));
for (index = 0; sal_chain; sal_chain = sal_chain->next, index++)
return_sals->sals[index] = sal_chain->sal;
return return_sals;
}
else
return NULL;
}
struct exception_event_record *
macosx_get_current_exception_event ()
{
static struct exception_event_record *exception_event = NULL;
struct frame_info *curr_frame;
struct frame_info *fi;
CORE_ADDR pc;
int stop_func_found;
char *stop_name;
char *typeinfo_str;
if (exception_event == NULL)
{
exception_event = (struct exception_event_record *)
xmalloc (sizeof (struct exception_event_record));
exception_event->exception_type = NULL;
}
curr_frame = get_current_frame ();
if (!curr_frame)
return (struct exception_event_record *) NULL;
pc = get_frame_pc (curr_frame);
stop_func_found = find_pc_partial_function (pc, &stop_name, NULL, NULL);
if (!stop_func_found)
return (struct exception_event_record *) NULL;
if (strcmp (stop_name, "__cxa_throw") == 0)
{
fi = get_prev_frame (curr_frame);
if (!fi)
return (struct exception_event_record *) NULL;
exception_event->throw_sal = find_pc_line (get_frame_pc (fi), 1);
exception_event->catch_sal.pc = 0x0;
exception_event->catch_sal.line = 0;
exception_event->kind = EX_EVENT_THROW;
}
else if (strcmp (stop_name, "__cxa_begin_catch") == 0)
{
fi = get_prev_frame (curr_frame);
if (!fi)
return (struct exception_event_record *) NULL;
exception_event->catch_sal = find_pc_line (get_frame_pc (fi), 1);
exception_event->throw_sal.pc = 0x0;
exception_event->throw_sal.line = 0;
exception_event->kind = EX_EVENT_CATCH;
}
#ifdef THROW_CATCH_FIND_TYPEINFO
typeinfo_str =
THROW_CATCH_FIND_TYPEINFO (curr_frame, exception_event->kind);
#else
typeinfo_str = NULL;
#endif
if (exception_event->exception_type != NULL)
xfree (exception_event->exception_type);
if (typeinfo_str == NULL)
{
exception_event->exception_type = NULL;
}
else
{
exception_event->exception_type = xstrdup (typeinfo_str);
}
return exception_event;
}
void
update_command (char *args, int from_tty)
{
registers_changed ();
reinit_frame_cache ();
}
void
stack_flush_command (char *args, int from_tty)
{
reinit_frame_cache ();
if (from_tty)
printf_filtered ("Stack cache flushed.\n");
}
static void
open_command (char *args, int from_tty)
{
const char *filename = NULL;
const char *fullname = NULL;
struct stat sb;
int line_no = 0;
warning ("open command no longer supported - may be back in a future build.");
return;
if (args == NULL || args[0] == '\0')
{
filename = NULL;
line_no = 0;
}
else
{
char *colon_pos = strrchr (args, ':');
if (colon_pos == NULL)
line_no = 0;
else
{
line_no = atoi (colon_pos + 1);
*colon_pos = '\0';
}
filename = args;
}
if (filename == NULL)
{
struct symtab_and_line cursal = get_current_source_symtab_and_line ();
if (cursal.symtab)
fullname = symtab_to_fullname (cursal.symtab);
else
error ("No currently selected source file available; "
"please specify one.");
line_no = cursal.line + get_lines_to_list () / 2;
}
if (fullname == NULL)
{
struct symtab *s = lookup_symtab (filename);
if (s)
fullname = symtab_to_fullname (s);
else
error ("Filename '%s' not found in this program's debug information.",
filename);
}
if (stat (fullname, &sb) == 0)
filename = fullname;
else
if (stat (filename, &sb) != 0)
error ("File '%s' not found.", filename);
}
static CFUUIDRef
get_uuidref_for_bfd (struct bfd *abfd)
{
uint8_t uuid[16];
if (abfd == NULL)
return NULL;
if (bfd_mach_o_get_uuid (abfd, uuid, sizeof (uuid)))
return CFUUIDCreateWithBytes (kCFAllocatorDefault,
uuid[0], uuid[1], uuid[2], uuid[3], uuid[4], uuid[5],
uuid[6], uuid[7], uuid[8], uuid[9], uuid[10], uuid[11],
uuid[12], uuid[13], uuid[14], uuid[15]);
return NULL;
}
CFUUIDRef
get_uuidref_for_uuid_t (uint8_t *uuid)
{
if (uuid == NULL)
return NULL;
return CFUUIDCreateWithBytes (kCFAllocatorDefault,
uuid[0], uuid[1], uuid[2], uuid[3], uuid[4], uuid[5],
uuid[6], uuid[7], uuid[8], uuid[9], uuid[10], uuid[11],
uuid[12], uuid[13], uuid[14], uuid[15]);
}
static void
get_uuid_t_for_uuidref (CFUUIDRef uuid_in, uuid_t *uuid_out)
{
assert (sizeof (uuid_t) == sizeof (CFUUIDBytes));
CFUUIDBytes ret;
ret = CFUUIDGetUUIDBytes (uuid_in);
memcpy ((uint8_t *) uuid_out, (uint8_t *) &ret, sizeof (uuid_t));
}
static CFMutableArrayRef
gdb_DBGCopyMatchingUUIDsForURL (const char *path)
{
if (path == NULL || path[0] == '\0')
return NULL;
CFAllocatorRef alloc = kCFAllocatorDefault;
CFMutableArrayRef uuid_array = NULL;
struct gdb_exception e;
bfd *abfd = NULL;
TRY_CATCH (e, RETURN_MASK_ERROR)
{
abfd = symfile_bfd_open (path, 0, GDB_OSABI_UNKNOWN);
}
if (abfd == NULL || e.reason == RETURN_ERROR)
return NULL;
if (bfd_check_format (abfd, bfd_archive)
&& strcmp (bfd_get_target (abfd), "mach-o-fat") == 0)
{
bfd *nbfd = NULL;
for (;;)
{
nbfd = bfd_openr_next_archived_file (abfd, nbfd);
if (nbfd == NULL)
break;
if (!bfd_check_format (nbfd, bfd_object)
&& !bfd_check_format (nbfd, bfd_archive))
continue;
CFUUIDRef nbfd_uuid = get_uuidref_for_bfd (nbfd);
if (nbfd_uuid != NULL)
{
if (uuid_array == NULL)
uuid_array = CFArrayCreateMutable(alloc, 0, &kCFTypeArrayCallBacks);
if (uuid_array)
CFArrayAppendValue (uuid_array, nbfd_uuid);
CFRelease (nbfd_uuid);
}
}
bfd_free_cached_info (abfd);
}
else
{
CFUUIDRef abfd_uuid = get_uuidref_for_bfd (abfd);
if (abfd_uuid != NULL)
{
if (uuid_array == NULL)
uuid_array = CFArrayCreateMutable(alloc, 0, &kCFTypeArrayCallBacks);
if (uuid_array)
CFArrayAppendValue (uuid_array, abfd_uuid);
CFRelease (abfd_uuid);
}
}
bfd_close (abfd);
return uuid_array;
}
CFMutableDictionaryRef
create_dsym_uuids_for_path (char *dsym_bundle_path)
{
char path[PATH_MAX];
struct dirent* dp = NULL;
DIR* dirp = NULL;
char* dsym_path = NULL;
CFMutableDictionaryRef paths_and_uuids;
strncpy (path, dsym_bundle_path, sizeof (path));
if (path[sizeof (path) - 1])
return NULL;
int path_len = strlen (path);
if (path_len > 0)
{
if (path[path_len-1] != '/')
{
path[path_len] = '/';
if (path_len + 1 < sizeof (path))
path[++path_len] = '\0';
else
return NULL;
}
}
if (dsym_bundle_subdir_len + 1 > sizeof (path) - path_len)
return NULL;
strncat (path, dsym_bundle_subdir, sizeof (path) - path_len - 1);
if (path[sizeof (path) - 1])
return NULL;
dirp = opendir (path);
if (dirp == NULL)
return NULL;
path_len = strlen (path);
paths_and_uuids = CFDictionaryCreateMutable (kCFAllocatorDefault, 0,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
while (dsym_path == NULL && (dp = readdir (dirp)) != NULL)
{
if (dp->d_type == DT_DIR)
continue;
int full_path_len = path_len + dp->d_namlen + 1;
if (sizeof (path) > full_path_len)
{
CFURLRef path_url = NULL;
CFArrayRef uuid_array = NULL;
CFStringRef path_cfstr = NULL;
strcpy(&path[path_len], dp->d_name);
path_cfstr = CFStringCreateWithCString (NULL, path,
kCFStringEncodingUTF8);
path_url = CFURLCreateWithFileSystemPath (NULL, path_cfstr,
kCFURLPOSIXPathStyle, 0);
CFRelease (path_cfstr), path_cfstr = NULL;
if (path_url == NULL)
continue;
uuid_array = gdb_DBGCopyMatchingUUIDsForURL (path);
if (uuid_array != NULL)
CFDictionarySetValue (paths_and_uuids, path_url, uuid_array);
CFRelease (path_url);
path_url = NULL;
if (uuid_array != NULL)
CFRelease (uuid_array);
uuid_array = NULL;
}
}
closedir (dirp);
if (CFDictionaryGetCount (paths_and_uuids) == 0)
{
CFRelease (paths_and_uuids);
return NULL;
}
else
return paths_and_uuids;
}
struct search_baton
{
CFUUIDRef test_uuid;
int found_it;
CFURLRef path_url;
};
void
paths_and_uuids_map_func (const void *in_url,
const void *in_array,
void *in_results)
{
const CFURLRef path_url = (CFURLRef) in_url;
const CFArrayRef uuid_array = (CFArrayRef) in_array;
struct search_baton *results = (struct search_baton *) in_results;
const CFIndex count = CFArrayGetCount (uuid_array);
CFIndex i;
if (results->found_it)
return;
for (i = 0; i < count; i++)
{
CFUUIDRef tmp_uuid = CFArrayGetValueAtIndex (uuid_array, i);
if (CFEqual (tmp_uuid, results->test_uuid))
{
results->path_url = path_url;
CFRetain (path_url);
results->found_it = 1;
break;
}
}
}
static char *
locate_dsym_mach_in_bundle (CFUUIDRef uuid_ref, char *dsym_bundle_path)
{
CFMutableDictionaryRef paths_and_uuids;
struct search_baton results;
paths_and_uuids = create_dsym_uuids_for_path (dsym_bundle_path);
if (paths_and_uuids == NULL)
return NULL;
results.found_it = 0;
results.test_uuid = uuid_ref;
CFDictionaryApplyFunction (paths_and_uuids, paths_and_uuids_map_func,
&results);
CFRelease (paths_and_uuids);
if (results.found_it)
{
char path[PATH_MAX];
path[PATH_MAX-1] = '\0';
if (CFURLGetFileSystemRepresentation (results.path_url, 1,
(UInt8 *)path, sizeof (path)))
return xstrdup (path);
else
return NULL;
}
else
return NULL;
}
#if USE_DEBUG_SYMBOLS_FRAMEWORK
struct plist_filenames_search_baton {
CFPropertyListRef plist;
int found_it;
CFStringRef searching_for;
};
static void
plist_filenames_and_uuids_map_func (const void *in_key, const void *in_value,
void *context)
{
struct plist_filenames_search_baton *baton = (struct plist_filenames_search_baton *) context;
CFStringRef searching_for = baton->searching_for;
CFStringRef key = (CFStringRef) in_key;
CFPropertyListRef value = (CFPropertyListRef) in_value;
if (CFStringCompare (key, searching_for, 0))
{
baton->found_it = 1;
baton->plist = value;
}
return;
}
static void
find_source_path_mappings (CFUUIDRef uuid, CFURLRef dsym)
{
CFDictionaryRef plists = DBGCopyDSYMPropertyLists (dsym);
CFStringRef uuid_str = CFUUIDCreateString (kCFAllocatorDefault, uuid);
if (plists && uuid_str)
{
struct plist_filenames_search_baton results;
results.found_it = 0;
results.searching_for = uuid_str;
CFDictionaryApplyFunction (plists, plist_filenames_and_uuids_map_func,
&results);
if (results.found_it)
{
const char *build_src_path = macosx_get_plist_posix_value
(results.plist, "DBGBuildSourcePath");
const char *src_path = macosx_get_plist_posix_value
(results.plist, "DBGSourcePath");
if (src_path)
{
const char *src_path_tilde_expanded = tilde_expand (src_path);
xfree ((char *) src_path);
src_path = src_path_tilde_expanded;
}
if (build_src_path && src_path)
{
add_one_pathname_substitution (build_src_path, src_path);
}
}
}
if (uuid_str)
CFRelease (uuid_str);
if (plists)
CFRelease (plists);
}
void
find_source_path_mappings_posix (struct objfile *objfile, const char *dsym)
{
unsigned char uuid[16];
CFURLRef dsym_ref;
CFStringRef dsym_str_ref;
if (!bfd_mach_o_get_uuid (objfile->obfd, uuid, sizeof (uuid)))
return;
CFUUIDRef uuid_ref = CFUUIDCreateWithBytes (kCFAllocatorDefault, uuid[0],
uuid[1], uuid[2], uuid[3],
uuid[4], uuid[5], uuid[6],
uuid[7], uuid[8], uuid[9],
uuid[10], uuid[11], uuid[12],
uuid[13], uuid[14], uuid[15]);
if (uuid_ref == NULL)
return;
const char *j, *i = strstr (dsym, ".dSYM");
if (i == NULL)
{
CFRelease (uuid_ref);
return;
}
while ((j = strstr (i + 1, ".dSYM")) != NULL)
i = j;
if (i[5] != '\0')
{
i += 5;
int len = i - dsym + 1;
char *n = alloca (len);
strlcpy (n, dsym, len);
dsym = n;
}
dsym_str_ref = CFStringCreateWithCString (NULL, dsym,
kCFStringEncodingUTF8);
if (dsym_str_ref == NULL)
{
CFRelease (uuid_ref);
return;
}
dsym_ref = CFURLCreateWithFileSystemPath (NULL, dsym_str_ref,
kCFURLPOSIXPathStyle, 0);
CFRelease (dsym_str_ref);
if (dsym_ref == NULL)
{
CFRelease (uuid_ref);
return;
}
find_source_path_mappings (uuid_ref, dsym_ref);
CFRelease (uuid_ref);
CFRelease (dsym_ref);
}
static char *
locate_dsym_using_framework (struct objfile *objfile)
{
CFURLRef dsym_bundle_url = NULL;
char* dsym_path = NULL;
CFUUIDRef uuid_ref = get_uuidref_for_bfd (objfile->obfd);
if (uuid_ref == NULL)
return NULL;
dsym_bundle_url = DBGCopyDSYMURLForUUID (uuid_ref);
if (dsym_bundle_url)
{
char path[PATH_MAX];
path[PATH_MAX-1] = '\0';
if (CFURLGetFileSystemRepresentation (dsym_bundle_url, 1,
(UInt8 *)path, sizeof (path)))
{
char *dsym_ext = strcasestr (path, dsym_extension);
int search_bundle_dir = ((dsym_ext == NULL) ||
(dsym_ext[dsym_extension_len] == '\0') ||
(dsym_ext[dsym_extension_len] == '/' &&
dsym_ext[dsym_extension_len+1] == '\0'));
if (search_bundle_dir)
{
dsym_path = locate_dsym_mach_in_bundle (uuid_ref, path);
}
else
{
dsym_path = xstrdup (path);
}
}
find_source_path_mappings (uuid_ref, dsym_bundle_url);
CFRelease (dsym_bundle_url);
dsym_bundle_url = NULL;
}
CFRelease (uuid_ref);
uuid_ref = NULL;
return dsym_path;
}
#endif
char *
macosx_locate_dsym (struct objfile *objfile)
{
char *basename_str;
char *dot_ptr;
char *slash_ptr;
char *dsymfile;
const char *executable_name;
if (objfile->symflags != OBJF_SYM_ALL)
return NULL;
if (objfile->not_loaded_kext_filename != NULL)
executable_name = objfile->not_loaded_kext_filename;
else
executable_name = objfile->name;
if (strcasestr (executable_name, ".dSYM") == NULL)
{
basename_str = basename ((char *) executable_name);
dsymfile = alloca (strlen (executable_name)
+ strlen (APPLE_DSYM_EXT_AND_SUBDIRECTORY)
+ strlen (basename_str)
+ 1);
strcpy (dsymfile, executable_name);
strcat (dsymfile, APPLE_DSYM_EXT_AND_SUBDIRECTORY);
strcat (dsymfile, basename_str);
if (file_exists_p (dsymfile))
{
#if USE_DEBUG_SYMBOLS_FRAMEWORK
find_source_path_mappings_posix (objfile, dsymfile);
#endif
return xstrdup (dsymfile);
}
strcpy (dsymfile, dirname ((char *) executable_name));
strcat (dsymfile, "/");
while ((dot_ptr = strrchr (dsymfile, '.')))
{
slash_ptr = strchr (dot_ptr, '/');
if (slash_ptr)
{
*slash_ptr = '\0';
strcat (slash_ptr, APPLE_DSYM_EXT_AND_SUBDIRECTORY);
strcat (slash_ptr, basename_str);
if (file_exists_p (dsymfile))
{
#if USE_DEBUG_SYMBOLS_FRAMEWORK
find_source_path_mappings_posix (objfile, dsymfile);
#endif
return xstrdup (dsymfile);
}
}
*dot_ptr = '\0';
strcat (dot_ptr, APPLE_DSYM_EXT_AND_SUBDIRECTORY);
strcat (dot_ptr, basename_str);
if (file_exists_p (dsymfile))
{
#if USE_DEBUG_SYMBOLS_FRAMEWORK
find_source_path_mappings_posix (objfile, dsymfile);
#endif
return xstrdup (dsymfile);
}
*dot_ptr = '\0';
slash_ptr = strrchr (dsymfile, '/');
if (!slash_ptr)
break;
*slash_ptr = '\0';
}
#if USE_DEBUG_SYMBOLS_FRAMEWORK
if (dsym_locate_enabled)
return locate_dsym_using_framework (objfile);
#endif
}
return NULL;
}
int
dir_exists_p (const char *dir)
{
struct stat sb;
return (stat (dir, &sb) == 0) && S_ISDIR (sb.st_mode);
}
char *
strtrunc (char *str, const char *substr)
{
char *match;
u_long len;
match = strcasestr (str, substr);
if (!match)
return NULL;
char *best_match = match;
while (match && *match != '\0' && *(match + 1) != '\0')
{
match = strcasestr (match + 1, substr);
if (match)
best_match = match;
}
len = (best_match - str) + strlen (substr);
str[len] = '\0';
return str;
}
static char *
get_dbg_shell_command ()
{
CFTypeRef shell_cmd = CFPreferencesCopyAppValue (CFSTR ("DBGShellCommands"), CFSTR ("com.apple.DebugSymbols"));
if (shell_cmd == NULL || CFGetTypeID (shell_cmd) != CFStringGetTypeID())
{
if (shell_cmd)
CFRelease (shell_cmd);
return NULL;
}
char shell_cmd_cstr[PATH_MAX];
if (!CFStringGetCString ((CFStringRef) shell_cmd, shell_cmd_cstr, sizeof (shell_cmd_cstr), kCFStringEncodingUTF8))
return NULL;
CFRelease (shell_cmd);
if (file_exists_p (shell_cmd_cstr))
return xstrdup (shell_cmd_cstr);
return NULL;
}
char *
expand_kext_cstr (const char *kext_path)
{
char pathbuf[PATH_MAX];
if (file_exists_p (kext_path))
return xstrdup (kext_path);
const char *tilde_expanded_path = tilde_expand (kext_path);
if (file_exists_p (tilde_expanded_path))
return xstrdup (tilde_expanded_path);
strlcpy (pathbuf, tilde_expanded_path, sizeof (pathbuf));
char real_path[PATH_MAX];
real_path[0] = '\0';
if (realpath (kext_path, real_path))
{
if (file_exists_p (real_path))
return xstrdup (real_path);
}
return xstrdup (kext_path);
}
char *
macosx_locate_executable_by_dbg_shell_command (CFStringRef uuid)
{
if (uuid == NULL)
return NULL;
char *shell_cmd_cstr = get_dbg_shell_command ();
if (shell_cmd_cstr == NULL)
return NULL;
char uuid_cstr[80];
if (!CFStringGetCString (uuid, uuid_cstr, sizeof (uuid_cstr), kCFStringEncodingUTF8))
return NULL;
char command[PATH_MAX];
strlcpy (command, shell_cmd_cstr, sizeof (command));
strlcat (command, " ", sizeof (command));
strlcat (command, uuid_cstr, sizeof (command));
int data_buffer_size = 80 * PATH_MAX;
char *data_buffer = (char *) xmalloc (data_buffer_size);
data_buffer[0] = '\0';
FILE *input = popen (command, "r");
if (input == NULL)
return NULL;
char one_line[PATH_MAX];
while (fgets (one_line, sizeof (one_line), input) != NULL)
strlcat (data_buffer, one_line, data_buffer_size);
pclose (input);
data_buffer[data_buffer_size - 1] = '\0';
CFDataRef plist_data = CFDataCreate (kCFAllocatorDefault, (const UInt8 *) data_buffer, strlen (data_buffer) + 1);
if (plist_data == NULL)
return NULL;
CFPropertyListRef plist = CFPropertyListCreateWithData (kCFAllocatorDefault, plist_data, kCFPropertyListImmutable, NULL, NULL);
if (plist == NULL || CFGetTypeID (plist) != CFDictionaryGetTypeID())
{
CFRelease (plist_data);
xfree (data_buffer);
xfree (shell_cmd_cstr);
return NULL;
}
CFDictionaryRef per_arch_kv = CFDictionaryGetValue (plist, uuid);
if (per_arch_kv == NULL || CFGetTypeID (per_arch_kv) != CFDictionaryGetTypeID())
{
CFRelease (plist_data);
xfree (data_buffer);
xfree (shell_cmd_cstr);
return NULL;
}
CFStringRef sym_rich_exe = CFDictionaryGetValue (per_arch_kv, CFSTR ("DBGSymbolRichExecutable"));
if (sym_rich_exe == NULL || CFGetTypeID (sym_rich_exe) != CFStringGetTypeID())
{
CFRelease (plist_data);
xfree (data_buffer);
xfree (shell_cmd_cstr);
return NULL;
}
char tempbuf[PATH_MAX];
char *return_path = NULL;
if (CFStringGetCString ((CFStringRef) sym_rich_exe, tempbuf, sizeof (tempbuf), kCFStringEncodingUTF8))
return_path = xstrdup (tempbuf);
CFRelease (plist_data);
xfree (data_buffer);
xfree (shell_cmd_cstr);
char *possibly_expanded_path = expand_kext_cstr (return_path);
if (possibly_expanded_path != return_path)
{
xfree (return_path);
return_path = possibly_expanded_path;
}
return return_path;
}
const CFStringRef kSymbolRichExecutable = CFSTR("DBGSymbolRichExecutable");
char *
locate_kext_executable_by_dsym_plist (CFDictionaryRef dsym_info, CFUUIDRef uuid_ref)
{
CFDictionaryRef uuid_info = NULL;
CFStringRef kext_path = NULL;
CFStringRef uuid_string = NULL;
CFStringRef alt_exe_path = NULL;
char *path = NULL;
uuid_string = CFUUIDCreateString(kCFAllocatorDefault, uuid_ref);
if (!uuid_string)
{
warning ("could not convert CFUUIDRef into string");
goto finish;
}
uuid_info = CFDictionaryGetValue(dsym_info, uuid_string);
if (!uuid_info)
{
warning ("could not find UUID key in dSYM Info.plist");
goto finish;
}
kext_path = CFDictionaryGetValue (uuid_info, kSymbolRichExecutable);
if (!kext_path || CFGetTypeID (kext_path) != CFStringGetTypeID())
{
char *alt_exe_path = macosx_locate_executable_by_dbg_shell_command (uuid_string);
if (alt_exe_path)
{
CFRelease (uuid_string);
char *final_path = expand_kext_cstr (alt_exe_path);
xfree (alt_exe_path);
return final_path;
}
}
if (!kext_path || CFGetTypeID (kext_path) != CFStringGetTypeID())
{
warning ("could not find DBGSymbolRichExecutable key in dSYM Info.plist");
goto finish;
}
char temp_pathbuf[PATH_MAX];
if (!CFStringGetFileSystemRepresentation (kext_path, temp_pathbuf, sizeof (temp_pathbuf)))
goto finish;
path = expand_kext_cstr (temp_pathbuf);
if (!file_exists_p (path))
{
char *alt_exe_path = macosx_locate_executable_by_dbg_shell_command (uuid_string);
if (alt_exe_path)
{
char *expanded_path = expand_kext_cstr (alt_exe_path);
xfree (alt_exe_path);
if (file_exists_p (expanded_path))
{
if (path)
xfree (path);
CFRelease (uuid_string);
return expanded_path;
}
xfree (expanded_path);
}
}
if (!file_exists_p (path))
{
warning ("No kext binary found at '%s' from dSYM's Info.plist", path);
}
finish:
if (uuid_string)
CFRelease (uuid_string);
return path;
}
char *
locate_kext_executable_by_dsym_url (CFURLRef dsym_url)
{
char * result = NULL;
char path[PATH_MAX];
const char * identifier_name = NULL;
const char * exec_name = NULL;
char * bundle_path = NULL;
char * tmp;
if (!CFURLGetFileSystemRepresentation (dsym_url, 1, (UInt8 *) path, sizeof(path)))
{
goto finish;
}
tmp = strtrunc (path, ".kext");
if (!tmp)
{
goto finish;
}
if (!dir_exists_p (path))
{
if (*path != '\0')
warning ("No kext at path '%s'", path);
goto finish;
}
bundle_path = macosx_kext_info (path, &exec_name, &identifier_name);
if (!bundle_path)
{
goto finish;
}
strlcat (path, "/Contents/MacOS/", sizeof(path));
strlcat (path, exec_name, sizeof(path));
if (!file_exists_p (path))
{
strtrunc (path, ".kext");
strlcat (path, "/", sizeof(path));
strlcat (path, exec_name, sizeof(path));
if (!file_exists_p (path))
{
goto finish;
}
}
result = xstrdup (path);
finish:
if (bundle_path)
xfree (bundle_path);
if (exec_name)
xfree ((char *) exec_name);
if (identifier_name)
xfree ((char *) identifier_name);
return result;
}
static char *
look_for_kext_binary_next_to_dsym (const char *dsym_path, CFUUIDRef kext_uuid)
{
if (dsym_path == NULL || kext_uuid == NULL || dsym_path[0] == '\0')
return NULL;
if (strlen (dsym_path) < 11)
return NULL;
char kextpath[PATH_MAX];
strlcpy (kextpath, dsym_path, sizeof (kextpath));
if (strcmp (kextpath + strlen (kextpath) - 10, ".kext.dSYM") != 0)
return NULL;
kextpath[strlen (kextpath) - 10] = '\0';
if (kextpath[0] == '\0' || !file_exists_p (kextpath))
return NULL;
uint8_t uuid[16];
get_uuid_t_for_uuidref (kext_uuid, &uuid);
uint8_t **file_uuids = get_binary_file_uuids (kextpath);
uint8_t **i;
if (file_uuids == NULL)
return NULL;
for (i = file_uuids ; *i != NULL; i++)
{
if (memcmp (*i, uuid, sizeof (uuid_t)) == 0)
{
free_uuids_array (file_uuids);
return xstrdup (kextpath);
}
}
free_uuids_array (file_uuids);
return NULL;
}
static char *
macosx_locate_kext_executable_by_symfile_helper (CFUUIDRef kext_uuid,
const char *kext_name)
{
#if USE_DEBUG_SYMBOLS_FRAMEWORK
char *result = NULL;
CFUUIDBytes kext_uuid_bytes;
CFDictionaryRef dsym_info = NULL;
CFURLRef dsym_url = NULL;
char *kext_executable_name = NULL;
bfd *kext_executable_bfd = NULL;
CFUUIDRef kext_executable_uuid = NULL;
CFUUIDBytes kext_executable_uuid_bytes;
uuid_t uuid;
get_uuid_t_for_uuidref (kext_uuid, &uuid);
dsym_url = DBGCopyDSYMURLForUUID (kext_uuid);
if (!dsym_url)
{
const char *basep = strrchr (kext_name, '/');
const char *name = kext_name;
if (basep && *basep != '\0' && *(basep + 1) != '\0')
name = ++basep;
warning ("Can't find dSYM for %s (%s)", name, puuid (uuid));
goto finish;
}
char dsym_path[PATH_MAX];
if (dsym_url == NULL
|| CFGetTypeID (dsym_url) != CFURLGetTypeID()
|| !CFURLGetFileSystemRepresentation (dsym_url, 1, (UInt8 *) dsym_path, sizeof (dsym_path)))
{
dsym_path[0] = '\0';
}
char *kext_next_to_dsym = look_for_kext_binary_next_to_dsym (dsym_path, kext_uuid);
if (file_exists_p (kext_next_to_dsym))
{
if (dsym_url)
CFRelease (dsym_url);
return kext_next_to_dsym;
}
dsym_info = DBGCopyDSYMPropertyLists (dsym_url);
if (dsym_info)
{
kext_executable_name = locate_kext_executable_by_dsym_plist (dsym_info, kext_uuid);
}
else
{
kext_executable_name = locate_kext_executable_by_dsym_url (dsym_url);
}
if (!kext_executable_name)
{
char path[PATH_MAX];
if (CFURLGetFileSystemRepresentation (dsym_url, 1, (UInt8 *) path, sizeof (path)))
{
path[sizeof (path) - 1] = '\0';
warning ("Unable to locate symbol-rich-executable for dSYM at %s (%s)",
path, puuid (uuid));
}
goto finish;
}
if (!file_exists_p (kext_executable_name))
{
warning ("The needed symbol-rich-executable at '%s' does not exist.",
kext_executable_name);
goto finish;
}
kext_executable_bfd = symfile_bfd_open (kext_executable_name, 0, GDB_OSABI_UNKNOWN);
if (!kext_executable_bfd)
{
warning ("Unable to open symbol-rich-executable for reading at '%s'.",
kext_executable_name);
goto finish;
}
kext_executable_uuid = get_uuidref_for_bfd (kext_executable_bfd);
if (!kext_executable_uuid)
goto finish;
kext_uuid_bytes = CFUUIDGetUUIDBytes (kext_uuid);
kext_executable_uuid_bytes = CFUUIDGetUUIDBytes (kext_executable_uuid);
if (memcmp (&kext_uuid_bytes, &kext_executable_uuid_bytes,
sizeof (CFUUIDBytes)))
{
goto finish;
}
result = kext_executable_name;
kext_executable_name = NULL;
finish:
if (dsym_url)
CFRelease (dsym_url);
if (dsym_info)
CFRelease (dsym_info);
if (kext_executable_name)
xfree (kext_executable_name);
if (kext_executable_bfd)
bfd_close (kext_executable_bfd);
if (kext_executable_uuid)
CFRelease (kext_executable_uuid);
return result;
#else
warning("DebugSymbols framework unavailable. Can't locate kext bundle and dSYM.");
return NULL;
#endif
}
char *
macosx_locate_kext_executable_by_symfile (bfd *abfd)
{
if (abfd == NULL)
return NULL;
CFUUIDRef symfile_uuid = get_uuidref_for_bfd (abfd);
if (symfile_uuid == NULL)
return NULL;
char *ret;
ret = macosx_locate_kext_executable_by_symfile_helper (symfile_uuid,
abfd->filename);
CFRelease (symfile_uuid);
return ret;
}
struct objfile *
macosx_find_objfile_matching_dsym_in_bundle (char *dsym_bundle_path, char **out_full_path)
{
CFMutableDictionaryRef paths_and_uuids;
struct search_baton results;
struct objfile *objfile;
struct objfile *out_objfile = NULL;
paths_and_uuids = create_dsym_uuids_for_path (dsym_bundle_path);
if (paths_and_uuids == NULL)
return NULL;
results.found_it = 0;
*out_full_path = NULL;
ALL_OBJFILES (objfile)
{
CFUUIDRef uuid_ref = get_uuidref_for_bfd (objfile->obfd);
if (uuid_ref == NULL)
continue;
results.test_uuid = uuid_ref;
CFDictionaryApplyFunction (paths_and_uuids, paths_and_uuids_map_func,
&results);
CFRelease (uuid_ref);
if (results.found_it)
{
*out_full_path = xmalloc (PATH_MAX);
*(*out_full_path) = '\0';
if (CFURLGetFileSystemRepresentation (results.path_url, 1,
(UInt8 *) (*out_full_path), PATH_MAX - 1))
{
out_objfile = objfile;
}
else
{
warning ("Could not get file system representation for URL:");
CFShow (results.path_url);
*out_full_path = NULL;
out_objfile = NULL;
}
CFRelease (results.path_url);
goto cleanup_and_return;
}
}
cleanup_and_return:
CFRelease (paths_and_uuids);
return out_objfile;
}
char *
macosx_kext_info (const char *filename,
const char **bundle_executable_name_from_plist,
const char **bundle_identifier_name_from_plist)
{
char *info_plist_name;
char *t;
*bundle_executable_name_from_plist = NULL;
*bundle_identifier_name_from_plist = NULL;
const void *plist = NULL;
info_plist_name = find_info_plist_filename_from_bundle_name
(filename, ".kext");
if (info_plist_name == NULL)
return NULL;
plist = macosx_parse_plist (info_plist_name);
*bundle_executable_name_from_plist = macosx_get_plist_posix_value (plist,
"CFBundleExecutable");
*bundle_identifier_name_from_plist = macosx_get_plist_string_value (plist,
"CFBundleIdentifier");
macosx_free_plist (&plist);
t = strstr (info_plist_name, "/Contents");
if (t != NULL)
t[0] = '\0';
t = strstr (info_plist_name, "/Info.plist");
if (t != NULL)
t[0] = '\0';
if (*bundle_executable_name_from_plist == NULL
|| *bundle_identifier_name_from_plist == NULL)
return NULL;
else
return info_plist_name;
}
static char *
find_info_plist_filename_from_bundle_name (const char *bundle,
const char *bundle_suffix)
{
char *t;
char *bundle_copy;
char tmp_path[PATH_MAX];
char realpath_buf[PATH_MAX];
char *retval = NULL;
bundle_copy = tilde_expand (bundle);
tmp_path[0] = '\0';
t = strstr (bundle_copy, bundle_suffix);
char *best_t = t;
while (t)
{
t = strstr (t + 1, bundle_suffix);
if (t)
best_t = t;
}
t = best_t;
if (t != NULL && t > bundle_copy)
{
t += strlen (bundle_suffix);
if (t[0] == '/')
{
strncpy (tmp_path, bundle_copy, t - bundle_copy);
tmp_path[t - bundle_copy] = '\0';
}
}
t = strstr (bundle_copy, bundle_suffix);
best_t = t;
while (t)
{
t = strstr (t + 1, bundle_suffix);
if (t)
best_t = t;
}
t = best_t;
if (t != NULL && t > bundle_copy && t[strlen (bundle_suffix)] == '\0')
{
strcpy (tmp_path, bundle_copy);
}
if (tmp_path[0] == '\0')
{
if (bundle && *bundle != '\0')
warning ("No Info.plist found under %s", bundle);
return NULL;
}
strcpy (realpath_buf, tmp_path);
strcat (realpath_buf, "/Contents/Info.plist");
if (file_exists_p (realpath_buf))
{
retval = realpath_buf;
}
else
{
strcpy (realpath_buf, tmp_path);
strcat (realpath_buf, "/Info.plist");
if (file_exists_p (realpath_buf))
{
retval = realpath_buf;
}
}
if (retval == NULL)
{
if (*tmp_path != '\0')
warning ("No Info.plist found under %s", tmp_path);
return retval;
}
tmp_path[0] = '\0';
if (realpath (realpath_buf, tmp_path) == NULL)
retval = xstrdup (realpath_buf);
else
retval = xstrdup (tmp_path);
xfree (bundle_copy);
return retval;
}
extern const bfd_arch_info_type *bfd_default_compatible
(const bfd_arch_info_type *a, const bfd_arch_info_type *b);
static enum gdb_osabi
generic_mach_o_osabi_sniffer_use_dyld_hint (bfd *abfd,
enum bfd_architecture arch,
unsigned long mach_32,
unsigned long mach_64)
{
if (osabi_seen_in_attached_dyld == GDB_OSABI_UNKNOWN)
return GDB_OSABI_UNKNOWN;
bfd *nbfd = NULL;
for (;;)
{
nbfd = bfd_openr_next_archived_file (abfd, nbfd);
if (nbfd == NULL)
break;
if (bfd_check_format (nbfd, bfd_archive))
return GDB_OSABI_UNKNOWN;
if (!bfd_check_format (nbfd, bfd_object))
continue;
if (bfd_default_compatible (bfd_get_arch_info (nbfd),
bfd_lookup_arch (arch,
mach_64))
&& osabi_seen_in_attached_dyld == GDB_OSABI_DARWIN64)
return GDB_OSABI_DARWIN64;
else if (bfd_default_compatible (bfd_get_arch_info (nbfd),
bfd_lookup_arch (arch,
mach_32))
&& osabi_seen_in_attached_dyld == GDB_OSABI_DARWIN)
return GDB_OSABI_DARWIN;
}
return GDB_OSABI_UNKNOWN;
}
enum gdb_osabi
generic_mach_o_osabi_sniffer (bfd *abfd, enum bfd_architecture arch,
unsigned long mach_32,
unsigned long mach_64,
int (*query_64_bit_fn) ())
{
enum gdb_osabi ret;
ret = generic_mach_o_osabi_sniffer_use_dyld_hint (abfd, arch, mach_32, mach_64);
if (ret == GDB_OSABI_DARWIN64 || ret == GDB_OSABI_DARWIN)
return ret;
if (bfd_check_format (abfd, bfd_archive))
{
bfd *nbfd = NULL;
if (strcmp (bfd_get_target (abfd), "mach-o-fat") == 0)
{
enum gdb_osabi best = GDB_OSABI_UNKNOWN;
enum gdb_osabi cur = GDB_OSABI_UNKNOWN;
for (;;)
{
nbfd = bfd_openr_next_archived_file (abfd, nbfd);
if (nbfd == NULL)
break;
if (!bfd_check_format (nbfd, bfd_object)
&& !bfd_check_format (nbfd, bfd_archive))
continue;
cur = generic_mach_o_osabi_sniffer (nbfd, arch,
mach_32, mach_64,
query_64_bit_fn);
if (cur == GDB_OSABI_DARWIN64 &&
best != GDB_OSABI_DARWIN64 && query_64_bit_fn ())
best = cur;
if (cur == GDB_OSABI_DARWIN
&& best != GDB_OSABI_DARWIN64
&& best != GDB_OSABI_DARWIN)
best = cur;
}
return best;
}
else
{
for (;;)
{
nbfd = bfd_openr_next_archived_file (abfd, nbfd);
if (nbfd == NULL)
break;
if (!bfd_check_format (nbfd, bfd_object))
continue;
return generic_mach_o_osabi_sniffer (nbfd, arch,
mach_32, mach_64,
query_64_bit_fn);
}
}
}
if (!bfd_check_format (abfd, bfd_object))
return GDB_OSABI_UNKNOWN;
if (bfd_get_arch (abfd) == arch)
{
if (bfd_default_compatible (bfd_get_arch_info (abfd),
bfd_lookup_arch (arch,
mach_64)))
return GDB_OSABI_DARWIN64;
if (bfd_default_compatible (bfd_get_arch_info (abfd),
bfd_lookup_arch (arch,
mach_32)))
return GDB_OSABI_DARWIN;
return GDB_OSABI_UNKNOWN;
}
return GDB_OSABI_UNKNOWN;
}
int
fast_show_stack_trace_prologue (unsigned int count_limit,
unsigned int print_start,
unsigned int print_end,
unsigned int wordsize,
CORE_ADDR *sigtramp_start_ptr,
CORE_ADDR *sigtramp_end_ptr,
unsigned int *count,
struct frame_info **out_fi,
void (print_fun) (struct ui_out * uiout, int *frame_num,
CORE_ADDR pc, CORE_ADDR fp))
{
ULONGEST pc = 0;
struct frame_id selected_frame_id;
struct frame_info *selected_frame;
if (*sigtramp_start_ptr == 0)
{
char *name;
struct minimal_symbol *msymbol;
struct objfile *ofile;
struct objfile *temp;
ALL_OBJFILES_SAFE (ofile, temp)
{
struct objfile *libsystem_objfile = NULL;
if (ofile->name == NULL
|| (strstr (ofile->name, "libSystem.B.dylib") == NULL
&& strstr (ofile->name, "/usr/lib/system") != ofile->name))
continue;
if (ofile->msymbols == NULL
&& ofile->separate_debug_objfile_backlink)
libsystem_objfile = ofile->separate_debug_objfile_backlink;
else
libsystem_objfile = ofile;
if (!target_check_is_objfile_loaded (libsystem_objfile))
continue;
objfile_set_load_state (libsystem_objfile, OBJF_SYM_ALL, 1);
msymbol = lookup_minimal_symbol ("_sigtramp", NULL, libsystem_objfile);
if (msymbol != NULL)
{
pc = SYMBOL_VALUE_ADDRESS (msymbol);
if (*sigtramp_start_ptr != 0 && *sigtramp_start_ptr != (CORE_ADDR) -1)
{
warning ("Found two versions of sigtramp, one at 0x%s and one at 0x%s."
" Using the latter.",
paddr_nz (*sigtramp_start_ptr), paddr_nz (pc));
}
if (find_pc_partial_function (pc, &name, sigtramp_start_ptr,
sigtramp_end_ptr) == 0)
{
warning
("Couldn't find minimal bounds for \"_sigtramp\" - "
"backtraces may be unreliable");
*sigtramp_start_ptr = (CORE_ADDR) -1;
*sigtramp_end_ptr = (CORE_ADDR) -1;
}
else
break;
}
}
}
selected_frame_id = get_frame_id (get_selected_frame (NULL));
flush_cached_frames ();
selected_frame = frame_find_by_id (selected_frame_id);
if (selected_frame == NULL)
select_frame (get_current_frame ());
else
select_frame (selected_frame);
actually_do_stack_frame_prologue (count_limit,
print_start,
print_end,
wordsize,
count,
out_fi,
NULL);
return actually_do_stack_frame_prologue (count_limit,
print_start,
print_end,
wordsize,
count,
out_fi,
print_fun);
}
int
actually_do_stack_frame_prologue (unsigned int count_limit,
unsigned int print_start,
unsigned int print_end,
unsigned int wordsize,
unsigned int *count,
struct frame_info **out_fi,
void (print_fun) (struct ui_out * uiout, int *frame_num,
CORE_ADDR pc, CORE_ADDR fp))
{
CORE_ADDR fp;
ULONGEST pc;
struct frame_info *fi = NULL;
int more_frames;
int old_load_state;
start_again:
if (print_fun)
ui_out_begin (uiout, ui_out_type_list, "frames");
more_frames = 1;
pc = 0;
fi = get_current_frame ();
if (fi == NULL)
{
more_frames = -1;
goto count_finish;
}
old_load_state = pc_set_load_state (get_frame_pc (fi), OBJF_SYM_ALL, 0);
if (old_load_state >= 0 && old_load_state != OBJF_SYM_ALL && print_fun == NULL)
{
flush_cached_frames ();
goto start_again;
}
int frames_printed = 0;
if (print_fun && 0 >= print_start && 0 < print_end)
print_fun (uiout, &frames_printed, get_frame_pc (fi), get_frame_base (fi));
frames_printed++;
if (frames_printed > 1)
{
int j;
for (j = 0; j < (frames_printed - 1) && fi != NULL; j++)
fi = get_prev_frame (fi);
if (fi == NULL)
{
more_frames = 0;
goto count_finish;
}
}
do
{
if (frames_printed >= count_limit)
{
more_frames = 0;
goto count_finish;
}
fi = get_prev_frame (fi);
if (fi == NULL)
{
more_frames = 0;
goto count_finish;
}
pc = get_frame_pc (fi);
fp = get_frame_base (fi);
old_load_state = pc_set_load_state (pc, OBJF_SYM_ALL, 0);
if (old_load_state >= 0 && old_load_state != OBJF_SYM_ALL && print_fun == NULL)
{
flush_cached_frames ();
goto start_again;
}
if (print_fun && frames_printed >= print_start && frames_printed < print_end)
print_fun (uiout, &frames_printed, pc, fp);
frames_printed++;
int j;
for (j = frame_relative_level (fi); j < (frames_printed - 1) && fi != NULL; j++)
fi = get_prev_frame (fi);
if (fi == NULL)
{
more_frames = 0;
goto count_finish;
}
if (!backtrace_past_main
&& inside_main_func (fi)
&& get_frame_type (fi) != INLINED_FRAME)
{
more_frames = 0;
goto count_finish;
}
}
while (frames_printed < 5);
count_finish:
*out_fi = fi;
*count = frames_printed;
return more_frames;
}
struct loaded_kext_info {
char name[KMOD_MAX_NAME];
uuid_t uuid;
uint64_t address;
};
struct loaded_kexts_table {
uint32_t version;
uint32_t entry_size; uint32_t count;
struct loaded_kext_info *kexts;
};
struct loaded_kexts_table *
get_list_of_loaded_kexts ()
{
struct loaded_kexts_table *kext_table;
ULONGEST val;
struct minimal_symbol *msym = lookup_minimal_symbol ("gLoadedKextSummaries", NULL, NULL);
if (msym == NULL)
return NULL;
if (!safe_read_memory_unsigned_integer (SYMBOL_VALUE_ADDRESS (msym), TARGET_PTR_BIT / 8, &val))
return NULL;
if (val == 0)
error ("gLoadedKextSummaries has an address of 0x0 - you must be attached to a live kernel or debugging with a core file.");
kext_table = (struct loaded_kexts_table*) xmalloc (sizeof (struct loaded_kexts_table));
if (kext_table == NULL)
return NULL;
CORE_ADDR p = val;
if (!safe_read_memory_unsigned_integer (p, 4, &val))
{
xfree (kext_table);
return NULL;
}
kext_table->version = (uint32_t) val;
p += sizeof (uint32_t);
if (kext_table->version == 1)
{
kext_table->entry_size = 112;
}
else
{
if (!safe_read_memory_unsigned_integer (p, 4, &val))
{
xfree (kext_table);
return NULL;
}
kext_table->entry_size = (uint32_t) val;
p += sizeof (uint32_t);
}
if (!safe_read_memory_unsigned_integer (p, 4, &val))
{
xfree (kext_table);
return NULL;
}
kext_table->count = (uint32_t) val;
p += sizeof (uint32_t);
if (kext_table->version > 1)
p += sizeof (uint32_t);
if (kext_table->count == 0 || kext_table->count > 65535)
return NULL;
uint8_t *tmpbuf = (uint8_t*) xmalloc (kext_table->entry_size * kext_table->count);
kext_table->kexts = (struct loaded_kext_info *) xmalloc
(sizeof (struct loaded_kext_info) * kext_table->count);
if (kext_table->kexts == NULL || tmpbuf == NULL)
{
xfree (kext_table);
xfree (tmpbuf);
error ("Unable to allocate space to load kext infos.");
}
if (target_read_memory (p, (uint8_t *) tmpbuf,
kext_table->entry_size * kext_table->count))
{
xfree (tmpbuf);
xfree (kext_table->kexts);
xfree (kext_table);
error ("Unable to read kext infos from kernel.");
}
uint8_t *raw_kext_p = tmpbuf;
int i;
for (i = 0; i < kext_table->count; i++)
{
uint8_t *start_of_kext_entry = raw_kext_p;
strlcpy ((char *) kext_table->kexts[i].name, (char *) raw_kext_p, KMOD_MAX_NAME);
raw_kext_p += KMOD_MAX_NAME;
memcpy (kext_table->kexts[i].uuid, raw_kext_p, sizeof (uuid_t));
raw_kext_p += sizeof (uuid_t);
kext_table->kexts[i].address = (uint64_t) extract_unsigned_integer (raw_kext_p, 8);
raw_kext_p = start_of_kext_entry + kext_table->entry_size;
}
xfree (tmpbuf);
return kext_table;
}
void
free_list_of_loaded_kexts (struct loaded_kexts_table *lks)
{
if (lks && lks->kexts)
xfree (lks->kexts);
if (lks)
xfree (lks);
}
struct section_addr_info *
get_section_addresses_for_macho_in_memory (CORE_ADDR mh_addr)
{
struct section_addr_info *addrs = NULL;
if (!get_information_about_macho (NULL, mh_addr, NULL, 0, 1, NULL, NULL, NULL, NULL, NULL, &addrs))
return NULL;
return addrs;
}
struct section_addr_info *
get_section_addrs_of_macho_on_disk (const char *filename)
{
struct section_addr_info *addrs;
if (!get_information_about_macho (filename, 0, NULL, 0, 0, NULL, NULL, NULL, NULL, NULL, &addrs))
addrs = NULL;
return addrs;
}
struct section_addr_info *
get_section_addresses_for_bfd (bfd *abfd)
{
struct bfd_section *sect;
int section_count = 0;
for (sect = abfd->sections; sect; sect = sect->next)
if (sect->name)
section_count++;
struct section_addr_info *sect_addrs = alloc_section_addr_info (section_count);
sect_addrs->num_sections = section_count;
sect_addrs->addrs_are_offsets = 0;
int sectnum = 0;
for (sect = abfd->sections; sect; sect = sect->next)
{
sect_addrs->other[sectnum].sectindex = sect->index;
sect_addrs->other[sectnum].name = xstrdup (sect->name);
sect_addrs->other[sectnum].addr = sect->vma;
sectnum++;
}
return sect_addrs;
}
CORE_ADDR
get_load_addr_of_macho_on_disk (const char *filename, enum gdb_osabi osabi)
{
if (filename == NULL || filename[0] == '\0' || !file_exists_p (filename))
return INVALID_ADDRESS;
bfd *abfd = symfile_bfd_open_safe (filename, 0, osabi);
if (abfd == NULL)
return INVALID_ADDRESS;
CORE_ADDR addr = INVALID_ADDRESS;
if (!get_information_about_macho (NULL, 0, abfd, 0, 0, NULL, NULL, NULL, &addr, NULL, NULL))
addr = INVALID_ADDRESS;
bfd_close (abfd);
return addr;
}
int
get_information_about_macho (const char *filename, CORE_ADDR mh_addr, bfd *abfd,
int require_kernel, int force_live_memory_reads,
uuid_t *uuid, enum gdb_osabi *osabi,
int *wordsize, CORE_ADDR *intended_load_address, CORE_ADDR *slide,
struct section_addr_info **addrs)
{
bfd *mem_bfd = NULL;
struct cleanup *bfd_cleanups = make_cleanup (null_cleanup, NULL);
struct mach_header h;
if (mh_addr == 0)
mh_addr = INVALID_ADDRESS;
int file_exists = file_exists_p (filename);
if (file_exists == 0
&& mh_addr == INVALID_ADDRESS
&& abfd == NULL)
{
return 0;
}
if (file_exists && (mh_addr != INVALID_ADDRESS || abfd != NULL))
{
filename = NULL;
file_exists = 0;
}
if (mh_addr != INVALID_ADDRESS && abfd != NULL)
{
mh_addr = INVALID_ADDRESS;
}
if (file_exists)
{
abfd = symfile_bfd_open_safe (filename, 0, gdbarch_osabi (current_gdbarch));
if (abfd == NULL)
return 0;
make_cleanup_bfd_close (abfd);
filename = NULL;
file_exists = 0;
}
if (abfd == NULL)
{
if (mh_addr == 0 || mh_addr == INVALID_ADDRESS)
return 0;
if (force_live_memory_reads)
{
make_cleanup (set_trust_readonly_cleanup, (void *) set_trust_readonly (0));
make_cleanup (set_only_read_from_live_memory_cleanup, (void *) set_only_read_from_live_memory (1));
}
if (target_read_mach_header (mh_addr, &h) != 0)
return 0;
if (h.magic != MH_MAGIC && h.magic != MH_MAGIC_64)
return 0;
if (h.sizeofcmds > 15000) return 0;
int header_size = target_get_mach_header_size (&h);
gdb_byte *buf = (gdb_byte *) xmalloc (h.sizeofcmds + header_size);
make_cleanup (xfree, buf);
if (target_read_memory (mh_addr, buf, h.sizeofcmds + header_size))
{
do_cleanups (bfd_cleanups);
return 0;
}
mem_bfd = bfd_memopenr ("tempbfd", NULL, buf, h.sizeofcmds + header_size);
if (mem_bfd == NULL)
{
do_cleanups (bfd_cleanups);
return 0;
}
make_cleanup_bfd_close (mem_bfd);
}
else
{
mem_bfd = abfd;
}
if (bfd_check_format (mem_bfd, bfd_object) == 0)
{
do_cleanups (bfd_cleanups);
return 0;
}
if (require_kernel && !bfd_mach_o_kernel_image (mem_bfd))
{
do_cleanups (bfd_cleanups);
return 0;
}
if (uuid)
{
uuid_t mem_uuid;
if (bfd_mach_o_get_uuid (mem_bfd, (unsigned char *) &mem_uuid, sizeof (uuid_t)))
{
memcpy (uuid, mem_uuid, sizeof (uuid_t));
}
}
if (osabi)
{
*osabi = gdbarch_lookup_osabi_from_bfd (mem_bfd);
}
if (wordsize)
{
if (h.magic == MH_MAGIC)
*wordsize = 4;
else
*wordsize = 8;
}
if ((intended_load_address || slide) && bfd_get_section_by_name (mem_bfd, TEXT_SEGMENT_NAME))
{
CORE_ADDR addr = bfd_section_vma (mem_bfd, bfd_get_section_by_name (mem_bfd, TEXT_SEGMENT_NAME));
if (intended_load_address)
*intended_load_address = addr;
if (slide && mh_addr != INVALID_ADDRESS && mh_addr != 0)
*slide = mh_addr - addr;
}
if (addrs)
{
*addrs = get_section_addresses_for_bfd (mem_bfd);
}
do_cleanups (bfd_cleanups);
return 1;
}
struct section_addr_info *
macosx_get_kext_sect_addrs_from_kernel (const char *filename,
uint8_t **kext_uuids,
const char *kext_bundle_ident)
{
struct loaded_kexts_table *loaded_kexts = get_list_of_loaded_kexts ();
if (loaded_kexts == NULL)
return NULL;
CORE_ADDR mh_addr = INVALID_ADDRESS;
int found_match;
int i;
for (found_match = 0, i = 0; i < loaded_kexts->count && found_match == 0; i++)
{
int j = 0;
while (kext_uuids[j] != 0)
{
if (memcmp (kext_uuids[j], loaded_kexts->kexts[i].uuid, sizeof (uuid_t)) == 0)
{
mh_addr = loaded_kexts->kexts[i].address;
found_match = 1;
break;
}
j++;
}
}
if (mh_addr == INVALID_ADDRESS)
return NULL;
free_list_of_loaded_kexts (loaded_kexts);
return get_section_addresses_for_macho_in_memory (mh_addr);
}
static void
add_all_kexts_command (char *args, int from_tty)
{
#if !defined (USE_DEBUG_SYMBOLS_FRAMEWORK)
error ("DebugSymbols framework not available, add-all-kexts command not unavailable.");
#else
struct loaded_kexts_table *lks = get_list_of_loaded_kexts ();
if (lks == NULL)
error ("Unable to read list of kexts from the kernel memroy.");
int i;
for (i = 0; i < lks->count; i++)
{
if (find_objfile_by_uuid (lks->kexts[i].uuid))
continue;
CFUUIDRef kext_uuid_ref = get_uuidref_for_uuid_t (lks->kexts[i].uuid);
if (kext_uuid_ref == NULL)
continue;
const char *symbol_rich = macosx_locate_kext_executable_by_symfile_helper
(kext_uuid_ref, lks->kexts[i].name);
int have_symbol_rich_exe = 0;
if (symbol_rich && file_exists_p (symbol_rich))
have_symbol_rich_exe = 1;
CFURLRef dsym_url = DBGCopyDSYMURLForUUID (kext_uuid_ref);
char dsym_path[PATH_MAX];
dsym_path[0] = '\0';
int have_dsym_path = 0;
if (dsym_url)
if (CFURLGetFileSystemRepresentation (dsym_url, 1, (UInt8*) dsym_path, PATH_MAX))
{
dsym_path[PATH_MAX - 1] = '\0';
if (file_exists_p (dsym_path))
have_dsym_path = 1;
}
CFRelease (kext_uuid_ref);
struct section_addr_info *sect_addrs;
sect_addrs = get_section_addresses_for_macho_in_memory (lks->kexts[i].address);
if (sect_addrs == NULL)
continue;
if (have_symbol_rich_exe)
{
struct section_offsets *sect_offsets;
int num_offsets;
sect_offsets = convert_sect_addrs_to_offsets_via_on_disk_file
(sect_addrs, symbol_rich,
&num_offsets);
symbol_file_add_name_with_addrs_or_offsets (symbol_rich, from_tty, NULL, sect_offsets, num_offsets, 0, OBJF_USERLOADED, OBJF_SYM_ALL, 0, NULL, NULL);
xfree (sect_offsets);
}
else
{
#if 0
uint8_t *buf = (uint8_t*) xmalloc (lks->kexts[i].size);
if (buf == NULL)
{
free_section_addr_info (sect_addrs);
continue;
}
if (target_read_memory (lks->kexts[i].address, buf, lks->kexts[i].size))
{
free_section_addr_info (sect_addrs);
continue;
}
const char *bfd_target_name = NULL;
if (gdbarch_byte_order (current_gdbarch) == BFD_ENDIAN_LITTLE)
bfd_target_name = "mach-o-le";
if (gdbarch_byte_order (current_gdbarch) == BFD_ENDIAN_BIG)
bfd_target_name = "mach-o-be";
bfd *abfd = bfd_memopenr (lks->kexts[i].name, bfd_target_name,
buf, lks->kexts[i].size);
if (abfd == NULL || !bfd_check_format (abfd, bfd_object))
{
free_section_addr_info (sect_addrs);
xfree (buf);
continue;
}
symbol_file_add_bfd_safe (abfd, 0,
0, NULL, 0,
OBJF_USERLOADED,
OBJF_SYM_ALL,
0, NULL, NULL);
#endif
}
free_section_addr_info (sect_addrs);
}
update_section_tables ();
update_current_target ();
breakpoint_update ();
reinit_frame_cache ();
free_list_of_loaded_kexts (lks);
#endif // USE_DEBUG_SYMBOLS_FRAMEWORK
}
static int
mach_kernel_starts_here_p (CORE_ADDR addr, uuid_t *file_uuid, uuid_t *discovered_uuid, enum gdb_osabi *discovered_osabi)
{
uuid_t mem_uuid;
enum gdb_osabi mem_osabi;
if (!get_information_about_macho (NULL, addr, NULL, 1, 1, &mem_uuid, &mem_osabi, NULL, NULL, NULL, NULL))
return 0;
if (file_uuid)
{
if (memcmp (*file_uuid, mem_uuid, sizeof (uuid_t)) != 0)
{
warning ("Kernel found in memory at 0x%s has a UUID of %s but the binary specified to gdb has a UUID of %s\n",
paddr_nz (addr), puuid (*file_uuid), puuid (mem_uuid));
}
}
if (discovered_osabi)
*discovered_osabi = mem_osabi;
if (discovered_uuid)
memcpy (*discovered_uuid, mem_uuid, sizeof (uuid_t));
return 1;
}
int
exhaustive_search_for_kernel_in_mem (struct objfile *ofile, CORE_ADDR *addr, uuid_t *uuid_output)
{
uuid_t kernel_uuid;
uuid_t *uuid = NULL;
uuid_t in_memory_uuid;
enum gdb_osabi in_memory_osabi = GDB_OSABI_UNKNOWN;
struct cleanup *override_trust_readonly;
if (ofile && !ofile->obfd)
ofile = NULL;
if (ofile && !bfd_mach_o_kernel_image (ofile->obfd))
return 0;
if (kaslr_memory_search_enabled == 0)
return 0;
override_trust_readonly = make_cleanup (set_trust_readonly_cleanup,
(void *) set_trust_readonly (0));
make_cleanup (set_only_read_from_live_memory_cleanup,
(void *) set_only_read_from_live_memory (1));
if (ofile && bfd_mach_o_kernel_image (ofile->obfd) && bfd_get_section_by_name (ofile->obfd, TEXT_SEGMENT_NAME))
{
if (bfd_mach_o_get_uuid (ofile->obfd, kernel_uuid, sizeof (uuid_t)))
{
uuid = &kernel_uuid;
}
}
CORE_ADDR cur_addr;
CORE_ADDR stop_addr;
CORE_ADDR stride = 0x100000;
CORE_ADDR offset = 0x1000;
int wordsize = gdbarch_tdep (current_gdbarch)->wordsize;
if (wordsize == 4)
{
cur_addr = 1ULL << 31;
stop_addr = UINT32_MAX;
}
else
{
cur_addr = 1ULL << 63;
stop_addr = UINT64_MAX;
}
int found_kernel = 0;
uint64_t file_address = INVALID_ADDRESS;
if (ofile && bfd_get_section_by_name (ofile->obfd, TEXT_SEGMENT_NAME))
{
file_address = bfd_section_vma (ofile->obfd, bfd_get_section_by_name (ofile->obfd, TEXT_SEGMENT_NAME));
if (mach_kernel_starts_here_p (file_address, uuid, &in_memory_uuid, &in_memory_osabi))
{
cur_addr = file_address;
found_kernel = 1;
}
else if (file_address != INVALID_ADDRESS)
{
offset = file_address & 0xfffff;
}
}
ULONGEST val = 0;
if (!found_kernel
&& wordsize == 4
&& !safe_read_memory_unsigned_integer (0xffff0110, 4, &val)
&& val > cur_addr
&& val < stop_addr
&& mach_kernel_starts_here_p (val, uuid, &in_memory_uuid, &in_memory_osabi))
{
found_kernel = 1;
cur_addr = val & 0xffffffff;
}
val = 0;
if (!found_kernel
&& wordsize == 8
&& !safe_read_memory_unsigned_integer (0xffffff8000002010ULL, 8, &val)
&& val > cur_addr
&& val < stop_addr
&& mach_kernel_starts_here_p (val, uuid, &in_memory_uuid, &in_memory_osabi))
{
found_kernel = 1;
cur_addr = val;
}
val = 0;
if (!found_kernel
&& wordsize == 4
&& !safe_read_memory_unsigned_integer (0xffff011c, 4, &val)
&& val > cur_addr
&& val < stop_addr)
{
CORE_ADDR try_this_addr = (val & ~0xfffff) - 0x1000000;
if (try_this_addr < cur_addr)
try_this_addr = cur_addr;
while (!found_kernel && try_this_addr < val)
{
if (mach_kernel_starts_here_p (try_this_addr, uuid, &in_memory_uuid, &in_memory_osabi))
{
found_kernel = 1;
}
else if (mach_kernel_starts_here_p (try_this_addr + offset, uuid, &in_memory_uuid, &in_memory_osabi))
{
found_kernel = 1;
try_this_addr += offset;
}
else
{
try_this_addr += stride;
}
}
if (found_kernel)
cur_addr = try_this_addr;
}
if (!found_kernel && stop_pc != 0 && stop_pc != INVALID_ADDRESS
&& stop_pc >= cur_addr && stop_pc < stop_addr)
{
CORE_ADDR try_this_addr = (stop_pc & ~0xfffff) - 0x2000000;
if (try_this_addr < cur_addr)
try_this_addr = cur_addr;
while (!found_kernel && try_this_addr < stop_pc)
{
if (mach_kernel_starts_here_p (try_this_addr, uuid, &in_memory_uuid, &in_memory_osabi))
{
found_kernel = 1;
}
else if (mach_kernel_starts_here_p (try_this_addr + offset, uuid, &in_memory_uuid, &in_memory_osabi))
{
found_kernel = 1;
try_this_addr += offset;
}
else
{
try_this_addr += stride;
}
}
if (found_kernel)
cur_addr = try_this_addr;
}
if (wordsize == 4 && !found_kernel)
{
printf_filtered ("Starting exhaustive search for kernel in memory, do 'set kaslr-memory-search 0' to disable this in the future.\n");
while (!found_kernel && cur_addr != 0 && cur_addr < stop_addr)
{
if (mach_kernel_starts_here_p (cur_addr, uuid, &in_memory_uuid, &in_memory_osabi))
{
found_kernel = 1;
}
else if (mach_kernel_starts_here_p (cur_addr + offset, uuid, &in_memory_uuid, &in_memory_osabi))
{
found_kernel = 1;
cur_addr += offset;
}
else
{
cur_addr += stride;
}
}
}
if (found_kernel)
{
int succeeded = 0;
if (ofile)
succeeded = slide_kernel_objfile (ofile, cur_addr, in_memory_uuid, in_memory_osabi);
if (!succeeded)
succeeded = try_to_find_and_load_kernel_via_uuid (cur_addr, in_memory_uuid, in_memory_osabi);
if (succeeded)
{
do_cleanups (override_trust_readonly);
if (uuid_output)
memcpy (*uuid_output, &in_memory_uuid, sizeof (uuid_t));
if (addr)
*addr = cur_addr;
return 1;
}
}
do_cleanups (override_trust_readonly);
return 0;
}
int
slide_kernel_objfile (struct objfile *o, CORE_ADDR in_memory_addr, uuid_t in_memory_uuid, enum gdb_osabi osabi)
{
CORE_ADDR file_load_addr = INVALID_ADDRESS;
if (o == NULL || o->obfd == NULL || in_memory_addr == 0 || in_memory_addr == INVALID_ADDRESS)
return 0;
if (!get_information_about_macho (NULL, INVALID_ADDRESS, o->obfd, 1, 0, NULL, NULL, NULL, &file_load_addr, NULL, NULL))
return 0;
if (file_load_addr == 0 || file_load_addr == INVALID_ADDRESS)
return 0;
if (osabi != GDB_OSABI_UNKNOWN)
{
const char *osabi_name = gdbarch_osabi_name (osabi);
#if defined (TARGET_I386)
if (strcmp (osabi_name, "Darwin") == 0)
set_architecture_from_string ("i386");
else if (strcmp (osabi_name, "Darwin64") == 0)
set_architecture_from_string ("i386:x86-64");
#endif
#if defined (TARGET_ARM)
if (strcmp (osabi_name, "Darwin") == 0)
set_architecture_from_string ("armv7");
else if (strcmp (osabi_name, "DarwinV7") == 0)
set_architecture_from_string ("armv7");
else if (strcmp (osabi_name, "DarwinV7S") == 0)
set_architecture_from_string ("armv7s");
#endif
set_osabi_option (osabi_name);
}
if (in_memory_addr != file_load_addr)
{
kernel_slide = in_memory_addr - file_load_addr;
slide_objfile (symfile_objfile, kernel_slide, NULL);
update_section_tables ();
update_current_target ();
breakpoint_update ();
reinit_frame_cache ();
flush_cached_frames ();
}
else
kernel_slide = 0;
printf_filtered ("Kernel is located in memory at 0x%s with uuid of %s\n",
paddr_nz (in_memory_addr), puuid (in_memory_uuid));
if (kernel_slide != 0)
{
printf_filtered ("Kernel slid 0x%s in memory.\n", paddr_nz (kernel_slide));
}
return 1;
}
int
try_to_find_and_load_kernel_via_uuid (CORE_ADDR in_memory_addr, uuid_t in_memory_uuid, enum gdb_osabi osabi)
{
if (osabi != GDB_OSABI_UNKNOWN)
{
const char *osabi_name = gdbarch_osabi_name (osabi);
#if defined (TARGET_I386)
if (strcmp (osabi_name, "Darwin") == 0)
set_architecture_from_string ("i386");
else if (strcmp (osabi_name, "Darwin64") == 0)
set_architecture_from_string ("i386:x86-64");
#endif
#if defined (TARGET_ARM)
if (strcmp (osabi_name, "Darwin") == 0)
set_architecture_from_string ("armv7");
else if (strcmp (osabi_name, "DarwinV7") == 0)
set_architecture_from_string ("armv7");
else if (strcmp (osabi_name, "DarwinV7S") == 0)
set_architecture_from_string ("armv7s");
#endif
set_osabi_option (osabi_name);
}
int loaded_kernel_for_user = 0;
CFUUIDRef uuidref = get_uuidref_for_uuid_t (in_memory_uuid);
CFStringRef uuid_string = CFUUIDCreateString(kCFAllocatorDefault, uuidref);
if (uuid_string)
{
char *kernel_path = macosx_locate_executable_by_dbg_shell_command (uuid_string);
if (!file_exists_p (kernel_path))
{
if (kernel_path != NULL)
xfree (kernel_path);
kernel_path = macosx_locate_kext_executable_by_symfile_helper (uuidref, "mach kernel");
}
CORE_ADDR on_disk_load_addr;
if (get_information_about_macho (kernel_path, 0, NULL, 1, 0, NULL, NULL, NULL, &on_disk_load_addr, NULL, NULL))
{
kernel_slide = in_memory_addr - on_disk_load_addr;
struct objfile *o = symbol_file_add_name_with_addrs_or_offsets (kernel_path, 1, NULL, NULL, 0, 1,
OBJF_USERLOADED, OBJF_SYM_ALL, 0, NULL, NULL);
xfree (kernel_path);
if (o == NULL)
{
CFRelease (uuid_string);
CFRelease (uuidref);
return 0;
}
loaded_kernel_for_user = 1;
if (kernel_slide != 0 && kernel_slide != INVALID_ADDRESS)
slide_objfile (o, kernel_slide, NULL);
exec_bfd = o->obfd;
update_section_tables ();
update_current_target ();
breakpoint_update ();
reinit_frame_cache ();
flush_cached_frames ();
}
CFRelease (uuid_string);
}
CFRelease (uuidref);
printf_filtered ("Kernel is located in memory at 0x%s with uuid of %s\n",
paddr_nz (in_memory_addr), puuid (in_memory_uuid));
if (kernel_slide != 0 && kernel_slide != INVALID_ADDRESS)
{
printf_filtered ("Kernel slid 0x%s in memory.\n", paddr_nz (kernel_slide));
}
if (loaded_kernel_for_user)
{
char *exe_for_uuid_cmd = get_dbg_shell_command ();
char *cmd_for_printing = exe_for_uuid_cmd;
if (exe_for_uuid_cmd)
{
char *bname = basename (exe_for_uuid_cmd);
if (bname != NULL)
cmd_for_printing = bname;
}
if (cmd_for_printing)
printf_filtered ("Kernel binary has been loaded into gdb via %s.\n", cmd_for_printing);
else
printf_filtered ("Kernel binary has been loaded into gdb.\n");
}
return 1;
}
static void
maintenance_list_kexts (char *arg, int from_tty)
{
struct loaded_kexts_table *kexts = get_list_of_loaded_kexts ();
if (kexts == NULL)
return;
int padcount = 0;
if (kexts->count > 9)
padcount = 2;
if (kexts->count > 99)
padcount = 3;
int i;
for (i = 0; i < kexts->count; i++)
printf_filtered ("%*d 0x%s %s %s\n",
padcount, i,
paddr_nz (kexts->kexts[i].address),
puuid (kexts->kexts[i].uuid),
kexts->kexts[i].name);
free_list_of_loaded_kexts (kexts);
}
#ifdef NM_NEXTSTEP
#include "macosx-nat-infthread.h"
#endif
char *
macosx_pid_or_tid_to_str (ptid_t ptid)
{
static char buf[64];
#ifdef NM_NEXTSTEP
xsnprintf (buf, sizeof buf, "process %d thread 0x%x", ptid_get_pid (ptid), get_application_thread_port ((thread_t) ptid_get_tid (ptid)));
#else
xsnprintf (buf, sizeof buf, "process %d thread 0x%x", ptid_get_pid (ptid), (unsigned int) ptid_get_tid (ptid));
#endif
return buf;
}
struct gdb_copy_dyld_cache_header
{
char magic[16];
uint32_t mappingOffset;
uint32_t mappingCount;
uint32_t imagesOffset;
uint32_t imagesCount;
uint64_t dyldBaseAddress;
uint64_t codeSignatureOffset;
uint64_t codeSignatureSize;
uint64_t slideInfoOffset;
uint64_t slideInfoSize;
uint64_t localSymbolsOffset;
uint64_t localSymbolsSize;
};
struct gdb_copy_dyld_cache_local_symbols_info
{
uint32_t nlistOffset;
uint32_t nlistCount;
uint32_t stringsOffset;
uint32_t stringsSize;
uint32_t entriesOffset;
uint32_t entriesCount;
};
uint8_t *dyld_shared_cache_raw;
uint8_t *dyld_shared_cache_local_nlists = NULL;
int dyld_shared_cache_local_nlists_count = 0;
char *dyld_shared_cache_strings = NULL;
int dyld_shared_cache_strings_size = 0;
struct gdb_copy_dyld_cache_local_symbols_entry *dyld_shared_cache_entries = NULL;
int dyld_shared_cache_entries_count = 0;
void
free_dyld_shared_cache_local_syms ()
{
if (dyld_shared_cache_raw)
xfree (dyld_shared_cache_raw);
dyld_shared_cache_raw = NULL;
dyld_shared_cache_local_nlists = NULL;
dyld_shared_cache_local_nlists_count = 0;
dyld_shared_cache_strings = NULL;
dyld_shared_cache_strings_size = 0;
dyld_shared_cache_entries = NULL;
dyld_shared_cache_entries_count = 0;
}
void
get_dyld_shared_cache_local_syms ()
{
#if defined (TARGET_ARM) && defined (NM_NEXTSTEP)
if (dyld_shared_cache_raw != NULL)
return;
int wordsize = gdbarch_tdep (current_gdbarch)->wordsize;
int nlist_entry_size;
if (wordsize == 4)
nlist_entry_size = 12;
else
nlist_entry_size = 16;
const char *osabi_name = gdbarch_osabi_name (gdbarch_osabi (current_gdbarch));
const char *arch_name = NULL;
if (strcmp (osabi_name, "Darwin") == 0)
arch_name = "arm";
else if (strcmp (osabi_name, "DarwinV6") == 0)
arch_name = "armv6";
else if (strcmp (osabi_name, "DarwinV7") == 0)
arch_name = "armv7";
else if (strcmp (osabi_name, "DarwinV7S") == 0)
arch_name = "armv7s";
else if (strcmp (osabi_name, "DarwinV7F") == 0)
arch_name = "armv7";
if (arch_name == NULL)
return;
char dsc_path[PATH_MAX];
snprintf(dsc_path, sizeof(dsc_path),
"/System/Library/Caches/com.apple.dyld/dyld_shared_cache_%s",
arch_name);
FILE *dsc = fopen (dsc_path, "r");
if (dsc == NULL)
return;
struct gdb_copy_dyld_cache_header dsc_header;
if (fread (&dsc_header, sizeof (dsc_header), 1, dsc) != 1)
{
fclose (dsc);
return;
}
if (dsc_header.mappingOffset < sizeof (struct gdb_copy_dyld_cache_header))
{
fclose (dsc);
return;
}
if (fseek (dsc, dsc_header.localSymbolsOffset, SEEK_SET) != 0)
{
fclose (dsc);
return;
}
dyld_shared_cache_raw = (uint8_t *) xmalloc (dsc_header.localSymbolsSize);
if (fread (dyld_shared_cache_raw, dsc_header.localSymbolsSize, 1, dsc) != 1)
{
free_dyld_shared_cache_local_syms ();
fclose (dsc);
return;
}
fclose (dsc);
struct gdb_copy_dyld_cache_local_symbols_info *locsyms_header;
locsyms_header = (struct gdb_copy_dyld_cache_local_symbols_info *) dyld_shared_cache_raw;
dyld_shared_cache_local_nlists = dyld_shared_cache_raw + locsyms_header->nlistOffset;
dyld_shared_cache_local_nlists_count = locsyms_header->nlistCount;
dyld_shared_cache_strings = (char *) dyld_shared_cache_raw + locsyms_header->stringsOffset;
dyld_shared_cache_strings_size = locsyms_header->stringsSize;
dyld_shared_cache_entries = (struct gdb_copy_dyld_cache_local_symbols_entry *) dyld_shared_cache_raw + locsyms_header->entriesOffset;
dyld_shared_cache_entries_count = locsyms_header->entriesCount;
#endif
}
struct gdb_copy_dyld_cache_local_symbols_entry *
get_dyld_shared_cache_entry (CORE_ADDR intended_load_addr)
{
get_dyld_shared_cache_local_syms ();
int i = 0;
while (i < dyld_shared_cache_entries_count)
{
if (dyld_shared_cache_entries[i].dylibOffset == intended_load_addr - 0x30000000)
return &dyld_shared_cache_entries[i];
i++;
}
return NULL;
}
void
_initialize_macosx_tdep ()
{
struct cmd_list_element *c;
macosx_symbol_types_init ();
add_info ("trampoline", info_trampoline_command,
"Resolve function for DYLD trampoline stub and/or Objective-C call");
c = add_com ("open", class_support, open_command, _("\
Open the named source file in an application determined by LaunchServices.\n\
With no arguments, open the currently selected source file.\n\
Also takes file:line to hilight the file at the given line."));
set_cmd_completer (c, filename_completer);
add_com_alias ("op", "open", class_support, 1);
add_com_alias ("ope", "open", class_support, 1);
add_com ("flushstack", class_maintenance, stack_flush_command,
"Force gdb to flush its stack-frame cache (maintainer command)");
add_com_alias ("flush", "flushregs", class_maintenance, 1);
add_com ("update", class_obscure, update_command,
"Re-read current state information from inferior.");
add_setshow_boolean_cmd ("locate-dsym", class_obscure,
&dsym_locate_enabled, _("\
Set locate dSYM files using the DebugSymbols framework."), _("\
Show locate dSYM files using the DebugSymbols framework."), _("\
If set, gdb will try and locate dSYM files using the DebugSymbols framework."),
NULL, NULL,
&setlist, &showlist);
add_setshow_boolean_cmd ("kaslr-memory-search", class_obscure,
&kaslr_memory_search_enabled, _("\
Set whether gdb should do a search through memory for the kernel on 'target remote'."), _("\
Show whether gdb should do a search through memory for the kernel on 'target remote'."), _("\
If set, gdb may look through memory for a kernel when doing 'target remote'"),
NULL, NULL,
&setlist, &showlist);
add_cmd ("list-kexts", class_maintenance,
maintenance_list_kexts,
"List kexts loaded by the kernel (when kernel debugging).",
&maintenancelist);
add_setshow_boolean_cmd ("disable-aslr", class_obscure,
&disable_aslr_flag, _("\
Set if GDB should disable shared library address randomization."), _("\
Show if GDB should disable shared library address randomization."), NULL,
NULL, NULL,
&setlist, &showlist);
c = add_cmd ("add-all-kexts", class_files, add_all_kexts_command, _("\
Usage: add-all-kexts\n\
Load the dSYMs for all of the kexts loaded in a live kernel/kernel coredump.\n\
You must be attached to a live kernel (usually via kdp) or be debugging a\n\
kernel coredump to use this command -- gdb will examine the kernel memory\n\
to find the list of kexts and what addresses they are loaded at.\n"),
&cmdlist);
set_cmd_completer (c, filename_completer);
}