nptl: Add callback style attributes to pthread_create

Message ID 20261001141404.1111923-2-josef.melcr@suse.com (mailing list archive)
State Changes Requested
Headers
Series nptl: Add callback style attributes to pthread_create |

Checks

Context Check Description
redhat-pt-bot/TryBot-apply_patch success Patch applied to master at the time it was sent
linaro-tcwg-bot/tcwg_glibc_build--master-aarch64 success Build passed
linaro-tcwg-bot/tcwg_glibc_check--master-aarch64 success Test passed
redhat-pt-bot/TryBot-32bit success Build for i686
linaro-tcwg-bot/tcwg_glibc_build--master-arm success Build passed
linaro-tcwg-bot/tcwg_glibc_check--master-arm success Test passed

Commit Message

Josef Melcr Oct. 1, 2026, 2:14 p.m. UTC
  Add compiler-specific callback attribute to the pthread_create function
declaration.  These attributes provide additional information about the
hidden callsite in the function's body, which helps guide
interprocedural optimization of the callback.

Even though the semantics of the attributes are the same, their names
and syntax differs.  See the docs of the attributes here:
https://gcc.gnu.org/onlinedocs/gcc/Common-Attributes.html#index-callback_005fonly
https://clang.llvm.org/docs/AttributeReference.html#callback

Note that the callback_only attribute is not yet released and is only
available on gcc's master branch (since r17-3013-gcad654efbba62d).

Regtested on x86_64-linux.  I encountered an nss/tst-getaddrinfo4.c
fail, but it's failing on me with a clean tree as well.

Signed-off-by: Josef Melcr <josef.melcr@suse.com>
---
 misc/sys/cdefs.h       | 16 ++++++++++++++++
 sysdeps/nptl/pthread.h |  9 +++++----
 2 files changed, 21 insertions(+), 4 deletions(-)
  

Comments

Adhemerval Zanella Oct. 7, 2026, 6:12 p.m. UTC | #1
On 01/10/26 16:14, Josef Melcr wrote:
> Add compiler-specific callback attribute to the pthread_create function
> declaration.  These attributes provide additional information about the
> hidden callsite in the function's body, which helps guide
> interprocedural optimization of the callback.
> 
> Even though the semantics of the attributes are the same, their names
> and syntax differs.  See the docs of the attributes here:
> https://gcc.gnu.org/onlinedocs/gcc/Common-Attributes.html#index-callback_005fonly
> https://clang.llvm.org/docs/AttributeReference.html#callback

Is the semantic really the same? GCC's own manual says the attribute is 
"similar to the clang callback attribute but it is not compatible with it". At
least for glibc usage we can hide this with some macro foo.

Also, the documentation states the callback pointer "may not be captured", 
the callee "may not change or dereference" the arguments, and "The arguments also 
may not escape". The pthread_create stores both in the thread descriptor and 
calls the routine later, from another thread. 

The LLVM explicit states that this is allowed [1]: "the broker is also allowed 
to relay the callback callee invocation to a different thread". Maybe document
the same guarantee as for the gcc counterpart.

[1] https://llvm.org/docs/LangRef.html#callback-metadata

> 
> Note that the callback_only attribute is not yet released and is only
> available on gcc's master branch (since r17-3013-gcad654efbba62d).
> 
> Regtested on x86_64-linux.  I encountered an nss/tst-getaddrinfo4.c
> fail, but it's failing on me with a clean tree as well.
> 
> Signed-off-by: Josef Melcr <josef.melcr@suse.com>
> ---
>  misc/sys/cdefs.h       | 16 ++++++++++++++++
>  sysdeps/nptl/pthread.h |  9 +++++----
>  2 files changed, 21 insertions(+), 4 deletions(-)
> 
> diff --git a/misc/sys/cdefs.h b/misc/sys/cdefs.h
> index 84c4aae262..21fb8fe2ea 100644
> --- a/misc/sys/cdefs.h
> +++ b/misc/sys/cdefs.h
> @@ -570,6 +570,22 @@
>  # define __nonnull(params) __attribute_nonnull__ (params)
>  #endif
>  
> +/* The callback/callback_only attribute specifies that a function calls one of
> +   its arguments and passes its arguments to it without any modifications.
> +
> +   There are two callback attributes.  'callback' is only supported by clang,
> +   'callback_only' is only supported by GCC.  Both capture the same meaning,
> +   but their syntax differs.  */
> +#ifndef __callback
> +# if __GNUC_PREREQ (17,0) || __glibc_has_attribute(__callback_only__)

Missing space after __glibc_has_attribute

> +#  define __callback(params) __attribute__ ((__callback_only__ params))
> +# elif __glibc_clang_prereq (9,0) || __glibc_has_attribute(__callback__)
> +#  define __callback(params) __attribute__ ((__callback__ params))
> +# else
> +#  define __callback(params) /* Ignore */
> +# endif
> +#endif /* __calback */

s/__calback/__callaback

> +

I think we should either follow other attribute names (__attribute_callback__) or
as reecent cdefs.h additions to use the __attr_ prefix (__attr_callback).

