[PATCH v2] [RFC][ld] Introduce static bundle object support

Eyal Itkin eyal.itkin@gmail.com
Sun Oct 12 19:24:03 GMT 2025


> There are many resolvable relocations (PC32 and PLT32) in x86-64
> -fno-function-sections builds, piggybacking the feature just for this
> specific scenario (assuming x86-64, assuming it won't use a range
> extension thunk ABI) isn't justified.

The DPDK librte_eal.a example was given as a real-life user scenario,
as is shipped by various distros as part of the official release package
build. Calling it a "-fno-function-sections" build and saying that my goal
is "piggybacking the feature" (literally the official build) is a bit of a
stretch.

As was mentioned in my previous note on this thread, the new linker
feature is intended as yet another tool in the developer toolbox. If a
developer's build is suitable for it, it might be handy, and if they decide
to use a different build (-ffunction-sections) they are free to do so and
avoid using the flag I'm suggesting to introduce.

At the moment, the release build of DPDK is not using "ffunction-section"
and is in fact using "--whole-archive" as part of their pkg-config.

> If your concern is about the relocation section size, use CREL
> https://groups.google.com/g/generic-abi/c/ppkaxtLb0P0
> (binutils feature request
> https://sourceware.org/bugzilla/show_bug.cgi?id=31475 )

At no point in this thread, the gabi thread, or the technical paper about
my proposal have I ever raised a concern about the relocation section
size. On the contrary, my suggestion was clearly stated to sacrifice
binary granularity (overall binary size) in favor of scoping (visibility) and
symbol reduction (exposure).

I sincerely appreciate your technical work as was attached by you here
and in your previous notes on this thread. In fact, your website was one
of my learning sources about the ELF format when I began my learning
for this topic. Given your familiarity with the linker, and the ELF format
and relocations, I was hoping this thread could serve as a constructive
platform to review and improve my proposal.

I will address the x64 GOT-related relocation issue that you pointed out
and will add it as part of the existing testsuite that is included in my
draft. If there are any additional technical notes they will be more than
appreciated.


On Sun, Oct 12, 2025 at 9:36 PM Fangrui Song <maskray@sourceware.org> wrote:
>
> On Sun, Oct 12, 2025 at 8:57 AM Eyal Itkin <eyal.itkin@gmail.com> wrote:
> >
> > > That said, porting this feature from objcopy to ld needs clear justification.
> > > If relocatable linking output already requires post-processing by
> > > objcopy, moving this option to ld provides no benefit.
> >
> > I agree and I am trying to achieve more than just symbol hiding. If the
> > conclusion will be that the only remaining value will be symbol hiding, we
> > could keep using objcopy.
> >
> > > The relocation resolution provides less value than it might appear. In
> > > builds using -ffunction-sections (commonly paired with ld
> > > --gc-sections), most global symbols reside in their own dedicated
> > > sections such as .text.symname and .data.symname. Since these sections
> > > have distinct names and are not merged with other sections in -r links
> > > (even without using ld --unique), any relocation referencing them from
> > > another function becomes a cross-section reference that cannot be
> > > resolved.
> >
> > There are several aspects here, so I'll try to break them down. In the
> > current suggested draft (with slight fix for one relocation) if we take
> > one of DPDK's libraries as a test case (librte_eal.a), we can see that
> > there are 262 function symbols that are not placed in the
> > eal_exports.map out of which only 9 were left in the finalized binary.
> > This means 96.5% success rate for the relocation finalization.
> >
> > I fully agree that when using -ffunction-sections there is no point in
> > performing finalization for the relocations. The user clearly specified
> > that their top priority is granularity, and they created a binary that
> > literally uses the function (symbols) names in the created sections.
> > Accordingly, providing less impact in this case is acceptable.
>
>
> There are many resolvable relocations (PC32 and PLT32) in x86-64
> -fno-function-sections builds, piggybacking the feature just for this
> specific scenario (assuming x86-64, assuming it won't use a range
> extension thunk ABI) isn't justified.
>
> % fllvm-readelf -r out/release/lib/librte_eal.a | awk '/R_/{r[$3]++}
> END{ for (t in r) print t, r[t] }'
> R_X86_64_TLSGD 365
> R_X86_64_REX_GOTPCRELX 175
> R_X86_64_PLT32 4693
> R_X86_64_TLSLD 15
> R_X86_64_64 121
> R_X86_64_DTPOFF32 49
> R_X86_64_PC32 5569
>
> If your concern is about the relocation section size, use CREL
> https://groups.google.com/g/generic-abi/c/ppkaxtLb0P0
> (binutils feature request
> https://sourceware.org/bugzilla/show_bug.cgi?id=31475 )
>
> .rw-rw-r-- 499k ray 12 Oct 11:19 rte_eal_crel.ro
> .rw-rw-r-- 720k ray 12 Oct 11:19 rte_eal_rela.ro
> .rw-rw-r-- 644k ray 12 Oct 11:12 out/release/lib/librte_eal.a  # CREL
> .rw-rw-r-- 872k ray 12 Oct 11:17 out/rela/lib/librte_eal.a
> (
> # dpdk
> CC=clang CXX=clang++ meson setup out/release --buildtype=release
> -Dc_args='-Wa,--crel,--allow-experimental-crel' -Denable_libs=static
> CC=clang CXX=clang++ meson setup out/rela --buildtype=release
> -Denable_libs=static
> ld.lld -r --whole-archive out/release/lib/librte_eal.a -o rte_eal_crel.ro )
>
>
> > > Architecturally, while x86-64 can resolve `R_X86_64_PLT32` to local
> > > symbols within the same section, this approach fails for many RISC
> > > architectures (AArch64, Power, RISC-V, etc.) where linkers require
> > > relocations to generate range extension thunks or perform linker
> > > relaxation
> >
> > This is a strong argument, which clarifies the tradeoffs between symbol
> > hiding and binary granularity. I would argue that some users are willing
> > to sacrifice their granularity, otherwise the "--whole-archive" flag wouldn't
> > have been so common in provided pkg-config files (DPDK is again an
> > example). If, for these users, there would be an ability to merge together
> > the sections and minimize the cross-section relocations, this would also
> > improve the overall impact of the finalization itself.
> >
> > This idea was also brought up by Rafael in
> > https://groups.google.com/g/generic-abi/c/sT25-xfX9yc/m/1lzc9nCCAQAJ.
> > As was also phrased by Ali before they encouraged the submission to
> > GNU ld:
> > "
> > What you're doing is something else: You're producing a single relocatable
> > object that's been finalized so that it provides an entire unit of things.
> > "
> > https://groups.google.com/g/generic-abi/c/sT25-xfX9yc/m/1gElX6vZAgAJ
> >
> > For x86-64, the finalization relocation works pretty well, even without
> > any section-related adjustment. However, if you are saying that for
> > other architectures (such as aarch64), this approach will no longer hold,
> > then my question is what is done for shared objects / ELF executables?
> >
> > I understand that many users will keep on using the current static archives.
> > Some of these users even use -ffunction-sections as granularity is a
> > priority for them. These users might still benefit from symbol visibility
> > features, and it would be great if some ergonomic solution for them could
> > be achieved.
> >
> > That said, there are other users for which using a single binary blob "as-is"
> > is acceptable (again, --whole-archive). If there is a technical solution that
> > allows the adjustment of the ET_REL so that this will be accomplished,
> > while maximizing the relocation finalization, this will maximize the
> > benefits for both the scoping and exposure metrics. This could be named
> > "--finalize" as thrown by Ali, or "--static-lib --combine-secs" as mentioned
> > by Rafael. The naming could be adjusted, yet the goal itself stays the
> > same.
> >
> > This "--finalize-locals" draft does not aim to solve all issues. There will
> > be drawbacks, some relocations might be kept and not finalized, and
> > some users might prefer not to use this option. This is acceptable.
> > Symbol visibility (scoping) and exposure are real issues that trouble
> > users, otherwise the https://github.com/tux3/armerge project wouldn't
> > have been created in the first place. This might come with a tradeoff,
> > offered granularity will be degraded or even completely sacrificed. Yet
> > there seems to be a crowd of users for such an option, and this is
> > what this patch is trying to aim for.
> >
> > On Sun, Oct 12, 2025 at 8:44 AM Fangrui Song <maskray@sourceware.org> wrote:
> > >
> > > On Sat, Oct 11, 2025 at 12:15 PM Eyal Itkin <eyal.itkin@gmail.com> wrote:
> > > >
> > > > > Discussions on the generic-abi thread mentioned that "static bundle
> > > > > object" is probably not a good name.
> > > >
> > > > I was never good at naming. Any name that will be accepted by this forum
> > > > is more than fine by me. Please note this anyway only applies at the code
> > > > and testing level, as the user-facing CLI flag doesn't mention this name.
> > >
> > > If STB_LOCAL conversion for hidden visibility is the only effect, I
> > > would suggest ld --localize-hidden, similar to objcopy
> > > --localize-hidden.
> > > (If we ever introduce --no-localize-hidden , we should ensure an error
> > > when --no-localize-hidden is used together with non-relocatable links
> > > (-no-pie, -pie, -shared).)
> > >
> > > That said, porting this feature from objcopy to ld needs clear justification.
> > > If relocatable linking output already requires post-processing by
> > > objcopy, moving this option to ld provides no benefit.
> > >
> > > > > The main idea of the ld -r modifier, if I understand correctly, is to
> > > > > constrain symbol bindings based on visibility.
> > > >
> > > > As mentioned in the thread you linked to, and in my technical paper that
> > > > started said thread, there are two goals for this effort:
> > > > 1. As in the shared-library case, apply symbol visibility so to hide internal
> > > > library functions by converting them to STB_LOCAL.
> > > > 2. Allow the ability to strip symbols of internal functions so to avoid exposing
> > > > them at the binary level.
> > > >
> > > > The "strip" utility will not be able to strip said symbols as long as they
> > > > are tied to a relocation, hence the motivation to resolve said relocations.
> > > > Again, as is done in shared objects.
> > > >
> > > > I am aware of the limitations for such relocation resolution, hence the
> > > > code-level check that skip it for cross-section relocations (as was also
> > > > added to the testsuite) and which were added following your comment
> > > > on the thread. It is possible that I indeed missed some code-level checks
> > > > to enforce some of the constraints you mentioned as I am new to this
> > > > code base. As is mentioned at the commit title (RFC) I will appreciate
> > > > any feedback and assistance in implementing my suggestion, as this
> > > > current revision is still only a draft.
> > >
> > > The relocation resolution provides less value than it might appear. In
> > > builds using -ffunction-sections (commonly paired with ld
> > > --gc-sections), most global symbols reside in their own dedicated
> > > sections such as .text.symname and .data.symname. Since these sections
> > > have distinct names and are not merged with other sections in -r links
> > > (even without using ld --unique), any relocation referencing them from
> > > another function becomes a cross-section reference that cannot be
> > > resolved.
> > >
> > > objcopy --discard-all or --strip-unneeded does not discard the
> > > referenced targets.
> > >
> > > Architecturally, while x86-64 can resolve `R_X86_64_PLT32` to local
> > > symbols within the same section, this approach fails for many RISC
> > > architectures (AArch64, Power, RISC-V, etc.) where linkers require
> > > relocations to generate range extension thunks or perform linker
> > > relaxation
> > >
> > > > > Note that ld -r combines sections with the same name, which degrades
> > > > > section garbage collection
> > > >
> > > > The flag that is being introduced by this commit is the --finalize-locals
> > > > flag, which is added on top of "ld -r" yet does not restrict ld (or the user)
> > > > from adding additional flags as they see fit. In the testsuite there is
> > > > an example also with ffunction-sections. Hence, user are free to use
> > > > the --unique flag, unless I'm missing some restriction that it imposes
> > > > on the design or implementation.
> > > >
> > > > >> Many scenarios prevent relocation resolution:
> > > > I am aware of that, yet many of the scenarios mentioned are also less
> > > > relevant for the case at hand (internal function calls). The goal is a
> > > > best effort approach for removing the internal function names at the
> > > > code level, the suggestion is not aiming to be a silver bullet that will
> > > > handle all cases.
> > > >
> > > > Given the positive overall notion in the generic-abi thread in favor of
> > > > submitting this proposal as a patch to GNU ld, I indeed adjusted my
> > > > draft and submitted said patch. I will gladly adjust whatever needed
> > > > in the patch so to polish this suggested feature, and will more than
> > > > appreciate feedback from maintainers who are more familiar with
> > > > the code base than I am.
> > >
> > > I want to clarify the feedback from the generic-abi thread:
> > > The overall assessment is that this proposal doesn't justify a new
> > > e_type. However, a few messages recognize there might be room for
> > > ergonomic improvement worth pursuing within tooling.
> > >
> > > I second https://groups.google.com/g/generic-abi/c/sT25-xfX9yc/m/NRo09GQcAwAJ
> > > Cary's suggestion of
> > > preserving the notion of individual compilation units and the
> > > granularity of the archive library format.
> > > This requires more thoughts about modifying or designing an archive format.
> > >
> > > Merging compilation units has some undesired properties:
> > >
> > > * Requiring ld --gc-sections to discard unneeded code/data
> > > * Losing diagnostics that pointing to individual .o files
> > >
> > > (
> > > gcc -c -g a.c b.c
> > > ld.bfd -r --unique a.o b.o  # combines .debug_info sections.
> > > .debug_info is specified by the internal linker script, therefore not
> > > orphan
> > > ld.bfd -r --unique='*' a.o b.o  # separate .debug_info sections
> > > )
> > >
> > > > On Sat, Oct 11, 2025 at 9:13 PM Fangrui Song <maskray@sourceware.org> wrote:
> > > > >
> > > > > On Fri, Oct 10, 2025 at 5:24 AM Eyal Itkin <eyal.itkin@gmail.com> wrote:
> > > > > >
> > > > > > Note: This patch is a draft for review.
> > > > > >
> > > > > > Introduce a new "Static Bundle Object" which is a
> > > > > > relocatable object that supports symbol visibility
> > > > > > and finalization of local relocations. This format
> > > > > > is aimed to be incorporated within static archives
> > > > > > so to replace the current static archives of plain
> > > > > > object code files that are used to form static
> > > > > > libraries.
> > > > > >
> > > > > > The full discussion about the format is available in the
> > > > > > generic-abi group that is responsible for the ELF standard:
> > > > > > https://groups.google.com/g/generic-abi/c/sT25-xfX9yc
> > > > > >
> > > > > > Some questions I had about the process itself:
> > > > > > 1. The patch is architecture dependent given that
> > > > > > relocations support is found in both bfd/elflink.c AND
> > > > > > in some arch-specific files (such as bfd/elf64-x86-64.c).
> > > > > > Hence, what is the standard way for marking some archs as
> > > > > > not yet supported? And what is the recommended process for
> > > > > > adding said support (especially given need to test it)?
> > > > > >
> > > > > > Signed-off-by: Eyal Itkin <eyal.itkin@gmail.com>
> > > > >
> > > > > Discussions on the generic-abi thread mentioned that "static bundle
> > > > > object" is probably not a good name.
> > > > >
> > > > > The main idea of the ld -r modifier, if I understand correctly, is to
> > > > > constrain symbol bindings based on visibility.
> > > > > In my opinion, resolving relocations isn't useful for this purpose, as
> > > > > I mentioned in my comment at
> > > > > https://groups.google.com/g/generic-abi/c/sT25-xfX9yc/m/bE-MMmxTAQAJ
> > > > >
> > > > >     Only a limited set of relocations can actually be resolved in
> > > > > relocatable files.
> > > > >     Specifically, this applies to relocations following the (S-P+A)
> > > > > formula where S refers to a non-ifunc local symbol in the same section
> > > > > as P.
> > > > >     This behavior mirrors assembler fixup resolution
> > > > > (https://maskray.me/blog/2025-03-16-relocation-generation-in-assemblers
> > > > > ):
> > > > >
> > > > >     > Fixup resolution depends on the fixup type:
> > > > >     >
> > > > >     > - PC-relative fixups that describe the symbol itself (the
> > > > > relocation operation looks like S - P + A) resolve to a constant if
> > > > > sym_a is a non-ifunc local symbol defined in the current section.
> > > > >     > - relocation_specifier(S + A) style fixups resolve when S refers
> > > > > to an absolute symbol.
> > > > >     > - Other fixups, including TLS and GOT related ones, remain unresolved.
> > > > >
> > > > >     Many scenarios prevent relocation resolution:
> > > > >
> > > > >     - Cross-section symbol references
> > > > >     - Symbols with STB_WEAK or STB_GLOBAL binding (due to potential
> > > > > symbol interposition in shared libraries and linker script symbol
> > > > > assignments)
> > > > >     - TLS or GOT-related relocations
> > > > >     - Relocations potentially requiring PLT entries or range-extension thunks
> > > > >
> > > > > I've analyzed the following assembly with a patched ld-new
> > > > >
> > > > > .hidden ext
> > > > > .hidden hid
> > > > > .global _start, hid
> > > > > _start:
> > > > > hid:
> > > > > .L1:
> > > > >   call ext
> > > > >   call hid
> > > > >   #mov hid@gotpcrel(%rip), %rax
> > > > >
> > > > > .section .text,"ax",@progbits,unique,1
> > > > >   .long .L1 - .
> > > > >   call .L1
> > > > >
> > > > > .section .data,"aw"
> > > > >   .long .L1 - .
> > > > >
> > > > > % ~/Dev/binutils-gdb/out/debug/ld/ld-new -r a.o --finalize-locals -o
> > > > > a.fo && readelf -W -r a.fo
> > > > >
> > > > > Relocation section '.rela.text' at offset 0xf8 contains 2 entries:
> > > > >     Offset             Info             Type               Symbol's
> > > > > Value  Symbol's Name + Addend
> > > > > 000000000000000a  0000000100000002 R_X86_64_PC32
> > > > > 0000000000000000 .text + 0
> > > > > 000000000000000f  0000000100000004 R_X86_64_PLT32
> > > > > 0000000000000000 .text - 4
> > > > >
> > > > > Relocation section '.rela.data' at offset 0x128 contains 1 entry:
> > > > >     Offset             Info             Type               Symbol's
> > > > > Value  Symbol's Name + Addend
> > > > > 0000000000000000  0000000100000002 R_X86_64_PC32
> > > > > 0000000000000000 .text + 0
> > > > >
> > > > > Two relocations are resolved, which is not useful.
> > > > > ld-new would crash if I add this GOT-related relocation
> > > > >
> > > > >   mov hid@gotpcrel(%rip), %rax
> > > > >
> > > > > ---
> > > > >
> > > > > Note that ld -r combines sections with the same name, which degrades
> > > > > section garbage collection
> > > > > (shameless plug https://maskray.me/blog/2021-02-28-linker-garbage-collection )
> > > > > You can use --unique to preserve multiple sections of the same name,
> > > > > at least for sections from different .o files. However, this doesn't
> > > > > help with the .text sections in this example.


More information about the Binutils mailing list