[PATCH 1/5] string: Add fallback implementation for ctz/clz

Adhemerval Zanella Netto adhemerval.zanella@linaro.org
Mon Sep 1 20:36:15 GMT 2025



On 01/09/25 17:21, Adhemerval Zanella Netto wrote:
> 
> 
> On 01/09/25 14:51, Adhemerval Zanella Netto wrote:
>>
>>
>> On 01/09/25 14:09, Cristian Rodríguez wrote:
>>> On Mon, Sep 1, 2025 at 11:40 AM Adhemerval Zanella Netto
>>> <adhemerval.zanella@linaro.org> wrote:
>>>
>>>> I found on s390x that calling the libgcc builtin is not fully supported without
>>>> setting up TLS because the builtin could be built with stack-protection enabled.
>>>> It might happen on other ABIs that uses the generic strchrnul implementation and
>>>> relying on libgcc for __builtin_c[t,l]z.
>>>
>>> Huh..what? everything else I have seen assumes this builtins are unsequenced..
>>> is this hiding a bug somewhere?
>>
>> I am not sure, but using the tst-assert-startup-static test from patchset:
>>
>> $ qemu-s390x -g 1234 elf/tst-assert-startup-static
>> [...]
>>
>> $ gdb-multiarch
>> [...]
>> (gdb) target remote localhost:1234
>> [...]
>> _start () at ../sysdeps/s390/s390-64/start.S:65
>> 65		la	%r4,8(%r15)		# get argv
>> (gdb) c
>> Continuing.
>>
>> Program received signal SIGSEGV, Segmentation fault.
>> 0x000002aa000972b6 in __clzdi2 (x=<optimized out>) at /home/azanella/toolchain/src/gcc/libgcc/libgcc2.c:692
>> 692	  return ret;
>> (gdb) bt
>> #0  0x000002aa000972b6 in __clzdi2 (x=<optimized out>) at /home/azanella/toolchain/src/gcc/libgcc/libgcc2.c:692
>> #1  0x000002aa00014f2a in clz (c=<optimized out>) at ../sysdeps/generic/string-fzi.h:60
>> #2  index_first (c=<optimized out>) at ../sysdeps/generic/string-fzi.h:115
>> #3  index_first_zero_eq (x1=<optimized out>, x2=<optimized out>) at ../sysdeps/generic/string-fzc.h:59
>> #4  __strchrnul_c (str=<optimized out>, c_in=<optimized out>) at ../string/strchrnul.c:51
>> #5  0x000002aa0000a0e6 in __libc_message_impl (fmt=fmt@entry=0x2aa000a1eee "Fatal glibc error: %s:%s (%s): assertion failed: %s\n")
>>     at ../sysdeps/posix/libc_fatal.c:77
>> #6  0x000002aa00009338 in __libc_assert_fail (assertion=assertion@entry=0x2aa000a5ef6 "error",
>>     file=file@entry=0x2aa000a1d80 "tst-assert-startup-static.c", line=line@entry=28,
>>     function=function@entry=0x2aa000a1d9c <__func__.0> "__tunables_init") at __libc_assert_fail.c:31
>> #7  0x000002aa0000820e in __tunables_init (env=<optimized out>) at tst-assert-startup-static.c:28
>> #8  0x000002aa00008ab8 in __libc_start_main_impl (main=0x2aa00008104 <__wrap_main>, argc=1, argv=0x200007ffb38, init=<optimized out>,
>>     fini=<optimized out>, rtld_fini=0x0, stack_end=0x200007ffa80) at ../csu/libc-start.c:267
>> #9  0x000002aa00008102 in _start () at ../sysdeps/s390/s390-64/start.S:101
>> (gdb) disas
>> Dump of assembler code for function __clzdi2:
>>    0x000002aa00097260 <+0>:	lghi	%r1,56
>>    0x000002aa00097264 <+4>:	lghi	%r3,7
>>    0x000002aa00097268 <+8>:	srlg	%r4,%r2,0(%r1)
>>    0x000002aa0009726e <+14>:	tmll	%r4,255
>>    0x000002aa00097272 <+18>:	jne	0x2aa0009729e <__clzdi2+62>
>>    0x000002aa00097276 <+22>:	aghi	%r1,-8
>>    0x000002aa0009727a <+26>:	brctg	%r3,0x2aa00097268 <__clzdi2+8>
>>    0x000002aa0009727e <+30>:	larl	%r5,0x2aa000d0ed8
>>    0x000002aa00097284 <+36>:	lghi	%r0,64
>>    0x000002aa00097288 <+40>:	lg	%r1,0(%r5)
>>    0x000002aa0009728e <+46>:	llgc	%r2,0(%r2,%r1)
>>    0x000002aa00097294 <+52>:	sgr	%r0,%r2
>>    0x000002aa00097298 <+56>:	lgfr	%r2,%r0
>>    0x000002aa0009729c <+60>:	br	%r14
>>    0x000002aa0009729e <+62>:	lgr	%r2,%r4
>>    0x000002aa000972a2 <+66>:	larl	%r5,0x2aa000d0ed8
>>    0x000002aa000972a8 <+72>:	lghi	%r0,64
>>    0x000002aa000972ac <+76>:	sgr	%r0,%r1
>>    0x000002aa000972b0 <+80>:	lg	%r1,0(%r5)
>> => 0x000002aa000972b6 <+86>:	llgc	%r2,0(%r2,%r1)
>>    0x000002aa000972bc <+92>:	sgr	%r0,%r2
>>    0x000002aa000972c0 <+96>:	lgfr	%r2,%r0
>>    0x000002aa000972c4 <+100>:	br	%r14
>> End of assembler dump.
>> (gdb) i r r2 r1
>> r2             0x80                128
>> r1             0xbe148             778568
>> (gdb) p &__clz_tab
>> $1 = (const UQItype (*)[256]) 0x2aa000be148 <__clz_tab>
>>
>> Digging into further, it seems that for s390 we need to self-relocate it first
>> before being able to access __clz_tab.
> 
> And this is due static-pie, where extern symbol access in positionindependent 
> code usually involves GOT indirection which needs RELATIVE relocs.  The __clzdi2
> and __clz_tab are in different TU:
> 
> _clzsi2.o:     file format elf64-big
> 
> SYMBOL TABLE:
> [..]
> 0000000000000000 g     F .text  0000000000000066 .hidden __clzdi2
> 0000000000000000         *UND*  0000000000000000 __clz_tab
> 
> _clz.o:     file format elf64-big
> SYMBOL TABLE:
> [...]
> 0000000000000000 g     O .rodata        0000000000000100 .hidden __clz_tab
> 
> I think there is a bug in libgcc where the __clz_tab prototype is missing a
> hidden definition (as done on glibc with 47618209d05).
> 
> In any case, I still think we will need to work around this on glibc for
> the current gcc versions.

And I think it is because libgcc is relying solely on -fvisibility=hidden, which does
not affect 'extern' declarations. That's why the __clz_tab on _clzsi2.o is not marked
as hidden, as expected.

It is not a problem in generic usage, but it is for this specific usage where the
process is not yet self-relocated.



More information about the Libc-alpha mailing list