Set the retain attribute on _elf_set_element if CC supports [BZ #27492]

Message ID 20210315011050.309228-1-maskray@google.com
State Superseded
Headers
Series Set the retain attribute on _elf_set_element if CC supports [BZ #27492] |

Commit Message

Fangrui Song March 15, 2021, 1:10 a.m. UTC
  So that text_set_element/data_set_element/bss_set_element defined
variables will be retained by the linker.

Note: 'used' and 'retain' are orthogonal: 'used' makes sure the variable
will not be optimized out; 'retain' prevents section garbage collection
if the linker support SHF_GNU_RETAIN.

GNU ld 2.36 and LLD 13 support -z start-stop-gc which allow C identifier
name sections to be GCed even if there are live __start_/__stop_
references.

Without the change, there are some static linking problems, e.g.
_IO_cleanup (libio/genops.c) may be discarded by ld --gc-sections, so
stdout is not flushed on exit.

Note: `#if defined (__has_attribute) && __has_attribute (retain)`
does not work in GCC assembly mode:

    error: missing binary operator before token "("
---
 include/libc-symbols.h | 24 ++++++++++++++++++++----
 1 file changed, 20 insertions(+), 4 deletions(-)
  

Comments

Florian Weimer March 15, 2021, 7:28 a.m. UTC | #1
* Fangrui Song via Libc-alpha:

> So that text_set_element/data_set_element/bss_set_element defined
> variables will be retained by the linker.
>
> Note: 'used' and 'retain' are orthogonal: 'used' makes sure the variable
> will not be optimized out; 'retain' prevents section garbage collection
> if the linker support SHF_GNU_RETAIN.

This needs recent-ish GCC 11, see commit 6347f4a0904fce17e ("Add retain
attribute to place symbols in SHF_GNU_RETAIN section).

> +#ifdef __has_attribute
> +#if __has_attribute (retain)
> +# ifdef SHARED

Indentation is off there.

It's probably simpler to define a new macro, like
ELEMENT_SECTION_ATTRIBUTES, and use that unconditionally, perhaps like
this?

#ifndef __ASSEMBLER__
# if defined (__has_attribute) && __has_attribute (retain)
#  define ELEMENT_SECTION_ATTRIBUTES used, retain
# else
#  define ELEMENT_SECTION_ATTRIBUTES used
# endif
#endif

Have you checked that __has_attribute (retain) is only true on GCC if
the underlying linker has retain support and the attribute does not
produce a warning?

Thanks,
Florian
  
Fangrui Song March 15, 2021, 7:45 a.m. UTC | #2
On Mon, Mar 15, 2021 at 12:28 AM Florian Weimer <fweimer@redhat.com> wrote:
>
> * Fangrui Song via Libc-alpha:
>
> > So that text_set_element/data_set_element/bss_set_element defined
> > variables will be retained by the linker.
> >
> > Note: 'used' and 'retain' are orthogonal: 'used' makes sure the variable
> > will not be optimized out; 'retain' prevents section garbage collection
> > if the linker support SHF_GNU_RETAIN.
>
> This needs recent-ish GCC 11, see commit 6347f4a0904fce17e ("Add retain
> attribute to place symbols in SHF_GNU_RETAIN section).
>
> > +#ifdef __has_attribute
> > +#if __has_attribute (retain)
> > +# ifdef SHARED
>
> Indentation is off there.

OK. I can increase the level of indentation...

> It's probably simpler to define a new macro, like
> ELEMENT_SECTION_ATTRIBUTES, and use that unconditionally, perhaps like
> this?
>
> #ifndef __ASSEMBLER__
> # if defined (__has_attribute) && __has_attribute (retain)
> #  define ELEMENT_SECTION_ATTRIBUTES used, retain
> # else
> #  define ELEMENT_SECTION_ATTRIBUTES used
> # endif
> #endif

`#ifdef __has_attribute` should be fine without #ifndef __ASSEMBLER__ ?

> Have you checked that __has_attribute (retain) is only true on GCC if
> the underlying linker has retain support and the attribute does not
> produce a warning?

There is a GCC bug. I've reported
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=99587

>
> Thanks,
> Florian
>
  
Florian Weimer March 15, 2021, 8:13 a.m. UTC | #3
* Fāng-ruì Sòng:

>> It's probably simpler to define a new macro, like
>> ELEMENT_SECTION_ATTRIBUTES, and use that unconditionally, perhaps like
>> this?
>>
>> #ifndef __ASSEMBLER__
>> # if defined (__has_attribute) && __has_attribute (retain)
>> #  define ELEMENT_SECTION_ATTRIBUTES used, retain
>> # else
>> #  define ELEMENT_SECTION_ATTRIBUTES used
>> # endif
>> #endif
>
> `#ifdef __has_attribute` should be fine without #ifndef __ASSEMBLER__ ?

ELEMENT_SECTION_ATTRIBUTES is not useful to the assembler, and checking
for __ASSEMBLER__ on the outer level makes it simpler to define the
no-retain case.

>> Have you checked that __has_attribute (retain) is only true on GCC if
>> the underlying linker has retain support and the attribute does not
>> produce a warning?
>
> There is a GCC bug. I've reported
> https://gcc.gnu.org/bugzilla/show_bug.cgi?id=99587

If this isn't fixed in GCC, we'll need a configure check because we want
to keep building with -Werror=attributes.

Thank,s
Florian
  

Patch

diff --git a/include/libc-symbols.h b/include/libc-symbols.h
index c83e550b03..291a8d8f08 100644
--- a/include/libc-symbols.h
+++ b/include/libc-symbols.h
@@ -373,16 +373,32 @@  for linking")
 
 /* These are all done the same way in ELF.
    There is a new section created for each set.  */
-#ifdef SHARED
+#ifdef __has_attribute
+#if __has_attribute (retain)
+# ifdef SHARED
 /* When building a shared library, make the set section writable,
    because it will need to be relocated at run time anyway.  */
-# define _elf_set_element(set, symbol) \
+#  define _elf_set_element(set, symbol) \
   static const void *__elf_set_##set##_element_##symbol##__ \
-    __attribute__ ((used, section (#set))) = &(symbol)
+    __attribute__ ((used, retain, section (#set))) = &(symbol)
+# else
+#  define _elf_set_element(set, symbol) \
+  static const void *const __elf_set_##set##_element_##symbol##__ \
+    __attribute__ ((used, retain, section (#set))) = &(symbol)
+# endif
 #else
-# define _elf_set_element(set, symbol) \
+# ifdef SHARED
+/* When building a shared library, make the set section writable,
+   because it will need to be relocated at run time anyway.  */
+#  define _elf_set_element(set, symbol) \
+  static const void *__elf_set_##set##_element_##symbol##__ \
+    __attribute__ ((used, section (#set))) = &(symbol)
+# else
+#  define _elf_set_element(set, symbol) \
   static const void *const __elf_set_##set##_element_##symbol##__ \
     __attribute__ ((used, section (#set))) = &(symbol)
+# endif
+#endif
 #endif
 
 /* Define SET as a symbol set.  This may be required (it is in a.out) to