[PATCH] bfd: xtensa: keep property tables without linker script KEEP
Max Filippov
jcmvbkbc@gmail.com
Mon Aug 24 11:33:07 GMT 2026
Hi Alexey,
On Tue, Aug 11, 2026 at 11:12 AM Alexey Lapshin
<alexey.lapshin@espressif.com> wrote:
>
> Property sections are unreferenced, so linker scripts kept them alive
> with KEEP(). That does keep the tables, but it also keeps dead code:
> gas puts .xt.prop/.xt.lit for a COMDAT function into that function's
> group, and _bfd_elf_gc_mark marks every member of a group when one
> member is marked. KEEP() on the property section therefore retains the
> code, literals, rodata and exception tables of unused C++ template
> instantiations and inline functions, bloating the final image. Dropping
> KEEP() instead loses the property tables of live code, because
> non-COMDAT objects share one .xt.prop that nothing references.
>
> Mark property tables from the gc_mark_extra_sections hook, once the
> sections they describe have been marked, using the existing
> xtensa_get_property_section lookup. Set gc_mark directly rather than
> calling _bfd_elf_gc_mark, so a property section that belongs to a COMDAT
> group does not drag in the rest of the group. Linker scripts no longer
> need KEEP() for .xt.prop/.xt.lit. Warn once if KEEP is still used for a
> property section base name.
>
> bfd/
> * elf32-xtensa.c (elf_xtensa_gc_mark_extra_sections): New
> function. Warn if KEEP is used for a property section.
> (elf_backend_gc_mark_extra_sections): Define.
> (elf_xtensa_gc_mark_hook): Update comment.
>
> include/
> * elf/xtensa.h (XTENSA_PROPERTY_SEC_NAMES): New macro.
>
> ld/
> * testsuite/ld-xtensa/gc-prop.d: New test.
> * testsuite/ld-xtensa/gc-prop.map: New test map check.
> * testsuite/ld-xtensa/gc-prop.s: New test source.
> * testsuite/ld-xtensa/gc-prop.t: New test script.
> * testsuite/ld-xtensa/xtensa.exp: Run it.
> ---
> bfd/elf32-xtensa.c | 76 ++++++++++++++++++++++++++----
> include/elf/xtensa.h | 3 ++
> ld/testsuite/ld-xtensa/gc-prop.d | 7 +++
> ld/testsuite/ld-xtensa/gc-prop.map | 9 ++++
> ld/testsuite/ld-xtensa/gc-prop.s | 9 ++++
> ld/testsuite/ld-xtensa/gc-prop.t | 7 +++
> ld/testsuite/ld-xtensa/xtensa.exp | 1 +
> 7 files changed, 102 insertions(+), 10 deletions(-)
> create mode 100644 ld/testsuite/ld-xtensa/gc-prop.d
> create mode 100644 ld/testsuite/ld-xtensa/gc-prop.map
> create mode 100644 ld/testsuite/ld-xtensa/gc-prop.s
> create mode 100644 ld/testsuite/ld-xtensa/gc-prop.t
Please resend it in a plain text form against the binutils master.
> diff --git a/bfd/elf32-xtensa.c b/bfd/elf32-xtensa.c
> index eb7fef9331e..2eaaee409b2 100644
> --- a/bfd/elf32-xtensa.c
> +++ b/bfd/elf32-xtensa.c
> @@ -1328,16 +1328,12 @@ elf_xtensa_gc_mark_hook (asection *sec,
> struct elf_link_hash_entry *h,
> Elf_Internal_Sym *sym)
> {
> - /* Property sections are marked "KEEP" in the linker scripts, but they
> - should not cause other sections to be marked. (This approach relies
> - on elf_xtensa_discard_info to remove property table entries that
> - describe discarded sections. Alternatively, it might be more
> - efficient to avoid using "KEEP" in the linker scripts and instead use
> - the gc_mark_extra_sections hook to mark only the property sections
> - that describe marked sections. That alternative does not work well
> - with the current property table sections, which do not correspond
> - one-to-one with the sections they describe, but that should be fixed
> - someday.) */
> + /* Nothing refers to a property section, so it is kept alive by
> + elf_xtensa_gc_mark_extra_sections instead, and its relocations must
> + not cause other sections to be marked. (This relies on
> + elf_xtensa_discard_info to remove property table entries that
> + describe discarded sections, which is needed anyway because a
> + property section may describe more than one section.) */
> if (xtensa_is_property_section (sec))
> return NULL;
>
> @@ -1353,6 +1349,65 @@ elf_xtensa_gc_mark_hook (asection *sec,
> }
>
>
> +/* Property sections are not referenced by anything, so this pass marks
> + the ones describing sections that have been marked. */
> +
> +static bool
> +elf_xtensa_gc_mark_extra_sections (struct bfd_link_info *info,
> + elf_gc_mark_hook_fn gc_mark_hook)
> +{
> + static const char *const prop_names[] = XTENSA_PROPERTY_SEC_NAMES;
> + bfd *sub;
> + bool warned = false;
> +
> + _bfd_elf_gc_mark_extra_sections (info, gc_mark_hook);
> +
> + for (sub = info->input_bfds; sub != NULL; sub = sub->link.next)
> + {
> + asection *sec;
> +
> + if (!is_xtensa_elf (sub))
> + continue;
> +
> + for (sec = sub->sections; sec != NULL; sec = sec->next)
> + {
> + size_t i;
> +
> + if (!sec->gc_mark
> + || (sec->flags & SEC_ALLOC) == 0
> + || xtensa_is_property_section (sec))
> + continue;
> +
> + for (i = 0; i < sizeof (prop_names) / sizeof (prop_names[0]); i++)
> + {
> + asection *prop_sec =
> + xtensa_get_property_section (sec, prop_names[i]);
> +
> + /* Set "gc_mark" directly rather than calling _bfd_elf_gc_mark:
> + a property section can be a member of the COMDAT group of
> + the section it describes, and marking a group member retains
> + the entire group. Nothing else needs to be marked because
> + elf_xtensa_gc_mark_hook ignores relocations in property
> + sections. */
> + if (prop_sec != NULL)
> + {
> + if (!warned && (prop_sec->flags & SEC_KEEP) != 0)
> + {
> + info->callbacks->einfo
> + (_("%P: warning: KEEP should not be used for Xtensa "
> + "property section %s\n"), prop_names[i]);
> + warned = true;
> + }
> + prop_sec->gc_mark = 1;
> + }
> + }
> + }
> + }
> +
> + return true;
> +}
> +
> +
> /* Create all the dynamic sections. */
>
> static bool
> @@ -11505,6 +11560,7 @@ static const struct bfd_elf_special_section elf_xtensa_special_sections[] =
> #define elf_backend_finish_dynamic_sections elf_xtensa_finish_dynamic_sections
> #define elf_backend_finish_dynamic_symbol elf_xtensa_finish_dynamic_symbol
> #define elf_backend_gc_mark_hook elf_xtensa_gc_mark_hook
> +#define elf_backend_gc_mark_extra_sections elf_xtensa_gc_mark_extra_sections
> #define elf_backend_grok_prstatus elf_xtensa_grok_prstatus
> #define elf_backend_grok_psinfo elf_xtensa_grok_psinfo
> #define elf_backend_hide_symbol elf_xtensa_hide_symbol
> diff --git a/include/elf/xtensa.h b/include/elf/xtensa.h
> index 523855e7e81..5fb8485638f 100644
> --- a/include/elf/xtensa.h
> +++ b/include/elf/xtensa.h
> @@ -131,6 +131,9 @@ END_RELOC_NUMBERS (R_XTENSA_max)
> #define XTENSA_LIT_SEC_NAME ".xt.lit"
> #define XTENSA_PROP_SEC_NAME ".xt.prop"
>
> +#define XTENSA_PROPERTY_SEC_NAMES \
> + { XTENSA_INSN_SEC_NAME, XTENSA_LIT_SEC_NAME, XTENSA_PROP_SEC_NAME }
> +
> typedef struct property_table_entry_t
> {
> bfd_vma address;
> diff --git a/ld/testsuite/ld-xtensa/gc-prop.d b/ld/testsuite/ld-xtensa/gc-prop.d
> new file mode 100644
> index 00000000000..3e8d3a07186
> --- /dev/null
> +++ b/ld/testsuite/ld-xtensa/gc-prop.d
> @@ -0,0 +1,7 @@
> +#source: gc-prop.s
> +#ld: -T gc-prop.t --gc-sections
> +#map: gc-prop.map
> +#readelf: -SW
> +#...
> + +\[ *[0-9]+\] \.xt\.prop +PROGBITS +[0-9a-f]+ +[0-9a-f]+ +0*[1-9a-f][0-9a-f]* .*
> +#pass
> diff --git a/ld/testsuite/ld-xtensa/gc-prop.map b/ld/testsuite/ld-xtensa/gc-prop.map
> new file mode 100644
> index 00000000000..85ffa79dff0
> --- /dev/null
> +++ b/ld/testsuite/ld-xtensa/gc-prop.map
> @@ -0,0 +1,9 @@
> +#...
> +Discarded input sections
> +#...
> + \.text\.dead +0x[0-9a-f]+ +0x[0-9a-f]+ .*
> +#...
> + \.xt\.prop\.dead +0x[0-9a-f]+ +0x[0-9a-f]+ .*
> +#...
> +\.xt\.prop +0x[0-9a-f]+ +0x0*[1-9a-f][0-9a-f]*
> +#pass
> diff --git a/ld/testsuite/ld-xtensa/gc-prop.s b/ld/testsuite/ld-xtensa/gc-prop.s
> new file mode 100644
> index 00000000000..56396206018
> --- /dev/null
> +++ b/ld/testsuite/ld-xtensa/gc-prop.s
> @@ -0,0 +1,9 @@
> + .section .text.dead,"axG",@progbits,dead,comdat
> + .global dead
> +dead:
> + ret
> +
> + .text
> + .global _start
> +_start:
> + ret
> diff --git a/ld/testsuite/ld-xtensa/gc-prop.t b/ld/testsuite/ld-xtensa/gc-prop.t
> new file mode 100644
> index 00000000000..d493873cfce
> --- /dev/null
> +++ b/ld/testsuite/ld-xtensa/gc-prop.t
> @@ -0,0 +1,7 @@
> +ENTRY(_start)
> +SECTIONS
> +{
> + .text 0x1000 : { *(.literal .literal.* .text .text.*) }
> + .xt.prop 0 : { *(.xt.prop .xt.prop.* .gnu.linkonce.prop.*) }
> + .xt.lit 0 : { *(.xt.lit .xt.lit.* .gnu.linkonce.p.*) }
> +}
> diff --git a/ld/testsuite/ld-xtensa/xtensa.exp b/ld/testsuite/ld-xtensa/xtensa.exp
> index 06349571d4f..8517f3437fd 100644
> --- a/ld/testsuite/ld-xtensa/xtensa.exp
> +++ b/ld/testsuite/ld-xtensa/xtensa.exp
> @@ -26,6 +26,7 @@ if { !([istarget "xtensa*-*-*"]) } {
> run_dump_test "call_overflow"
> run_dump_test "coalesce"
> run_dump_test "diff_overflow"
> +run_dump_test "gc-prop"
> run_dump_test "lcall"
> run_dump_test "relax-diff1"
> run_dump_test "relax-loc"
> --
> 2.43.0
>
--
Thanks.
-- Max
More information about the Binutils
mailing list