Mold 2.42 a été étiqueté aux premières heures du mercredi 12 août, mettant fin à la période la plus calme qu'ait connue l'éditeur de liens rapide depuis longtemps : la version 2.41 datait d'avril. L'annonce principale porte sur un travail d'optimisation large, surtout visible sur les grosses éditions de liens et les machines à nombreux cœurs, mais la version est plus intéressante qu'un simple gain de vitesse. Mold analyse désormais les sections de déroulement de pile SFrame version 3 au lieu de les traiter comme des octets opaques, gère le format de relocations compactées d'Android, accepte un niveau de compression sur les sections de débogage, et corrige trois bogues de fusion de code identique capables de changer discrètement le comportement d'un programme.
The short answer
Mold 2.42 est disponible et referme le plus long intervalle entre deux versions depuis un moment. Le thème général, c'est l'optimisation, avec les gains les plus nets attendus sur les grosses éditions de liens et les machines à nombreux cœurs. La substance est ailleurs : mold comprend désormais les sections de déroulement de pile SFrame version 3 au lieu de les recopier aveuglément, gère les relocations compactées Android, accepte un niveau de compression sur les sections de débogage, jette par défaut les symboles locaux internes au compilateur, et corrige trois bogues de fusion de code identique discrètement capables de changer le comportement d'un programme.
Les versions d'éditeurs de liens se résument d'ordinaire facilement : c'est plus rapide, voici les chiffres. Mold 2.42 revendique bien d'être sensiblement plus rapide que ses prédécesseurs, en particulier sur les gros programmes et les machines à nombreux cœurs, et après quatre mois de travail d'optimisation, c'est crédible.
Nous préférons parler de l'autre moitié de la version, parce que les correctifs de justesse qu'elle contient sont de ceux qui ne s'annoncent pas.
Mold apprend à lire SFrame
La nouveauté la plus lourde de conséquences est celle dont la plupart des gens n'ont pas encore eu besoin.
SFrame est un format compact de déroulement de pile, conçu comme une alternative plus légère à .eh_frame pour le déroulement asynchrone que pratiquent profileurs et débogueurs. Jusqu'à récemment, il était assez rare pour qu'un éditeur de liens puisse en ignorer les détails. Cela a changé avec binutils 2.46, où l'assembleur GNU émet du SFrame version 3 quand on lui passe --gsframe. Les fichiers objets porteurs de sections .sframe sont en train de devenir ordinaires.
Voici pourquoi un éditeur de liens ne peut pas les laisser passer telles quelles. Les données de déroulement forment une table indexée par adresse de fonction, et l'éditeur de liens est justement le composant qui décide quelles fonctions survivent. Concaténez les sections d'entrée aveuglément et vous obtenez des entrées décrivant des fonctions supprimées par --gc-sections ou par la fusion de code identique, dans un ordre que personne n'a trié. Une recherche dichotomique dans cette table donne de mauvaises réponses sur la pile d'un programme vivant.
Mold 2.42 analyse donc correctement les sections .sframe d'entrée : il jette les entrées des fonctions retirées, reconstruit une section fusionnée unique triée par adresse de fonction, et émet un segment PT_GNU_SFRAME pour que l'exécution retrouve le résultat. La sortie relogeable avec -r est prise en charge également.
Des binaires plus petits, de deux façons
Deux changements s'attaquent à la taille de sortie par des angles différents.
Les relocations compactées Android arrivent sous la forme --pack-dyn-relocs=android et --pack-dyn-relocs=android+relr. Un binaire indépendant de la position porte une entrée de relocation dynamique par adresse que le chargeur doit corriger au chargement, et dans une grosse bibliothèque native cette table n'est pas une broutille. Le format Android, APS2, l'encode bien plus compactement, et la variante relr combine le compactage avec le format de table de relocations relatives. Le bénéfice se concentre là où le chargeur comprend le format, ce qui veut dire en pratique les bibliothèques natives livrées dans une application Android.
L'autre changement est plus discret et concerne tout le monde. Mold jette désormais par défaut les symboles locaux temporaires comme .L.str.42, à l'image de lld. Les compilateurs en émettent en masse pour des éléments sans nom comme les littéraux de chaînes. Rien ne les référence via la table des symboles, et les informations de débogage localisent leurs données par décalage de section : les retirer ne fait rien perdre d'observable, tout en enlevant quelques pour cent aux gros builds de débogage et en gardant les étiquettes internes du compilateur hors des traces symbolisées. --discard-none rétablit l'ancien comportement, et -r ou --emit-relocs conserve tout de toute façon.
Le niveau de compression cachait une valeur par défaut
--compress-debug-sections accepte désormais un niveau, ce qui permet de demander zstd:9 ou zlib:6.
L'intéressant, c'est ce que cela révèle. Les valeurs nues zstd et zlib restent acceptées et sont maintenant documentées comme équivalant à zstd:3 et zlib:1, qui étaient auparavant des valeurs par défaut codées en dur que personne n'avait de raison de connaître. Si vous produisez de gros artefacts de débogage et que vous en conservez beaucoup, il y a désormais une molette, et elle échange du temps d'édition de liens contre de la place. Si cela vous est égal, rien n'a changé.
Deux ajouts de compatibilité plus modestes complètent le tableau : -w et --no-warnings suppriment les avertissements et annulent --fatal-warnings tout en continuant de signaler les vraies erreurs, comme lld, et -Ttext-segment est pris en charge pour la compatibilité avec GNU ld.
Les bogues qui méritent la lecture
La fusion de code identique regroupe les fonctions qui compilent vers le même code machine, ce qui rapporte gros sur les binaires C++ pleins d'instanciations de patrons. C'est aussi un endroit où se montrer un peu trop généreux sur le mot identique se traduit par un changement de comportement plutôt que par un échec d'édition de liens. Mold 2.42 en corrige trois.
Le premier traîne depuis longtemps : deux fonctions au code machine identique mais aux tables d'exceptions ou routines de personnalité différentes pouvaient être fusionnées. Les instructions coïncident, le comportement face aux exceptions non, et la survivante attrape un jeu d'exceptions différent de celui d'une des originales.
Le deuxième concerne les symboles préemptibles. Une relocation vers un symbole préemptible était traitée comme équivalente à une relocation vers un autre symbole dont le corps se trouvait identique, ce qui ignore tout l'intérêt de la préemption : ce symbole peut être interposé à l'exécution, et alors les deux ne sont plus du tout la même chose.
Le troisième est le plus concret. Quand la fusion regroupait des sections aux exigences d'alignement différentes, la survivante ne prenait pas nécessairement l'alignement le plus strict du groupe. Une fonction demandant un alignement sur quatre octets pouvait se retrouver fusionnée dans une copie moins alignée et placée à une adresse impaire. En x86, cela casse les pointeurs sur fonction membre, parce qu'ils utilisent le bit de poids faible de l'adresse pour distinguer les fonctions virtuelles des autres. Une adresse impaire fait passer un appel non virtuel pour un appel virtuel.
À côté de cela, mold corrige une erreur parasite de section COMDAT rejetée lors de l'édition de liens mêlant archives LTO et non LTO, une situation de concurrence dans la sélection du propriétaire COMDAT pendant le LTO, et plusieurs problèmes de versionnage de symboles, dont un où un alias de compatibilité créé avec .symver foo, foo@VER pouvait satisfaire une référence non versionnée et lier silencieusement les appelants à l'implémentation historique au lieu de la version par défaut.
Ce que nous ferions
Mettre à jour, et lire deux lignes des notes de version avant plutôt qu'après.
Les correctifs de justesse en sont la raison. Si vous compilez du C++ avec la fusion de code identique activée, les trois bogues sont des risques bien vivants dans votre éditeur de liens actuel, et celui de l'alignement en particulier échoue d'une manière qui ne ressemblera pas à un problème d'édition de liens quand vous le déboguerez à deux heures du matin.
Les deux choses qui peuvent surprendre sont bénignes mais visibles. Le rejet des symboles locaux est actif par défaut, donc tout outil qui lit des symboles locaux temporaires dans la table des symboles ne les trouvera plus tant que vous ne passerez pas --discard-none. Et si vous passez déjà un zstd ou un zlib nu à --compress-debug-sections, votre niveau de compression est maintenant un nombre documenté plutôt qu'un détail d'implémentation.
Sources et pour aller plus loin
- Notes de version de mold 2.42.0, GitHub, 12 août 2026
- Mold 2.42 delivering greater performance for this high speed linker, Phoronix, 12 août 2026
- mold, un éditeur de liens moderne, dépôt du projet
- The SFrame stack trace format, documentation binutils
Questions fréquentes
Qu'est-ce que SFrame, et pourquoi un éditeur de liens doit-il le comprendre ?
SFrame est un format compact de déroulement de pile, une alternative bien plus légère à .eh_frame, utilisée par les profileurs et les débogueurs pour remonter une pile d'appels de façon asynchrone. Depuis binutils 2.46, l'assembleur GNU émet du SFrame version 3 quand on lui passe le drapeau --gsframe, ce qui rend les fichiers objets porteurs de sections .sframe ordinaires plutôt qu'exotiques. Un éditeur de liens ne peut pas traiter ces sections comme des octets opaques à concaténer. S'il le faisait, des entrées survivraient pour des fonctions déjà supprimées par --gc-sections ou par la fusion de code identique, et la table fusionnée ne serait pas triée, si bien qu'une recherche dichotomique d'adresse à l'exécution donnerait de mauvaises réponses. Mold 2.42 analyse donc les sections .sframe d'entrée, retire les entrées des fonctions disparues, reconstruit une section fusionnée unique triée par adresse de fonction, et émet un segment PT_GNU_SFRAME pour que l'exécution la retrouve.
Que sont les relocations compactées Android, et comptent-elles hors d'Android ?
C'est un schéma de compression pour la table de relocations dynamiques. Un binaire indépendant de la position porte une entrée de relocation pour chaque adresse que le chargeur dynamique doit corriger au chargement, et dans une grosse bibliothèque cette table peut représenter une part substantielle du fichier. Le format Android, appelé APS2, encode ces entrées bien plus compactement. Mold 2.42 ajoute --pack-dyn-relocs=android et --pack-dyn-relocs=android+relr, le second combinant le compactage avec le format de table de relocations relatives. Le nom dit Android parce que c'est là que le format est né et qu'il y est standard, donc le bénéfice direct va à ceux qui livrent des bibliothèques natives dans une application Android. Le mécanisme n'est pas verrouillé à une plateforme, mais le chargeur d'en face doit comprendre le format, ce qui en pratique veut dire Android.
Pourquoi mold rejette-t-il maintenant par défaut les symboles locaux comme .L.str.42 ?
Parce qu'ils sont internes au compilateur et que personne n'en a besoin. Les compilateurs émettent quantité de symboles locaux temporaires pour des éléments de programme sans nom, comme les littéraux de chaînes. Rien ne les référence via la table des symboles, et les informations de débogage localisent leurs données par décalage de section plutôt que par symbole : les jeter ne fait donc rien perdre d'observable. Ce qu'on y gagne, c'est quelques pour cent sur les gros builds de débogage et des adresses symbolisées plus propres, puisque les étiquettes internes du compilateur cessent d'apparaître dans les traces. C'est le comportement qu'a déjà lld. Si vous dépendez de l'ancien fonctionnement, --discard-none le rétablit, et la sortie relogeable avec -r ou --emit-relocs conserve tous les symboles locaux quoi qu'il arrive.
Quelle est la gravité des correctifs sur la fusion de code identique ?
Plus grande que celle de la plupart des correctifs d'éditeur de liens, parce que chacun pouvait changer le comportement d'un programme au lieu de simplement faire échouer l'édition de liens. La fusion de code identique regroupe les fonctions au code machine identique pour réduire le binaire, et les trois bogues viennent d'une définition d'identique un peu trop lâche. Deux fonctions aux mêmes instructions mais aux tables d'exceptions ou routines de personnalité différentes pouvaient être fusionnées, ce qui change les exceptions attrapées par la survivante. Une relocation vers un symbole préemptible était traitée comme équivalente à une relocation vers un autre symbole dont le corps se trouvait identique, en oubliant qu'un symbole préemptible peut être interposé à l'exécution. Et lors de la fusion de sections aux exigences d'alignement différentes, la survivante ne prenait pas toujours l'alignement le plus strict : une fonction demandant quatre octets pouvait atterrir à une adresse impaire, ce qui casse les pointeurs sur fonction membre en x86, car ils utilisent le bit de poids faible pour distinguer les appels virtuels des autres.
Faut-il mettre à jour, et quelque chose risque-t-il de bouger sous nos pieds ?
Oui, et les correctifs de justesse suffisent à le justifier si vous compilez du C++ avec la fusion de code identique activée. Deux changements méritent d'être connus avant la mise à jour plutôt qu'après. Le rejet des symboles locaux est désormais actif par défaut : si un de vos outils lit des symboles locaux temporaires dans la table des symboles, il ne les trouvera plus, et --discard-none les ramène. Et --compress-debug-sections prend maintenant un niveau, ce qui signifie que les valeurs nues zstd et zlib que vous passez peut-être déjà sont documentées comme zstd:3 et zlib:1 au lieu d'être des valeurs par défaut non spécifiées. Si vos artefacts de débogage sont volumineux et que vous en stockez beaucoup, zstd:9 est maintenant disponible et coûte du temps d'édition de liens pour gagner de la place.