And I don't see why we can't add clang support as well. It should be something
as:

#ifndef __attr_callback
# if __glibc_has_attribute (__callback_only__)
#  define __attr_callback(params) __attribute__ ((__callback_only__ params))
#  define __attr_callback_unknown 0
# elif __glibc_has_attribute (__callback__)
#  define __attr_callback(params) __attribute__ ((__callback__ params))
#  define __attr_callback_unknown -1
# else
#  define __attr_callback(params) /* Ignore */
# endif
#endif


>  /* The returns_nonnull function attribute marks the return type of the function
>     as always being non-null.  */
>  #ifndef __returns_nonnull
> diff --git a/sysdeps/nptl/pthread.h b/sysdeps/nptl/pthread.h
> index 95c0eb7e03..7f8ca92e6b 100644
> --- a/sysdeps/nptl/pthread.h
> +++ b/sysdeps/nptl/pthread.h
> @@ -199,10 +199,11 @@ __BEGIN_DECLS
>  /* Create a new thread, starting with execution of START-ROUTINE
>     getting passed ARG.  Creation attributed come from ATTR.  The new
>     handle is stored in *NEWTHREAD.  */
> -extern int pthread_create (pthread_t *__restrict __newthread,
> -			   const pthread_attr_t *__restrict __attr,
> -			   void *(*__start_routine) (void *),
> -			   void *__restrict __arg) __THROWNL __nonnull ((1, 3));
> +__callback ((3, 4)) extern int

glibc usually puts attributes after the declarator.

> +pthread_create (pthread_t *__restrict __newthread,
> +                const pthread_attr_t *__restrict __attr,
> +                void *(*__start_routine) (void *),
> +                void *__restrict __arg) __THROWNL __nonnull ((1, 3));

Hurd's sysdeps/htl/pthread.h declares pthread_create too and needs the same annotation.

>  
>  /* Terminate calling thread.
>  

I think we should add also instrument all other functions that uses callback (U being
__attr_callback_unknown):

Function			Header		Annotation
thrd_create			threads.h	(2, 3)
on_exit				stdlib.h	(1, U, 2)
qsort_r				stdlib.h	(4, U, U, 5)
bsearch				stdlib.h	(5, 1, U)
lfind				search.h	(5, 1, U)
tfind, tdelete			search.h	(3, 1, U)
twalk_r				search.h	(2, U, U, 3)
td_ta_thr_iter / td_ta_tsd_iter	thread_db.h	(2, U, 3) / (2, U, U, 3)
dl_iterate_phdr			link.h		(1, U, U, 2)
  
Adhemerval Zanella Oct. 7, 2026, 7 p.m. UTC | #2
On 01/10/26 16:14, Josef Melcr wrote:
> Add compiler-specific callback attribute to the pthread_create function
> declaration.  These attributes provide additional information about the
> hidden callsite in the function's body, which helps guide
> interprocedural optimization of the callback.
> 
> Even though the semantics of the attributes are the same, their names
> and syntax differs.  See the docs of the attributes here:
> https://gcc.gnu.org/onlinedocs/gcc/Common-Attributes.html#index-callback_005fonly
> https://clang.llvm.org/docs/AttributeReference.html#callback

Is the semantic really the same? GCC's own manual says the attribute is 
"similar to the clang callback attribute but it is not compatible with it". At
least for glibc usage we can hide this with some macro foo.

Also, the documentation states the callback pointer "may not be captured", 
the callee "may not change or dereference" the arguments, and "The arguments also 
may not escape". The pthread_create stores both in the thread descriptor and 
calls the routine later, from another thread. 

The LLVM explicit states that this is allowed [1]: "the broker is also allowed 
to relay the callback callee invocation to a different thread". Maybe document
the same guarantee as for the gcc counterpart.

[1] https://llvm.org/docs/LangRef.html#callback-metadata

> 
> Note that the callback_only attribute is not yet released and is only
> available on gcc's master branch (since r17-3013-gcad654efbba62d).
> 
> Regtested on x86_64-linux.  I encountered an nss/tst-getaddrinfo4.c
> fail, but it's failing on me with a clean tree as well.
> 
> Signed-off-by: Josef Melcr <josef.melcr@suse.com>
> ---
>  misc/sys/cdefs.h       | 16 ++++++++++++++++
>  sysdeps/nptl/pthread.h |  9 +++++----
>  2 files changed, 21 insertions(+), 4 deletions(-)
> 
> diff --git a/misc/sys/cdefs.h b/misc/sys/cdefs.h
> index 84c4aae262..21fb8fe2ea 100644
> --- a/misc/sys/cdefs.h
> +++ b/misc/sys/cdefs.h
> @@ -570,6 +570,22 @@
>  # define __nonnull(params) __attribute_nonnull__ (params)
>  #endif
>  
> +/* The callback/callback_only attribute specifies that a function calls one of
> +   its arguments and passes its arguments to it without any modifications.
> +
> +   There are two callback attributes.  'callback' is only supported by clang,
> +   'callback_only' is only supported by GCC.  Both capture the same meaning,
> +   but their syntax differs.  */
> +#ifndef __callback
> +# if __GNUC_PREREQ (17,0) || __glibc_has_attribute(__callback_only__)

