This is an archive of the discontinued LLVM Phabricator instance.

[ELF] Allow non-bitcode archive with an empty index
ClosedPublic

Authored by MaskRay on Jan 13 2022, 11:56 PM.

Details

Summary

When an archive with an empty index contains only bitcode files, it is
handled as a group of lazy (--start-lib) object files. If there is a
non-bitcode file, there will be a diagnostic a la GNU ld.

For some programs, the archive member extraction ratio is high (e.g. for chrome,
79% archive members are extracted according to --print-archive-stats=). Because
symbol interning is cached for ObjFile::parseLazy but not for ArchiveFile,
parsing an archive as a group of --start-lib object files may be faster.

If the linker speculatively creates section representations for archive members,
the archive index will not be used.

If we take the above view, the archive index is essentially useless. If a user
wants a fast build without using --start-lib, they may just build thin archives
without index (ar rcS --thin).

Therefore, I suggest that we no longer treat the code as a hack, instead as a
supported feature. I believe we will do this anyway if we add parallel symbol
interning (parallel symbol interning for lazy object files is simpler than that
for archives).

Ecosystem issues:

  • parseLazy actually has nearly the same behavior as ArchiveFile::parse, but the symbol order may be different.
  • users may get addicted to the behavior and build archives not working with GNU ld and gold. I think it is easy to rebuild archives to be compatible.

Diff Detail

Event Timeline

MaskRay created this revision.Jan 13 2022, 11:56 PM
MaskRay requested review of this revision.Jan 13 2022, 11:56 PM
Herald added a project: Restricted Project. · View Herald TranscriptJan 13 2022, 11:56 PM
MaskRay edited the summary of this revision. (Show Details)Jan 13 2022, 11:57 PM
MaskRay edited the summary of this revision. (Show Details)Jan 14 2022, 12:04 AM

I can see that something like this could be valuable for a program using archives for major subsystems, although less well for a set of loosely connected utility routines (C-library). Doing this transparently makes me a little bit nervous for the reasons you suggest in the description.

A couple of alternative suggestions:

Could introduce something like --start-lib --end-lib to treat archives in this way (regardless of whether they have an index or not. Something like --start-as-lazy archive1.a archive2.a --end-as-lazy. That would permit people to get the potential speed boost while being under control, their archives (with indexes) will still work on linkers that don't support the option.

Another option would be to enable the behaviour change via the command-line. At least people would then know that they would be doing something speciall
--archive-no-index-as-lazy=<archive type> where <archive type> is one of:

  • none. All archives without an index get an error message.
  • bitcode. All bitcode archives without an index are treated as a lazy archive. This is the default.
  • all. All archives without an index are treated as a lazy archive.

Another advantage is that this behaviour allows users to "repair" archives with incomplete symbol tables by stripping the symbol table (maybe someone has added both ET_RELs and bitcode objects to the same archive with an ar which ignored the bitcocde symbols).

We have had a similar behaviour on PlayStation in our proprietary linker for many years without any reported problems. Given that, I think that we could enable this behaviour by default and add a command line option to revert to the current behaviour (--emulate-gnu-ld) to handle corner cases.

Another advantage is that this behaviour allows users to "repair" archives with incomplete symbol tables by stripping the symbol table (maybe someone has added both ET_RELs and bitcode objects to the same archive with an ar which ignored the bitcocde symbols).

Isn't it already trivial to "repair" such libraries by running ranlib on them?

Another advantage is that this behaviour allows users to "repair" archives with incomplete symbol tables by stripping the symbol table (maybe someone has added both ET_RELs and bitcode objects to the same archive with an ar which ignored the bitcocde symbols).

Isn't it already trivial to "repair" such libraries by running ranlib on them?

True. This has proved useful in the past in my world of proprietary toolchains - but in opensource if you have access to LLD then you also also have access to a ranlib that is aware of all the same file formats that LLD supports.

MaskRay updated this revision to Diff 400066.Jan 14 2022, 10:29 AM

add a diagnostic for non-ET_REL-non-bitcode

I can see that something like this could be valuable for a program using archives for major subsystems, although less well for a set of loosely connected utility routines (C-library). Doing this transparently makes me a little bit nervous for the reasons you suggest in the description.

Thanks for the comments. For the less well part, if the user cares about GNU ld compatibility, I think it's pretty straightforward to detect the problem (GNU ld does not link) and fix it by running ranlib.

A couple of alternative suggestions:

Could introduce something like --start-lib --end-lib to treat archives in this way (regardless of whether they have an index or not. Something like --start-as-lazy archive1.a archive2.a --end-as-lazy. That would permit people to get the potential speed boost while being under control, their archives (with indexes) will still work on linkers that don't support the option.

Another option would be to enable the behaviour change via the command-line. At least people would then know that they would be doing something speciall
--archive-no-index-as-lazy=<archive type> where <archive type> is one of:

  • none. All archives without an index get an error message.
  • bitcode. All bitcode archives without an index are treated as a lazy archive. This is the default.
  • all. All archives without an index are treated as a lazy archive.

I imagine that we will introduce an option to treat all archives as a group of lazy files, maybe --archive-handling=<type> where <type> is one of:

  • default: with index: traditional behavior, without index: a group of lazy object files
  • lazy: a group of lazy object files ignoring the index

An option specifically for archives without an index seems to do too much.

I think it is good for ld.lld to influence the ecosystem in good directions. Normally we tend to be strict. For this archive case, if the traditional index constraint may not be useful and isn't difficult to repair (ranlib), I think losing some strictness is fine.
The patch also improves consistency. ET_REL files should not be handled differently than LLVM bitcode files.

ikudrin accepted this revision.Jan 17 2022, 3:19 AM

LGTM.

The change looks safe. If it eventually helps us to move towards a parallel reading of input files, I am in favor of it.

lld/test/ELF/lto/archive-no-index.ll
27
31–32

There is only one error message in the test now, so the prefix can be just ERR.

This revision is now accepted and ready to land.Jan 17 2022, 3:19 AM
MaskRay updated this revision to Diff 400898.Jan 18 2022, 10:03 AM
MaskRay marked 2 inline comments as done.

update test

No objections. Reading your blog post and my copy of Linkers and Loaders it does seem that early linkers would handle archives with indexes, it just seems like indexes were ubiquitous when GNU ld were written. So it does seem like rejecting archives without an index is a property of the implentation of GNU ld rather than something that should be relied upon.

No objections. Reading your blog post and my copy of Linkers and Loaders it does seem that early linkers would handle archives with indexes, it just seems like indexes were ubiquitous when GNU ld were written. So it does seem like rejecting archives without an index is a property of the implentation of GNU ld rather than something that should be relied upon.

I'm in favour of this change but I think it is quite easy to make the argument both ways. Links which rely on complex GNU archive symbol resolution behaviour could be easily be broken - so insisting that archives have symbol tables makes sense for GNU linkers.

Rant: Archives are now terrible!

  • The format is very old and as pointed out in the description here the index is not efficient.
  • Low utilisation archives + complex GNU archive symbol resolution rules make implementing any sort of parallel scanning difficult.
  • It's also not true to say that thin archives can always be a replacement as thin archives are not suitable for distribution.

LLD's simplified archive semantics are a good improvement though :)

This revision was automatically updated to reflect the committed changes.