Hi, I'm using `build_function_call()` in a plugin I'm writing, creating `printf()` and `sprintf()` calls.
A few days ago I tested it with recent GCC (9.2.0) and -Wall, and got "internal compiler error: Segmentation fault" somewhere deep in `build_function_call()`. Spent some time comparing the `tree`s I pass to this function with `tree` nodes generated by the compiler (c-parser.c) for equivalent calls, and they were the same. Went ahead with GDB to find the crashing code in c-format.c:3123 (on releases/gcc-9.1.0), function is `check_format_types()`: `` location_t param_loc = UNKNOWN_LOCATION; if (EXPR_HAS_LOCATION (cur_param)) param_loc = EXPR_LOCATION (cur_param); else if (arglocs) { /* arg_num is 1-based. */ gcc_assert (types->arg_num > 0); param_loc = (*arglocs)[types->arg_num - 1]; } ``` In my case `cur_param` doesn't have a location (it's an INTEGER_CST). `arglocs` is not NULL, actually it can never be NULL in that particular code flow: `build_function_call_vec()` accepts `vec<location_t> arg_loc`, and it's passed onward to `check_function_arguments()` as `&arg_loc`. This `&arg_loc` later reaches the code snippet I presented above. `*arglocs` was initialized with `vNULL` by `build_function_call()`, so dereferencing it is invalid, and cc1 crashes. `build_function_call()` always uses `arglocs=vNULL`, but I see it's not used by the C frontend. I wrote a simple test program invoking the same behavior (`printf("%d\n", 0)`), and compiled it with a custom GCC with some added debug prints, and it proves `*arglocs` is not vNULL (the C frontend uses `c_build_function_call_vec()` directly and passes a non-vNULL `arglocs`). I tried wrapping the location-less INTEGER_CSTs with location-ed NOP_EXPR / `non_lvalue_loc()`, but these are seemingly stripped before this code is reached. So eventually to solve the problem locally in my plugin, I resorted to using `c_build_function_call_vec()` directly, passing non-vNULL `arglocs`. I think this behavior is weird; either `build_function_call`, or the snippet above, are broken. It's a legit (I think?) code flow that always crashes if it's executed... I created a "fixed" version of GCC by changing `else if (arglocs)` to `else if (arglocs && *arglocs != vNULL)`. Does this fix make sense, or I've been using the API incorrectly? I'd be happy to send a fixing patch if it's deemed relevant. Reference plugin exhibiting the error (tested with GCC 9.2.0 + 9.1.0): ``` $ cat bug.c #include <stdio.h> #include <gcc-plugin.h> #include <tree.h> #include <c-family/c-common.h> int plugin_is_GPL_compatible; static void finish_decl_callback(void *event_data, void *user_data) { tree decl = (tree)event_data; if (TREE_CODE(decl) == FUNCTION_DECL && 0 == strcmp("printf", IDENTIFIER_POINTER(DECL_NAME(decl)))) { build_function_call(BUILTINS_LOCATION, decl, tree_cons(NULL_TREE, build_string_literal(4, "%d\n"), tree_cons(NULL_TREE, integer_zero_node, NULL_TREE))); } } int plugin_init(struct plugin_name_args *plugin_info, struct plugin_gcc_version *version) { register_callback(plugin_info->base_name, PLUGIN_FINISH_DECL, finish_decl_callback, NULL); return 0; } $ cat trigger.c #include <stdio.h> $ g++ -g -Wall -Werror -I`gcc -print-file-name=plugin`/include \ -fpic -shared -o bug.so bug.c $ gcc -Wall -fplugin=./bug.so -c trigger.c ... internal compiler error: Segmentation fault ... ``` Am I using it wrong? Would appreciate advice. Thanks, Yonatan