Missing space after __glibc_has_attribute

> +#  define __callback(params) __attribute__ ((__callback_only__ params))
> +# elif __glibc_clang_prereq (9,0) || __glibc_has_attribute(__callback__)
> +#  define __callback(params) __attribute__ ((__callback__ params))
> +# else
> +#  define __callback(params) /* Ignore */
> +# endif
> +#endif /* __calback */

s/__calback/__callaback

> +

I think we should either follow other attribute names (__attribute_callback__) or
as reecent cdefs.h additions to use the __attr_ prefix (__attr_callback).

And I don't see why we can't add clang support as well. It should be something
as:

#ifndef __attr_callback
# if __glibc_has_attribute (__callback_only__)
#  define __attr_callback(params) __attribute__ ((__callback_only__ params))
#  define __attr_callback_unknown 0
# elif __glibc_has_attribute (__callback__)
#  define __attr_callback(params) __attribute__ ((__callback__ params))
#  define __attr_callback_unknown -1
# else
#  define __attr_callback(params) /* Ignore */
# endif
#endif


>  /* The returns_nonnull function attribute marks the return type of the function
>     as always being non-null.  */
>  #ifndef __returns_nonnull
> diff --git a/sysdeps/nptl/pthread.h b/sysdeps/nptl/pthread.h
> index 95c0eb7e03..7f8ca92e6b 100644
> --- a/sysdeps/nptl/pthread.h
> +++ b/sysdeps/nptl/pthread.h
> @@ -199,10 +199,11 @@ __BEGIN_DECLS
>  /* Create a new thread, starting with execution of START-ROUTINE
>     getting passed ARG.  Creation attributed come from ATTR.  The new
>     handle is stored in *NEWTHREAD.  */
> -extern int pthread_create (pthread_t *__restrict __newthread,
> -			   const pthread_attr_t *__restrict __attr,
> -			   void *(*__start_routine) (void *),
> -			   void *__restrict __arg) __THROWNL __nonnull ((1, 3));
> +__callback ((3, 4)) extern int

glibc usually puts attributes after the declarator.

> +pthread_create (pthread_t *__restrict __newthread,
> +                const pthread_attr_t *__restrict __attr,
> +                void *(*__start_routine) (void *),
> +                void *__restrict __arg) __THROWNL __nonnull ((1, 3));

Hurd's sysdeps/htl/pthread.h declares pthread_create too and needs the same annotation.

