10 min readJust now
–
TL;DR
In the previous chapter I discussed some of the technical limitations of the .a
archive of plain object-code (.o
) files that is used for static libraries, limitations which are magnified when the linking relies on pkg-config or CMake. In the end, I proposed having a new ELF type (ET_STAT
) that will be used to create a new file format for static libraries — Static Bundle Object (.sbo
).
Two months after the initial publication, the [technical white paper](https://github.com/eyalitki/presentations/blob/master/2025-09%20-%20Static%20Bundle%20Object%20-%20Proposal%20for%20a%20New%20ELF%20Type%20for%20Static%20Library%20Linking.pdf…
10 min readJust now
–
TL;DR
In the previous chapter I discussed some of the technical limitations of the .a
archive of plain object-code (.o
) files that is used for static libraries, limitations which are magnified when the linking relies on pkg-config or CMake. In the end, I proposed having a new ELF type (ET_STAT
) that will be used to create a new file format for static libraries — Static Bundle Object (.sbo
).
Two months after the initial publication, the technical white paper about this new format was finalized and submitted to the ELF committee. The paper was accompanied by a working proof-of-concept based on GNU’s ld
linker.
After being discussed in the committee, the final verdict is mixed:
- The committee supports the proposed binary format.
- The motion to introduce it as part of the ELF standard, and specifically using a new
e_type
value, was denied. What does it mean? Well, the majority agreed the paper presents a strong case against the currently formed (.a
) static archives of plain object-code (.o
) files. They also agreed with the essence of the proposal which is to combine together all of these.o
files, and finalize their local relocations so that internal functions will no longer be exposed to the library’s users.
That said, the committee argued that the existing ET_REL
ELF e_type
is broad enough to cover such a use case, hence there is no need to involve the ELF standard at the moment. In essence, they declared it is more of a “tooling issue” than an “ELF issue”, and encouraged me to work directly with the maintainers of the linkers so to add support for my proposed feature.
While the Static Bundle Object format was not accepted “as-is”, it was still generally approved. The revised format proposal is to keep the existing relocatable e_type
for the newly created Static Bundle Object file, and embed this file inside a .a
archive so to maintain compatibility.
In practice, since the proposed format has not (yet) been officially incorporated into the ELF standard, its success will ultimately depend on its adoption by the various linkers.
Background
Approximately 3 months ago, we had one of our periodic integration crises at work. Some minor change in our SDK suddenly caused the build of an important project to break. As is often the case, code freeze was imminent, hence the issue was immediately declared a show stopper and required all hands on deck.
One hour later, the root cause was identified as yet another static linking issue related to the limitations of pkg-config and CMake. There were several possible fixes for the issue, with each party having their own recommended fix, which, naturally, involved the other party doing the work. After some failed attempts, a compromise was reached, and the tense work day finished with the build being restored.
In my line of work, we get these crises roughly twice a year — Always related to static linking and always just before the release. This case was the last straw. Surely somethingcan be done to help resolve some of these technical limitations.
One weekend later, I published the following medium post about it: The .a File is a Relic: Why Static Archives Were a Bad Idea All Along.
What are we trying to solve?
Current day static libraries are implemented using what is also referred to as “static archives”. These are .a
archives of plain object-code .o
files which are passed to the linker. In turn, the linker will pick-and-choose from the archive’s list of files, and will only pick the files that export symbols the linker is looking for. The linker will recursively do this resolution process, until satisfied.
This means that by design, the linker is prioritizing granularity and is aiming to consume as little as possible from the archive of .o
files. In order to do so, the linker must be provided with dependency information between the internal .o
files of the library, otherwise it won’t be able to traverse the internal dependencies between the library’s files.
As a result, all non-static functions within the library are treated as “global” functions, leading to the following issues:
- Scoping — Internal library functions can have a name conflict with functions from the integrating project.
- Exposure of internal logic — Archives of closed-source SDKs effectively leak the names of all internal (non-static) functions, without any strong user-requirement of doing so. Full explanation about the fine-prints of these issues can be found in the technical white paper.
Static Bundle Object
In my post, I called for the addition of a new ELF type (ET_STAT
) which will be used by a brand new file type — Static Bundle Object. The intention being that this new format will replace the .a
archive of plain object files that is currently being used by static libraries. The proposed ELF type will be based on the existing ld -r
option that creates an ET_REL
amalgamation of all .o
files, with three main distinctions:
- Symbol visibility will be supported, allowing developers to only expose the symbols of official API functions (currently only supported for shared objects).
- Relocations that are tied to local symbols will be finalized, allowing the stripping of said symbols (at the moment no finalization is done for
ET_REL
objects). - A new
e_type
value will be allocated for this binary file. As a side note, while de-prioritizing linking granularity, fine-grained linking can still be enabled by the developers of the shipped library, at the cost of exposing internal function names. This can be done using the-ffunction-sections
linking flag alongside the--gc-sections
flag. The combination of flags will create a section per function (bearing the function’s name) and will eliminate unused sections, hence achieving fine-grained linking granularity.
The initial post received mixed reviews. While some reviewers have also gone tired of the existing technical limitations, others rejected the proposal and cited the famous quote:
A good carpenter never blames their tools
Still, as promised, I went to work on my proposal. Two months later, my proposal was ready:
- The technical white paper with the official proposal was submitted to the ELF committee: link.
- The proposal included a working proof-of-concept on top of GNU’s
ld
linker: GitHub.
Verdict of the ELF committee
Friends and colleagues helped me keep my expectations low. After all, the odds of this effort being approved by the committee were very slim. Not even discussing the integration to all toolchains if and when the proposal will be accepted.
And indeed, the discussion in the committee had a rocky start. I did my best to explain my proposal, answered the questions raised by the committee and shared the feedback from the initial article about the subject.
This is a good opportunity to thank everyone that helped review my paper as well as to those that participated in the original thread. Special thanks goes to tux3
, the maintainer of https://github.com/tux3/armerge, who supported my proposal and provided useful input about some of the drawbacks of the current static archive format.
After some discussion in the committee, the proposal was analyzed and was declared to need some adjustments. The notion of a new e_type
was rejected(even if not unanimously), yet the proposal for a new format for static libraries was approved (even if not standardized yet).
The committee had mixed opinions about the proposal, yet some members did support it ‘as-is’ and called for treating static libraries as first class citizens recognized by the format, just like EY_DYN
for dynamic libraries (Shared objects). However, the prevailing notion was that from a theoretical perspective, any attempt to improve the current state of static libraries is more of a “tooling” issue (ld
, objcopy
, etc.) as the existing ET_REL
type is broad enough to cover this “new” use-case.
Aside from the theoretical perspective, there was also a practical reason behind my proposal for a new ELF type. The e_type
field is the main way for tools, the linker included, to be able to differentiate between object-code files (ET_REL
) and other ELF inputs (libraries). However, the deployment costs of extending this enum were determined to be too high at this late stage of the standard, hence outweighing the benefits such an extension will provide.
Reasoning
The committee’s decision has 3 important aspects.
Soundness of the current ET_REL
definition
Even if the proposal underscores existing gaps in the static archives format, it still does not manage to convince that the solution should be based on a new e_type
. Overall, the proposed static library could be implemented as a subset of features of the existing ET_REL
format, indicating that the ET_REL
type was defined well enough 40+ years ago, and there is no need to “replace” it for this use case.
“It’s telling that in 40+ year run, ELF has only needed 4 types, REL, DYN, EXEC, and CORE. There’s nothing new about static linking, so it seems surprising that a new type to cater to it would be needed.”
High cost for extending the e_type enum
The committee cited previous cases in which adding a new e_type
value would have been the “right” approach, and yet was not chosen. For instance, in the debuginfo case customers were comparing the e_type
value of the original binary and the debuginfo binary, which blocked the introduction of a new e_type
value.
One could argue that if the committee still decided that this is an “ld
issue” and encouraged me to submit the support for it to my linker of choosing, what exactly is the added cost to formally label it with a new e_type
? Well, my understanding is that the costs are the following:
- All toolchains will need to support it, and not only those who accept my pull request (assuming any linker will accept them).
- All various parsers/disassemblers will need to extend their support to this new
e_type
value. - The committee itself will also need to complete the necessary paperwork to officially adopt the proposal into the ELF standard, which might be premature at the moment. The effort for the 2nd item is relatively negligible. Which means that everything boils down to gradual deployment and feedback from users.
You don’t standardize something like this and just expect the users to follow. What you do is roll it out, start using it, refine it based on experience, and win users as they see value.
Even if you ultimately did need a standards change, this would be the necessary first step, because there’s nothing worse than standardizing something that turns out to need tweaking later. New gABI additions need to be already proven somehow.
Pragmatic approach to the meaning of the e_type field
Only a single member of the committee shared my view that it is time that static libraries will be treated as first-class citizens, and will be formally recognized by the ELF standard. Although shared libraries already enjoy this status (ET_DYN
), the committee doesn’t believe there is enough value in extending it also for static libraries and creating a symmetry between the two.
In addition, the raised arguments regarding a need for ld
(or any other tool) to treat static libraries differently than object-code files were regarded as not strong enough. After all, the newly formed binary could still be wrapped inside the existing .a
archive, thus telling ld
that it isn’t a plain object-code file.
Implications
Although my paper recommended that the Static Bundle Object format be implemented through a dedicated ELF e_type
value of ET_STAT
, the format can be implemented using the existing ET_REL
type. As is explained in the whitepaper, and as suggested by the committee, a new --finalize-locals
flag (name to be determined) can be added to the ld
linker so to create this proposed binary format — ld -r --finalize-locals
.
That said, there are some drawbacks to the approach of avoiding the introduction of a new e_type
value:
binutils
will not be able to differentiate between a “simple” object-code (.o
) relocatable file, and a “complex” Static Bundle Object static library file (.sbo
). This will be solved at the linker level by keeping the use of a.a
archive that will wrap the single produced Static Bundle Object.o
file.- There is still the task of convincing the “world” about the proposed format given that it was not directly adopted into the ELF standard.
It is important to note however that there is no change to the deliverables of any project, nor to the external linking instructions. This is because we keep the existing
.a
archives wrapping the single built.o
file per library. Hence, the change is on the project’s side, and not on the “client” side.
Conclusion
While keeping my hopes low, I misjudged the way the committee will view my proposal. I was prepared for an utter dismissal (as almost happened at the start of the thread), yet I was not prepared for this mixed “acceptance”. On the one hand, the decision implies that the process for introducing changes into the standard is longer than I expected. On the other hand, the proposal to replace the currently used static archives was acknowledged as having a strong case, and got the blessings of the committee.
The bottom line is that my proposed new format for static libraries did get the “green light” by the committee. The format will address all of the issues that were raised:
- Scoping — Handled through symbol visibility support.
- Exposing names of internal (closed-source) functions — Handled through finalization of local relocations, which allows for stripping of said symbols.
- Various structural issues with full/partial archive linking — We will only have a single
.o
file, hence problematic flags like--whole-archive
will no longer be needed (flags which were the root cause for this discussion in the first place). As a first step I have ported my draft to GNU’sld
to be based on the revised format, and will try to send it to them to see how it goes. Only the future will tell if this entire effort was done in vain, or if little-by-little some progress will be made.
Going back to the quote from the feedback on the original article, it seems that in some cases a carpenter should blame their tools and try to fix them. Because sometimes the tools indeed have faults, and if we won’t acknowledge it and try to improve them, no progress will ever be made.
Press enter or click to view image in full size
A good carpenter never blames their tools