https://maskray.me/blog/2021-07-25-comdat-and-section-group#group-signature-localization The generic ABI says: > A symbol table entry with STB_LOCAL binding that is defined relative to one of a group's sections, and that is contained in a symbol table section that is not part of the group, must be discarded if the group members are discarded. References to this symbol table entry from outside the group are not allowed. Generally, if you want to reference a local symbol relative to a section in a COMDAT group, the referencing section should be part of the same group. .sframe cat > a.cc <<'eof' [[gnu::noinline]] inline int inl() { return 0; } auto *fa = inl; eof cat > b.cc <<'eof' [[gnu::noinline]] inline int inl() { return 0; } auto *fb = inl; eof ~/opt/gcc-15/bin/g++ -Wa,--gsframe -c a.cc b.cc % ld.lld a.o b.o ld.lld: error: relocation refers to a discarded section: .text._Z3inlv >>> defined in b.o >>> referenced by b.cc >>> b.o:(.sframe+0x1c) % gold a.o b.o b.o(.sframe+0x1c): error: relocation refers to local symbol ".text._Z3inlv" [2], which is defined in a discarded section section group signature: "inl()" prevailing definition is from a.o % readelf -W -S a.o | grep sframe [11] .sframe PROGBITS 0000000000000000 0000b0 00003f 00 A 0 0 8 [12] .rela.sframe RELA 0000000000000000 0001b0 000018 18 I 13 11 8 .sframe violates this rule.
Such SHF_ALLOC metadata sections that reference the text section should be placed in separate sections. See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=93195 for the __patchable_function_entries section fix on the GCC side. See also .gcc_except_table . I have some notes at https://maskray.me/blog/2021-01-31-metadata-sections-comdat-and-shf-link-order
Reading up on a few things, but meanwhile...I'd expect the sframe section type to be SHT_GNU_SFRAME. Starting Binutils 2.45.
I think the core issue is that SFrame sections (section type SHT_GNU_SFRAME) need to be merged and not concatenated by the linker. An abiding linker should also then remove SFrame data corresponding to a discarded symbol. An SFrame section has a SFrame header, followed by a list of fixed length entries (Frame Descriptor Entries, one each for a function), and then a list of variable length entries (Frame row entries, a function may have any number of FREs). Note the rearrangement of FDE and FRE data necessary to be done for the output section due to this layout. Relying on COMDAT/SHT_GROUP addresses the issue when the section contents are amenable to concatenation. SFrame sections cannot be concatenated. Similar reasoning for SHF_LINK_ORDER. I think we need to fix ld.lld to error out till it has the functionality to merge SFrame sections.
(In reply to Indu Bhagat from comment #3) > I think the core issue is that SFrame sections (section type SHT_GNU_SFRAME) need to be merged and not concatenated by the linker. An abiding linker should also then remove SFrame data corresponding to a discarded symbol. A design that permits concatenated components would be a more robust approach and fits into the ELF spirit well. Modern metadata sections should move beyond the .eh_frame style hack. When a.o:inl and b.o:inl are COMDAT groups with the same signature, and a.o:inl is the prevailing copy, does GNU ld discard the '.sframe' element that describes b.o:inl? This is a crucial in cases of ODR (One Definition Rule) violation, where it's essential to retain the metadata for the prevailing copy. --- In the following example, does linker garbage collection properly discard the .sframe element that describes `unused`? % cat a.cc [[gnu::noinline]] inline int inl() { return 0; } int unused() { return 1; } extern "C" void _start() { inl(); } % $HOME/Dev/binutils-gdb/out/debug/bin/ld --version | head -n 1 GNU ld (GNU Binutils) 2.45.50.20250905 % ~/opt/gcc-15/bin/g++ -B$HOME/Dev/binutils-gdb/out/debug/bin -ffunction-sections -Wa,--gsframe -c a.cc % ~/Dev/binutils-gdb/out/debug/ld/ld-new a.o --gc-sections --print-gc-sections /home/ray/Dev/binutils-gdb/out/debug/ld/ld-new: removing unused section '.sframe' in file 'a.o' This seems worse than I thought. .sframe is just entirely discarded. If the .sframe element for `unused` is in a separate section (a la __patchable_function_entries), and it has SHF_LINK_ORDER flag, the default linker garbage collection will discard it. In a generic linker design, section content finalization occurs after COMDAT section deduplication and section based garbage collection. This allows for smart, linker-generated content to function correctly alongside standard ELF features. We could further reduce .sframe content if the linker wants to implement such features.
(In reply to Fangrui Song from comment #4) > (In reply to Indu Bhagat from comment #3) > > I think the core issue is that SFrame sections (section type SHT_GNU_SFRAME) need to be merged and not concatenated by the linker. An abiding linker should also then remove SFrame data corresponding to a discarded symbol. > > A design that permits concatenated components would be a more robust > approach and fits into the ELF spirit well. > Modern metadata sections should move beyond the .eh_frame style hack. > > When a.o:inl and b.o:inl are COMDAT groups with the same signature, and > a.o:inl is the prevailing copy, does GNU ld discard the '.sframe' element > that describes b.o:inl? > This is a crucial in cases of ODR (One Definition Rule) violation, where > it's essential to retain the metadata for the prevailing copy. > Yes, I tested a bit. GNU ld will discard SFrame FDE for the respective discarded symbol. > --- > > In the following example, does linker garbage collection properly discard > the .sframe element that describes `unused`? > > % cat a.cc > [[gnu::noinline]] inline int inl() { return 0; } > int unused() { return 1; } > > extern "C" void _start() { > inl(); > } > > % $HOME/Dev/binutils-gdb/out/debug/bin/ld --version | head -n 1 > GNU ld (GNU Binutils) 2.45.50.20250905 > % ~/opt/gcc-15/bin/g++ -B$HOME/Dev/binutils-gdb/out/debug/bin > -ffunction-sections -Wa,--gsframe -c a.cc > % ~/Dev/binutils-gdb/out/debug/ld/ld-new a.o --gc-sections > --print-gc-sections > /home/ray/Dev/binutils-gdb/out/debug/ld/ld-new: removing unused section > '.sframe' in file 'a.o' > > This seems worse than I thought. .sframe is just entirely discarded. > > If the .sframe element for `unused` is in a separate section (a la > __patchable_function_entries), and it has SHF_LINK_ORDER flag, the default > linker garbage collection will discard it. > Yes, the --gc-sections issue is tracked here https://sourceware.org/bugzilla/show_bug.cgi?id=33370. I plan to fix it before 2.46 release.
(In reply to Fangrui Song from comment #4) > In a generic linker design, section content finalization occurs after COMDAT > section deduplication and section based garbage collection. Right. In current context, GNU ld behavior aligns to above for SFrame sections. > This allows for smart, linker-generated content to function correctly > alongside standard ELF features. We could further reduce .sframe content if > the linker wants to implement such features. What further reduction does the alternative bring ?