>  
>  /* Terminate calling thread.
>  

I think we should add also instrument all other functions that uses callback (U being
__attr_callback_unknown):

Function			Header		Annotation
thrd_create			threads.h	(2, 3)
on_exit				stdlib.h	(1, U, 2)
qsort_r				stdlib.h	(4, U, U, 5)
bsearch				stdlib.h	(5, 1, U)
lfind				search.h	(5, 1, U)
tfind, tdelete			search.h	(3, 1, U)
twalk_r				search.h	(2, U, U, 3)
td_ta_thr_iter / td_ta_tsd_iter	thread_db.h	(2, U, 3) / (2, U, U, 3)
dl_iterate_phdr			link.h		(1, U, U, 2)
  
Florian Weimer Oct. 8, 2026, 2:20 p.m. UTC | #3
* Josef Melcr:

> Add compiler-specific callback attribute to the pthread_create function
> declaration.  These attributes provide additional information about the
> hidden callsite in the function's body, which helps guide
> interprocedural optimization of the callback.
>
> Even though the semantics of the attributes are the same, their names
> and syntax differs.  See the docs of the attributes here:
> https://gcc.gnu.org/onlinedocs/gcc/Common-Attributes.html#index-callback_005fonly
> https://clang.llvm.org/docs/AttributeReference.html#callback

The GCC documentation says this:

| The specified pointer to the callback may not escape the translation
| unit of the annotated function and it may not be captured.

The glibc implementation of pthread_create captures the pointer, and it
escapes the translation unit.

Thanks,
Florian
  
Josef Melcr Oct. 8, 2026, 2:58 p.m. UTC | #4
On Wed Oct 7, 2026 at 8:12 PM CEST, Adhemerval Zanella Netto wrote:
>
>
> On 01/10/26 16:14, Josef Melcr wrote:
>> Add compiler-specific callback attribute to the pthread_create function
>> declaration.  These attributes provide additional information about the
>> hidden callsite in the function's body, which helps guide
>> interprocedural optimization of the callback.
>> 
>> Even though the semantics of the attributes are the same, their names
>> and syntax differs.  See the docs of the attributes here:
>> https://gcc.gnu.org/onlinedocs/gcc/Common-Attributes.html#index-callback_005fonly
>> https://clang.llvm.org/docs/AttributeReference.html#callback
>
> Is the semantic really the same? GCC's own manual says the attribute is 
> "similar to the clang callback attribute but it is not compatible with it". At
> least for glibc usage we can hide this with some macro foo.
>
> Also, the documentation states the callback pointer "may not be captured", 
> the callee "may not change or dereference" the arguments, and "The arguments also 
> may not escape". The pthread_create stores both in the thread descriptor and 
> calls the routine later, from another thread. 
>
> The LLVM explicit states that this is allowed [1]: "the broker is also allowed 
> to relay the callback callee invocation to a different thread". Maybe document
> the same guarantee as for the gcc counterpart.

I'll revisit it and try to unify them as much as possible.  For example,
capturing the pointers may or may not be an issue.  If the function is
invoked exactly as the descriptor says then there is no problem.  But
since the function pointer may be replaced with a highly specialized
version, any change in the arguments or escaping to where the same
guarantees cannot be made could cause a lot of trouble.

>
> [1] https://llvm.org/docs/LangRef.html#callback-metadata
>
>> 
>> Note that the callback_only attribute is not yet released and is only
>> available on gcc's master branch (since r17-3013-gcad654efbba62d).
>> 
>> Regtested on x86_64-linux.  I encountered an nss/tst-getaddrinfo4.c
>> fail, but it's failing on me with a clean tree as well.
>> 
>> Signed-off-by: Josef Melcr <josef.melcr@suse.com>
>> ---
>>  misc/sys/cdefs.h       | 16 ++++++++++++++++
>>  sysdeps/nptl/pthread.h |  9 +++++----
>>  2 files changed, 21 insertions(+), 4 deletions(-)
>> 
>> diff --git a/misc/sys/cdefs.h b/misc/sys/cdefs.h
>> index 84c4aae262..21fb8fe2ea 100644
>> --- a/misc/sys/cdefs.h
>> +++ b/misc/sys/cdefs.h
>> @@ -570,6 +570,22 @@
>>  # define __nonnull(params) __attribute_nonnull__ (params)
>>  #endif
>>  
>> +/* The callback/callback_only attribute specifies that a function calls one of
>> +   its arguments and passes its arguments to it without any modifications.
>> +
>> +   There are two callback attributes.  'callback' is only supported by clang,
>> +   'callback_only' is only supported by GCC.  Both capture the same meaning,
>> +   but their syntax differs.  */
>> +#ifndef __callback
>> +# if __GNUC_PREREQ (17,0) || __glibc_has_attribute(__callback_only__)
>
> Missing space after __glibc_has_attribute
>
>> +#  define __callback(params) __attribute__ ((__callback_only__ params))
>> +# elif __glibc_clang_prereq (9,0) || __glibc_has_attribute(__callback__)
>> +#  define __callback(params) __attribute__ ((__callback__ params))
>> +# else
>> +#  define __callback(params) /* Ignore */
>> +# endif
>> +#endif /* __calback */
>
> s/__calback/__callaback
>
>> +
>
> I think we should either follow other attribute names (__attribute_callback__) or
> as reecent cdefs.h additions to use the __attr_ prefix (__attr_callback).
>
> And I don't see why we can't add clang support as well. It should be something
> as:
>
> #ifndef __attr_callback
> # if __glibc_has_attribute (__callback_only__)
> #  define __attr_callback(params) __attribute__ ((__callback_only__ params))
> #  define __attr_callback_unknown 0
> # elif __glibc_has_attribute (__callback__)
> #  define __attr_callback(params) __attribute__ ((__callback__ params))
> #  define __attr_callback_unknown -1
> # else
> #  define __attr_callback(params) /* Ignore */
> # endif
> #endif
>

Good point, I'll amend it in a future version, along with the typos fixed.
I didn't to include the unknown values as they weren't needed yet.

>
>>  /* The returns_nonnull function attribute marks the return type of the function
>>     as always being non-null.  */
>>  #ifndef __returns_nonnull
>> diff --git a/sysdeps/nptl/pthread.h b/sysdeps/nptl/pthread.h
>> index 95c0eb7e03..7f8ca92e6b 100644
>> --- a/sysdeps/nptl/pthread.h
>> +++ b/sysdeps/nptl/pthread.h
>> @@ -199,10 +199,11 @@ __BEGIN_DECLS
>>  /* Create a new thread, starting with execution of START-ROUTINE
>>     getting passed ARG.  Creation attributed come from ATTR.  The new
>>     handle is stored in *NEWTHREAD.  */
>> -extern int pthread_create (pthread_t *__restrict __newthread,
>> -			   const pthread_attr_t *__restrict __attr,
>> -			   void *(*__start_routine) (void *),
>> -			   void *__restrict __arg) __THROWNL __nonnull ((1, 3));
>> +__callback ((3, 4)) extern int
>
> glibc usually puts attributes after the declarator.

The two placements don't mean the same thing.  Placing an attribute
after the declaration makes it apply to the function type, rather than
the declaration.  With the modern attribute syntax ([[attr]]), placing
it after the declaration wouldn't work.  Though with the old
__attribute__ syntax, it should work fine.  I'll change it then.

