On Mon, Dec 01, 2025 at 03:28:17PM +0100, Josef Melcr wrote:
> previously, clones of callback functions had their local flag set.
> Because callback edges are direct rather than indirect, GCC falsely
> assumes that their callsites are available and that it can change their
> ABI, leading to segfaults. This patch fixes that. Bootstrapped and
> regtested on x86_64-pc-linux-gnu with
> RUNTESTFLAGS="--target_board='unix{-m32}'". OK for master?
> gcc/ChangeLog:
>
> PR ipa/122798
> * cgraph.cc (cgraph_edge::redirect_callee): Set address taken
> for new callee.
> (cgraph_node::callback_called_p): New function.
> * cgraph.h (struct cgraph_node): Likewise.
> * cgraphclones.cc (cgraph_node::create_virtual_clone): Set local
> flag to false when cloning a function with a callback call.
>
> Signed-off-by: Josef Melcr <[email protected]>
> ---
> gcc/cgraph.cc | 10 ++++++++++
> gcc/cgraph.h | 3 +++
> gcc/cgraphclones.cc | 4 ++++
> 3 files changed, 17 insertions(+)
>
> diff --git a/gcc/cgraph.cc b/gcc/cgraph.cc
> index 3c21e174943..64b19b5e5ac 100644
> --- a/gcc/cgraph.cc
> +++ b/gcc/cgraph.cc
> @@ -1698,6 +1698,7 @@ cgraph_edge::redirect_callee (cgraph_node *n)
> new_ref->lto_stmt_uid = lto_stmt_uid;
> if (!old_callee->referred_to_p ())
> old_callee->address_taken = 0;
> + n->mark_address_taken();
Space before (
> }
>
> if (!inline_failed)
> @@ -3575,6 +3576,15 @@ cgraph_node::only_called_directly_p (void)
> NULL, true);
> }
>
Please repeat the cgraph. comment here too.
> +bool
> +cgraph_node::callback_called_p () const
> +{
> + cgraph_edge *e;
> + for (e = callers; e; e = e->next_caller)
> + if (e->callback)
> + return true;
> + return false;
> +}
>
> /* Collect all callers of NODE. Worker for collect_callers_of_node. */
>
> diff --git a/gcc/cgraph.h b/gcc/cgraph.h
> index 313610fbe2c..b534a470b77 100644
> --- a/gcc/cgraph.h
> +++ b/gcc/cgraph.h
> @@ -1278,6 +1278,9 @@ struct GTY((tag ("SYMTAB_FUNCTION"))) cgraph_node :
> public symtab_node
> it is not used in any other non-standard way. */
> bool only_called_directly_p (void);
>
> + /* Return TRUE if the node has at least one callback edge pointing to it.
> */
> + bool callback_called_p (void) const;
> +
> /* Turn profile to global0. Walk into inlined functions. */
> void make_profile_local ();
>
> diff --git a/gcc/cgraphclones.cc b/gcc/cgraphclones.cc
> index 49f0e58fa1e..3cfbfec4c1a 100644
> --- a/gcc/cgraphclones.cc
> +++ b/gcc/cgraphclones.cc
> @@ -687,6 +687,7 @@ cgraph_node::create_virtual_clone (const vec<cgraph_edge
> *> &redirect_callers,
> clone_function_name (old_decl, suffix, num_suffix));
> SET_DECL_RTL (new_decl, NULL);
>
> + bool has_callback_call = callback_called_p ();
> new_node = create_clone (new_decl, count, false,
> redirect_callers, false, NULL, param_adjustments,
> suffix);
> @@ -697,6 +698,9 @@ cgraph_node::create_virtual_clone (const vec<cgraph_edge
> *> &redirect_callers,
> ??? We cannot use COMDAT linkage because there is no
> ABI support for this. */
> set_new_clone_decl_and_node_flags (new_node);
> + if (has_callback_call)
> + /* Callback clone can never be local, since it has its address taken. */
> + new_node->local = false;
> new_node->ipcp_clone = ipcp_clone;
> if (tree_map)
> clone_info::get_create (new_node)->tree_map = tree_map;
Jakub