#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 <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;
static unsigned int objc_class_method_limit = 10000;
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 *classname_tree = NULL;
static char *lookup_classname_in_cache (CORE_ADDR class);
static void add_classname_to_cache (CORE_ADDR class, char *classname);
static int
get_addrsize (void)
{
if (exec_bfd && gdbarch_lookup_osabi (exec_bfd) == GDB_OSABI_DARWIN64)
return 8;
else
return 4;
}
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;
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);
if (target_check_safe_call () == 1)
{
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);
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);
}
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);
}
static void
print_object_command (char *args, int from_tty)
{
struct value *object, *function, *description;
struct cleanup *cleanup_chain;
int unwind;
CORE_ADDR string_addr, object_addr;
int i = 0;
gdb_byte c = 0;
const char *fn_name;
if (!args || !*args)
error (
"The 'print-object' command requires an argument (an Objective-C object)");
if (!call_po_at_unsafe_times)
{
if (target_check_safe_call () == 0)
{
warning ("Set call-po-at-unsafe-times to 1 to override this check.");
return;
}
}
{
struct expression *expr;
struct cleanup *old_chain;
int pc = 0;
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"));
unwind = set_unwind_on_signal (1);
cleanup_chain = make_cleanup (set_unwind_on_signal, unwind);
description = call_function_by_hand (function, 1, &object);
do_cleanups (cleanup_chain);
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 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_fixup", resolve_msgsendsuper2_fixup, 0, 0},
{ "_objc_msgSendSuper2_fixedup", resolve_msgsendsuper2_fixedup, 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 (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 __attribute__ ((__unused__)))
{
cached_objc_msgsend_table_is_valid = 0;
}
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;
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;
}
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);
}
static int
new_objc_runtime_internals ()
{
if (objc_runtime_version == 1)
return 0;
else if (objc_runtime_version == 2)
return 1;
#if defined (TARGET_ARM)
return 1;
#else
if (get_addrsize () == 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;
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)
{
ret_value = call_function_by_hand
(lookup_cached_function (validate_function),
1, &infargs);
if (ret_value == NULL)
return (CORE_ADDR) 0;
return (CORE_ADDR) value_as_address (ret_value);
}
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;
struct value *ret_value;
struct cleanup *scheduler_cleanup;
CORE_ADDR retval = 0;
int unwind;
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);
unwind = set_unwind_on_signal (1);
make_cleanup (set_unwind_on_signal, unwind);
if (target_check_safe_call () == 1)
{
struct value *classval, *selval;
struct value *infargs[2];
char *imp_name;
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;
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);
retval = (CORE_ADDR) value_as_address (ret_value);
retval = gdbarch_addr_bits_remove (current_gdbarch, retval);
if (find_pc_partial_function (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);
cleanups:
do_cleanups (scheduler_cleanup);
return retval;
}
static void
read_objc_method (CORE_ADDR addr, struct objc_method *method)
{
int addrsize = get_addrsize ();
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 = get_addrsize ();
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 = get_addrsize ();
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)
{
int addrsize = get_addrsize ();
object->isa = read_memory_unsigned_integer (addr, addrsize);
}
static void
read_objc_super (CORE_ADDR addr, struct objc_super *super)
{
int addrsize = get_addrsize ();
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 = get_addrsize ();
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)
free_fn (root->data);
xfree (root);
}
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;
}
}
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 = get_addrsize ();
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;
if (object == 0)
return 0;
read_objc_object (object, &ostr);
if (ostr.isa == 0)
return 0;
if (new_objc_runtime_internals ())
{
CORE_ADDR resolves_to;
resolves_to = lookup_implementation_in_cache (ostr.isa, sel);
if (resolves_to != 0)
return resolves_to;
resolves_to = new_objc_runtime_find_impl (ostr.isa, sel, stret);
if (resolves_to != 0)
{
return (resolves_to);
}
}
return find_implementation_from_class (ostr.isa, sel);
}
#define OBJC_FETCH_POINTER_ARGUMENT(argi) \
FETCH_POINTER_ARGUMENT (get_current_frame (), argi, builtin_type_void_func_ptr)
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 = get_addrsize ();
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 = get_addrsize ();
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);
}
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_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;
}
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;
static struct ui_out *null_uiout = NULL;
struct ui_out *old_uiout;
int retval = 0;
int unwind;
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);
unwind = set_unwind_on_signal (1);
make_cleanup (set_unwind_on_signal, unwind);
if (target_check_safe_call () == 1)
{
struct value *ret_value;
struct gdb_exception e;
if (null_uiout == NULL)
null_uiout = cli_out_new (gdb_null);
if (null_uiout == NULL)
error ("Unable to open null uiout in objc-lang.c.");
old_uiout = uiout;
uiout = null_uiout;
TRY_CATCH (e, RETURN_MASK_ALL)
{
classval = value_from_pointer (lookup_pointer_type
(builtin_type_void_data_ptr), class);
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;
}
}
if (e.reason != NO_ERROR)
retval = 0;
}
ui_file_rewind (gdb_null);
uiout = old_uiout;
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;
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, 256);
else
{
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, 255);
}
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)
{
warning ("can't find class named `%s' given by ObjC class object", class_name);
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 = get_addrsize ();
if (dynamic_type_handle != NULL)
*dynamic_type_handle = NULL;
base_type = check_typedef (value_type (val));
for (;;)
{
CHECK_TYPEDEF (base_type);
if (TYPE_CODE (base_type) != TYPE_CODE_PTR
&& TYPE_CODE (base_type) != TYPE_CODE_REF)
break;
base_type = 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
{
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;
}
}
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;
}
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 ("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,
"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);
}