>
>> +pthread_create (pthread_t *__restrict __newthread,
>> +                const pthread_attr_t *__restrict __attr,
>> +                void *(*__start_routine) (void *),
>> +                void *__restrict __arg) __THROWNL __nonnull ((1, 3));
>
> Hurd's sysdeps/htl/pthread.h declares pthread_create too and needs the same annotation.
>
>>  
>>  /* Terminate calling thread.
>>  
>
> I think we should add also instrument all other functions that uses callback (U being
> __attr_callback_unknown):
>
> Function			Header		Annotation
> thrd_create			threads.h	(2, 3)
> on_exit				stdlib.h	(1, U, 2)
> qsort_r				stdlib.h	(4, U, U, 5)
> bsearch				stdlib.h	(5, 1, U)
> lfind				search.h	(5, 1, U)
> tfind, tdelete			search.h	(3, 1, U)
> twalk_r				search.h	(2, U, U, 3)
> td_ta_thr_iter / td_ta_tsd_iter	thread_db.h	(2, U, 3) / (2, U, U, 3)
> dl_iterate_phdr			link.h		(1, U, U, 2)

I am aware that there are other functions which could be annotated as
well.  I was mainly testing the waters with this patch, with the intent
of expanding on it in the future.  I can add it everywhere at once if
you'd prefer that.

On a side note, is this patch OK under DCO?  I tried to make sense of
the rules before submitting this patch, but couldn't figure out if I
needed a copyright form or not.

Best regards,
Josef
  
Adhemerval Zanella Oct. 8, 2026, 4:39 p.m. UTC | #5
On 08/10/26 16:58, Josef Melcr wrote:
> On Wed Oct 7, 2026 at 8:12 PM CEST, Adhemerval Zanella Netto wrote:
>>
>>
>> On 01/10/26 16:14, Josef Melcr wrote:
>>> Add compiler-specific callback attribute to the pthread_create function
>>> declaration.  These attributes provide additional information about the
>>> hidden callsite in the function's body, which helps guide
>>> interprocedural optimization of the callback.
>>>
>>> Even though the semantics of the attributes are the same, their names
>>> and syntax differs.  See the docs of the attributes here:
>>> https://gcc.gnu.org/onlinedocs/gcc/Common-Attributes.html#index-callback_005fonly
>>> https://clang.llvm.org/docs/AttributeReference.html#callback
>>
>> Is the semantic really the same? GCC's own manual says the attribute is 
>> "similar to the clang callback attribute but it is not compatible with it". At
>> least for glibc usage we can hide this with some macro foo.
>>
>> Also, the documentation states the callback pointer "may not be captured", 
>> the callee "may not change or dereference" the arguments, and "The arguments also 
>> may not escape". The pthread_create stores both in the thread descriptor and 
>> calls the routine later, from another thread. 
>>
>> The LLVM explicit states that this is allowed [1]: "the broker is also allowed 
>> to relay the callback callee invocation to a different thread". Maybe document
>> the same guarantee as for the gcc counterpart.
> 
> I'll revisit it and try to unify them as much as possible.  For example,
> capturing the pointers may or may not be an issue.  If the function is
> invoked exactly as the descriptor says then there is no problem.  But
> since the function pointer may be replaced with a highly specialized
> version, any change in the arguments or escaping to where the same
> guarantees cannot be made could cause a lot of trouble.
> 
>>
>> [1] https://llvm.org/docs/LangRef.html#callback-metadata
>>
>>>
>>> Note that the callback_only attribute is not yet released and is only
>>> available on gcc's master branch (since r17-3013-gcad654efbba62d).
>>>
>>> Regtested on x86_64-linux.  I encountered an nss/tst-getaddrinfo4.c
>>> fail, but it's failing on me with a clean tree as well.
>>>
>>> Signed-off-by: Josef Melcr <josef.melcr@suse.com>
>>> ---
>>>  misc/sys/cdefs.h       | 16 ++++++++++++++++
>>>  sysdeps/nptl/pthread.h |  9 +++++----
>>>  2 files changed, 21 insertions(+), 4 deletions(-)
>>>
>>> diff --git a/misc/sys/cdefs.h b/misc/sys/cdefs.h
>>> index 84c4aae262..21fb8fe2ea 100644
>>> --- a/misc/sys/cdefs.h
>>> +++ b/misc/sys/cdefs.h
>>> @@ -570,6 +570,22 @@
>>>  # define __nonnull(params) __attribute_nonnull__ (params)
>>>  #endif
>>>  
>>> +/* The callback/callback_only attribute specifies that a function calls one of
>>> +   its arguments and passes its arguments to it without any modifications.
>>> +
>>> +   There are two callback attributes.  'callback' is only supported by clang,
>>> +   'callback_only' is only supported by GCC.  Both capture the same meaning,
>>> +   but their syntax differs.  */
>>> +#ifndef __callback
>>> +# if __GNUC_PREREQ (17,0) || __glibc_has_attribute(__callback_only__)
>>
>> Missing space after __glibc_has_attribute
>>
>>> +#  define __callback(params) __attribute__ ((__callback_only__ params))
>>> +# elif __glibc_clang_prereq (9,0) || __glibc_has_attribute(__callback__)
>>> +#  define __callback(params) __attribute__ ((__callback__ params))
>>> +# else
>>> +#  define __callback(params) /* Ignore */
>>> +# endif
>>> +#endif /* __calback */
>>
>> s/__calback/__callaback
>>
>>> +
>>
>> I think we should either follow other attribute names (__attribute_callback__) or
>> as reecent cdefs.h additions to use the __attr_ prefix (__attr_callback).
>>
>> And I don't see why we can't add clang support as well. It should be something
>> as:
>>
>> #ifndef __attr_callback
>> # if __glibc_has_attribute (__callback_only__)
>> #  define __attr_callback(params) __attribute__ ((__callback_only__ params))
>> #  define __attr_callback_unknown 0
>> # elif __glibc_has_attribute (__callback__)
>> #  define __attr_callback(params) __attribute__ ((__callback__ params))
>> #  define __attr_callback_unknown -1
>> # else
>> #  define __attr_callback(params) /* Ignore */
>> # endif
>> #endif
>>
> 
> Good point, I'll amend it in a future version, along with the typos fixed.
> I didn't to include the unknown values as they weren't needed yet.
> 
>>
>>>  /* The returns_nonnull function attribute marks the return type of the function
>>>     as always being non-null.  */
>>>  #ifndef __returns_nonnull
>>> diff --git a/sysdeps/nptl/pthread.h b/sysdeps/nptl/pthread.h
>>> index 95c0eb7e03..7f8ca92e6b 100644
>>> --- a/sysdeps/nptl/pthread.h
>>> +++ b/sysdeps/nptl/pthread.h
>>> @@ -199,10 +199,11 @@ __BEGIN_DECLS
>>>  /* Create a new thread, starting with execution of START-ROUTINE
>>>     getting passed ARG.  Creation attributed come from ATTR.  The new
>>>     handle is stored in *NEWTHREAD.  */
>>> -extern int pthread_create (pthread_t *__restrict __newthread,
>>> -			   const pthread_attr_t *__restrict __attr,
>>> -			   void *(*__start_routine) (void *),
>>> -			   void *__restrict __arg) __THROWNL __nonnull ((1, 3));
>>> +__callback ((3, 4)) extern int
>>
>> glibc usually puts attributes after the declarator.
> 
> The two placements don't mean the same thing.  Placing an attribute
> after the declaration makes it apply to the function type, rather than
> the declaration.  With the modern attribute syntax ([[attr]]), placing
> it after the declaration wouldn't work.  Though with the old
> __attribute__ syntax, it should work fine.  I'll change it then.

