Related PR: #177298
Background
At Meta, we are experimenting with the medium code model to solve relocation overflow issues in large binaries. During this work, we encountered a problem when linking objects compiled with different code models (small vs. medium/large).
The Problem
When linking mixed code model objects, COMDAT groups may have both small and large (SHF_X86_64_LARGE) versions. Currently, LLD uses first-come-first-served selection, which can keep the large version even when small code model code references it. This causes relocation overflows because large sections can be placed beyond ±2GB from the code, exceeding the range of R_X86_64_PC32 relocations used by the small code model.
Proposed Solution
The submitted PR makes LLD prefer small COMDAT sections over large ones, ensuring compatibility when mixing code models. When a COMDAT with SHF_X86_64_LARGE
sections is already selected and a small version is encountered, the small version takes over.
Feedback Received
@MaskRay raised the following concerns:
In ELF,
GRP_COMDAT
indicates a section group subject to deduplication. While the specification doesn’t mandate which group prevails, all linkers consistently select the first one encountered. This patch fundamentally changes that behavior (guarantee).
This feels like an almost ODR violation scenario. The COMDAT contract is that all instances are identical and interchangeable. If they have different flags, they’re not truly identical.
The problem could potentially be addressed on the build system side—can you ensure small code model relocatable files are passed before large code model files?
@smithp35 provided additional context:
Arm’s proprietary linker doesn’t follow the first come first served model for COMDAT group selection. For various reasons, groups were ranked, such as the group with smallest size, highest optimization level, most use of architectural features, presence of optional metadata.
Our Perspective
-
Build system solutions are fragile: Solving this problem reliably at the build system level is challenging and feels like a workaround. Large codebases with complex dependency graphs make it difficult to guarantee link order. It seems the linker should be able to handle any order.
-
Trade-off between guarantees: Violating the “first COMDAT section wins” convention seems preferable to violating the “small code model cannot reference large sections” guarantee. The former is a linker implementation detail; the latter causes hard link failures.
-
Precedent exists: As @smithp35 noted, there is precedent for linkers using more sophisticated COMDAT selection strategies beyond first-come-first-served.
-
ODR considerations: While COMDAT sections with different flags aren’t strictly identical, they are semantically equivalent—the code/data content is the same, just compiled with different assumptions about addressing. The small version is strictly more compatible.
Looking forward to the community’s input on the best path forward.