#include "defs.h"
#include "symtab.h"
#include "gdbtypes.h"
#include "expression.h"
#include "parser-defs.h"
#include "language.h"
#include "c-lang.h"
#include "objc-lang.h"
#include "exceptions.h"
#include "complaints.h"
#include "value.h"
#include "symfile.h"
#include "objfiles.h"
#include "gdb_string.h"
#include "target.h"
#include "gdbcore.h"
#include "gdbcmd.h"
#include "frame.h"
#include "gdb_regex.h"
#include "regcache.h"
#include "block.h"
#include "infcall.h"
#include "valprint.h"
#include "gdb_assert.h"
#include "inferior.h"
#include "demangle.h"
#include "osabi.h"
#include "inlining.h"
#include "ui-out.h"
#include "cli-out.h"
#include "breakpoint.h"
#include <ctype.h>
struct objc_object {
CORE_ADDR isa;
};
struct objc_class {
CORE_ADDR isa;
CORE_ADDR super_class;
CORE_ADDR name;
long version;
long info;
long instance_size;
CORE_ADDR ivars;
CORE_ADDR methods;
CORE_ADDR cache;
CORE_ADDR protocols;
};
struct objc_super {
CORE_ADDR receiver;
CORE_ADDR class;
};
struct objc_method {
CORE_ADDR name;
CORE_ADDR types;
CORE_ADDR imp;
};
static void find_methods (struct symtab *symtab, char type,
const char *class, const char *category,
const char *selector, struct symbol **syms,
unsigned int *nsym, unsigned int *ndebug);
int lookup_objc_class_p = 1;
int call_po_at_unsafe_times = 0;
int let_po_run_all_threads = 1;
enum debug_modes
{
debug_mode_not_checked,
debug_mode_okay,
debug_mode_failed,
debug_mode_overridden
};
static enum debug_modes debug_mode_set_p;
static enum objc_debugger_mode_result debug_mode_set_reason;
static unsigned int objc_class_method_limit = 10000;
static int objc_runtime_version_user_override = 0;
static int objc_runtime_version = 0;
static struct rb_tree_node *implementation_tree = NULL;
static CORE_ADDR lookup_implementation_in_cache (CORE_ADDR class, CORE_ADDR sel);
static void add_implementation_to_cache (CORE_ADDR class, CORE_ADDR sel, CORE_ADDR implementation);
static struct rb_tree_node *real_class_tree = NULL;
int objc_setup_safe_print (struct cleanup **cleanup);
static int class_valid_p (CORE_ADDR class);
static struct rb_tree_node *add_class_to_cache (CORE_ADDR class);
static CORE_ADDR lookup_real_class_in_cache (CORE_ADDR class);
static void add_real_class_to_cache (CORE_ADDR class, CORE_ADDR real_class);
static struct rb_tree_node *classname_tree = NULL;
static char *lookup_classname_in_cache (CORE_ADDR class);
static void add_classname_to_cache (CORE_ADDR class, char *classname);
static struct objfile *cached_objc_objfile;
static char *objc_library_name = "libobjc.A.dylib";
static void objc_clear_trampoline_data ();
static void read_objc_object (CORE_ADDR addr, struct objc_object *object);
static CORE_ADDR get_class_address_from_object (CORE_ADDR);
#define OBJC_FETCH_POINTER_ARGUMENT(argi) \
FETCH_POINTER_ARGUMENT (get_current_frame (), argi, builtin_type_void_func_ptr)
#ifndef TARGET_ADDRESS_BYTES
#define TARGET_ADDRESS_BYTES (TARGET_LONG_BIT / TARGET_CHAR_BIT)
#endif
struct symbol *
lookup_struct_typedef (char *name, struct block *block, int noerr)
{
struct symbol *sym;
sym = lookup_symbol (name, block, STRUCT_DOMAIN, 0,
(struct symtab **) NULL);
if (sym == NULL)
{
if (noerr)
return 0;
else
error (_("No struct type named %s."), name);
}
if (TYPE_CODE (SYMBOL_TYPE (sym)) != TYPE_CODE_STRUCT)
{
if (noerr)
return 0;
else
error (_("This context has class, union or enum %s, not a struct."),
name);
}
return sym;
}
CORE_ADDR
lookup_objc_class (char *classname)
{
static struct cached_value *function = NULL;
struct value *classval;
struct value *ret_value;
struct cleanup *scheduler_cleanup;
CORE_ADDR retval = 0;
enum objc_debugger_mode_result objc_retval;
if (! target_has_execution)
{
return 0;
}
if (function == NULL)
{
if (lookup_minimal_symbol("objc_lookUpClass", 0, 0))
function = create_cached_function ("objc_lookUpClass",
builtin_type_voidptrfuncptr);
else if (lookup_minimal_symbol ("objc_lookup_class", 0, 0))
function = create_cached_function ("objc_lookup_class",
builtin_type_voidptrfuncptr);
else
return 0;
}
scheduler_cleanup = make_cleanup_set_restore_scheduler_locking_mode
(scheduler_locking_on);
objc_retval = make_cleanup_set_restore_debugger_mode (NULL, 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)
{
classval = value_string (classname, strlen (classname) + 1);
classval = value_coerce_array (classval);
ret_value = call_function_by_hand (lookup_cached_function (function),
1, &classval);
retval = (CORE_ADDR) value_as_address (ret_value);
}
do_cleanups (scheduler_cleanup);
return retval;
}
CORE_ADDR
lookup_child_selector_nocache (char *selname)
{
static struct cached_value *function = NULL;
struct value *selstring;
struct value *retval;
if (! target_has_execution)
{
return 0;
}
if (function == NULL)
{
if (lookup_minimal_symbol("sel_getUid", 0, 0))
function = create_cached_function ("sel_getUid",
builtin_type_voidptrfuncptr);
else if (lookup_minimal_symbol ("sel_get_any_uid", 0, 0))
function = create_cached_function ("sel_get_any_uid",
builtin_type_voidptrfuncptr);
else
{
complaint (&symfile_complaints, "no way to lookup Objective-C selectors");
return 0;
}
}
selstring = value_coerce_array (value_string (selname,
strlen (selname) + 1));
retval = call_function_by_hand (lookup_cached_function (function),
1, &selstring);
return value_as_long (retval);
}
struct selector_entry {
char *name;
CORE_ADDR val;
struct selector_entry *next;
};
#define SELECTOR_HASH_SIZE 127
static struct selector_entry *selector_hash[SELECTOR_HASH_SIZE] = { 0 };
static int selector_hash_generation = -1;
static void
reset_child_selector_cache (void)
{
int i;
for (i = 0; i < SELECTOR_HASH_SIZE; i++)
{
struct selector_entry *entry, *temp;
entry = selector_hash[i];
while (entry != NULL)
{
temp = entry;
entry = entry->next;
xfree (temp->name);
xfree (temp);
}
selector_hash[i] = NULL;
}
}
CORE_ADDR
lookup_child_selector (char *selname)
{
struct selector_entry *entry;
int hash;
int current_generation;
current_generation = ptid_get_pid (inferior_ptid);
if (current_generation != selector_hash_generation)
{
reset_child_selector_cache ();
selector_hash_generation = current_generation;
}
hash = msymbol_hash (selname) % SELECTOR_HASH_SIZE;
for (entry = selector_hash[hash]; entry != NULL; entry = entry->next)
if ((entry != NULL) && (strcmp (entry->name, selname) == 0))
break;
if (entry != NULL)
return entry->val;
entry = (struct selector_entry *) xmalloc (sizeof (struct selector_entry));
entry->name = xstrdup (selname);
entry->val = lookup_child_selector_nocache (selname);
entry->next = selector_hash[hash];
selector_hash[hash] = entry;
return entry->val;
}
struct value *
value_nsstring (char *ptr, int len)
{
struct value *stringValue[3];
struct value *function, *nsstringValue;
struct symbol *sym;
struct type *type;
static struct type *func_type = NULL;
struct type *ret_type;
if (!target_has_execution)
return 0;
sym = lookup_struct_typedef ("NSString", 0, 1);
if (sym == NULL)
sym = lookup_struct_typedef ("NXString", 0, 1);
if (sym == NULL)
type = lookup_pointer_type (builtin_type_void);
else
type = lookup_pointer_type (SYMBOL_TYPE (sym));
stringValue[2] = value_string (ptr, len);
stringValue[2] = value_coerce_array (stringValue[2]);
if (func_type == NULL)
func_type = alloc_type (NULL);
func_type = make_function_type (type, &func_type, 0);
ret_type = lookup_pointer_type (func_type);
if (lookup_minimal_symbol ("_NSNewStringFromCString", 0, 0))
{
function = find_function_in_inferior ("_NSNewStringFromCString",
ret_type);
nsstringValue = call_function_by_hand (function, 1, &stringValue[2]);
}
else if (lookup_minimal_symbol ("istr", 0, 0))
{
function = find_function_in_inferior ("istr",
ret_type);
nsstringValue = call_function_by_hand (function, 1, &stringValue[2]);
}
else if (lookup_minimal_symbol ("+[NSString stringWithCString:]", 0, 0))
{
function = find_function_in_inferior("+[NSString stringWithCString:]",
ret_type);
stringValue[0] = value_from_longest
(builtin_type_long, lookup_objc_class ("NSString"));
stringValue[1] = value_from_longest
(builtin_type_long, lookup_child_selector ("stringWithCString:"));
nsstringValue = call_function_by_hand (function, 3, &stringValue[0]);
}
else
error (_("NSString: internal error -- no way to create new NSString"));
return nsstringValue;
}
char *
objcplus_demangle (const char *mangled, int options)
{
char *demangled;
demangled = objc_demangle (mangled, options);
if (demangled != NULL)
return demangled;
demangled = cplus_demangle (mangled, options);
return demangled;
}
char *
objc_demangle (const char *mangled, int options)
{
char *demangled, *cp;
if (mangled[0] == '_' &&
(mangled[1] == 'i' || mangled[1] == 'c') &&
mangled[2] == '_')
{
cp = demangled = xmalloc(strlen(mangled) + 2);
if (mangled[1] == 'i')
*cp++ = '-';
else
*cp++ = '+';
*cp++ = '[';
strcpy(cp, mangled+3);
while (*cp && *cp == '_')
cp++;
cp = strchr(cp, '_');
if (!cp)
{
xfree(demangled);
return NULL;
}
if (cp[1] == '_') {
*cp++ = ' ';
strcpy(cp, mangled + (cp - demangled) + 2);
}
else {
*cp++ = '(';
cp = strchr(cp, '_');
if (!cp)
{
xfree(demangled);
return NULL;
}
*cp++ = ')';
*cp++ = ' ';
strcpy(cp, mangled + (cp - demangled));
}
while (*cp && *cp == '_')
cp++;
for (; *cp; cp++)
if (*cp == '_')
*cp = ':';
*cp++ = ']';
*cp++ = 0;
return demangled;
}
else
return NULL;
}
static void
objc_emit_char (int c, struct ui_file *stream, int quoter)
{
c &= 0xFF;
if (PRINT_LITERAL_FORM (c))
{
if (c == '\\' || c == quoter)
{
fputs_filtered ("\\", stream);
}
fprintf_filtered (stream, "%c", c);
}
else
{
switch (c)
{
case '\n':
fputs_filtered ("\\n", stream);
break;
case '\b':
fputs_filtered ("\\b", stream);
break;
case '\t':
fputs_filtered ("\\t", stream);
break;
case '\f':
fputs_filtered ("\\f", stream);
break;
case '\r':
fputs_filtered ("\\r", stream);
break;
case '\033':
fputs_filtered ("\\e", stream);
break;
case '\007':
fputs_filtered ("\\a", stream);
break;
default:
fprintf_filtered (stream, "\\%.3o", (unsigned int) c);
break;
}
}
}
static void
objc_printchar (int c, struct ui_file *stream)
{
fputs_filtered ("'", stream);
objc_emit_char (c, stream, '\'');
fputs_filtered ("'", stream);
}
static void
objc_printstr (struct ui_file *stream, const gdb_byte *string,
unsigned int length, int width, int force_ellipses)
{
unsigned int i;
unsigned int things_printed = 0;
int in_quotes = 0;
int need_comma = 0;
if ((!force_ellipses) && length > 0 && string[length-1] == '\0')
length--;
if (length == 0)
{
fputs_filtered ("\"\"", stream);
return;
}
for (i = 0; i < length && things_printed < print_max; ++i)
{
unsigned int rep1;
unsigned int reps;
QUIT;
if (need_comma)
{
fputs_filtered (", ", stream);
need_comma = 0;
}
rep1 = i + 1;
reps = 1;
while (rep1 < length && string[rep1] == string[i])
{
++rep1;
++reps;
}
if (reps > repeat_count_threshold)
{
if (in_quotes)
{
if (inspect_it)
fputs_filtered ("\\\", ", stream);
else
fputs_filtered ("\", ", stream);
in_quotes = 0;
}
objc_printchar (string[i], stream);
fprintf_filtered (stream, " <repeats %u times>", reps);
i = rep1 - 1;
things_printed += repeat_count_threshold;
need_comma = 1;
}
else
{
if (!in_quotes)
{
if (inspect_it)
fputs_filtered ("\\\"", stream);
else
fputs_filtered ("\"", stream);
in_quotes = 1;
}
objc_emit_char (string[i], stream, '"');
++things_printed;
}
}
if (in_quotes)
{
if (inspect_it)
fputs_filtered ("\\\"", stream);
else
fputs_filtered ("\"", stream);
}
if (force_ellipses || i < length)
fputs_filtered ("...", stream);
}
static struct type *
objc_create_fundamental_type (struct objfile *objfile, int typeid)
{
struct type *type = NULL;
switch (typeid)
{
default:
type = init_type (TYPE_CODE_INT,
TARGET_INT_BIT / TARGET_CHAR_BIT,
0, "<?type?>", objfile);
warning (_("internal error: no C/C++ fundamental type %d"), typeid);
break;
case FT_VOID:
type = init_type (TYPE_CODE_VOID,
TARGET_CHAR_BIT / TARGET_CHAR_BIT,
0, "void", objfile);
break;
case FT_CHAR:
type = init_type (TYPE_CODE_INT,
TARGET_CHAR_BIT / TARGET_CHAR_BIT,
0, "char", objfile);
break;
case FT_SIGNED_CHAR:
type = init_type (TYPE_CODE_INT,
TARGET_CHAR_BIT / TARGET_CHAR_BIT,
0, "signed char", objfile);
break;
case FT_UNSIGNED_CHAR:
type = init_type (TYPE_CODE_INT,
TARGET_CHAR_BIT / TARGET_CHAR_BIT,
TYPE_FLAG_UNSIGNED, "unsigned char", objfile);
break;
case FT_SHORT:
type = init_type (TYPE_CODE_INT,
TARGET_SHORT_BIT / TARGET_CHAR_BIT,
0, "short", objfile);
break;
case FT_SIGNED_SHORT:
type = init_type (TYPE_CODE_INT,
TARGET_SHORT_BIT / TARGET_CHAR_BIT,
0, "short", objfile);
break;
case FT_UNSIGNED_SHORT:
type = init_type (TYPE_CODE_INT,
TARGET_SHORT_BIT / TARGET_CHAR_BIT,
TYPE_FLAG_UNSIGNED, "unsigned short", objfile);
break;
case FT_INTEGER:
type = init_type (TYPE_CODE_INT,
TARGET_INT_BIT / TARGET_CHAR_BIT,
0, "int", objfile);
break;
case FT_SIGNED_INTEGER:
type = init_type (TYPE_CODE_INT,
TARGET_INT_BIT / TARGET_CHAR_BIT,
0, "int", objfile);
break;
case FT_UNSIGNED_INTEGER:
type = init_type (TYPE_CODE_INT,
TARGET_INT_BIT / TARGET_CHAR_BIT,
TYPE_FLAG_UNSIGNED, "unsigned int", objfile);
break;
case FT_LONG:
type = init_type (TYPE_CODE_INT,
TARGET_LONG_BIT / TARGET_CHAR_BIT,
0, "long", objfile);
break;
case FT_SIGNED_LONG:
type = init_type (TYPE_CODE_INT,
TARGET_LONG_BIT / TARGET_CHAR_BIT,
0, "long", objfile);
break;
case FT_UNSIGNED_LONG:
type = init_type (TYPE_CODE_INT,
TARGET_LONG_BIT / TARGET_CHAR_BIT,
TYPE_FLAG_UNSIGNED, "unsigned long", objfile);
break;
case FT_LONG_LONG:
type = init_type (TYPE_CODE_INT,
TARGET_LONG_LONG_BIT / TARGET_CHAR_BIT,
0, "long long", objfile);
break;
case FT_SIGNED_LONG_LONG:
type = init_type (TYPE_CODE_INT,
TARGET_LONG_LONG_BIT / TARGET_CHAR_BIT,
0, "signed long long", objfile);
break;
case FT_UNSIGNED_LONG_LONG:
type = init_type (TYPE_CODE_INT,
TARGET_LONG_LONG_BIT / TARGET_CHAR_BIT,
TYPE_FLAG_UNSIGNED, "unsigned long long", objfile);
break;
case FT_FLOAT:
type = init_type (TYPE_CODE_FLT,
TARGET_FLOAT_BIT / TARGET_CHAR_BIT,
0, "float", objfile);
break;
case FT_DBL_PREC_FLOAT:
type = init_type (TYPE_CODE_FLT,
TARGET_DOUBLE_BIT / TARGET_CHAR_BIT,
0, "double", objfile);
break;
case FT_EXT_PREC_FLOAT:
type = init_type (TYPE_CODE_FLT,
TARGET_LONG_DOUBLE_BIT / TARGET_CHAR_BIT,
0, "long double", objfile);
break;
}
return (type);
}
#define OBJC_TRAMPOLINE_MESSAGE (1<<0) // trampoline acts like objc_msgSend_fixedup
#define OBJC_TRAMPOLINE_STRET (1<<1) // trampoline is struct-returning
#define OBJC_TRAMPOLINE_VTABLE (1<<2) // trampoline is vtable dispatcher
struct objc_trampoline_record
{
CORE_ADDR start_addr;
uint64_t flags;
};
struct objc_trampoline_region
{
CORE_ADDR next_region_start;
CORE_ADDR trampoline_start;
CORE_ADDR trampoline_end;
struct objc_trampoline_region *next;
int num_records;
struct objc_trampoline_record records[];
};
struct objc_trampolines
{
int initialized;
CORE_ADDR gdb_objc_trampoline_addr;
struct breakpoint *update_bpt;
struct objc_trampoline_region *head;
};
static struct objc_trampolines objc_trampolines;
static struct objc_trampoline_region *
objc_read_trampoline_region (CORE_ADDR addr)
{
ULONGEST header_size;
ULONGEST desc_size;
ULONGEST num_records;
struct objc_trampoline_region *region;
int wordsize = TARGET_ADDRESS_BYTES;
CORE_ADDR orig_addr = addr;
int i;
struct cleanup *region_cleanup;
int entry_size;
header_size = read_memory_unsigned_integer (addr, 2);
addr += 2;
desc_size = read_memory_unsigned_integer (addr, 2);
addr += 2;
num_records = read_memory_unsigned_integer (addr, 4);
addr += 4;
if (wordsize == 4)
entry_size = 4;
else if (wordsize == 8)
{
if (desc_size == 8)
entry_size = 4;
else
entry_size = wordsize;
}
else
internal_error (__FILE__, __LINE__, "Unrecognized word size: %d for trampoline, skipping.", wordsize);
region = xmalloc (sizeof (struct objc_trampoline_region)
+ num_records * sizeof (struct objc_trampoline_record));
region_cleanup = make_cleanup (xfree, region);
region->next_region_start = read_memory_unsigned_integer (addr, wordsize);
region->num_records = num_records;
addr = orig_addr + header_size;
for (i = 0; i < region->num_records; i++)
{
CORE_ADDR record_start = addr;
CORE_ADDR offset;
offset = read_memory_unsigned_integer (addr, entry_size);
region->records[i].start_addr = addr + offset;
addr += entry_size;
region->records[i].flags = read_memory_unsigned_integer (addr, entry_size);
addr = record_start + desc_size;
}
if (region->num_records > 0)
{
region->trampoline_start = region->records[0].start_addr;
region->trampoline_end = region->records[region->num_records - 1].start_addr;
}
discard_cleanups (region_cleanup);
return region;
}
void
objc_init_runtime_version ()
{
struct objfile *objc_objfile = find_libobjc_objfile ();
struct obj_section *os;
if (objc_objfile == NULL)
{
objc_runtime_version = 0;
return;
}
ALL_OBJFILE_OSECTIONS (objc_objfile, os)
{
if (os->the_bfd_section)
{
const char *n = bfd_section_name (objc_objfile, os->the_bfd_section);
if (n && strcmp (n, "LC_SEGMENT.__OBJC") == 0)
{
objc_runtime_version = 1;
return;
}
}
}
objc_runtime_version = 2;
}
void
objc_init_trampoline_observer ()
{
struct minimal_symbol *trampoline;
struct minimal_symbol *observer;
struct objfile *objc_objfile;
int wordsize = TARGET_ADDRESS_BYTES;
CORE_ADDR region_addr = 0;
CORE_ADDR update_fn_addr;
struct breakpoint *b;
struct gdb_exception e;
if (objc_trampolines.initialized)
return;
objc_objfile = find_libobjc_objfile ();
if (objc_objfile == NULL)
return;
objfile_set_load_state (objc_objfile, OBJF_SYM_ALL, 1);
trampoline = lookup_minimal_symbol ("gdb_objc_trampolines", NULL, objc_objfile);
observer = lookup_minimal_symbol ("gdb_objc_trampolines_changed", NULL, objc_objfile);
if (trampoline == NULL || observer == NULL)
{
objc_trampolines.initialized = 1;
return;
}
objc_trampolines.gdb_objc_trampoline_addr = SYMBOL_VALUE_ADDRESS (trampoline);
TRY_CATCH (e, RETURN_MASK_ALL)
{
region_addr = read_memory_unsigned_integer (objc_trampolines.gdb_objc_trampoline_addr, wordsize);
}
if (e.reason != NO_ERROR)
{
objc_trampolines.initialized = 0;
return;
}
while (region_addr != 0)
{
struct objc_trampoline_region *region = NULL;
TRY_CATCH (e, RETURN_MASK_ALL)
{
region = objc_read_trampoline_region (region_addr);
}
if (e.reason != NO_ERROR)
{
objc_clear_trampoline_data ();
return;
}
if (region != NULL)
{
region_addr = region->next_region_start;
region->next = objc_trampolines.head;
objc_trampolines.head = region;
}
else
break;
}
update_fn_addr = SYMBOL_VALUE_ADDRESS (observer);
b = create_solib_event_breakpoint (update_fn_addr);
b->addr_string = xstrdup ("gdb_objc_trampolines_changed");
b->bp_objfile = objc_objfile;
b->requested_shlib = xstrdup (objc_objfile->name);
objc_trampolines.update_bpt = b;
objc_trampolines.initialized = 1;
if (info_verbose)
printf_filtered ("objc trampoline structures initialized.");
}
int
objc_handle_update (CORE_ADDR pc)
{
CORE_ADDR region_addr;
struct objc_trampoline_region *region;
if (objc_trampolines.update_bpt == NULL
|| pc != objc_trampolines.update_bpt->loc->address)
return 0;
region_addr = OBJC_FETCH_POINTER_ARGUMENT (0);
region = objc_read_trampoline_region (region_addr);
if (region != NULL)
{
region->next = objc_trampolines.head;
objc_trampolines.head = region;
}
return 1;
}
static void
objc_clear_trampoline_data ()
{
struct objc_trampoline_region *region;
region = objc_trampolines.head;
while (region != NULL)
{
struct objc_trampoline_region *tmp;
tmp = region;
region = region->next;
xfree (tmp);
}
if (objc_trampolines.update_bpt != NULL)
{
delete_breakpoint (objc_trampolines.update_bpt);
objc_trampolines.update_bpt = NULL;
}
objc_trampolines.gdb_objc_trampoline_addr = 0;
objc_trampolines.head = NULL;
objc_trampolines.initialized = 0;
}
int
pc_in_objc_trampoline_p (CORE_ADDR pc, uint32_t *flags)
{
struct objc_trampoline_region *region;
int in_tramp = 0;
for (region = objc_trampolines.head; region != NULL; region = region->next)
{
if (region->trampoline_start <= pc && pc <= region->trampoline_end)
{
int i;
in_tramp = 1;
if (flags != NULL)
{
for (i = region->num_records - 1; i >= 0; i--)
{
if (region->records[i].start_addr <= pc)
{
*flags = region->records[i].flags;
return in_tramp;
}
}
}
}
}
return in_tramp;
}
static CORE_ADDR
objc_skip_trampoline (CORE_ADDR stop_pc)
{
CORE_ADDR real_stop_pc;
CORE_ADDR method_stop_pc;
real_stop_pc = SKIP_TRAMPOLINE_CODE (stop_pc);
if (real_stop_pc != 0)
find_objc_msgcall (real_stop_pc, &method_stop_pc);
else
find_objc_msgcall (stop_pc, &method_stop_pc);
if (method_stop_pc)
{
real_stop_pc = SKIP_TRAMPOLINE_CODE (method_stop_pc);
if (real_stop_pc == 0)
real_stop_pc = method_stop_pc;
}
return real_stop_pc;
}
static const struct op_print objc_op_print_tab[] =
{
{",", BINOP_COMMA, PREC_COMMA, 0},
{"=", BINOP_ASSIGN, PREC_ASSIGN, 1},
{"||", BINOP_LOGICAL_OR, PREC_LOGICAL_OR, 0},
{"&&", BINOP_LOGICAL_AND, PREC_LOGICAL_AND, 0},
{"|", BINOP_BITWISE_IOR, PREC_BITWISE_IOR, 0},
{"^", BINOP_BITWISE_XOR, PREC_BITWISE_XOR, 0},
{"&", BINOP_BITWISE_AND, PREC_BITWISE_AND, 0},
{"==", BINOP_EQUAL, PREC_EQUAL, 0},
{"!=", BINOP_NOTEQUAL, PREC_EQUAL, 0},
{"<=", BINOP_LEQ, PREC_ORDER, 0},
{">=", BINOP_GEQ, PREC_ORDER, 0},
{">", BINOP_GTR, PREC_ORDER, 0},
{"<", BINOP_LESS, PREC_ORDER, 0},
{">>", BINOP_RSH, PREC_SHIFT, 0},
{"<<", BINOP_LSH, PREC_SHIFT, 0},
{"+", BINOP_ADD, PREC_ADD, 0},
{"-", BINOP_SUB, PREC_ADD, 0},
{"*", BINOP_MUL, PREC_MUL, 0},
{"/", BINOP_DIV, PREC_MUL, 0},
{"%", BINOP_REM, PREC_MUL, 0},
{"@", BINOP_REPEAT, PREC_REPEAT, 0},
{"-", UNOP_NEG, PREC_PREFIX, 0},
{"!", UNOP_LOGICAL_NOT, PREC_PREFIX, 0},
{"~", UNOP_COMPLEMENT, PREC_PREFIX, 0},
{"*", UNOP_IND, PREC_PREFIX, 0},
{"&", UNOP_ADDR, PREC_PREFIX, 0},
{"sizeof ", UNOP_SIZEOF, PREC_PREFIX, 0},
{"++", UNOP_PREINCREMENT, PREC_PREFIX, 0},
{"--", UNOP_PREDECREMENT, PREC_PREFIX, 0},
{NULL, OP_NULL, PREC_NULL, 0}
};
struct type ** const (objc_builtin_types[]) =
{
&builtin_type_int,
&builtin_type_long,
&builtin_type_short,
&builtin_type_char,
&builtin_type_float,
&builtin_type_double,
&builtin_type_void,
&builtin_type_long_long,
&builtin_type_signed_char,
&builtin_type_unsigned_char,
&builtin_type_unsigned_short,
&builtin_type_unsigned_int,
&builtin_type_unsigned_long,
&builtin_type_unsigned_long_long,
&builtin_type_long_double,
&builtin_type_complex,
&builtin_type_double_complex,
0
};
const struct language_defn objc_language_defn = {
"objective-c",
language_objc,
objc_builtin_types,
range_check_off,
type_check_off,
case_sensitive_on,
array_row_major,
&exp_descriptor_standard,
objc_parse,
objc_error,
null_post_parser,
objc_printchar,
objc_printstr,
objc_emit_char,
objc_create_fundamental_type,
c_print_type,
c_val_print,
c_value_print,
objc_skip_trampoline,
value_of_this,
basic_lookup_symbol_nonlocal,
basic_lookup_transparent_type,
objc_demangle,
NULL,
objc_op_print_tab,
1,
0,
&builtin_type_char,
default_word_break_characters,
NULL,
LANG_MAGIC
};
const struct language_defn objcplus_language_defn = {
"objective-c++",
language_objcplus,
objc_builtin_types,
range_check_off,
type_check_off,
case_sensitive_on,
array_row_major,
&exp_descriptor_standard,
objc_parse,
objc_error,
null_post_parser,
objc_printchar,
objc_printstr,
objc_emit_char,
objc_create_fundamental_type,
c_print_type,
c_val_print,
c_value_print,
objc_skip_trampoline,
value_of_this,
basic_lookup_symbol_nonlocal,
basic_lookup_transparent_type,
objcplus_demangle,
NULL,
objc_op_print_tab,
1,
0,
&builtin_type_char,
default_word_break_characters,
NULL,
LANG_MAGIC
};
struct selname
{
struct selname *next;
char *msglist_sel;
int msglist_len;
};
static int msglist_len;
static struct selname *selname_chain;
static char *msglist_sel;
void
start_msglist(void)
{
struct selname *new =
(struct selname *) xmalloc (sizeof (struct selname));
new->next = selname_chain;
new->msglist_len = msglist_len;
new->msglist_sel = msglist_sel;
msglist_len = 0;
msglist_sel = (char *)xmalloc(1);
*msglist_sel = 0;
selname_chain = new;
}
void
add_msglist(struct stoken *str, int addcolon)
{
char *s, *p;
int len, plen;
if (str == 0) {
if (addcolon == 0) {
msglist_len++;
return;
}
p = "";
plen = 0;
} else {
p = str->ptr;
plen = str->length;
}
len = plen + strlen(msglist_sel) + 2;
s = (char *)xmalloc(len);
strcpy(s, msglist_sel);
strncat(s, p, plen);
xfree(msglist_sel);
msglist_sel = s;
if (addcolon) {
s[len-2] = ':';
s[len-1] = 0;
msglist_len++;
} else
s[len-2] = '\0';
}
int
end_msglist(void)
{
int val = msglist_len;
struct selname *sel = selname_chain;
char *p = msglist_sel;
CORE_ADDR selid;
selname_chain = sel->next;
msglist_len = sel->msglist_len;
msglist_sel = sel->msglist_sel;
selid = lookup_child_selector(p);
if (!selid)
error (_("Can't find selector \"%s\""), p);
write_exp_elt_longcst (selid);
xfree(p);
write_exp_elt_longcst (val);
xfree(sel);
return val;
}
static int
specialcmp (char *a, char *b)
{
while (*a && *a != ' ' && *a != ']' && *b && *b != ' ' && *b != ']')
{
if (*a != *b)
return *a - *b;
a++, b++;
}
if (*a && *a != ' ' && *a != ']')
return 1;
if (*b && *b != ' ' && *b != ']')
return -1;
return 0;
}
static int
compare_selectors (const void *a, const void *b)
{
char *aname, *bname;
aname = SYMBOL_PRINT_NAME (*(struct symbol **) a);
bname = SYMBOL_PRINT_NAME (*(struct symbol **) b);
if (aname == NULL || bname == NULL)
error (_("internal: compare_selectors(1)"));
aname = strchr(aname, ' ');
bname = strchr(bname, ' ');
if (aname == NULL || bname == NULL)
error (_("internal: compare_selectors(2)"));
return specialcmp (aname+1, bname+1);
}
static void
selectors_info (char *regexp, int from_tty)
{
struct objfile *objfile;
struct minimal_symbol *msymbol;
char *name;
char *val;
int matches = 0;
int maxlen = 0;
int ix;
char myregexp[2048];
char asel[256];
struct symbol **sym_arr;
int plusminus = 0;
if (regexp == NULL)
strcpy(myregexp, ".*]");
else
{
if (*regexp == '+' || *regexp == '-')
{
plusminus = *regexp++;
while (*regexp == ' ' || *regexp == '\t')
regexp++;
}
if (*regexp == '\0')
strcpy(myregexp, ".*]");
else
{
strcpy(myregexp, regexp);
if (myregexp[strlen(myregexp) - 1] == '$')
myregexp[strlen(myregexp) - 1] = ']';
else
strcat(myregexp, ".*]");
}
}
if (regexp != NULL)
{
val = re_comp (myregexp);
if (val != 0)
error (_("Invalid regexp (%s): %s"), val, regexp);
}
ALL_MSYMBOLS (objfile, msymbol)
{
QUIT;
name = SYMBOL_NATURAL_NAME (msymbol);
if (name &&
(name[0] == '-' || name[0] == '+') &&
name[1] == '[')
{
if (plusminus && name[0] != plusminus)
continue;
name = (char *) strchr(name+2, ' ');
if (regexp == NULL || re_exec(++name) != 0)
{
char *mystart = name;
char *myend = (char *) strchr(mystart, ']');
if (myend && (myend - mystart > maxlen))
maxlen = myend - mystart;
matches++;
}
}
}
if (matches)
{
printf_filtered (_("Selectors matching \"%s\":\n\n"),
regexp ? regexp : "*");
sym_arr = alloca (matches * sizeof (struct symbol *));
matches = 0;
ALL_MSYMBOLS (objfile, msymbol)
{
QUIT;
name = SYMBOL_NATURAL_NAME (msymbol);
if (name &&
(name[0] == '-' || name[0] == '+') &&
name[1] == '[')
{
if (plusminus && name[0] != plusminus)
continue;
name = (char *) strchr(name+2, ' ');
if (regexp == NULL || re_exec(++name) != 0)
sym_arr[matches++] = (struct symbol *) msymbol;
}
}
qsort (sym_arr, matches, sizeof (struct minimal_symbol *),
compare_selectors);
asel[0] = 0;
for (ix = 0; ix < matches; ix++)
{
char *p = asel;
QUIT;
name = SYMBOL_NATURAL_NAME (sym_arr[ix]);
name = strchr (name, ' ') + 1;
if (p[0] && specialcmp(name, p) == 0)
continue;
while (*name && *name != ']')
*p++ = *name++;
*p++ = '\0';
puts_filtered_tabular(asel, maxlen + 1, 0);
}
begin_line();
}
else
printf_filtered (_("No selectors matching \"%s\"\n"), regexp ? regexp : "*");
}
static int
compare_classes (const void *a, const void *b)
{
char *aname, *bname;
aname = SYMBOL_PRINT_NAME (*(struct symbol **) a);
bname = SYMBOL_PRINT_NAME (*(struct symbol **) b);
if (aname == NULL || bname == NULL)
error (_("internal: compare_classes(1)"));
return specialcmp (aname+1, bname+1);
}
static void
classes_info (char *regexp, int from_tty)
{
struct objfile *objfile;
struct minimal_symbol *msymbol;
char *name;
char *val;
int matches = 0;
int maxlen = 0;
int ix;
char myregexp[2048];
char aclass[256];
struct symbol **sym_arr;
if (regexp == NULL)
strcpy(myregexp, ".* ");
else
{
strcpy(myregexp, regexp);
if (myregexp[strlen(myregexp) - 1] == '$')
myregexp[strlen(myregexp) - 1] = ' ';
else
strcat(myregexp, ".* ");
}
if (regexp != NULL)
{
val = re_comp (myregexp);
if (val != 0)
error (_("Invalid regexp (%s): %s"), val, regexp);
}
ALL_MSYMBOLS (objfile, msymbol)
{
QUIT;
name = SYMBOL_NATURAL_NAME (msymbol);
if (name &&
(name[0] == '-' || name[0] == '+') &&
name[1] == '[')
if (regexp == NULL || re_exec(name+2) != 0)
{
char *mystart = name + 2;
char *myend = (char *) strchr(mystart, ' ');
if (myend && (myend - mystart > maxlen))
maxlen = myend - mystart;
matches++;
}
}
if (matches)
{
printf_filtered (_("Classes matching \"%s\":\n\n"),
regexp ? regexp : "*");
sym_arr = alloca (matches * sizeof (struct symbol *));
matches = 0;
ALL_MSYMBOLS (objfile, msymbol)
{
QUIT;
name = SYMBOL_NATURAL_NAME (msymbol);
if (name &&
(name[0] == '-' || name[0] == '+') &&
name[1] == '[')
if (regexp == NULL || re_exec(name+2) != 0)
sym_arr[matches++] = (struct symbol *) msymbol;
}
qsort (sym_arr, matches, sizeof (struct minimal_symbol *),
compare_classes);
aclass[0] = 0;
for (ix = 0; ix < matches; ix++)
{
char *p = aclass;
QUIT;
name = SYMBOL_NATURAL_NAME (sym_arr[ix]);
name += 2;
if (p[0] && specialcmp(name, p) == 0)
continue;
while (*name && *name != ' ')
*p++ = *name++;
*p++ = '\0';
puts_filtered_tabular(aclass, maxlen + 1, 0);
}
begin_line();
}
else
printf_filtered (_("No classes matching \"%s\"\n"), regexp ? regexp : "*");
}
char *
parse_selector (char *method, char **selector)
{
char *s1 = NULL;
char *s2 = NULL;
int found_quote = 0;
char *nselector = NULL;
gdb_assert (selector != NULL);
s1 = method;
while (isspace (*s1))
s1++;
if (*s1 == '\'')
{
found_quote = 1;
s1++;
}
while (isspace (*s1))
s1++;
nselector = s1;
s2 = s1;
for (;;) {
if (isalnum (*s2) || (*s2 == '_') || (*s2 == ':'))
*s1++ = *s2;
else if (isspace (*s2))
;
else if ((*s2 == '\0') || (*s2 == '\''))
break;
else
return NULL;
s2++;
}
*s1++ = '\0';
while (isspace (*s2))
s2++;
if (found_quote)
{
if (*s2 == '\'')
s2++;
while (isspace (*s2))
s2++;
}
if (selector != NULL)
*selector = nselector;
return s2;
}
char *
parse_method (char *method, char *type, char **class,
char **category, char **selector)
{
char *s1 = NULL;
char *s2 = NULL;
int found_quote = 0;
char ntype = '\0';
char *nclass = NULL;
char *ncategory = NULL;
char *nselector = NULL;
gdb_assert (type != NULL);
gdb_assert (class != NULL);
gdb_assert (category != NULL);
gdb_assert (selector != NULL);
s1 = method;
while (isspace (*s1))
s1++;
if (*s1 == '\'')
{
found_quote = 1;
s1++;
}
while (isspace (*s1))
s1++;
if ((s1[0] == '+') || (s1[0] == '-'))
ntype = *s1++;
while (isspace (*s1))
s1++;
if (*s1 != '[')
return NULL;
s1++;
nclass = s1;
while (isalnum (*s1) || (*s1 == '_'))
s1++;
s2 = s1;
while (isspace (*s2))
s2++;
if (*s2 == '(')
{
s2++;
while (isspace (*s2))
s2++;
ncategory = s2;
while (isalnum (*s2) || (*s2 == '_'))
s2++;
*s2++ = '\0';
}
*s1++ = '\0';
nselector = s2;
s1 = s2;
for (;;) {
if (isalnum (*s2) || (*s2 == '_') || (*s2 == ':'))
*s1++ = *s2;
else if (isspace (*s2))
;
else if (*s2 == ']')
break;
else
return NULL;
s2++;
}
*s1++ = '\0';
s2++;
while (isspace (*s2))
s2++;
if (found_quote)
{
if (*s2 != '\'')
return NULL;
s2++;
while (isspace (*s2))
s2++;
}
if (type != NULL)
*type = ntype;
if (class != NULL)
*class = nclass;
if (category != NULL)
*category = ncategory;
if (selector != NULL)
*selector = nselector;
return s2;
}
static void
find_methods (struct symtab *symtab, char type,
const char *class, const char *category,
const char *selector, struct symbol **syms,
unsigned int *nsym, unsigned int *ndebug)
{
struct objfile *objfile = NULL;
struct minimal_symbol *msymbol = NULL;
struct block *block = NULL;
struct symbol *sym = NULL;
struct cleanup * old_list = NULL;
char *symname = NULL;
char ntype = '\0';
char *nclass = NULL;
char *ncategory = NULL;
char *nselector = NULL;
char *name_end = NULL;
unsigned int csym = 0;
unsigned int cdebug = 0;
static char *tmp = NULL;
static unsigned int tmplen = 0;
gdb_assert (nsym != NULL);
gdb_assert (ndebug != NULL);
if (symtab)
block = BLOCKVECTOR_BLOCK (BLOCKVECTOR (symtab), STATIC_BLOCK);
ALL_MSYMBOLS (objfile, msymbol)
{
QUIT;
if (MSYMBOL_OBSOLETED (msymbol))
continue;
if ((msymbol->type != mst_text) && (msymbol->type != mst_file_text))
continue;
if (symtab)
if (!block_contains_pc (block, SYMBOL_VALUE_ADDRESS (msymbol)))
continue;
symname = SYMBOL_NATURAL_NAME (msymbol);
if (symname == NULL)
continue;
if ((symname[0] != '-' && symname[0] != '+') || (symname[1] != '['))
continue;
while ((strlen (symname) + 1) >= tmplen)
{
tmplen = (tmplen == 0) ? 1024 : tmplen * 2;
tmp = xrealloc (tmp, tmplen);
}
strcpy (tmp, symname);
name_end = parse_method (tmp, &ntype, &nclass, &ncategory, &nselector);
if (name_end == NULL || *name_end != '\0')
continue;
if ((type != '\0') && (ntype != type))
continue;
if ((class != NULL)
&& ((nclass == NULL) || (strcmp (class, nclass) != 0)))
continue;
if ((category != NULL) &&
((ncategory == NULL) || (strcmp (category, ncategory) != 0)))
continue;
if ((selector != NULL) &&
((nselector == NULL) || (strcmp (selector, nselector) != 0)))
continue;
sym = NULL;
if (objfile->separate_debug_objfile)
{
old_list =
make_cleanup_restrict_to_objfile (objfile->separate_debug_objfile);
sym = find_pc_sect_function (SYMBOL_VALUE_ADDRESS (msymbol),
SYMBOL_BFD_SECTION (msymbol));
do_cleanups (old_list);
}
if (sym == NULL)
{
old_list = make_cleanup_restrict_to_objfile (objfile);
sym = find_pc_sect_function (SYMBOL_VALUE_ADDRESS (msymbol),
SYMBOL_BFD_SECTION (msymbol));
do_cleanups (old_list);
}
if (sym != NULL)
{
const char *newsymname = SYMBOL_NATURAL_NAME (sym);
if (strcmp (symname, newsymname) == 0)
{
if (syms != NULL)
{
syms[csym] = syms[cdebug];
syms[cdebug] = sym;
}
csym++;
cdebug++;
}
else
{
warning (
"debugging symbol \"%s\" does not match minimal symbol (\"%s\"); ignoring",
newsymname, symname);
if (syms != NULL)
syms[csym] = (struct symbol *) msymbol;
csym++;
}
}
else
{
if (syms != NULL)
syms[csym] = (struct symbol *) msymbol;
csym++;
}
}
if (nsym != NULL)
*nsym = csym;
if (ndebug != NULL)
*ndebug = cdebug;
}
char *
find_imps (struct symtab *symtab, struct block *block,
char *method, struct symbol **syms,
unsigned int *nsym, unsigned int *ndebug)
{
char type = '\0';
char *class = NULL;
char *category = NULL;
char *selector = NULL;
unsigned int csym = 0;
unsigned int cdebug = 0;
unsigned int ncsym = 0;
unsigned int ncdebug = 0;
char *buf = NULL;
char *tmp = NULL;
gdb_assert (nsym != NULL);
gdb_assert (ndebug != NULL);
if (nsym != NULL)
*nsym = 0;
if (ndebug != NULL)
*ndebug = 0;
buf = (char *) alloca (strlen (method) + 1);
strcpy (buf, method);
tmp = parse_method (buf, &type, &class, &category, &selector);
if (tmp == NULL) {
struct symbol *sym = NULL;
struct minimal_symbol *msym = NULL;
strcpy (buf, method);
tmp = parse_selector (buf, &selector);
if (tmp == NULL)
return NULL;
sym = lookup_symbol (selector, block, VAR_DOMAIN, 0, NULL);
if (sym != NULL)
{
if (syms)
syms[csym] = sym;
csym++;
cdebug++;
}
if (sym == NULL)
msym = lookup_minimal_symbol (selector, 0, 0);
if (msym != NULL)
{
if (syms)
syms[csym] = (struct symbol *)msym;
csym++;
}
}
if (syms != NULL)
find_methods (symtab, type, class, category, selector,
syms + csym, &ncsym, &ncdebug);
else
find_methods (symtab, type, class, category, selector,
NULL, &ncsym, &ncdebug);
if (ncsym == 0 && ncdebug == 0)
return method;
if (syms != NULL)
{
while ((cdebug < csym) && (ncdebug > 0))
{
struct symbol *s = NULL;
unsigned int i = cdebug;
unsigned int j = csym + ncdebug - 1;
s = syms[j];
syms[j] = syms[i];
syms[i] = s;
cdebug++;
ncdebug--;
}
}
csym += ncsym;
cdebug += ncdebug;
if (nsym != NULL)
*nsym = csym;
if (ndebug != NULL)
*ndebug = cdebug;
if (syms == NULL)
return method + (tmp - buf);
if (csym > 1)
{
if (cdebug > 1)
qsort (syms, cdebug, sizeof (struct minimal_symbol *),
compare_classes);
if ((csym - cdebug) > 1)
qsort (&syms[cdebug], csym - cdebug,
sizeof (struct minimal_symbol *), compare_classes);
}
syms[csym] = 0;
return method + (tmp - buf);
}
int
objc_check_safe_to_run_all_threads ()
{
if (!target_check_safe_call (MALLOC_SUBSYSTEM, CHECK_CURRENT_THREAD))
{
warning ("Canceling operation - malloc lock could be held on current thread.");
return 0;
}
else if (!target_check_safe_call (SPINLOCK_SUBSYSTEM, CHECK_CURRENT_THREAD))
{
warning ("Canceling operation - spin_lock could be held on current thread.");
return 0;
}
else if (!target_check_safe_call (OBJC_SUBSYSTEM, CHECK_CURRENT_THREAD))
{
warning ("Canceling call as the ObjC runtime would deadlock.");
return 0;
}
return 1;
}
int
objc_setup_safe_print (struct cleanup **cleanup)
{
enum objc_debugger_mode_result retval;
struct cleanup *ret_cleanup = make_cleanup (null_cleanup, NULL);
struct cleanup *lock_cleanup;
int safe_p = 0;
if (cleanup != NULL)
*cleanup = ret_cleanup;
if (objc_runtime_check_enabled_p ())
{
static struct ui_out *null_uiout = NULL, *stored_uiout;
struct gdb_exception e;
if (null_uiout == NULL)
null_uiout = cli_out_new (gdb_null);
stored_uiout = uiout;
uiout = null_uiout;
lock_cleanup = make_cleanup_set_restore_scheduler_locking_mode (scheduler_locking_on);
TRY_CATCH (e, RETURN_MASK_ERROR)
{
retval = make_cleanup_set_restore_debugger_mode (NULL, 1);
}
if (debug_handcall_setup)
{
char *data = ui_file_data (gdb_null);
if (data == NULL)
data = "";
fprintf_unfiltered ( gdb_stdout, "Setting debugger mode returned: %d output: \"%s\"\n",
retval, data);
}
ui_file_rewind (gdb_null);
uiout = stored_uiout;
if (e.reason != NO_ERROR)
{
if (debug_handcall_setup || info_verbose)
fprintf_unfiltered (gdb_stdout, "Got uncaught error setting debugger mode, returning NULL cleanup.\n");
do_cleanups (lock_cleanup);
safe_p = 0;
}
else if (retval == objc_debugger_mode_fail_objc_api_unavailable)
{
if (debug_handcall_setup || info_verbose)
fprintf_unfiltered (gdb_stdout, "Can't find objc non-blocking mode, falling back to old print behavior.\n");
do_cleanups (lock_cleanup);
safe_p = 0;
}
else
{
if (retval == objc_debugger_mode_success)
{
safe_p = 1;
}
else
{
do_cleanups (lock_cleanup);
if (scheduler_lock_on_p ())
{
if (debug_handcall_setup || info_verbose)
fprintf_unfiltered (gdb_stdout, "Would have to allow all threads to run to avoid the "
"possible deadlock, but scheduler locking is on. Returning NULL cleanup.\n");
safe_p = 0;
}
else if (objc_check_safe_to_run_all_threads())
{
if (debug_handcall_setup || info_verbose)
fprintf_unfiltered (gdb_stdout, "Allowing all threads to run to avoid the possible deadlock.\n");
ret_cleanup = make_cleanup_set_restore_scheduler_locking_mode (scheduler_locking_off);
make_cleanup_set_restore_debugger_mode (NULL, 0);
safe_p = 1;
}
else
{
if (debug_handcall_setup || info_verbose)
fprintf_unfiltered (gdb_stdout, "It is unsafe to run code on the current thread, returning NULL cleanup.\n");
safe_p = 0;
}
}
}
}
return safe_p;
}
static void
print_object_command (char *args, int from_tty)
{
struct value *object, *function, *description;
struct cleanup *cleanup_chain = NULL;
struct cleanup *debugger_mode;
CORE_ADDR string_addr, object_addr;
int i = 0;
gdb_byte c = 0;
const char *fn_name;
struct expression *expr;
struct cleanup *old_chain;
int pc = 0;
if (!args || !*args)
error (
"The 'print-object' command requires an argument (an Objective-C object)");
if (call_po_at_unsafe_times)
{
make_cleanup_set_restore_debugger_mode (&debugger_mode, 0);
}
else
{
int safe_p;
safe_p = target_setup_safe_print (&debugger_mode);
if (!safe_p)
{
do_cleanups(debugger_mode);
if (scheduler_lock_on_p())
warning ("Running code with the scheduler locked on the current thread is likely to deadlock.\n"
"Set scheduler-locking to off and try again.");
else
warning ("Canceling call as it likely running code on the current thread will deadlock.\n"
"Set call-po-at-unsafe-times to override this check.");
return;
}
}
innermost_block = NULL;
expr = parse_expression (args);
old_chain = make_cleanup (free_current_contents, &expr);
object = expr->language_defn->la_exp_desc->evaluate_exp
(builtin_type_void_data_ptr, expr, &pc, EVAL_NORMAL);
do_cleanups (old_chain);
if (object != NULL && TYPE_CODE (value_type (object)) == TYPE_CODE_ERROR)
{
struct type *id_type;
id_type = lookup_typename ("id", NULL, 1);
if (id_type)
object = value_cast (id_type, object);
}
object_addr = value_as_address (object);
read_memory (object_addr, &c, 1);
fn_name = "_NSPrintForDebugger";
if (lookup_minimal_symbol (fn_name, NULL, NULL) == NULL)
{
fn_name = "_CFPrintForDebugger";
if (lookup_minimal_symbol (fn_name, NULL, NULL) == NULL)
error (_("Unable to locate _NSPrintForDebugger or _CFPrintForDebugger "
"in child process"));
}
function = find_function_in_inferior (fn_name,
builtin_type_voidptrfuncptr);
if (function == NULL)
error (_("Unable to locate _NSPrintForDebugger or _CFPrintForDebugger in "
"child process"));
cleanup_chain = make_cleanup_set_restore_unwind_on_signal (1);
description = call_function_by_hand (function, 1, &object);
do_cleanups (cleanup_chain);
do_cleanups (debugger_mode);
string_addr = value_as_address (description);
if (string_addr == 0)
error (_("object returns null description"));
read_memory (string_addr + i++, &c, 1);
if (c != 0)
do
{
QUIT;
printf_filtered ("%c", c);
read_memory (string_addr + i++, &c, 1);
} while (c != 0);
else
printf_filtered(_("<object returns empty description>"));
printf_filtered ("\n");
}
struct objc_methcall {
char *name;
int (*stop_at) (CORE_ADDR, CORE_ADDR *);
CORE_ADDR begin;
CORE_ADDR end;
};
static int resolve_msgsend (CORE_ADDR pc, CORE_ADDR *new_pc);
static int resolve_msgsend_stret (CORE_ADDR pc, CORE_ADDR *new_pc);
static int resolve_msgsend_super (CORE_ADDR pc, CORE_ADDR *new_pc);
static int resolve_msgsend_super_stret (CORE_ADDR pc, CORE_ADDR *new_pc);
static int resolve_msgsend_fixup (CORE_ADDR pc, CORE_ADDR *new_pc);
static int resolve_msgsend_fixedup (CORE_ADDR pc, CORE_ADDR *new_pc);
static int resolve_msgsend_stret_fixup (CORE_ADDR pc, CORE_ADDR *new_pc);
static int resolve_msgsend_stret_fixedup (CORE_ADDR pc, CORE_ADDR *new_pc);
static int resolve_msgsendsuper2_fixup (CORE_ADDR pc, CORE_ADDR *new_pc);
static int resolve_msgsendsuper2_fixedup (CORE_ADDR pc, CORE_ADDR *new_pc);
static int resolve_msgsendsuper2_stret_fixup (CORE_ADDR pc, CORE_ADDR *new_pc);
static int resolve_msgsendsuper2_stret_fixedup (CORE_ADDR pc, CORE_ADDR *new_pc);
static int resolve_msgsendsuper2 (CORE_ADDR pc, CORE_ADDR *new_pc);
static int resolve_msgsendsuper2_stret (CORE_ADDR pc, CORE_ADDR *new_pc);
static struct objc_methcall methcalls[] = {
{ "_objc_msgSend", resolve_msgsend, 0, 0},
{ "_objc_msgSend_rtp", resolve_msgsend, 0, 0},
{ "_objc_msgSend_stret", resolve_msgsend_stret, 0, 0},
{ "_objc_msgSendSuper", resolve_msgsend_super, 0, 0},
{ "_objc_msgSendSuper_stret", resolve_msgsend_super_stret, 0, 0},
{ "_objc_getClass", NULL, 0, 0},
{ "_objc_getMetaClass", NULL, 0, 0},
{ "_objc_msgSend_fixup", resolve_msgsend_fixup, 0, 0},
{ "_objc_msgSend_fixedup", resolve_msgsend_fixedup, 0, 0},
{ "_objc_msgSend_stret_fixup", resolve_msgsend_stret_fixup, 0, 0},
{ "_objc_msgSend_stret_fixedup", resolve_msgsend_stret_fixedup, 0, 0},
{ "_objc_msgSendSuper2", resolve_msgsendsuper2, 0, 0},
{ "_objc_msgSendSuper2_fixup", resolve_msgsendsuper2_fixup, 0, 0},
{ "_objc_msgSendSuper2_fixedup", resolve_msgsendsuper2_fixedup, 0, 0},
{ "_objc_msgSendSuper2_stret", resolve_msgsendsuper2_stret, 0, 0},
{ "_objc_msgSendSuper2_stret_fixup", resolve_msgsendsuper2_stret_fixup, 0, 0},
{ "_objc_msgSendSuper2_stret_fixedup", resolve_msgsendsuper2_stret_fixedup, 0, 0}
};
#define nmethcalls (sizeof (methcalls) / sizeof (methcalls[0]))
static int cached_objc_msgsend_table_is_valid = 0;
static void
find_objc_msgsend (void)
{
unsigned int i;
if (cached_objc_msgsend_table_is_valid)
return;
for (i = 0; i < nmethcalls; i++)
{
struct minimal_symbol *func, *orig_func;
func = lookup_minimal_symbol (methcalls[i].name, NULL, NULL);
if (func == NULL && methcalls[i].name[0] == '_')
func = lookup_minimal_symbol (methcalls[i].name + 1, NULL, NULL);
if (func == NULL)
{
methcalls[i].begin = 0;
methcalls[i].end = 0;
continue;
}
methcalls[i].begin = SYMBOL_VALUE_ADDRESS (func);
orig_func = func;
if (find_pc_partial_function_no_inlined (SYMBOL_VALUE_ADDRESS (func), NULL,
NULL,
&methcalls[i].end) == 0)
{
methcalls[i].end = methcalls[i].begin + 1;
}
}
cached_objc_msgsend_table_is_valid = 1;
}
void
tell_objc_msgsend_cacher_objfile_changed (struct objfile *obj )
{
cached_objc_msgsend_table_is_valid = 0;
if (obj != NULL && obj->name != NULL
&& strstr (obj->name, objc_library_name) != NULL)
{
cached_objc_objfile = NULL;
objc_clear_trampoline_data ();
}
}
struct objc_submethod_helper_data {
int (*f) (CORE_ADDR, CORE_ADDR *);
CORE_ADDR pc;
CORE_ADDR *new_pc;
};
static int
find_objc_msgcall_submethod_helper (void * arg)
{
struct objc_submethod_helper_data *s =
(struct objc_submethod_helper_data *) arg;
if (s->f (s->pc, s->new_pc) == 0)
return 1;
else
return 0;
}
static int
find_objc_msgcall_submethod (int (*f) (CORE_ADDR, CORE_ADDR *),
CORE_ADDR pc,
CORE_ADDR *new_pc)
{
struct objc_submethod_helper_data s;
s.f = f;
s.pc = pc;
s.new_pc = new_pc;
if (catch_errors (find_objc_msgcall_submethod_helper,
(void *) &s,
"Unable to determine target of Objective-C method call (ignoring):\n",
RETURN_MASK_ALL) == 0)
return 1;
else
return 0;
}
int
find_objc_msgcall (CORE_ADDR pc, CORE_ADDR *new_pc)
{
unsigned int i;
unsigned int flags;
find_objc_msgsend ();
if (new_pc != NULL)
*new_pc = 0;
for (i = 0; i < nmethcalls; i++)
if (pc >= methcalls[i].begin && pc < methcalls[i].end)
{
if (methcalls[i].stop_at != NULL)
return find_objc_msgcall_submethod (methcalls[i].stop_at, pc, new_pc);
else
return 0;
}
if (pc_in_objc_trampoline_p (pc, &flags))
{
if (flags & (OBJC_TRAMPOLINE_MESSAGE | OBJC_TRAMPOLINE_VTABLE))
{
if (flags & OBJC_TRAMPOLINE_STRET)
return resolve_msgsend_stret_fixedup (pc, new_pc);
else
return resolve_msgsend_fixedup (pc, new_pc);
}
}
return 0;
}
extern initialize_file_ftype _initialize_objc_language;
void
_initialize_objc_language (void)
{
add_language (&objc_language_defn);
add_language (&objcplus_language_defn);
add_info ("selectors", selectors_info,
_("All Objective-C selectors, or those matching REGEXP."));
add_info ("classes", classes_info,
_("All Objective-C classes, or those matching REGEXP."));
add_com ("print-object", class_vars, print_object_command,
_("Ask an Objective-C object to print itself."));
add_com_alias ("po", "print-object", class_vars, 1);
add_setshow_uinteger_cmd ("objc-class-method-limit", class_obscure,
&objc_class_method_limit,
"Set the maximum number of class methods we scan before deciding we are looking at an uninitialized object.",
"Show the maximum number of class methods we scan before deciding we are looking at an uninitialized object.",
NULL,
NULL, NULL,
&setlist, &showlist);
}
int
new_objc_runtime_internals ()
{
if (objc_runtime_version_user_override == 1)
return 0;
else if (objc_runtime_version_user_override == 2)
return 1;
if (objc_runtime_version != 0)
return objc_runtime_version;
#if defined (TARGET_ARM)
return 1;
#else
if (TARGET_ADDRESS_BYTES == 8)
return 1;
else
return 0;
#endif
}
static CORE_ADDR
new_objc_runtime_class_getClass (struct value *infargs)
{
static struct cached_value *validate_function = NULL;
static int already_warned = 0;
struct value *ret_value = NULL;
CORE_ADDR in_class_address = (CORE_ADDR) value_as_address (infargs);
if (class_valid_p (in_class_address))
return in_class_address;
if (validate_function == NULL)
{
if (lookup_minimal_symbol ("gdb_class_getClass", 0, 0))
validate_function = create_cached_function ("gdb_class_getClass",
builtin_type_voidptrfuncptr);
else
{
if (!already_warned)
{
already_warned = 1;
warning ("Couldn't find class validation function, calling "
"methods on uninitialized objects may deadlock your "
"program.");
}
}
}
if (validate_function != NULL)
{
struct gdb_exception e;
int old_timeout = set_hand_function_call_timeout (500000);
TRY_CATCH (e, RETURN_MASK_ALL)
{
ret_value = call_function_by_hand
(lookup_cached_function (validate_function),
1, &infargs);
}
set_hand_function_call_timeout (old_timeout);
if (e.reason != NO_ERROR || ret_value == NULL)
{
if (hand_function_call_timeout_p ())
warning ("Call to get object type timed out. Most likely somebody "
"has the objc runtime lock.");
return (CORE_ADDR) 0;
}
else
{
CORE_ADDR class_address = (CORE_ADDR) value_as_address (ret_value);
add_class_to_cache (class_address);
return class_address;
}
}
else
return (CORE_ADDR) 0;
}
static CORE_ADDR
new_objc_runtime_find_impl (CORE_ADDR class, CORE_ADDR sel, int stret)
{
static struct cached_value *function = NULL;
static struct cached_value *function_stret = NULL;
static struct cached_value *function_cache_getImp = NULL;
struct value *ret_value;
struct cleanup *scheduler_cleanup;
CORE_ADDR retval = 0;
enum objc_debugger_mode_result objc_retval;
if (!target_has_execution)
{
return 0;
}
if (stret == 0 && function == NULL)
{
if (lookup_minimal_symbol ("class_getMethodImplementation", 0, 0))
function = create_cached_function ("class_getMethodImplementation",
builtin_type_voidptrfuncptr);
else
return 0;
}
if (stret == 1 && function_stret == NULL)
{
if (lookup_minimal_symbol ("class_getMethodImplementation_stret", 0, 0))
function_stret = create_cached_function
("class_getMethodImplementation_stret",
builtin_type_voidptrfuncptr);
else
return 0;
}
scheduler_cleanup = make_cleanup_set_restore_scheduler_locking_mode
(scheduler_locking_on);
make_cleanup_set_restore_unwind_on_signal (1);
make_cleanup_ui_out_suppress_output (uiout);
objc_retval = make_cleanup_set_restore_debugger_mode (NULL, 1);
if (retval == objc_debugger_mode_fail_objc_api_unavailable)
if (target_check_safe_call (OBJC_SUBSYSTEM, CHECK_SCHEDULER_VALUE))
retval = objc_debugger_mode_success;
if (objc_retval == objc_debugger_mode_success)
{
struct value *classval, *selval;
struct value *infargs[2];
char *imp_name;
struct cleanup *value_cleanup;
classval = value_from_pointer (lookup_pointer_type
(builtin_type_void_data_ptr), class);
selval = value_from_pointer (lookup_pointer_type
(builtin_type_void_data_ptr), sel);
release_value (classval);
release_value (selval);
value_cleanup = make_cleanup ((make_cleanup_ftype *) value_free, classval);
make_cleanup ((make_cleanup_ftype *) value_free, selval);
infargs[0] = classval;
infargs[1] = selval;
retval = new_objc_runtime_class_getClass (classval);
if (retval == 0)
goto cleanups;
ret_value = call_function_by_hand
(lookup_cached_function (stret ? function_stret : function),
2, infargs);
do_cleanups (value_cleanup);
retval = (CORE_ADDR) value_as_address (ret_value);
retval = gdbarch_addr_bits_remove (current_gdbarch, retval);
if (find_pc_partial_function_no_inlined (retval, &imp_name, NULL, NULL))
{
if (strcmp (imp_name, "_objc_msgForward") == 0
|| strcmp (imp_name, "_objc_msgForward_stret") == 0)
{
retval = 0;
goto cleanups;
}
}
add_implementation_to_cache (class, sel, retval);
}
else
{
if (objc_retval == objc_debugger_mode_fail_malloc_lock_held)
{
struct cleanup *make_call_cleanup;
make_cleanup_set_restore_debugger_mode (&make_call_cleanup, 0);
struct value *classval, *selval;
struct value *infargs[2];
char *imp_name;
if (function_cache_getImp == NULL)
{
if (lookup_minimal_symbol ("_cache_getImp", 0, 0))
function_cache_getImp = create_cached_function ("_cache_getImp",
builtin_type_voidptrfuncptr);
else
{
retval = 0;
goto cleanups;
}
}
classval = value_from_pointer (lookup_pointer_type
(builtin_type_void_data_ptr), class);
selval = value_from_pointer (lookup_pointer_type
(builtin_type_void_data_ptr), sel);
infargs[0] = classval;
infargs[1] = selval;
ret_value = call_function_by_hand
(lookup_cached_function (function_cache_getImp), 2, infargs);
retval = (CORE_ADDR) value_as_address (ret_value);
retval = gdbarch_addr_bits_remove (current_gdbarch, retval);
if (retval != 0)
{
if (find_pc_partial_function_no_inlined (retval,
&imp_name, NULL, NULL))
{
if (strcmp (imp_name, "_objc_msgForward") == 0
|| strcmp (imp_name, "_objc_msgForward_stret") == 0)
{
retval = 0;
goto cleanups;
}
}
add_implementation_to_cache (class, sel, retval);
}
do_cleanups (make_call_cleanup);
}
else
{
fprintf_unfiltered (gdb_stdout, "Not safe to look up objc runtime data.\n");
}
}
cleanups:
do_cleanups (scheduler_cleanup);
return retval;
}
static void
read_objc_method (CORE_ADDR addr, struct objc_method *method)
{
int addrsize = TARGET_ADDRESS_BYTES;
method->name = read_memory_unsigned_integer (addr + 0, addrsize);
method->types = read_memory_unsigned_integer (addr + addrsize, addrsize);
method->imp = read_memory_unsigned_integer (addr + addrsize * 2, addrsize);
}
static unsigned long
read_objc_method_list_nmethods (CORE_ADDR addr)
{
int addrsize = TARGET_ADDRESS_BYTES;
return read_memory_unsigned_integer (addr + addrsize, 4);
}
static void
read_objc_method_list_method (CORE_ADDR addr, unsigned long num,
struct objc_method *method)
{
int addrsize = TARGET_ADDRESS_BYTES;
int offset;
if (addrsize == 8)
offset = addrsize + 4 + 4;
else
offset = addrsize + 4;
gdb_assert (num < read_objc_method_list_nmethods (addr));
read_objc_method (addr + offset + (3 * addrsize * num), method);
}
static void
read_objc_object (CORE_ADDR addr, struct objc_object *object)
{
static struct cached_value *object_get_class_function = NULL;
static int already_warned = 0;
int addrsize = TARGET_ADDRESS_BYTES;
int success;
struct ui_file *prev_stderr = gdb_stderr;
gdb_stderr = gdb_null;
success = safe_read_memory_unsigned_integer (addr, addrsize,
&(object->isa));
gdb_stderr = prev_stderr;
ui_file_rewind (gdb_null);
if (!new_objc_runtime_internals())
{
if (success)
{
return;
}
else
{
object->isa = (CORE_ADDR) 0;
return;
}
}
if (success)
{
if (class_valid_p (object->isa))
return;
}
else
object->isa = (CORE_ADDR) 0;
if (object_get_class_function == NULL)
{
if (lookup_minimal_symbol ("gdb_object_getClass", 0, 0))
{
object_get_class_function = create_cached_function
("gdb_object_getClass",
builtin_type_voidptrfuncptr);
}
else
{
if (info_verbose && !already_warned)
{
already_warned = 1;
warning ("Couldn't find gdb_object_getClass, we may not properly"
" handle objective-c tagged pointers.");
}
}
}
if (object_get_class_function == NULL)
{
if (success)
return;
else
{
object->isa = (CORE_ADDR) 0;
return;
}
}
else
{
struct value *ret_value = NULL;
struct gdb_exception e;
int old_timeout = set_hand_function_call_timeout (500000);
struct value *object_ptr_val;
struct cleanup *unwind_cleanup;
object_ptr_val = value_from_pointer (lookup_pointer_type
(builtin_type_void_data_ptr), addr);
unwind_cleanup = make_cleanup_set_restore_unwind_on_signal (1);
TRY_CATCH (e, RETURN_MASK_ALL)
{
ret_value = call_function_by_hand
(lookup_cached_function (object_get_class_function),
1, &object_ptr_val);
}
do_cleanups (unwind_cleanup);
set_hand_function_call_timeout (old_timeout);
if (e.reason != NO_ERROR || ret_value == NULL)
{
if (hand_function_call_timeout_p ())
warning ("Call to get object class timed out.");
object->isa = (CORE_ADDR) 0;
}
else
{
object->isa = (CORE_ADDR) value_as_address (ret_value);
add_class_to_cache (object->isa);
}
}
}
static void
read_objc_super (CORE_ADDR addr, struct objc_super *super)
{
int addrsize = TARGET_ADDRESS_BYTES;
super->receiver = read_memory_unsigned_integer (addr, addrsize);
super->class = read_memory_unsigned_integer (addr + addrsize, addrsize);
};
static void
read_objc_class (CORE_ADDR addr, struct objc_class *class)
{
int addrsize = TARGET_ADDRESS_BYTES;
class->isa = read_memory_unsigned_integer (addr, addrsize);
class->super_class = read_memory_unsigned_integer (addr + addrsize, addrsize);
class->name = read_memory_unsigned_integer (addr + addrsize * 2, addrsize);
class->version = read_memory_unsigned_integer (addr + addrsize * 3, addrsize);
class->info = read_memory_unsigned_integer (addr + addrsize * 4, addrsize);
class->instance_size = read_memory_unsigned_integer
(addr + addrsize * 5, addrsize);
class->ivars = read_memory_unsigned_integer (addr + addrsize * 6, addrsize);
class->methods = read_memory_unsigned_integer (addr + addrsize * 7, addrsize);
class->cache = read_memory_unsigned_integer (addr + addrsize * 8, addrsize);
class->protocols = read_memory_unsigned_integer
(addr + addrsize * 9, addrsize);
}
#define GC_IGNORED_SELECTOR_LE 0xfffeb010
static void
free_rb_tree_data (struct rb_tree_node *root, void (*free_fn) (void *))
{
if (root->left)
free_rb_tree_data (root->left, free_fn);
if (root->right)
free_rb_tree_data (root->right, free_fn);
if (free_fn != NULL && root->data != NULL)
free_fn (root->data);
xfree (root);
}
struct objfile *
find_libobjc_objfile ()
{
if (cached_objc_objfile == NULL)
cached_objc_objfile = executable_objfile (find_objfile_by_name (objc_library_name, 0));
return cached_objc_objfile;
}
void
reinitialize_objc ()
{
objc_clear_caches ();
objc_clear_trampoline_data ();
debug_mode_set_p = debug_mode_not_checked;
debug_mode_set_reason = objc_debugger_mode_unknown;
cached_objc_objfile = NULL;
}
void
objc_clear_caches ()
{
if (implementation_tree != NULL)
{
free_rb_tree_data (implementation_tree, xfree);
implementation_tree = NULL;
}
if (classname_tree != NULL)
{
free_rb_tree_data (classname_tree, xfree);
classname_tree = NULL;
}
if (real_class_tree != NULL)
{
free_rb_tree_data (real_class_tree, xfree);
real_class_tree = NULL;
}
}
static CORE_ADDR
lookup_implementation_in_cache (CORE_ADDR class, CORE_ADDR sel)
{
struct rb_tree_node *found;
found = rb_tree_find_node_all_keys (implementation_tree, class, -1, sel);
if (found == NULL)
return 0;
else
return *((CORE_ADDR *) found->data);
}
static void
add_implementation_to_cache (CORE_ADDR class, CORE_ADDR sel, CORE_ADDR implementation)
{
struct rb_tree_node *new_node = (struct rb_tree_node *) xmalloc (sizeof (struct rb_tree_node));
new_node->key = class;
new_node->secondary_key = -1;
new_node->third_key = sel;
new_node->data = xmalloc (sizeof (CORE_ADDR));
*((CORE_ADDR *) new_node->data) = implementation;
new_node->left = NULL;
new_node->right = NULL;
new_node->parent = NULL;
new_node->color = UNINIT;
rb_tree_insert (&implementation_tree, implementation_tree, new_node);
}
static CORE_ADDR
find_implementation_from_class (CORE_ADDR class, CORE_ADDR sel)
{
CORE_ADDR subclass = class;
char sel_str[2048];
int npasses;
int addrsize = TARGET_ADDRESS_BYTES;
int total_methods = 0;
CORE_ADDR implementation;
sel_str[0] = '\0';
implementation = lookup_implementation_in_cache (class, sel);
if (implementation != 0)
return implementation;
if (new_objc_runtime_internals ())
return (new_objc_runtime_find_impl (class, sel, 0));
if (sel == GC_IGNORED_SELECTOR_LE)
return 0;
{
CORE_ADDR class_addr;
struct objc_class class_str;
char class_name[2048];
char *ptr;
struct gdb_exception e;
TRY_CATCH (e, RETURN_MASK_ALL)
{
read_objc_class (class, &class_str);
read_memory_string (class_str.name, class_name, 2048);
}
if (e.reason != NO_ERROR)
{
if (info_verbose)
{
printf_unfiltered ("Got error reading class or its name.\n");
}
return 0;
}
for (ptr = class_name; *ptr != '\0'; ptr++)
{
if (!isprint (*ptr))
{
if (info_verbose)
{
printf_unfiltered ("Class name \"%s\" contains a non-printing character.\n", class_name);
}
return 0;
}
}
class_addr = lookup_objc_class (class_name);
if (class_addr == 0)
{
if (info_verbose)
printf_unfiltered ("Could not look up class for name: \"%s\".\n", class_name);
return 0;
}
else
{
struct objc_class verify_str;
TRY_CATCH (e, RETURN_MASK_ALL)
{
read_objc_class (class, &verify_str);
}
if (e.reason == NO_ERROR && verify_str.name != class_str.name)
{
if (info_verbose)
printf_unfiltered ("Class address for name: \"%s\": 0x%s didn't match input address: 0x%s.\n",
class_name, paddr_nz (class_addr), paddr_nz (class));
return 0;
}
}
}
while (subclass != 0)
{
struct objc_class class_str;
unsigned mlistnum = 0;
int class_initialized;
read_objc_class (subclass, &class_str);
class_initialized = ((class_str.info & 0x4L) == 0x4L);
if (!class_initialized)
{
if (sel_str[0] == '\0')
{
read_memory_string (sel, sel_str, 2047);
}
}
if (info_verbose)
{
char buffer[2048];
read_memory_string (class_str.name, buffer, 2047);
buffer[2047] = '\0';
printf_filtered ("Reading methods for %s, info is 0x%s\n", buffer, paddr_nz (class_str.info));
}
#define CLS_NO_METHOD_ARRAY 0x4000
npasses = 0;
for (;;)
{
CORE_ADDR mlist;
unsigned long nmethods;
unsigned long i;
npasses++;
if (class_str.methods == 0x0)
break;
else if (class_str.info & CLS_NO_METHOD_ARRAY)
{
if (npasses == 1)
mlist = class_str.methods;
else
break;
}
else
{
mlist = read_memory_unsigned_integer
(class_str.methods + (addrsize * mlistnum), addrsize);
if (mlist == 0 || mlist == 0xffffffff
|| mlist == 0xffffffffffffffffULL)
break;
}
nmethods = read_objc_method_list_nmethods (mlist);
for (i = 0; i < nmethods; i++)
{
struct objc_method meth_str;
char name_str[2048];
if (++total_methods >= objc_class_method_limit)
{
static int only_warn_once = 0;
if (only_warn_once == 0)
warning ("Read %d potential method entries, probably looking "
"at an unitialized object.\n"
"Set objc-class-method-limit to higher value if your class"
" really has this many methods.",
total_methods);
only_warn_once++;
return 0;
}
read_objc_method_list_method (mlist, i, &meth_str);
if (meth_str.name == GC_IGNORED_SELECTOR_LE)
continue;
if (!class_initialized)
read_memory_string (meth_str.name, name_str, 2047);
#if 0
if (class_initialized)
read_memory_string (meth_str.name, name_str, 2047);
fprintf (stderr,
"checking method 0x%lx (%s) against selector 0x%lx\n",
(long unsigned int) meth_str.name, name_str, (long unsigned int) sel);
#endif
if (meth_str.name == sel || (!class_initialized
&& (name_str[0] == sel_str[0])
&& (strcmp (name_str, sel_str) == 0)))
{
add_implementation_to_cache (class, sel, meth_str.imp);
return meth_str.imp;
}
}
mlistnum++;
}
subclass = class_str.super_class;
}
return 0;
}
CORE_ADDR
find_implementation (CORE_ADDR object, CORE_ADDR sel, int stret)
{
struct objc_object ostr;
CORE_ADDR real_class_addr;
if (object == 0)
return 0;
read_objc_object (object, &ostr);
if (ostr.isa == 0)
return 0;
real_class_addr = ostr.isa;
if (new_objc_runtime_internals ())
{
CORE_ADDR resolves_to;
struct cleanup *cleanup;
resolves_to = lookup_implementation_in_cache (ostr.isa, sel);
if (resolves_to != 0)
return resolves_to;
if (make_cleanup_set_restore_debugger_mode (&cleanup, 1) ==
objc_debugger_mode_fail_malloc_lock_held)
{
real_class_addr = ostr.isa;
}
else
{
real_class_addr = get_class_address_from_object (object);
if (real_class_addr != ostr.isa)
{
resolves_to = lookup_implementation_in_cache (real_class_addr,
sel);
if (resolves_to != 0)
{
add_implementation_to_cache (ostr.isa, sel, resolves_to);
do_cleanups (cleanup);
return resolves_to;
}
}
}
do_cleanups (cleanup);
resolves_to = new_objc_runtime_find_impl (real_class_addr, sel, stret);
if (resolves_to != 0)
{
if (real_class_addr != ostr.isa)
add_implementation_to_cache (ostr.isa, sel, resolves_to);
return (resolves_to);
}
}
if (new_objc_runtime_internals ())
return find_implementation_from_class (real_class_addr, sel);
else
return find_implementation_from_class (ostr.isa, sel);
}
static int
resolve_msgsend (CORE_ADDR pc, CORE_ADDR *new_pc)
{
CORE_ADDR object;
CORE_ADDR sel;
CORE_ADDR res;
object = OBJC_FETCH_POINTER_ARGUMENT (0);
sel = OBJC_FETCH_POINTER_ARGUMENT (1);
res = find_implementation (object, sel, 0);
if (new_pc != 0)
*new_pc = res;
if (res == 0)
return 1;
return 0;
}
static int
resolve_newruntime_objc_msgsend (CORE_ADDR pc, CORE_ADDR *new_pc,
int fixedup, int stret)
{
CORE_ADDR object;
CORE_ADDR sel;
CORE_ADDR res;
int addrsize = TARGET_ADDRESS_BYTES;
if (stret == 0)
{
object = OBJC_FETCH_POINTER_ARGUMENT (0);
sel = OBJC_FETCH_POINTER_ARGUMENT (1);
}
else
{
object = OBJC_FETCH_POINTER_ARGUMENT (1);
sel = OBJC_FETCH_POINTER_ARGUMENT (2);
}
sel = read_memory_unsigned_integer (sel + addrsize, addrsize);
if (fixedup == 0)
{
char selname[2048];
selname[0] = '\0';
read_memory_string (sel, selname, 2047);
sel = lookup_child_selector (selname);
}
res = find_implementation (object, sel, stret);
if (new_pc != 0)
*new_pc = res;
if (res == 0)
return 1;
return 0;
}
static int
resolve_msgsend_fixup (CORE_ADDR pc, CORE_ADDR *new_pc)
{
return resolve_newruntime_objc_msgsend (pc, new_pc, 0, 0);
}
static int
resolve_msgsend_fixedup (CORE_ADDR pc, CORE_ADDR *new_pc)
{
return resolve_newruntime_objc_msgsend (pc, new_pc, 1, 0);
}
static int
resolve_msgsend_stret_fixup (CORE_ADDR pc, CORE_ADDR *new_pc)
{
return resolve_newruntime_objc_msgsend (pc, new_pc, 0, 1);
}
static int
resolve_msgsend_stret_fixedup (CORE_ADDR pc, CORE_ADDR *new_pc)
{
return resolve_newruntime_objc_msgsend (pc, new_pc, 1, 1);
}
static int
resolve_newruntime_objc_msgsendsuper (CORE_ADDR pc, CORE_ADDR *new_pc,
int fixedup, int stret)
{
CORE_ADDR object;
CORE_ADDR sel;
CORE_ADDR res;
int addrsize = TARGET_ADDRESS_BYTES;
struct objc_super sstr;
if (stret == 0)
{
object = OBJC_FETCH_POINTER_ARGUMENT (0);
sel = OBJC_FETCH_POINTER_ARGUMENT (1);
}
else
{
object = OBJC_FETCH_POINTER_ARGUMENT (1);
sel = OBJC_FETCH_POINTER_ARGUMENT (2);
}
if (fixedup != -1)
sel = read_memory_unsigned_integer (sel + addrsize, addrsize);
if (fixedup == 0)
{
char selname[2048];
selname[0] = '\0';
read_memory_string (sel, selname, 2047);
sel = lookup_child_selector (selname);
}
object = read_memory_unsigned_integer (object + addrsize, addrsize);
read_objc_super (object, &sstr);
res = lookup_implementation_in_cache (sstr.class, sel);
if (res != 0)
return res;
res = new_objc_runtime_find_impl (sstr.class, sel, 0);
if (new_pc != 0)
*new_pc = res;
if (res == 0)
return 1;
return 0;
}
static int
resolve_msgsendsuper2_fixup (CORE_ADDR pc, CORE_ADDR *new_pc)
{
return resolve_newruntime_objc_msgsendsuper (pc, new_pc, 0, 0);
}
static int
resolve_msgsendsuper2_fixedup (CORE_ADDR pc, CORE_ADDR *new_pc)
{
return resolve_newruntime_objc_msgsendsuper (pc, new_pc, 1, 0);
}
static int
resolve_msgsendsuper2_stret_fixup (CORE_ADDR pc, CORE_ADDR *new_pc)
{
return resolve_newruntime_objc_msgsendsuper (pc, new_pc, 0, 1);
}
static int
resolve_msgsendsuper2_stret_fixedup (CORE_ADDR pc, CORE_ADDR *new_pc)
{
return resolve_newruntime_objc_msgsendsuper (pc, new_pc, 1, 1);
}
static int
resolve_msgsendsuper2 (CORE_ADDR pc, CORE_ADDR *new_pc)
{
return resolve_newruntime_objc_msgsendsuper (pc, new_pc, -1, 0);
}
static int
resolve_msgsendsuper2_stret (CORE_ADDR pc, CORE_ADDR *new_pc)
{
return resolve_newruntime_objc_msgsendsuper (pc, new_pc, -1, 1);
}
static int
resolve_msgsend_stret (CORE_ADDR pc, CORE_ADDR *new_pc)
{
CORE_ADDR object;
CORE_ADDR sel;
CORE_ADDR res;
object = OBJC_FETCH_POINTER_ARGUMENT (1);
sel = OBJC_FETCH_POINTER_ARGUMENT (2);
res = find_implementation (object, sel, 1);
if (new_pc != 0)
*new_pc = res;
if (res == 0)
return 1;
return 0;
}
static int
resolve_msgsend_super (CORE_ADDR pc, CORE_ADDR *new_pc)
{
struct objc_super sstr;
CORE_ADDR super;
CORE_ADDR sel;
CORE_ADDR res;
super = OBJC_FETCH_POINTER_ARGUMENT (0);
sel = OBJC_FETCH_POINTER_ARGUMENT (1);
read_objc_super (super, &sstr);
if (sstr.class == 0)
return 0;
res = find_implementation_from_class (sstr.class, sel);
if (new_pc != 0)
*new_pc = res;
if (res == 0)
return 1;
return 0;
}
static int
resolve_msgsend_super_stret (CORE_ADDR pc, CORE_ADDR *new_pc)
{
struct objc_super sstr;
CORE_ADDR super;
CORE_ADDR sel;
CORE_ADDR res;
super = OBJC_FETCH_POINTER_ARGUMENT (1);
sel = OBJC_FETCH_POINTER_ARGUMENT (2);
read_objc_super (super, &sstr);
if (sstr.class == 0)
return 0;
res = find_implementation_from_class (sstr.class, sel);
if (new_pc != 0)
*new_pc = res;
if (res == 0)
return 1;
return 0;
}
int
should_lookup_objc_class ()
{
return lookup_objc_class_p;
}
static int
new_objc_runtime_get_classname (CORE_ADDR class,
char *class_name, int size)
{
static struct cached_value *cached_class_getName = NULL;
struct value *classval;
CORE_ADDR addr;
struct cleanup *scheduler_cleanup;
int retval = 0;
enum objc_debugger_mode_result objc_retval;
if (cached_class_getName == NULL)
{
if (lookup_minimal_symbol ("class_getName", 0, 0))
cached_class_getName = create_cached_function ("class_getName",
builtin_type_voidptrfuncptr);
else
return 0;
}
scheduler_cleanup = make_cleanup_set_restore_scheduler_locking_mode
(scheduler_locking_on);
make_cleanup_set_restore_unwind_on_signal (1);
make_cleanup_ui_out_suppress_output (uiout);
objc_retval = make_cleanup_set_restore_debugger_mode (NULL, 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)
{
struct value *ret_value;
struct gdb_exception e;
TRY_CATCH (e, RETURN_MASK_ALL)
{
struct cleanup *value_cleanup;
classval = value_from_pointer (lookup_pointer_type
(builtin_type_void_data_ptr), class);
release_value (classval);
value_cleanup = make_cleanup ((make_cleanup_ftype *) value_free, classval);
addr = new_objc_runtime_class_getClass (classval);
if (addr != 0)
{
ret_value = call_function_by_hand (lookup_cached_function (cached_class_getName),
1, &classval);
addr = value_as_address (ret_value);
read_memory_string (addr, class_name, size);
retval = 1;
}
do_cleanups (value_cleanup);
}
if (e.reason != NO_ERROR)
retval = 0;
}
do_cleanups (scheduler_cleanup);
return retval;
}
static char *
lookup_classname_in_cache (CORE_ADDR class)
{
struct rb_tree_node *found;
found = rb_tree_find_node_all_keys (classname_tree, class, -1, -1);
if (found == NULL)
return NULL;
else
return (char *) found->data;
}
static void
add_classname_to_cache (CORE_ADDR class, char *classname)
{
struct rb_tree_node *new_node = (struct rb_tree_node *) xmalloc (sizeof (struct rb_tree_node));
new_node->key = class;
new_node->secondary_key = -1;
new_node->third_key = -1;
new_node->data = xstrdup (classname);
new_node->left = NULL;
new_node->right = NULL;
new_node->parent = NULL;
new_node->color = UNINIT;
rb_tree_insert (&classname_tree, classname_tree, new_node);
}
#define CLS_META 0x2L
struct type *
objc_target_type_from_object (CORE_ADDR object_addr,
struct block *block,
int addrsize,
char **class_name_ptr)
{
struct symbol *class_symbol;
struct type *dynamic_type = NULL;
char class_name[256];
char *name_ptr;
CORE_ADDR name_addr;
CORE_ADDR isa_addr;
long info_field;
int retval = 1;
if (class_name_ptr != NULL)
*class_name_ptr = NULL;
if (new_objc_runtime_internals ())
isa_addr = get_class_address_from_object (object_addr);
else
isa_addr =
read_memory_unsigned_integer (object_addr, addrsize);
name_ptr = lookup_classname_in_cache (isa_addr);
if (name_ptr == NULL)
{
name_ptr = class_name;
if (new_objc_runtime_internals ())
retval = new_objc_runtime_get_classname (isa_addr, class_name, sizeof (class_name));
else
{
int i;
info_field = read_memory_unsigned_integer
(isa_addr + addrsize * 4, addrsize);
if (info_field & CLS_META)
return NULL;
name_addr = read_memory_unsigned_integer
(isa_addr + addrsize * 2, addrsize);
read_memory_string (name_addr, class_name, sizeof (class_name));
if (!(isalpha (class_name[0]) || class_name[0] == '_'))
retval = 0;
else
{
for (i = 1; i < sizeof (class_name) - 1; i++)
{
if (class_name[i] == '\0')
break;
if (!(isalnum (class_name[i])
|| class_name[i] == '_'))
{
retval = 0;
break;
}
}
if (i == sizeof (class_name) - 1)
retval = 0;
}
}
if (retval == 0)
return NULL;
add_classname_to_cache (isa_addr, class_name);
}
if (class_name_ptr != NULL)
*class_name_ptr = xstrdup (name_ptr);
class_symbol = lookup_symbol (name_ptr, block, STRUCT_DOMAIN, 0, 0);
if (! class_symbol)
return NULL;
if (SYMBOL_CLASS (class_symbol) != LOC_TYPEDEF
|| TYPE_CODE (SYMBOL_TYPE (class_symbol)) != TYPE_CODE_CLASS)
{
warning ("The \"isa\" pointer gives a class name of `%s', but that isn't a type name",
name_ptr);
return NULL;
}
dynamic_type = SYMBOL_TYPE (class_symbol);
return dynamic_type;
}
struct type *
value_objc_target_type (struct value *val, struct block *block,
char **dynamic_type_handle)
{
struct type *base_type, *dynamic_type = NULL;
int addrsize = TARGET_ADDRESS_BYTES;
if (dynamic_type_handle != NULL)
*dynamic_type_handle = NULL;
base_type = check_typedef (value_type (val));
if (TYPE_CODE (base_type) != TYPE_CODE_PTR
&& TYPE_CODE (base_type) != TYPE_CODE_REF)
return NULL;
base_type = check_typedef (TYPE_TARGET_TYPE (base_type));
if ((base_type == NULL) || (TYPE_TAG_NAME (base_type) != NULL
&& (strcmp (TYPE_TAG_NAME (base_type), "objc_class") == 0)))
return NULL;
if (TYPE_CODE (base_type) == TYPE_CODE_CLASS)
{
char *t_field_name;
short nfields;
t_field_name = NULL;
nfields = TYPE_NFIELDS (base_type);
while (base_type && nfields != 0)
{
int n_base_class;
n_base_class = TYPE_N_BASECLASSES (base_type);
if (n_base_class == 1)
{
base_type = TYPE_FIELD_TYPE (base_type, 0);
if (base_type)
nfields = TYPE_NFIELDS (base_type);
else
nfields = 0;
}
else if (n_base_class == 0)
{
t_field_name = TYPE_FIELD_NAME (base_type, n_base_class);
if (t_field_name && t_field_name[0] == '\0')
{
base_type = TYPE_FIELD_TYPE (base_type, n_base_class);
if (base_type)
nfields = TYPE_NFIELDS (base_type);
else
nfields = 0;
}
else
break;
}
else
return NULL;
}
if (t_field_name && (strcmp_iw (t_field_name, "isa") == 0))
{
dynamic_type = objc_target_type_from_object (value_as_address (val),
block, addrsize,
dynamic_type_handle);
if (dynamic_type != NULL && dynamic_type_handle != NULL)
{
xfree (*dynamic_type_handle);
*dynamic_type_handle = NULL;
}
}
}
return dynamic_type;
}
int
objc_runtime_lock_taken_p ()
{
static struct cached_value *function = NULL;
if (function == NULL)
{
if (lookup_minimal_symbol("gdb_objc_isRuntimeLocked", 0, 0))
function = create_cached_function ("gdb_objc_isRuntimeLocked",
builtin_type_voidptrfuncptr);
}
if (function != NULL)
{
struct value *retval = NULL ;
struct gdb_exception e;
TRY_CATCH (e, RETURN_MASK_ALL)
{
retval = call_function_by_hand (lookup_cached_function (function),
0, NULL);
}
if (e.reason != NO_ERROR)
return -1;
else
return value_as_long (retval);
}
else
return -1;
}
void
objc_invalidate_objc_class (struct type *type)
{
int i;
if (!new_objc_runtime_internals ())
return;
for (i = 0; i < TYPE_NFIELDS (type); i++)
{
if (i >= TYPE_N_BASECLASSES (type))
{
int old_offset = TYPE_FIELD_BITPOS_ASSIGN (type, i);
if ( old_offset < 0)
internal_error (__FILE__, __LINE__,
"TYPE_FIELD_BITPOS < 0 for field %s of class %s.",
TYPE_FIELD_NAME (type, i), TYPE_NAME (type));
if (old_offset == 0)
TYPE_FIELD_BITPOS_ASSIGN (type, i) = INT_MIN;
else
TYPE_FIELD_BITPOS_ASSIGN (type, i) = -old_offset;
}
}
if (TYPE_LENGTH_ASSIGN (type) > 0)
TYPE_LENGTH_ASSIGN (type) = - TYPE_LENGTH_ASSIGN (type);
}
#define IVAR_OFFSET_PREFIX "OBJC_IVAR_$_"
static int bits_per_byte = 8;
static int first_bitfield_index = -1;
static int first_bitfield_offset = 0;
int
objc_fixup_ivar_offset (struct type *type, int ivar)
{
char *class_name;
char *field_name;
char *symbol_name;
struct minimal_symbol *ivar_sym;
int len;
static int prefix_len = 0;
struct cleanup *name_cleanup;
CORE_ADDR ivar_addr;
ULONGEST ivar_offset;
int ivar_len;
int class_len;
int is_bitfield_p = 0;
int cur_bitfield_offset = 0;
if (TYPE_FIELD_BITSIZE (type, ivar) != 0)
is_bitfield_p = 1;
if (! is_bitfield_p)
{
first_bitfield_index = -1;
first_bitfield_offset = 0;
}
else if (first_bitfield_index < 0)
{
first_bitfield_index = ivar;
first_bitfield_offset = TYPE_FIELD_BITPOS_ASSIGN (type, ivar);
if (first_bitfield_offset < 0)
first_bitfield_offset = -first_bitfield_offset;
}
if (is_bitfield_p)
{
cur_bitfield_offset = TYPE_FIELD_BITPOS_ASSIGN (type, ivar);
if (cur_bitfield_offset < 0)
cur_bitfield_offset = -cur_bitfield_offset;
cur_bitfield_offset = cur_bitfield_offset - first_bitfield_offset;
if (cur_bitfield_offset != 0)
cur_bitfield_offset = cur_bitfield_offset % bits_per_byte;
}
if (TYPE_CODE (type) == TYPE_CODE_ENUM)
return TYPE_FIELD_BITPOS_ASSIGN (type, ivar);
if (TYPE_CODE (type) != TYPE_CODE_STRUCT
&& TYPE_CODE (type) != TYPE_CODE_CLASS)
{
if (info_verbose)
{
fprintf_unfiltered (gdb_stdlog,
"Asked to fix up class: \"%s\" "
"which is not a struct type.\n",
TYPE_NAME (type) ? TYPE_NAME (type) : "<unknown>");
}
return TYPE_FIELD_BITPOS_ASSIGN (type, ivar);
}
if (ivar < TYPE_N_BASECLASSES (type))
return TYPE_FIELD_BITPOS_ASSIGN (type, ivar);
class_name = TYPE_NAME (type);
if (class_name == NULL)
class_name = TYPE_TAG_NAME (type);
if (class_name == NULL)
{
int old_offset = TYPE_FIELD_BITPOS_ASSIGN (type, ivar);
if (info_verbose)
{
fprintf_unfiltered (gdb_stdlog,
"Asked to fix up class with no name.\n");
}
if (old_offset == INT_MIN)
TYPE_FIELD_BITPOS_ASSIGN (type, ivar) = 0;
else
TYPE_FIELD_BITPOS_ASSIGN (type, ivar) = - old_offset;
return TYPE_FIELD_BITPOS_ASSIGN (type, ivar);
}
field_name = TYPE_FIELD_NAME (type, ivar);
if (prefix_len == 0)
prefix_len = strlen (IVAR_OFFSET_PREFIX);
len = prefix_len + strlen (class_name) + strlen (field_name) + 1 + 1;
symbol_name = xmalloc (len);
name_cleanup = make_cleanup (xfree, symbol_name);
snprintf (symbol_name, len, "%s%s.%s", IVAR_OFFSET_PREFIX, class_name, field_name);
ivar_sym = lookup_minimal_symbol (symbol_name, NULL, NULL);
if (ivar_sym == NULL)
{
int old_offset = TYPE_FIELD_BITPOS_ASSIGN (type, ivar);
if (info_verbose)
fprintf_unfiltered (gdb_stdlog,
"Couldn't find ivar symbol: \"%s\".\n", symbol_name);
if (old_offset == INT_MIN)
TYPE_FIELD_BITPOS_ASSIGN (type, ivar) = 0;
else
TYPE_FIELD_BITPOS_ASSIGN (type, ivar) = - old_offset;
goto done;
}
ivar_addr = SYMBOL_VALUE_ADDRESS (ivar_sym);
if (!safe_read_memory_unsigned_integer (ivar_addr, 4, &ivar_offset))
{
warning ("Couldn't read ivar offset at %s for ivar symbol \"%s\".\n",
paddr_nz (ivar_addr), symbol_name);
goto done;
}
ivar_len = TYPE_LENGTH (check_typedef (TYPE_FIELD_TYPE (type, ivar)));
class_len = TYPE_LENGTH_ASSIGN (type);
if (class_len < 0)
class_len = -class_len;
if (ivar_offset + ivar_len > class_len)
TYPE_LENGTH_ASSIGN (type) = ivar_offset + ivar_len;
ivar_offset *= bits_per_byte;
if (is_bitfield_p)
ivar_offset += cur_bitfield_offset;
if (info_verbose)
{
int orig_value;
if (TYPE_FIELD_BITPOS_ASSIGN (type, ivar) == INT_MIN)
orig_value = 0;
else
orig_value = -TYPE_FIELD_BITPOS_ASSIGN (type, ivar);
if (ivar_offset != orig_value)
{
fprintf_unfiltered (gdb_stdlog, "Changing ivar offset for ivar: %s"
" of class: %s from: %d to %lu.\n",
field_name,
class_name,
orig_value,
ivar_offset);
}
}
TYPE_FIELD_BITPOS_ASSIGN (type, ivar) = ivar_offset;
done:
do_cleanups (name_cleanup);
return TYPE_FIELD_BITPOS_ASSIGN (type, ivar);
}
int
objc_fixup_class_length (struct type *type)
{
int i;
first_bitfield_index = -1;
first_bitfield_offset = 0;
for (i = 0; i < TYPE_NFIELDS (type); i++)
objc_fixup_ivar_offset (type, i);
if (TYPE_LENGTH_ASSIGN (type) < 0)
TYPE_LENGTH_ASSIGN (type) = - TYPE_LENGTH_ASSIGN (type);
return TYPE_LENGTH_ASSIGN (type);
}
#ifndef LOCKS_DEBUGGING
#define LOCKS_DEBUGGING 0
#endif
static int spinlock_lock_is_present = 0;
static int malloc_lock_is_present = 0;
static int debug_mode_timer = -1;
static void
do_end_debugger_mode (void *arg)
{
struct cached_value *end_function = (struct cached_value *) arg;
struct cleanup *scheduler_cleanup;
struct gdb_exception e;
if (!target_has_execution)
{
debug_mode_set_p = debug_mode_not_checked;
debug_mode_set_reason = objc_debugger_mode_unknown;
return;
}
if (debug_handcall_setup)
fprintf_unfiltered (gdb_stdout, "Ending debugger mode, "
"debug_mode_set_p is %d\n", debug_mode_set_p);
scheduler_cleanup = make_cleanup_set_restore_scheduler_locking_mode
(scheduler_locking_on);
make_cleanup_set_restore_unwind_on_signal (1);
make_cleanup_enable_disable_bpts_during_operation ();
if (maint_use_timers)
start_timer (&debug_mode_timer, "objc-debug-mode",
"Turning off debugger mode");
debug_mode_set_p = debug_mode_okay;
debug_mode_set_reason = objc_debugger_mode_success;
TRY_CATCH (e, RETURN_MASK_ALL)
{
call_function_by_hand (lookup_cached_function (end_function),
0, NULL);
}
if (debug_handcall_setup)
fprintf_unfiltered (gdb_stdout, "Ended debugger mode.\n");
debug_mode_set_p = debug_mode_not_checked;
debug_mode_set_reason = objc_debugger_mode_unknown;
do_cleanups (scheduler_cleanup);
if (e.reason != NO_ERROR)
{
fprintf_unfiltered (gdb_stdout, "Error resetting ObjC debugger mode: %s",
e.message);
throw_exception (e);
}
}
static void
do_reset_debug_mode_flag (void *unused)
{
debug_mode_set_p = debug_mode_not_checked;
debug_mode_set_reason = objc_debugger_mode_unknown;
if (debug_handcall_setup)
fprintf_unfiltered (gdb_stdout, "Doing reset debug mode, "
"debug_mode_set_p is %d\n", debug_mode_set_p);
}
static struct breakpoint *debugger_mode_fail_breakpoint;
static struct breakpoint *objc_exception_throw_breakpoint;
static void
do_cleanup_objc_exception_breakpoint (void *unused)
{
if (objc_exception_throw_breakpoint != NULL)
{
delete_breakpoint (objc_exception_throw_breakpoint);
objc_exception_throw_breakpoint = NULL;
}
}
struct cleanup *
make_cleanup_init_objc_exception_catcher (void)
{
if (objc_exception_throw_breakpoint != NULL)
{
struct cleanup *suppress_cleanup
= make_cleanup_ui_out_suppress_output (uiout);
enable_breakpoint (objc_exception_throw_breakpoint);
do_cleanups (suppress_cleanup);
}
else
objc_exception_throw_breakpoint
= create_objc_hook_breakpoint ("objc_exception_throw");
return make_cleanup (do_cleanup_objc_exception_breakpoint, NULL);
}
static int
init_debugger_mode_fail_notification ()
{
if (debugger_mode_fail_breakpoint != NULL)
return 1;
debugger_mode_fail_breakpoint
= create_objc_hook_breakpoint ("gdb_objc_debuggerModeFailure");
return (debugger_mode_fail_breakpoint != NULL);
}
enum objc_handcall_fail_reasons
objc_pc_at_fail_point (CORE_ADDR pc)
{
if (debugger_mode_fail_breakpoint != NULL
&& pc == debugger_mode_fail_breakpoint->loc->address)
return objc_debugger_mode_fail;
if (objc_exception_throw_breakpoint != NULL
&& pc == objc_exception_throw_breakpoint->loc->address)
return objc_exception_thrown;
return objc_no_fail;
}
enum objc_debugger_mode_result
make_cleanup_set_restore_debugger_mode (struct cleanup **cleanup, int level)
{
static struct cached_value *start_function = NULL;
static struct cached_value *end_function = NULL;
struct value *tmp_value;
struct cleanup *scheduler_cleanup;
struct cleanup *timer_cleanup = NULL;
struct cleanup *unwind_cleanup;
struct cleanup *breakpoint_cleanup;
int success = 0;
struct gdb_exception e;
if (debug_handcall_setup)
fprintf_unfiltered (gdb_stdout, "make_cleanup_set_restore_debugger_mode:"
"Entering function: level %d.\n", level);
if (cleanup)
*cleanup = make_cleanup (null_cleanup, 0);
if (debug_mode_set_p == debug_mode_overridden)
{
if (debug_handcall_setup)
fprintf_unfiltered (gdb_stdout, "make_cleanup_set_restore_debugger_mode:"
" Debug mode set to overridden, returning null cleanup.\n");
return debug_mode_set_reason;
}
if (level == 0)
{
if (debug_handcall_setup)
fprintf_unfiltered (gdb_stdout, "make_cleanup_set_restore_debugger_mode:"
" LEVEL is 0, returning reset cleanup.\n");
if (debug_mode_set_p == debug_mode_okay)
do_hand_call_cleanups (ALL_CLEANUPS);
if (cleanup)
*cleanup = make_cleanup (do_reset_debug_mode_flag, 0);
else
make_cleanup (do_reset_debug_mode_flag, 0);
debug_mode_set_p = debug_mode_overridden;
debug_mode_set_reason = objc_debugger_mode_success;
return objc_debugger_mode_success;
}
if (debug_mode_set_p == debug_mode_okay)
{
if (debug_handcall_setup)
fprintf_unfiltered (gdb_stdout, "make_cleanup_set_restore_debugger_mode:"
" debug mode set, returning null cleanup.\n");
if (cleanup)
*cleanup = make_cleanup_set_restore_scheduler_locking_mode (scheduler_locking_on);
else
make_cleanup_set_restore_scheduler_locking_mode (scheduler_locking_on);
return objc_debugger_mode_success;
}
else if (debug_mode_set_p == debug_mode_failed)
{
if (debug_handcall_setup)
fprintf_unfiltered (gdb_stdout, "make_cleanup_set_restore_debugger_mode:"
" Debug mode set to %d, returning null cleanup.\n", debug_mode_set_p);
return debug_mode_set_reason;
}
if (find_libobjc_objfile () == NULL)
return objc_debugger_mode_success;
init_debugger_mode_fail_notification ();
if (start_function == NULL)
{
if (lookup_minimal_symbol ("gdb_objc_startDebuggerMode", 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);
start_function = create_cached_function ("gdb_objc_startDebuggerMode",
func_type);
}
else
{
return objc_debugger_mode_fail_objc_api_unavailable;
}
}
if (end_function == NULL)
{
if (lookup_minimal_symbol ("gdb_objc_endDebuggerMode", 0, 0))
{
end_function = create_cached_function ("gdb_objc_endDebuggerMode",
builtin_type_voidptrfuncptr);
}
else
{
return objc_debugger_mode_fail_objc_api_unavailable;
}
}
scheduler_cleanup = make_cleanup_set_restore_scheduler_locking_mode
(scheduler_locking_on);
unwind_cleanup = make_cleanup_set_restore_unwind_on_signal (1);
if (level == 1)
tmp_value = value_from_longest (builtin_type_int, 0);
else if (level == -1)
tmp_value = value_from_longest (builtin_type_int, 1);
else
internal_error (__FILE__, __LINE__,
"Should not be calling the objc debugger mode if override was requested.");
release_value (tmp_value);
make_cleanup ((make_cleanup_ftype *) value_free, tmp_value);
if (maint_use_timers)
timer_cleanup = start_timer (&debug_mode_timer, "objc-debug-mode",
"Turning on debugger mode");
debug_mode_set_p = debug_mode_okay;
debug_mode_set_reason = objc_debugger_mode_success;
TRY_CATCH (e, RETURN_MASK_ALL)
{
success = target_check_safe_call (SPINLOCK_SUBSYSTEM,
CHECK_SCHEDULER_VALUE);
}
if (LOCKS_DEBUGGING && spinlock_lock_is_present)
success = 0;
if (e.reason != NO_ERROR || !success)
{
do_cleanups (scheduler_cleanup);
make_hand_call_cleanup (do_reset_debug_mode_flag, 0);
debug_mode_set_p = debug_mode_failed;
debug_mode_set_reason = objc_debugger_mode_fail_spinlock_held;
return objc_debugger_mode_fail_spinlock_held;
}
debug_mode_set_p = debug_mode_okay;
debug_mode_set_reason = objc_debugger_mode_success;
TRY_CATCH (e, RETURN_MASK_ALL)
{
success = target_check_safe_call (MALLOC_SUBSYSTEM,
CHECK_SCHEDULER_VALUE);
}
if (LOCKS_DEBUGGING && malloc_lock_is_present)
success = 0;
if (e.reason != NO_ERROR || !success)
{
do_cleanups (scheduler_cleanup);
make_hand_call_cleanup (do_reset_debug_mode_flag, 0);
debug_mode_set_p = debug_mode_failed;
debug_mode_set_reason = objc_debugger_mode_fail_malloc_lock_held;
return objc_debugger_mode_fail_malloc_lock_held;
}
debug_mode_set_p = debug_mode_okay;
debug_mode_set_reason = objc_debugger_mode_success;
breakpoint_cleanup = make_cleanup_enable_disable_bpts_during_operation ();
TRY_CATCH (e, RETURN_MASK_ALL)
{
tmp_value = call_function_by_hand
(lookup_cached_function (start_function), 1, &tmp_value);
}
do_cleanups (breakpoint_cleanup);
if (timer_cleanup != NULL)
do_cleanups (timer_cleanup);
do_cleanups (unwind_cleanup);
debug_mode_set_p = debug_mode_not_checked;
debug_mode_set_reason = objc_debugger_mode_unknown;
if (e.reason != NO_ERROR)
throw_exception (e);
success = value_as_long (tmp_value);
if (debug_handcall_setup)
fprintf_filtered (gdb_stdout, "Tried to start debugger mode, return value: %d.\n", success);
if (success == 0)
{
do_cleanups (scheduler_cleanup);
make_hand_call_cleanup (do_reset_debug_mode_flag, 0);
debug_mode_set_p = debug_mode_failed;
debug_mode_set_reason = objc_debugger_mode_fail_unable_to_enter_debug_mode;
return objc_debugger_mode_fail_unable_to_enter_debug_mode;
}
debug_mode_set_p = debug_mode_okay;
debug_mode_set_reason = objc_debugger_mode_success;
if (objc_runtime_check_enabled_p ())
{
if (debug_handcall_setup)
fprintf_unfiltered (gdb_stdout,
"Adding do_end_debugger_mode cleanup to hand_call_cleanup.\n");
make_hand_call_cleanup (do_end_debugger_mode, end_function);
}
else
{
if (debug_handcall_setup)
fprintf_unfiltered (gdb_stdout,
"Adding do_end_debugger_mode cleanup to generic cleanup chain.\n");
make_cleanup (do_end_debugger_mode, end_function);
}
if (cleanup)
*cleanup = scheduler_cleanup;
return objc_debugger_mode_success;
}
static int
class_valid_p (CORE_ADDR class)
{
struct rb_tree_node *found;
found = rb_tree_find_node (real_class_tree, class, -1);
return (found != NULL);
}
static CORE_ADDR
lookup_real_class_in_cache (CORE_ADDR class)
{
struct rb_tree_node *found;
found = rb_tree_find_node (real_class_tree, class, -1);
if (found == NULL || found->data == NULL)
return (CORE_ADDR) 0;
else
return *((CORE_ADDR *) found->data);
}
static struct rb_tree_node *
add_class_to_cache (CORE_ADDR class)
{
struct rb_tree_node *new_node;
new_node = rb_tree_find_node (real_class_tree, class, -1);
if (new_node != NULL)
return new_node;
new_node = (struct rb_tree_node *) xmalloc (sizeof (struct rb_tree_node));
new_node->key = class;
new_node->secondary_key = -1;
new_node->third_key = -1;
new_node->data = NULL;
new_node->left = NULL;
new_node->right = NULL;
new_node->parent = NULL;
new_node->color = UNINIT;
rb_tree_insert (&real_class_tree, real_class_tree, new_node);
return new_node;
}
static void
add_real_class_to_cache (CORE_ADDR class, CORE_ADDR real_class)
{
struct rb_tree_node *new_node;
add_class_to_cache (real_class);
new_node = add_class_to_cache (class);
new_node->data = xmalloc (sizeof (CORE_ADDR));
*((CORE_ADDR *) new_node->data) = real_class;
}
static CORE_ADDR
get_class_address_from_object (CORE_ADDR object_addr)
{
struct value *function;
struct objc_object orig_object;
static char class_selname[6] = "class";
CORE_ADDR class_sel = (CORE_ADDR) 0;
CORE_ADDR class_method_addr = (CORE_ADDR) 0;
CORE_ADDR ret_addr = (CORE_ADDR) 0;
struct value *infargs[2];
struct value *objval;
struct value *selval;
struct value *retval;
read_objc_object (object_addr, &orig_object);
ret_addr = lookup_real_class_in_cache (orig_object.isa);
if (ret_addr != 0x0)
{
return ret_addr;
}
if (!target_has_execution)
{
return orig_object.isa;
}
class_sel = lookup_child_selector (class_selname);
if (class_sel != 0)
class_method_addr = new_objc_runtime_find_impl (orig_object.isa,
class_sel,
0);
if (class_method_addr != 0)
{
struct cleanup *scheduler_cleanup;
enum objc_debugger_mode_result objc_retval;
scheduler_cleanup = make_cleanup_set_restore_scheduler_locking_mode
(scheduler_locking_on);
make_cleanup_set_restore_unwind_on_signal (1);
make_cleanup_ui_out_suppress_output (uiout);
objc_retval = make_cleanup_set_restore_debugger_mode (NULL, 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)
{
objval = value_from_pointer (lookup_pointer_type
(builtin_type_void_data_ptr),
object_addr);
selval = value_from_pointer (lookup_pointer_type
(builtin_type_void_data_ptr),
class_sel);
infargs[0] = objval;
infargs[1] = selval;
function = value_from_pointer (builtin_type_voidptrfuncptr,
class_method_addr);
retval = call_function_by_hand (function, 2, infargs);
ret_addr = (CORE_ADDR) value_as_address (retval);
ret_addr = gdbarch_addr_bits_remove (current_gdbarch, ret_addr);
}
do_cleanups (scheduler_cleanup);
}
if (ret_addr == 0
&& orig_object.isa != 0)
ret_addr = orig_object.isa;
if (ret_addr == object_addr)
ret_addr = orig_object.isa;
if (ret_addr != 0)
add_real_class_to_cache (orig_object.isa, ret_addr);
return ret_addr;
}
int
is_objc_exception_throw_breakpoint (struct breakpoint *b)
{
return (b == objc_exception_throw_breakpoint);
}
static int use_non_blocking_mode = 1;
int
objc_runtime_check_enabled_p ()
{
return use_non_blocking_mode;
}
static void
set_non_blocking_mode_func (char *args, int from_tty,
struct cmd_list_element *c)
{
if (objc_runtime_check_enabled_p () == 0)
{
do_hand_call_cleanups (ALL_CLEANUPS);
}
}
void
_initialize_objc_lang ()
{
add_setshow_boolean_cmd ("call-po-at-unsafe-times", no_class, &call_po_at_unsafe_times,
"Set whether to override the check for potentially unsafe"
" situations before calling print-object.",
"Show whether to override the check for potentially unsafe"
" situations before calling print-object.",
"??",
NULL, NULL,
&setlist, &showlist);
add_setshow_boolean_cmd ("let-po-run-all-threads", no_class, &let_po_run_all_threads,
"Set whether po should run all threads if it can't "
" safely run only the current thread.",
"Show whether po should run all threads if it can't "
" safely run only the current thread.",
"By default, \"po\" will try to run only the current thread\n"
"However, that may cause deadlocks between the po function and\n"
"a lock held on some other thread. In that case, po will allow\n"
"the other threads to run as well. If you don't want it to do that\n"
"then set this variable to off.",
NULL, NULL,
&setlist, &showlist);
add_setshow_boolean_cmd ("lookup-objc-class", no_class, &lookup_objc_class_p,
"Set whether we should attempt to lookup Obj-C classes when we resolve symbols.",
"Show whether we should attempt to lookup Obj-C classes when we resolve symbols.",
"??",
NULL, NULL,
&setlist, &showlist);
add_setshow_zinteger_cmd ("objc-version", no_class, &objc_runtime_version_user_override,
"Set the current Objc runtime version. "
"If non-zero, this will override the default selection.",
"Show the current Objc runtime version.",
"??",
NULL, NULL,
&setlist, &showlist);
add_setshow_boolean_cmd ("objc-non-blocking-mode", no_class, &use_non_blocking_mode,
"Set whether all inferior function calls should use the objc non-blocking mode.\n"
"Note that if this is on the attempt to call the function will fail if we can't turn on\n"
"the non-blocking mode.\n",
"Show whether all inferior function calls should use the objc non-blocking mode.",
"??",
set_non_blocking_mode_func, NULL,
&setlist, &showlist);
}