I meant for the __attribute__ case, since it was what I suggested and
what would be more widely available on an installed header (since it
is inclusion is not tied to an specific C standard version).

> 
>>
>>> +pthread_create (pthread_t *__restrict __newthread,
>>> +                const pthread_attr_t *__restrict __attr,
>>> +                void *(*__start_routine) (void *),
>>> +                void *__restrict __arg) __THROWNL __nonnull ((1, 3));
>>
>> Hurd's sysdeps/htl/pthread.h declares pthread_create too and needs the same annotation.
>>
>>>  
>>>  /* Terminate calling thread.
>>>  
>>
>> I think we should add also instrument all other functions that uses callback (U being
>> __attr_callback_unknown):
>>
>> Function			Header		Annotation
>> thrd_create			threads.h	(2, 3)
>> on_exit				stdlib.h	(1, U, 2)
>> qsort_r				stdlib.h	(4, U, U, 5)
>> bsearch				stdlib.h	(5, 1, U)
>> lfind				search.h	(5, 1, U)
>> tfind, tdelete			search.h	(3, 1, U)
>> twalk_r				search.h	(2, U, U, 3)
>> td_ta_thr_iter / td_ta_tsd_iter	thread_db.h	(2, U, 3) / (2, U, U, 3)
>> dl_iterate_phdr			link.h		(1, U, U, 2)
> 
> I am aware that there are other functions which could be annotated as
> well.  I was mainly testing the waters with this patch, with the intent
> of expanding on it in the future.  I can add it everywhere at once if
> you'd prefer that.

It should be fine to add per-function basis or as whole. I prefer the later
because the whole idea is to use the attribute anyway.

> 
> On a side note, is this patch OK under DCO?  I tried to make sense of
> the rules before submitting this patch, but couldn't figure out if I
> needed a copyright form or not.
I am not sure, but I think DCO should cover this patch. Carlos can give you
a better answer.
  
Sam James Oct. 8, 2026, 4:42 p.m. UTC | #6
"Josef Melcr" <josef.melcr@suse.com> writes:

> On Wed Oct 7, 2026 at 8:12 PM CEST, Adhemerval Zanella Netto wrote:
>>
>>
>> On 01/10/26 16:14, Josef Melcr wrote:
>>> Add compiler-specific callback attribute to the pthread_create function
>>> declaration.  These attributes provide additional information about the
>>> hidden callsite in the function's body, which helps guide
>>> interprocedural optimization of the callback.
>>> 
>>> Even though the semantics of the attributes are the same, their names
>>> and syntax differs.  See the docs of the attributes here:
>>> https://gcc.gnu.org/onlinedocs/gcc/Common-Attributes.html#index-callback_005fonly
>>> https://clang.llvm.org/docs/AttributeReference.html#callback
>>
>> Is the semantic really the same? GCC's own manual says the attribute is 
>> "similar to the clang callback attribute but it is not compatible with it". At
>> least for glibc usage we can hide this with some macro foo.
>>
>> Also, the documentation states the callback pointer "may not be captured", 
>> the callee "may not change or dereference" the arguments, and "The arguments also 
>> may not escape". The pthread_create stores both in the thread descriptor and 
>> calls the routine later, from another thread. 
>>
>> The LLVM explicit states that this is allowed [1]: "the broker is also allowed 
>> to relay the callback callee invocation to a different thread". Maybe document
>> the same guarantee as for the gcc counterpart.
>
> I'll revisit it and try to unify them as much as possible.  For example,
> capturing the pointers may or may not be an issue.  If the function is
> invoked exactly as the descriptor says then there is no problem.  But
> since the function pointer may be replaced with a highly specialized
> version, any change in the arguments or escaping to where the same
> guarantees cannot be made could cause a lot of trouble.
>
>>
>> [1] https://llvm.org/docs/LangRef.html#callback-metadata
>>
>>> 
>>> Note that the callback_only attribute is not yet released and is only
>>> available on gcc's master branch (since r17-3013-gcad654efbba62d).
>>> 
>>> Regtested on x86_64-linux.  I encountered an nss/tst-getaddrinfo4.c
>>> fail, but it's failing on me with a clean tree as well.
>>> 
>>> Signed-off-by: Josef Melcr <josef.melcr@suse.com>
>>> ---
>>>  misc/sys/cdefs.h       | 16 ++++++++++++++++
>>>  sysdeps/nptl/pthread.h |  9 +++++----
>>>  2 files changed, 21 insertions(+), 4 deletions(-)
>>> 
>>> diff --git a/misc/sys/cdefs.h b/misc/sys/cdefs.h
>>> index 84c4aae262..21fb8fe2ea 100644
>>> --- a/misc/sys/cdefs.h
>>> +++ b/misc/sys/cdefs.h
>>> @@ -570,6 +570,22 @@
>>>  # define __nonnull(params) __attribute_nonnull__ (params)
>>>  #endif
>>>  
>>> +/* The callback/callback_only attribute specifies that a function calls one of
>>> +   its arguments and passes its arguments to it without any modifications.
>>> +
>>> +   There are two callback attributes.  'callback' is only supported by clang,
>>> +   'callback_only' is only supported by GCC.  Both capture the same meaning,
>>> +   but their syntax differs.  */
>>> +#ifndef __callback
>>> +# if __GNUC_PREREQ (17,0) || __glibc_has_attribute(__callback_only__)
>>
>> Missing space after __glibc_has_attribute
>>
>>> +#  define __callback(params) __attribute__ ((__callback_only__ params))
>>> +# elif __glibc_clang_prereq (9,0) || __glibc_has_attribute(__callback__)
>>> +#  define __callback(params) __attribute__ ((__callback__ params))
>>> +# else
>>> +#  define __callback(params) /* Ignore */
>>> +# endif
>>> +#endif /* __calback */
>>
>> s/__calback/__callaback
>>
>>> +
>>
>> I think we should either follow other attribute names (__attribute_callback__) or
>> as reecent cdefs.h additions to use the __attr_ prefix (__attr_callback).
>>
>> And I don't see why we can't add clang support as well. It should be something
>> as:
>>
>> #ifndef __attr_callback
>> # if __glibc_has_attribute (__callback_only__)
>> #  define __attr_callback(params) __attribute__ ((__callback_only__ params))
>> #  define __attr_callback_unknown 0
>> # elif __glibc_has_attribute (__callback__)
>> #  define __attr_callback(params) __attribute__ ((__callback__ params))
>> #  define __attr_callback_unknown -1
>> # else
>> #  define __attr_callback(params) /* Ignore */
>> # endif
>> #endif
>>
>
> Good point, I'll amend it in a future version, along with the typos fixed.
> I didn't to include the unknown values as they weren't needed yet.
>
>>
>>>  /* The returns_nonnull function attribute marks the return type of the function
>>>     as always being non-null.  */
>>>  #ifndef __returns_nonnull
>>> diff --git a/sysdeps/nptl/pthread.h b/sysdeps/nptl/pthread.h
>>> index 95c0eb7e03..7f8ca92e6b 100644
>>> --- a/sysdeps/nptl/pthread.h
>>> +++ b/sysdeps/nptl/pthread.h
>>> @@ -199,10 +199,11 @@ __BEGIN_DECLS
>>>  /* Create a new thread, starting with execution of START-ROUTINE
>>>     getting passed ARG.  Creation attributed come from ATTR.  The new
>>>     handle is stored in *NEWTHREAD.  */
>>> -extern int pthread_create (pthread_t *__restrict __newthread,
>>> -			   const pthread_attr_t *__restrict __attr,
>>> -			   void *(*__start_routine) (void *),
>>> -			   void *__restrict __arg) __THROWNL __nonnull ((1, 3));
>>> +__callback ((3, 4)) extern int
>>
>> glibc usually puts attributes after the declarator.
>
> The two placements don't mean the same thing.  Placing an attribute
> after the declaration makes it apply to the function type, rather than
> the declaration.  With the modern attribute syntax ([[attr]]), placing
> it after the declaration wouldn't work.  Though with the old
> __attribute__ syntax, it should work fine.  I'll change it then.
>
>>
>>> +pthread_create (pthread_t *__restrict __newthread,
>>> +                const pthread_attr_t *__restrict __attr,
>>> +                void *(*__start_routine) (void *),
>>> +                void *__restrict __arg) __THROWNL __nonnull ((1, 3));
>>
>> Hurd's sysdeps/htl/pthread.h declares pthread_create too and needs the same annotation.
>>
>>>  
>>>  /* Terminate calling thread.
>>>  
>>
>> I think we should add also instrument all other functions that uses callback (U being
>> __attr_callback_unknown):
>>
>> Function			Header		Annotation
>> thrd_create			threads.h	(2, 3)
>> on_exit				stdlib.h	(1, U, 2)
>> qsort_r				stdlib.h	(4, U, U, 5)
>> bsearch				stdlib.h	(5, 1, U)
>> lfind				search.h	(5, 1, U)
>> tfind, tdelete			search.h	(3, 1, U)
>> twalk_r				search.h	(2, U, U, 3)
>> td_ta_thr_iter / td_ta_tsd_iter	thread_db.h	(2, U, 3) / (2, U, U, 3)
>> dl_iterate_phdr			link.h		(1, U, U, 2)
>
> I am aware that there are other functions which could be annotated as
> well.  I was mainly testing the waters with this patch, with the intent
> of expanding on it in the future.  I can add it everywhere at once if
> you'd prefer that.

I think we're fine with it and the only issue was the kind of thing
Adhemerval covered (placement and such).

>
> On a side note, is this patch OK under DCO?  I tried to make sense of
> the rules before submitting this patch, but couldn't figure out if I
> needed a copyright form or not.

I don't think we have any rules on size or significance of patches
someone contributes under DCO. The only question would be if SUSE has
copyright assignment done whether you have the right / ability to use
DCO instead, I think.

>
> Best regards,
> Josef
  

Patch

diff --git a/misc/sys/cdefs.h b/misc/sys/cdefs.h
index 84c4aae262..21fb8fe2ea 100644
--- a/misc/sys/cdefs.h
+++ b/misc/sys/cdefs.h
@@ -570,6 +570,22 @@ 
 # define __nonnull(params) __attribute_nonnull__ (params)
 #endif
 
+/* The callback/callback_only attribute specifies that a function calls one of
+   its arguments and passes its arguments to it without any modifications.
+
+   There are two callback attributes.  'callback' is only supported by clang,
+   'callback_only' is only supported by GCC.  Both capture the same meaning,
+   but their syntax differs.  */
+#ifndef __callback
+# if __GNUC_PREREQ (17,0) || __glibc_has_attribute(__callback_only__)
+#  define __callback(params) __attribute__ ((__callback_only__ params))
+# elif __glibc_clang_prereq (9,0) || __glibc_has_attribute(__callback__)
+#  define __callback(params) __attribute__ ((__callback__ params))
+# else
+#  define __callback(params) /* Ignore */
+# endif
+#endif /* __calback */
+
 /* The returns_nonnull function attribute marks the return type of the function
    as always being non-null.  */
 #ifndef __returns_nonnull
diff --git a/sysdeps/nptl/pthread.h b/sysdeps/nptl/pthread.h
index 95c0eb7e03..7f8ca92e6b 100644
--- a/sysdeps/nptl/pthread.h
+++ b/sysdeps/nptl/pthread.h
@@ -199,10 +199,11 @@  __BEGIN_DECLS
 /* Create a new thread, starting with execution of START-ROUTINE
    getting passed ARG.  Creation attributed come from ATTR.  The new
    handle is stored in *NEWTHREAD.  */
-extern int pthread_create (pthread_t *__restrict __newthread,
-			   const pthread_attr_t *__restrict __attr,
-			   void *(*__start_routine) (void *),
-			   void *__restrict __arg) __THROWNL __nonnull ((1, 3));
+__callback ((3, 4)) extern int
+pthread_create (pthread_t *__restrict __newthread,
+                const pthread_attr_t *__restrict __attr,
+                void *(*__start_routine) (void *),
+                void *__restrict __arg) __THROWNL __nonnull ((1, 3));
 
 /* Terminate calling thread.