From 5e80600ed0e929faaeac205dbf0d4e7f9b2842cb Mon Sep 17 00:00:00 2001 From: Tom Tromey Date: Mon, 8 Mar 2021 07:27:57 -0700 Subject: [PATCH] Split out eval_op_objc_msgcall This splits OP_OBJC_MSGCALL into a new function for future use. gdb/ChangeLog 2021-03-08 Tom Tromey * eval.c (eval_op_objc_msgcall): New function. (evaluate_subexp_standard): Use it. --- gdb/ChangeLog | 5 + gdb/eval.c | 530 ++++++++++++++++++++++++++------------------------ 2 files changed, 286 insertions(+), 249 deletions(-) diff --git a/gdb/ChangeLog b/gdb/ChangeLog index 9b4c9c80b6..a495f90516 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,8 @@ +2021-03-08 Tom Tromey + + * eval.c (eval_op_objc_msgcall): New function. + (evaluate_subexp_standard): Use it. + 2021-03-08 Tom Tromey * eval.c (eval_binop_assign_modify): New function. diff --git a/gdb/eval.c b/gdb/eval.c index 78a10f741f..3033778e43 100644 --- a/gdb/eval.c +++ b/gdb/eval.c @@ -2089,6 +2089,277 @@ eval_binop_assign_modify (struct type *expect_type, struct expression *exp, return value_assign (arg1, arg2); } +/* Note that ARGS needs 2 empty slots up front and must end with a + null pointer. */ +static struct value * +eval_op_objc_msgcall (struct type *expect_type, struct expression *exp, + enum noside noside, CORE_ADDR selector, + value *target, gdb::array_view args) +{ + CORE_ADDR responds_selector = 0; + CORE_ADDR method_selector = 0; + + int struct_return = 0; + + struct value *msg_send = NULL; + struct value *msg_send_stret = NULL; + int gnu_runtime = 0; + + struct value *method = NULL; + struct value *called_method = NULL; + + struct type *selector_type = NULL; + struct type *long_type; + struct type *type; + + struct value *ret = NULL; + CORE_ADDR addr = 0; + + value *argvec[5]; + + long_type = builtin_type (exp->gdbarch)->builtin_long; + selector_type = builtin_type (exp->gdbarch)->builtin_data_ptr; + + if (value_as_long (target) == 0) + return value_from_longest (long_type, 0); + + if (lookup_minimal_symbol ("objc_msg_lookup", 0, 0).minsym) + gnu_runtime = 1; + + /* Find the method dispatch (Apple runtime) or method lookup + (GNU runtime) function for Objective-C. These will be used + to lookup the symbol information for the method. If we + can't find any symbol information, then we'll use these to + call the method, otherwise we can call the method + directly. The msg_send_stret function is used in the special + case of a method that returns a structure (Apple runtime + only). */ + if (gnu_runtime) + { + type = selector_type; + + type = lookup_function_type (type); + type = lookup_pointer_type (type); + type = lookup_function_type (type); + type = lookup_pointer_type (type); + + msg_send = find_function_in_inferior ("objc_msg_lookup", NULL); + msg_send_stret + = find_function_in_inferior ("objc_msg_lookup", NULL); + + msg_send = value_from_pointer (type, value_as_address (msg_send)); + msg_send_stret = value_from_pointer (type, + value_as_address (msg_send_stret)); + } + else + { + msg_send = find_function_in_inferior ("objc_msgSend", NULL); + /* Special dispatcher for methods returning structs. */ + msg_send_stret + = find_function_in_inferior ("objc_msgSend_stret", NULL); + } + + /* Verify the target object responds to this method. The + standard top-level 'Object' class uses a different name for + the verification method than the non-standard, but more + often used, 'NSObject' class. Make sure we check for both. */ + + responds_selector + = lookup_child_selector (exp->gdbarch, "respondsToSelector:"); + if (responds_selector == 0) + responds_selector + = lookup_child_selector (exp->gdbarch, "respondsTo:"); + + if (responds_selector == 0) + error (_("no 'respondsTo:' or 'respondsToSelector:' method")); + + method_selector + = lookup_child_selector (exp->gdbarch, "methodForSelector:"); + if (method_selector == 0) + method_selector + = lookup_child_selector (exp->gdbarch, "methodFor:"); + + if (method_selector == 0) + error (_("no 'methodFor:' or 'methodForSelector:' method")); + + /* Call the verification method, to make sure that the target + class implements the desired method. */ + + argvec[0] = msg_send; + argvec[1] = target; + argvec[2] = value_from_longest (long_type, responds_selector); + argvec[3] = value_from_longest (long_type, selector); + argvec[4] = 0; + + ret = call_function_by_hand (argvec[0], NULL, {argvec + 1, 3}); + if (gnu_runtime) + { + /* Function objc_msg_lookup returns a pointer. */ + argvec[0] = ret; + ret = call_function_by_hand (argvec[0], NULL, {argvec + 1, 3}); + } + if (value_as_long (ret) == 0) + error (_("Target does not respond to this message selector.")); + + /* Call "methodForSelector:" method, to get the address of a + function method that implements this selector for this + class. If we can find a symbol at that address, then we + know the return type, parameter types etc. (that's a good + thing). */ + + argvec[0] = msg_send; + argvec[1] = target; + argvec[2] = value_from_longest (long_type, method_selector); + argvec[3] = value_from_longest (long_type, selector); + argvec[4] = 0; + + ret = call_function_by_hand (argvec[0], NULL, {argvec + 1, 3}); + if (gnu_runtime) + { + argvec[0] = ret; + ret = call_function_by_hand (argvec[0], NULL, {argvec + 1, 3}); + } + + /* ret should now be the selector. */ + + addr = value_as_long (ret); + if (addr) + { + struct symbol *sym = NULL; + + /* The address might point to a function descriptor; + resolve it to the actual code address instead. */ + addr = gdbarch_convert_from_func_ptr_addr (exp->gdbarch, addr, + current_top_target ()); + + /* Is it a high_level symbol? */ + sym = find_pc_function (addr); + if (sym != NULL) + method = value_of_variable (sym, 0); + } + + /* If we found a method with symbol information, check to see + if it returns a struct. Otherwise assume it doesn't. */ + + if (method) + { + CORE_ADDR funaddr; + struct type *val_type; + + funaddr = find_function_addr (method, &val_type); + + block_for_pc (funaddr); + + val_type = check_typedef (val_type); + + if ((val_type == NULL) + || (val_type->code () == TYPE_CODE_ERROR)) + { + if (expect_type != NULL) + val_type = expect_type; + } + + struct_return = using_struct_return (exp->gdbarch, method, + val_type); + } + else if (expect_type != NULL) + { + struct_return = using_struct_return (exp->gdbarch, NULL, + check_typedef (expect_type)); + } + + /* Found a function symbol. Now we will substitute its + value in place of the message dispatcher (obj_msgSend), + so that we call the method directly instead of thru + the dispatcher. The main reason for doing this is that + we can now evaluate the return value and parameter values + according to their known data types, in case we need to + do things like promotion, dereferencing, special handling + of structs and doubles, etc. + + We want to use the type signature of 'method', but still + jump to objc_msgSend() or objc_msgSend_stret() to better + mimic the behavior of the runtime. */ + + if (method) + { + if (value_type (method)->code () != TYPE_CODE_FUNC) + error (_("method address has symbol information " + "with non-function type; skipping")); + + /* Create a function pointer of the appropriate type, and + replace its value with the value of msg_send or + msg_send_stret. We must use a pointer here, as + msg_send and msg_send_stret are of pointer type, and + the representation may be different on systems that use + function descriptors. */ + if (struct_return) + called_method + = value_from_pointer (lookup_pointer_type (value_type (method)), + value_as_address (msg_send_stret)); + else + called_method + = value_from_pointer (lookup_pointer_type (value_type (method)), + value_as_address (msg_send)); + } + else + { + if (struct_return) + called_method = msg_send_stret; + else + called_method = msg_send; + } + + if (noside == EVAL_SKIP) + return eval_skip_value (exp); + + if (noside == EVAL_AVOID_SIDE_EFFECTS) + { + /* If the return type doesn't look like a function type, + call an error. This can happen if somebody tries to + turn a variable into a function call. This is here + because people often want to call, eg, strcmp, which + gdb doesn't know is a function. If gdb isn't asked for + it's opinion (ie. through "whatis"), it won't offer + it. */ + + struct type *callee_type = value_type (called_method); + + if (callee_type && callee_type->code () == TYPE_CODE_PTR) + callee_type = TYPE_TARGET_TYPE (callee_type); + callee_type = TYPE_TARGET_TYPE (callee_type); + + if (callee_type) + { + if ((callee_type->code () == TYPE_CODE_ERROR) && expect_type) + return allocate_value (expect_type); + else + return allocate_value (callee_type); + } + else + error (_("Expression of type other than " + "\"method returning ...\" used as a method")); + } + + /* Now depending on whether we found a symbol for the method, + we will either call the runtime dispatcher or the method + directly. */ + + args[0] = target; + args[1] = value_from_longest (long_type, selector); + + if (gnu_runtime && (method != NULL)) + { + /* Function objc_msg_lookup returns a pointer. */ + struct type *tem_type = value_type (called_method); + tem_type = lookup_pointer_type (lookup_function_type (tem_type)); + deprecated_set_value_type (called_method, tem_type); + called_method = call_function_by_hand (called_method, NULL, args); + } + + return call_function_by_hand (called_method, NULL, args); +} + struct value * evaluate_subexp_standard (struct type *expect_type, struct expression *exp, int *pos, @@ -2376,36 +2647,20 @@ evaluate_subexp_standard (struct type *expect_type, case OP_OBJC_MSGCALL: { /* Objective C message (method) call. */ - - CORE_ADDR responds_selector = 0; - CORE_ADDR method_selector = 0; - CORE_ADDR selector = 0; - int struct_return = 0; enum noside sub_no_side = EVAL_NORMAL; - struct value *msg_send = NULL; - struct value *msg_send_stret = NULL; - int gnu_runtime = 0; - struct value *target = NULL; - struct value *method = NULL; - struct value *called_method = NULL; struct type *selector_type = NULL; - struct type *long_type; - - struct value *ret = NULL; - CORE_ADDR addr = 0; selector = exp->elts[pc + 1].longconst; nargs = exp->elts[pc + 2].longconst; - argvec = XALLOCAVEC (struct value *, nargs + 5); + argvec = XALLOCAVEC (struct value *, nargs + 3); (*pos) += 3; - long_type = builtin_type (exp->gdbarch)->builtin_long; selector_type = builtin_type (exp->gdbarch)->builtin_data_ptr; if (noside == EVAL_AVOID_SIDE_EFFECTS) @@ -2416,249 +2671,26 @@ evaluate_subexp_standard (struct type *expect_type, target = evaluate_subexp (selector_type, exp, pos, sub_no_side); if (value_as_long (target) == 0) - return value_from_longest (long_type, 0); - - if (lookup_minimal_symbol ("objc_msg_lookup", 0, 0).minsym) - gnu_runtime = 1; - - /* Find the method dispatch (Apple runtime) or method lookup - (GNU runtime) function for Objective-C. These will be used - to lookup the symbol information for the method. If we - can't find any symbol information, then we'll use these to - call the method, otherwise we can call the method - directly. The msg_send_stret function is used in the special - case of a method that returns a structure (Apple runtime - only). */ - if (gnu_runtime) - { - type = selector_type; - - type = lookup_function_type (type); - type = lookup_pointer_type (type); - type = lookup_function_type (type); - type = lookup_pointer_type (type); - - msg_send = find_function_in_inferior ("objc_msg_lookup", NULL); - msg_send_stret - = find_function_in_inferior ("objc_msg_lookup", NULL); - - msg_send = value_from_pointer (type, value_as_address (msg_send)); - msg_send_stret = value_from_pointer (type, - value_as_address (msg_send_stret)); - } + sub_no_side = EVAL_SKIP; else - { - msg_send = find_function_in_inferior ("objc_msgSend", NULL); - /* Special dispatcher for methods returning structs. */ - msg_send_stret - = find_function_in_inferior ("objc_msgSend_stret", NULL); - } - - /* Verify the target object responds to this method. The - standard top-level 'Object' class uses a different name for - the verification method than the non-standard, but more - often used, 'NSObject' class. Make sure we check for both. */ - - responds_selector - = lookup_child_selector (exp->gdbarch, "respondsToSelector:"); - if (responds_selector == 0) - responds_selector - = lookup_child_selector (exp->gdbarch, "respondsTo:"); - - if (responds_selector == 0) - error (_("no 'respondsTo:' or 'respondsToSelector:' method")); - - method_selector - = lookup_child_selector (exp->gdbarch, "methodForSelector:"); - if (method_selector == 0) - method_selector - = lookup_child_selector (exp->gdbarch, "methodFor:"); - - if (method_selector == 0) - error (_("no 'methodFor:' or 'methodForSelector:' method")); - - /* Call the verification method, to make sure that the target - class implements the desired method. */ - - argvec[0] = msg_send; - argvec[1] = target; - argvec[2] = value_from_longest (long_type, responds_selector); - argvec[3] = value_from_longest (long_type, selector); - argvec[4] = 0; - - ret = call_function_by_hand (argvec[0], NULL, {argvec + 1, 3}); - if (gnu_runtime) - { - /* Function objc_msg_lookup returns a pointer. */ - argvec[0] = ret; - ret = call_function_by_hand (argvec[0], NULL, {argvec + 1, 3}); - } - if (value_as_long (ret) == 0) - error (_("Target does not respond to this message selector.")); - - /* Call "methodForSelector:" method, to get the address of a - function method that implements this selector for this - class. If we can find a symbol at that address, then we - know the return type, parameter types etc. (that's a good - thing). */ - - argvec[0] = msg_send; - argvec[1] = target; - argvec[2] = value_from_longest (long_type, method_selector); - argvec[3] = value_from_longest (long_type, selector); - argvec[4] = 0; - - ret = call_function_by_hand (argvec[0], NULL, {argvec + 1, 3}); - if (gnu_runtime) - { - argvec[0] = ret; - ret = call_function_by_hand (argvec[0], NULL, {argvec + 1, 3}); - } - - /* ret should now be the selector. */ - - addr = value_as_long (ret); - if (addr) - { - struct symbol *sym = NULL; - - /* The address might point to a function descriptor; - resolve it to the actual code address instead. */ - addr = gdbarch_convert_from_func_ptr_addr (exp->gdbarch, addr, - current_top_target ()); - - /* Is it a high_level symbol? */ - sym = find_pc_function (addr); - if (sym != NULL) - method = value_of_variable (sym, 0); - } - - /* If we found a method with symbol information, check to see - if it returns a struct. Otherwise assume it doesn't. */ - - if (method) - { - CORE_ADDR funaddr; - struct type *val_type; - - funaddr = find_function_addr (method, &val_type); - - block_for_pc (funaddr); - - val_type = check_typedef (val_type); - - if ((val_type == NULL) - || (val_type->code () == TYPE_CODE_ERROR)) - { - if (expect_type != NULL) - val_type = expect_type; - } - - struct_return = using_struct_return (exp->gdbarch, method, - val_type); - } - else if (expect_type != NULL) - { - struct_return = using_struct_return (exp->gdbarch, NULL, - check_typedef (expect_type)); - } - - /* Found a function symbol. Now we will substitute its - value in place of the message dispatcher (obj_msgSend), - so that we call the method directly instead of thru - the dispatcher. The main reason for doing this is that - we can now evaluate the return value and parameter values - according to their known data types, in case we need to - do things like promotion, dereferencing, special handling - of structs and doubles, etc. - - We want to use the type signature of 'method', but still - jump to objc_msgSend() or objc_msgSend_stret() to better - mimic the behavior of the runtime. */ - - if (method) - { - if (value_type (method)->code () != TYPE_CODE_FUNC) - error (_("method address has symbol information " - "with non-function type; skipping")); - - /* Create a function pointer of the appropriate type, and - replace its value with the value of msg_send or - msg_send_stret. We must use a pointer here, as - msg_send and msg_send_stret are of pointer type, and - the representation may be different on systems that use - function descriptors. */ - if (struct_return) - called_method - = value_from_pointer (lookup_pointer_type (value_type (method)), - value_as_address (msg_send_stret)); - else - called_method - = value_from_pointer (lookup_pointer_type (value_type (method)), - value_as_address (msg_send)); - } - else - { - if (struct_return) - called_method = msg_send_stret; - else - called_method = msg_send; - } - - if (noside == EVAL_SKIP) - return eval_skip_value (exp); - - if (noside == EVAL_AVOID_SIDE_EFFECTS) - { - /* If the return type doesn't look like a function type, - call an error. This can happen if somebody tries to - turn a variable into a function call. This is here - because people often want to call, eg, strcmp, which - gdb doesn't know is a function. If gdb isn't asked for - it's opinion (ie. through "whatis"), it won't offer - it. */ - - struct type *callee_type = value_type (called_method); - - if (callee_type && callee_type->code () == TYPE_CODE_PTR) - callee_type = TYPE_TARGET_TYPE (callee_type); - callee_type = TYPE_TARGET_TYPE (callee_type); - - if (callee_type) - { - if ((callee_type->code () == TYPE_CODE_ERROR) && expect_type) - return allocate_value (expect_type); - else - return allocate_value (callee_type); - } - else - error (_("Expression of type other than " - "\"method returning ...\" used as a method")); - } + sub_no_side = noside; /* Now depending on whether we found a symbol for the method, we will either call the runtime dispatcher or the method directly. */ - argvec[0] = called_method; - argvec[1] = target; - argvec[2] = value_from_longest (long_type, selector); + argvec[0] = nullptr; + argvec[1] = nullptr; /* User-supplied arguments. */ for (tem = 0; tem < nargs; tem++) - argvec[tem + 3] = evaluate_subexp_with_coercion (exp, pos, noside); + argvec[tem + 2] = evaluate_subexp_with_coercion (exp, pos, + sub_no_side); argvec[tem + 3] = 0; - auto call_args = gdb::make_array_view (argvec + 1, nargs + 2); - - if (gnu_runtime && (method != NULL)) - { - /* Function objc_msg_lookup returns a pointer. */ - deprecated_set_value_type (argvec[0], - lookup_pointer_type (lookup_function_type (value_type (argvec[0])))); - argvec[0] = call_function_by_hand (argvec[0], NULL, call_args); - } + auto call_args = gdb::make_array_view (argvec, nargs + 3); - return call_function_by_hand (argvec[0], NULL, call_args); + return eval_op_objc_msgcall (expect_type, exp, noside, selector, + target, call_args); } break; -- 2.34.1