While working on a mini-testsuite for the Solaris ABI symbols (cf. ld/emultempl/solaris2.em), I noticed an inconsistency between GNU ld and Solaris ld. Consider the following testcase: $ cat vers33.c extern void show_undef_weak (void) __attribute__((weak)); void foo (void) { if (show_undef_weak) show_undef_weak (); } $ cat vers33.map VERS_1 { global: foo; local: *; }; $ gcc -fPIC -shared -o vers33.so vers33.c -Wl,--version-script vers33.map $ objdump --dynamic-syms vers33.so|grep Base 0000000000000000 w D *UND* 0000000000000000 Base _ITM_deregisterTMCloneTable 0000000000000000 w D *UND* 0000000000000000 Base __gmon_start__ 0000000000000000 w D *UND* 0000000000000000 Base show_undef_weak 0000000000000000 w D *UND* 0000000000000000 Base _ITM_registerTMCloneTable Several undef weak symbols show up as bound to the Base version: show_undef_weak from the testcase, as well as a couple of others from crtbeginS.o and others. This differs from the Solaris ld (which I consider as a reference for symbol versioning given that Sun invented this stuff) and makes no sense at all: those weak undef symbols certainly aren't an exported interface of the shared objects, thus have no reason at all to show up as bound to the Base version in the version *definition* (.gnu.version_d). One might argue that they could be in .gnu.version_r, but even that's not completely clear. I tried to determine where this happens, but got lost in the maze of elflink.c I'm attaching an initial patch with a testcase for this.
Created attachment 16435 [details] Initial testcase 2025-10-23 Rainer Orth <ro@CeBiTec.Uni-Bielefeld.DE> ld: PR ld/33577 * testsuite/ld-elfvers/vers33.c: New test. * testsuite/ld-elfvers/vers33.dsym: Likewise. * testsuite/ld-elfvers/vers33.map: Likewise. * testsuite/ld-elfvers/vers33.ver: Likewise. * testsuite/ld-elfvers/vers.exp: Run it.
Symbol version script only applies to definitions, not references, since linker doesn't know where/if/how show_undef_weak is defined at run-time. It can be unversioned or have some other version.
Is it usual here to close bugs immediately, without a chance to discuss? I'd like to ask that it be reopened, and that the discussion continue. No one claimed that linker scripts control references. They don't, but that's not the issue being reported. The issue is that this reference symbol is being added to a version *definition* section. If it was being added to a version needed section, that would be another matter. Here, the linker script doesn't control the version assigned to show_undef_weak, but it does serve to enable the versioning plumbing generally, and that seems to be involved. Let's look closer, using gld on Solaris, because that's what I'm running, but I would expect that it will be the same on Linux. To provide a point of reference, I've added a call to printf() to the original source file: % cat vers33.c #include <stdio.h> extern void show_undef_weak (void) __attribute__((weak)); void foo (void) { if (show_undef_weak) show_undef_weak (); printf("done"); } Start with a .o: % gcc -fPIC -shared -c vers33.c Linking it without the linker script, we get the expected result: % gld --shared /usr/lib/64/crti.o vers33.o /usr/lib/64/crtn.o -lc % elfdump -v a.out Version Needed Section: .gnu.version_r index file version [2] libc.so.1 SUNW_0.7 % pvs -os a.out a.out - libc.so.1 (SUNW_0.7): printf; That's all reasonable: printf() has been assigned to a version needed section for libc, and otherwise, there's no versioning in play. Now, see what happens when we add in the linker script: % ld --shared /usr/lib/64/crti.o vers33.o --version-script vers33.map /usr/lib/64/crtn.o -lc % elfdump -v a.out Version Definition Section: .gnu.version_d index version dependency [1] a.out [ BASE ] [2] VERS_1 Version Needed Section: .gnu.version_r index file version [3] libc.so.1 SUNW_0.7 % pvs -os a.out a.out - libc.so.1 (SUNW_0.7): printf; a.out - a.out: _PROCEDURE_LINKAGE_TABLE_; a.out - a.out: _edata; a.out - a.out: _etext; a.out - a.out: _end; a.out - a.out: _DYNAMIC; a.out - a.out: show_undef_weak; a.out - a.out: _GLOBAL_OFFSET_TABLE_; a.out - VERS_1: foo; Note that show_undef_weak has been assigned to the Version Definition Section, and not a Version Needed Section (like printf). Doesn't it seem like something is off here? I agree with your point that "linker doesn't know where/if/how show_undef_weak is defined at run-time", but it certainly does know that it wasn't defined by the object it is building (a.out). My guess is that this is an unhandled case that simply "fell through" to the base definition, and not really the intended outcome. That leaves the question of where it should be assigned. As you said, "It can be unversioned or have some other version". However, in this case, it can only be unversioned, because the only mention of it is in the declaration: extern void show_undef_weak (void) __attribute__((weak)); For it to have some other version, that symbol would need to have been seen in a dependency (like printf is seen in libc), which would then cause a Version Needed Record to be created. Here, there is nothing, so unversioned would seem to be the only valid option. ???
We may be talking different things. For me, the BASE version of a symbol is the symbol without the symbol version. The BASE version in the '.gnu.version_d' section: Version definition section '.gnu.version_d' contains 2 entries: Addr: 0x00000000000011c0 Offset: 0x000011c0 Link: 9 (.dynstr) 000000: Rev: 1 Flags: BASE Index: 1 Cnt: 1 Name: libx.so 0x001c: Rev: 1 Flags: none Index: 2 Cnt: 1 Name: VERS_1 comes from the shared library name. Is the BASE version from the shared library name an issue on Solaris?
I don't think this is specifically a Solaris issue, but rather, just one that was noticed on Solaris by virtue of comparing objects from the two link-editors. My understanding of versioning: - Versioned symbols defined by the object (definitions) are associated with a VERDEF record. - Versioned symbols that are defined externally by some other object (references) are associated with a VERNEED record. - Symbols that are not versioned are given version index 0. These can be either definitions, or external references. - The BASE version, which has index 1, is the version that holds the symbols defined by the object itself, that have not otherwise been assigned to a different version, possibly with a linker script. By convention, the version is given the SONAME of the object. This is a VERDEF, so external references don't belong here. - Named versions, first VERDEF, and then VERNEED, are given indexes starting at 2. So in this example, we have the following versions: % elfdump -v a.out Version Definition Section: .gnu.version_d index version dependency [1] a.out [ BASE ] [2] VERS_1 Version Needed Section: .gnu.version_r index file version [3] libc.so.1 SUNW_0.7 Or summarizing: [0] Not versioned [1] BASE definitions [2] Definitions assigned to version VERS_1 by the linker script [3] External Symbols identified as coming from version SUNW_0.7 in libc The problem then, is that show_undef_weak is an external reference, not a definition, so it should not be assigned to any VERDEF (BASE, or otherwise). The valid options would be version index 0 (not versioned), or the index of a VERNEED record. As you pointed out earlier, we have no information that would identify such a VERNEED. The source of this symbol, if it even exists, is completely unknown to the link-edit, so it has no version, and version index 0 is the appropriate assignment for it. I believe that these details are the same on Solaris as on GNU. I also don't think it's causing any known problems today, as the example is artificial, and the approach using a weak symbol is poor. It's just something that was noticed while testing.
(In reply to Ali Bahrami from comment #5) > I believe that these details are the same on Solaris as on GNU. I also > don't think it's causing any known problems today, as the example is > artificial, and the approach using a weak symbol is poor. It's just > something that was noticed while testing. As I said in comment #4, BASE is unrelated to weak symbol: [hjl@gnu-tgl-3 tmp]$ cat x.c void foo (void) { } [hjl@gnu-tgl-3 tmp]$ cat x.map VERS_1 { global: foo; local: *; }; [hjl@gnu-tgl-3 tmp]$ gcc -c -fPIC x.c [hjl@gnu-tgl-3 tmp]$ ld -o libx.so -shared x.o --version-script=x.map [hjl@gnu-tgl-3 tmp]$ readelf -V libx.so Version symbols section '.gnu.version' contains 3 entries: Addr: 0x0000000000001084 Offset: 0x00001084 Link: 3 (.dynsym) 000: 0 (*local*) 2 (VERS_1) 2 (VERS_1) Version definition section '.gnu.version_d' contains 2 entries: Addr: 0x0000000000001090 Offset: 0x00001090 Link: 4 (.dynstr) 000000: Rev: 1 Flags: BASE Index: 1 Cnt: 1 Name: libx.so 0x001c: Rev: 1 Flags: none Index: 2 Cnt: 1 Name: VERS_1 [hjl@gnu-tgl-3 tmp]$
(In reply to H.J. Lu from comment #6) > (In reply to Ali Bahrami from comment #5) > > I believe that these details are the same on Solaris as on GNU. I also > > don't think it's causing any known problems today, as the example is > > artificial, and the approach using a weak symbol is poor. It's just > > something that was noticed while testing. > > As I said in comment #4, BASE is unrelated to weak symbol: That's right. Other than having been used to create this particular example, the weak attribute is not the issue, and has nothing to do with BASE. As you've said at the start, and as I've mentioned at every opportunity, the symbol show_undef_weak is an external *reference*, and not a *definition*. REFerences should not be assigned to a verDEF index, and so show_undef_weak should not be assigned to BASE. It should be set to version index 0.
(In reply to Ali Bahrami from comment #7) > > As you've said at the start, and as I've mentioned at every > opportunity, the symbol show_undef_weak is an external *reference*, > and not a *definition*. REFerences should not be assigned to > a verDEF index, and so show_undef_weak should not be assigned to > BASE. It should be set to version index 0. I didn't see it: [hjl@gnu-tgl-3 pr33577]$ cat x.c extern void show_undef_weak (void) __attribute__((weak)); void foo (void) { if (show_undef_weak) show_undef_weak (); } [hjl@gnu-tgl-3 pr33577]$ make ld -shared --version-script=x.map -o libx.so x.o readelf -s -D -W -V libx.so Symbol table for image contains 4 entries: Num: Value Size Type Bind Vis Ndx Name 0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND 1: 0000000000000000 0 NOTYPE WEAK DEFAULT UND show_undef_weak 2: 0000000000000220 17 FUNC GLOBAL DEFAULT 3 foo@@VERS_1 3: 0000000000000000 0 OBJECT GLOBAL DEFAULT ABS VERS_1 Version symbols section '.gnu.version' contains 4 entries: Addr: 0x00000000000010ac Offset: 0x000010ac Link: 5 (.dynsym) 000: 0 (*local*) 1 (*global*) 2 (VERS_1) 2 (VERS_1) Version definition section '.gnu.version_d' contains 2 entries: Addr: 0x00000000000010b8 Offset: 0x000010b8 Link: 6 (.dynstr) 000000: Rev: 1 Flags: BASE Index: 1 Cnt: 1 Name: libx.so 0x001c: Rev: 1 Flags: none Index: 2 Cnt: 1 Name: VERS_1 [hjl@gnu-tgl-3 pr33577]$
Sorry, I'm not following what the previous note is pointing out. Don't you need to use objdump, as in Rainer's original note, to show the version index? He showed: $ objdump --dynamic-syms vers33.so ... 0000000000000000 w D *UND* 0000000000000000 Base show_undef_weak ^^^^ While we're arguing that the output should be: 0000000000000000 w D *UND* 0000000000000000 show_undef_weak ??? Thanks, - Ali
Reopened
Created attachment 16441 [details] A patch Try this.
Thank you, I appreciate it. I happen to know from an offline conversation that Rainer is away for a couple of weeks, but I'm sure he'll jump on testing the patch when he returns (I'm just the analysis part of the team) but I looked at the patch and it looks like the right thing to me. There seems to be some repetition that maybe could be factored out, but I'll leave that to you guys, and am not worried about it either way. Thanks Again.
Created attachment 16442 [details] An updated patch
The master branch has been updated by H.J. Lu <hjl@sourceware.org>: https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;h=f685e3953f9a38a41bbd0a597f9882870cee13d5 commit f685e3953f9a38a41bbd0a597f9882870cee13d5 Author: H.J. Lu <hjl.tools@gmail.com> Date: Wed Oct 29 09:49:57 2025 +0800 elf: Don't set its DT_VERSYM entry for unversioned symbol 1. Referenced symbol without '@' has no version. 2. Defined symbol without the .symver directive has no version if there is no linker version script. Symbol without version shouldn't have the base version in its DT_VERSYM entry. Instead, its DT_VERSYM entry should be all zero to indicate that the symbol doesn't have a version. NB: Symbol with the base version has a '@' suffix, like "foo@", defined with .symver hide_original_foo, foo@ bfd/ PR ld/33577 * elflink.c (elf_link_output_extsym): Don't set its DT_VERSYM entry for the symbol without version. ld/ PR ld/33577 * ld-elfvers/vers16.dsym: Remove the "Base" version on symbols without version. Signed-off-by: H.J. Lu <hjl.tools@gmail.com>
Fixed for 2.46.
I think the old linker behavior, which kept undefined unversioned symbols at version 1, was more sensible. This is because version 0 (VER_NDX_LOCAL) used to be exclusively for defined symbols, but now it can be applied to undefined ones. This change breaks the simple rule that an absent symbol versioning section (like .gnu.version) meant everything was version 1. Now, a symbol's default version depends on whether it's defined. The fact that version index 1 in the .gnu.version_d section specifies the library name (SONAME) shouldn't be a reason to default undefined symbols to version 0. Could the objdump output be updated to stop printing "BASE" for undefined symbols?
The old behavior was wrong, as was beaten to death in the preceding discussion. Hence, not sensible. Undefined symbols can't belong to a symbol definition version. I do understand that having something that seemed to be stable change can be annoying to deal with, but the assumption that version 0 was only for definitions isn't right. Version 0 is for completely unversioned symbols.
(In reply to Ali Bahrami from comment #17) > The old behavior was wrong, as was beaten to death in the > preceding discussion. Hence, not sensible. Undefined > symbols can't belong to a symbol definition version. > > I do understand that having something that seemed to > be stable change can be annoying to deal with, but the > assumption that version 0 was only for definitions isn't > right. Version 0 is for completely unversioned symbols. I think that VER_NDX_LOCAL (0): Symbol has local scope may confuse people.
This change completely breaks compatibility with the current and all previous versions of the mold linker. Specifically, mold cannot link against shared libraries if they are linked with GNU ld after f685e3953f9a38a41bbd0a597f9882870cee13d5. This appears to be an aesthetic change with no real user benefit, yet it already causes a major compatibility issue. It is probably not limited to mold, as GNU ld has been emitting the symbol table version the way it currently does probably since the very beginning. I don’t think it is worth taking the risk. It seems we are breaking user environments with no real reward.
Yes, I see the same text in the Solaris <sys/link.h>, and I agree that it is confusing: /* * Versym symbol index values. Values greater than VER_NDX_GLOBAL * and less then VER_NDX_LORESERVE associate symbols with user * specified version descriptors. */ #define VER_NDX_LOCAL 0 /* symbol is local */ #define VER_NDX_GLOBAL 1 /* symbol is global and assigned to */ /* the base version */ #define VER_NDX_LORESERVE 0xff00 /* beginning of RESERVED entries */ #define VER_NDX_ELIMINATE 0xff01 /* symbol is to be eliminated */ Before replying, I went into our records to find the original PSARC case that created Sun's implementation from 1994. It's evident that the authors expected that only local symbols would be in version index 0. The notes from then are nearly 1:1 with what's in the current Solaris Linker and Libraries manual. The case we've been discussing, of an undefined global symbol in a final object, is really unusual, because normally you'd be unable to link such an object without getting an unsatisfied symbol error that would fail the link. Remember that we had to use a weak reference symbol in order to make it happen. That's very artificial, I think it's safe to say that it doesn't really happen in useful code, and is just a weird corner case. As such, I think it's fair to say that the original authors weren't thinking about the scenario we've been discussing at all, hence the naming they used. However, the fact that index 0 is the only valid index for these weird unsatisfied globals follows from the way these data structures are defined. Supporting that is the fact that it matches what the Solaris link-editor does, and that code is also largely unchanged from 1994. A local symbol will always have a version index of 0, but a version index of 0 does not always imply a local symbol. Just 99+% of the time. Given how obscure this is, I think the name VER_NDX_LOCAL is OK, if not perfect. Not worth trying to change now, but something to be aware of.
(In reply to Rui Ueyama from comment #19) > This change completely breaks compatibility with the current and all > previous versions of the mold linker. Specifically, mold cannot link against > shared libraries if they are linked with GNU ld after > f685e3953f9a38a41bbd0a597f9882870cee13d5. > > This appears to be an aesthetic change with no real user benefit, yet it > already causes a major compatibility issue. It is probably not limited to > mold, as GNU ld has been emitting the symbol table version the way it > currently does probably since the very beginning. I don’t think it is worth > taking the risk. It seems we are breaking user environments with no real > reward. That's surprising. It is indeed an obscure thing, and I'm surprised that any link-editor really cares. Can you provide details about how it breaks? I have to assume that this change passed tests before it was pushed, so I am assuming that the GNU ld itself is not bothered by the change. Similarly, the Solaris ld handles either form (we sometimes end up linking against objects built by GNU ld). It would be helpful to know why mold is breaking. How does it depend on the old behavior, and what's throwing it off now? Correctness is its own reward, and this was really intended to be a cleanup of a corner case. I agree that this is not a hill to die on, but am surprised that it's a hill at all, so I'd ask that you categorize why it breaks, and then we can discuss.
Unversioned symbols apply to both defined and undefined. The new behavior: [hjl@gnu-tgl-3 pr33577]$ cat x.c #include <stdio.h> extern void show_undef_weak (void) __attribute__((weak)); extern void show_undef (void); void foo (void) { show_undef_weak (); show_undef (); printf ("hello\n"); } [hjl@gnu-tgl-3 pr33577]$ gcc -fPIC -shared -o libx.so x.c [hjl@gnu-tgl-3 pr33577]$ objdump -T libx.so libx.so: file format elf64-x86-64 DYNAMIC SYMBOL TABLE: 0000000000000000 w D *UND* 0000000000000000 _ITM_deregisterTMCloneTable 0000000000000000 D *UND* 0000000000000000 show_undef 0000000000000000 DF *UND* 0000000000000000 (GLIBC_2.2.5) puts 0000000000000000 w D *UND* 0000000000000000 __gmon_start__ 0000000000000000 w D *UND* 0000000000000000 show_undef_weak 0000000000000000 w D *UND* 0000000000000000 _ITM_registerTMCloneTable 0000000000000000 w DF *UND* 0000000000000000 (GLIBC_2.2.5) __cxa_finalize 00000000000003b9 g DF .text 0000000000000020 foo [hjl@gnu-tgl-3 pr33577]$ Both unversioned defined and undefined symbols no longer have the BASE version. Neither ld nor glibc ld.so use VER_NDX_LOCAL for symbol visibility.
We essentially ignore all dynamic symbols with VER_NDX_LOCAL as if they didn't exist in .dynsym at all. I don't exactly remember why we chose to do that, but I believe that was my interpretation of "VER_NDX_LOCAL (0): symbol has local scope". Symbols with local scope are generally not visible from other files, so it shouldn't be an unnatural interpretation of the sentence. We can change mold so that it handles dynamic symbols with VER_NDX_LOCAL in the same manner as GNU ld. That's probably a good idea because mold aims to be as compatible with GNU ld as possible. However, I'd like you guys to reevaluate the risk of introducing a major compatibility problem with such short notice. It is likely to break many user environments.
(In reply to Ali Bahrami from comment #17) > The old behavior was wrong, as was beaten to death in the > preceding discussion. Hence, not sensible. Undefined > symbols can't belong to a symbol definition version. > > I do understand that having something that seemed to > be stable change can be annoying to deal with, but the > assumption that version 0 was only for definitions isn't > right. Version 0 is for completely unversioned symbols. There are follow-up discussions on the patch thread https://inbox.sourceware.org/binutils/CAMe9rOqZ_+WSJUC_ARjU-o1C5b-CL836kqtS4VpJjXMNjzgJ5w@mail.gmail.com/T/#m08dfe58e142eb12396ce73322bc7cc4bf3584bde Andreas Schwab and Michael Matz seem to agree with the old behavior (index 1) as well. While the statement that "non-zero indexes are for defined symbols" is legitimate for Solaris, I maintain my strong arguments that "version 0 (VER_NDX_LOCAL) used to be exclusively for defined symbols" and that "an absent symbol versioning section (like .gnu.version) meant everything was version 1." I don't think it's worth changing the reasonable GNU ld behavior implemented before 1999. ( Linker needs to know the versions of undefined symbols in shared object files for two reasons: (see https://reviews.llvm.org/D80059) - Export a versioned symbol referenced by a shared object, if it is defined in the executable - Make --no-allow-shlib-undefined work for versioned symbols referenced by a shared object in another object file I believe in most ELF linkers, zero index for undefined symbols will lead to more code. )
(In reply to Rui Ueyama from comment #23) > We essentially ignore all dynamic symbols with VER_NDX_LOCAL as if they > didn't exist in .dynsym at all. I don't exactly remember why we chose to do > that, but I believe that was my interpretation of "VER_NDX_LOCAL (0): symbol > has local scope". > Symbols with local scope are generally not visible from other files, so it > shouldn't be an unnatural interpretation of the sentence. I agree, it seems to follow from the documentation, which in hindsight seems a bit incomplete or ambiguous. At best, VER_NDX_LOCAL is derived information, based on the actual binding, which is in the symbol's st_info field. I think it would be better to key off that. You probably do, for objects that don't have a versym section (aren't versioned). Or do you not encounter non-versioned objects? > We can change mold so that it handles dynamic symbols with VER_NDX_LOCAL in > the same manner as GNU ld. That's probably a good idea because mold aims to > be as compatible with GNU ld as possible. That's probably a good idea for general correctness as well. Versioning augments the basic symbol, but isn't intended to contradict it. > > However, I'd like you guys to reevaluate the risk of introducing a major > compatibility problem with such short notice. It is likely to break many > user environments. When different implementations of a written spec make different interpretations like this, it can be hard to spot, and hard to fix, particularly when a lot of time passes. It seems that we've found another one of those. Had this been understood as a compatibility concern, we wouldn't have gone there, and now that we understand that it is, and how, I won't stand in the way of rolling it back. Perhaps it could be wrapped in an "#ifdef __sun" for now? The correctness of the GNU choice (assigning an undefined reference to a VERDEF index) is questionable, so this is entirely about not breaking existing tools. It would be nice and tidy if objects made for Solaris could follow our implementation. On the other hand, we seem to handle it either way, so I don't insist.
Created attachment 16461 [details] A testcase for VER_NDX_GLOBAL This is a testcase for VER_NDX_GLOBAL with asm (".symver foo_base, foo@"); VER_NDX_GLOBAL is used to create the base version to provide an unversioned compatible symbol.
As the first step, we should clarify exactly what version index 0, VER_NDX_LOCAL, means. Currently both ld and ld.so in glibc don't treat version index 0 as hidden visibility. In ld, only ia64 has local dynamic symbols: [hjl@gnu-tgl-3 ia64]$ readelf -VDsW local1.so Symbol table for image contains 4 entries: Num: Value Size Type Bind Vis Ndx Name 0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND 1: 00000000000001f0 16 FUNC LOCAL DEFAULT 7 hidden 2: 0000000000010348 8 OBJECT GLOBAL DEFAULT 10 foo@@VERS_1 3: 0000000000000000 0 OBJECT GLOBAL DEFAULT ABS VERS_1 Version symbols section '.gnu.version' contains 4 entries: Addr: 0x000000000000018e Offset: 0x0000018e Link: 2 (.dynsym) 000: 0 (*local*) 0 (*local*) 2 (VERS_1) 2 (VERS_1) Version definition section '.gnu.version_d' contains 2 entries: Addr: 0x0000000000000198 Offset: 0x00000198 Link: 3 (.dynstr) 000000: Rev: 1 Flags: BASE Index: 1 Cnt: 1 Name: local1.so 0x001c: Rev: 1 Flags: none Index: 2 Cnt: 1 Name: VERS_1 [hjl@gnu-tgl-3 ia64]$ hidden is a local dynamic symbol with VER_NDX_LOCAL. I think the spec should say something like that a global/weak defined/undefined, with version index 0 is a symbol without version.
Reopening while we discuss.
(In reply to H.J. Lu from comment #27) > As the first step, we should clarify exactly what version index 0, > VER_NDX_LOCAL, > means. Currently both ld and ld.so in glibc don't treat version index 0 as > hidden > visibility. ... > hidden is a local dynamic symbol with VER_NDX_LOCAL. > > I think the spec should say something like that a global/weak > defined/undefined, > with version index 0 is a symbol without version. In my opinion, the only basis for treating a symbol as hidden should be that it specifies hidden visibility it it's st_other field, as extracted by ELF[32|64]_ST_VISIBILITY. Similarly, the only basis for treating a symbol as local is that it has STB_LOCAL visibility. The version may reflect those things, but it doesn't determine them. While symbols with VER_NDX_LOCAL are almost always local (as opposed to globals that actually have hidden visibility), we know there are other cases where it applies, as we've been discussing. So I would say that index 0 is the index to be used when no other version index applies. If I could go back to 1994 with this discussion fresh in my mind, I might suggest that it be named VER_NDX_NONE. I don't think we should rename it now of course, that ship has sailed.
(In reply to Ali Bahrami from comment #29) > Similarly, the only basis for treating a symbol as local > is that it has STB_LOCAL visibility. s/visiblity/binding And of course a global with hidden visibility behaves a lot like an actual local binding in many ways. I really was just trying to say that the symbol itself is the place to look for binding and visibility, not the version.
(In reply to Ali Bahrami from comment #29) > In my opinion, the only basis for treating a symbol > as hidden should be that it specifies hidden visibility > it it's st_other field, as extracted by ELF[32|64]_ST_VISIBILITY. > Similarly, the only basis for treating a symbol as local > is that it has STB_LOCAL visibility. The version may reflect > those things, but it doesn't determine them. > > While symbols with VER_NDX_LOCAL are almost always local > (as opposed to globals that actually have hidden visibility), > we know there are other cases where it applies, as we've been > discussing. So I would say that index 0 is the index to be used > when no other version index applies. If I could go back to 1994 > with this discussion fresh in my mind, I might suggest that > it be named VER_NDX_NONE. > > I don't think we should rename it now of course, that > ship has sailed. Rename to VER_NDX_NONE may be too late. But https://docs.oracle.com/en/operating-systems/solaris/oracle-solaris/11.4/linkers-libraries/version-symbol-section.html can be updated to clarify what version index 0 really means for unversioned global/weak defined/undefined symbols.
In the meantime, can we roll back f685e3953f9a38a41bbd0a597f9882870cee13d5? If we don’t, significant breakage across many environments is almost certainly guaranteed when GNU binutils 2.46 is released for those who upgrade binutils and mold individually. I will make a change to mold so that it can consume shared libraries linked with that particular change, but we cannot expect users to upgrade all of their tools at the same time.
(In reply to H.J. Lu from comment #31) > Rename to VER_NDX_NONE may be too late. But > > https://docs.oracle.com/en/operating-systems/solaris/oracle-solaris/11.4/ > linkers-libraries/version-symbol-section.html > > can be updated to clarify what version index 0 really means for unversioned > global/weak defined/undefined symbols. I can do that. It may take awhile for the change to appear online, but will happen in due course.
Michael on the binutils mailing list pointed out that an old document from Solaris https://shrubbery.net/solaris9ab/SUNWdev/LLM/p45.html doesn't mention version index (0) for unversioned undefined symbols. Citing a Solaris specification is not convincing, given that the GNU ld versioning scheme was implemented in the 1990s and its subsequent evolution and divergence from the original design are unclear. However, in practical testing using a 2019 GNU ld version (before sourceware.org/PR26002 , I forgot that I had reported this issue), I observed that it produced index 0 for unversioned undefined symbols in several manually crafted cases. --- The GNU and Linus Standard Base documentation is ambiguous about the version index for unversioned undefined symbols. The current specification at sourceware.org/gnu-gabi/program-loading-and-dynamic-linking.txt defines VER_NDX_LOCAL (0) as "The symbol is private, and is not available outside this object." However, this naming is misleading for undefined symbols. As suggested in discussions, VER_NDX_LOCAL should conceptually be VER_NDX_NONE and apply to unversioned undefined symbols as well. However, this alone does not justify the GNU ld producer change. GNU ld has used index 0 for unversioned undefined symbols both before version 2.35 (see sourceware.org/PR26002 in 2020) and in the upcoming 2.46 release (see sourceware.org/PR33577). Therefore, I think it makes sense for other ELF linkers to adopt index 0 for unversioned undefined symbols.
Well, citing a Sun specification from the 90's is relevant, because Ali used the ARCs to point at the original intent of NDX_LOCAL (and it being more along the line of "unknown version"), and the docu shows that that wasn't explicitely put down externally back then (i.e. added only later there). Now, whatever the original intent may have been (and I of course agree, it absolutely makes sense to use '0' as the nothing-known index for mere references), it wasn't what was written in most places, which only was NDX_LOCAL - local scope [full stop] . There really isn't much wiggle room in interpreting that single line (and to remind, the GNU version stuff was designed in large part by getting inspired by relevant and available Sun docs). Either way, that is what several binary tools implemented over a long time, and we can't now willy-nilly change that without some allowance period for that change. I will note that the original report from Rainer here was about a mere perceived inconsistency in objdump output, not any specific breakage. It seems a disservice to fix that confusion by introducing real breakage with other linkers. So, IMHO: we (GNU ld) should continue to emit NDX_GLOBAL for the undefined symbols, as we had. Other linkers should be changed to not particularly care for either NDX_LOCAL or NDX_GLOBAL for undefined symbols (GNU ld already is fine with NDX_LOCAL on such syms). Then, wait a release, then change GNU ld to emit NDX_LOCAL.
(In reply to H.J. Lu from comment #31) > Rename to VER_NDX_NONE may be too late. But > > https://docs.oracle.com/en/operating-systems/solaris/oracle-solaris/11.4/ > linkers-libraries/version-symbol-section.html > > can be updated to clarify what version index 0 really means for unversioned > global/weak defined/undefined symbols. I apologize for not spotting this last week when we first discussed this, but I think the Solaris docs do spell out the details for index 0 pretty thoroughly. It's true that the table says simply "Symbol has local scope", and that text goes back to the original implementation, but that table is followed by a bulleted list that gets into the details: > A symbol may be assigned the special reserved > index 0. This index can be assigned for any of > the following reasons. > > - A non-global symbol is always assigned VER_NDX_LOCAL. > However, this is rare in practice. Versioning sections > are usually created only in conjunction with the dynamic > symbol table, .dynsym, which only contains global symbols. > > - A global symbol defined within an object that does not > have a SHT_SUNW_verdef version definition section. > > - An undefined global symbol defined within an object that > does not have a SHT_SUNW_verneed version dependency section. > Or, an undefined global symbol defined within an object in > which the version dependency section does not assign version > indexes. > > - The first entry of a symbol table is always NULL. This > entry always receives VER_NDX_LOCAL, however the value > has no particular meaning. The third bullet ("An undefined global symbol") covers the situation we've been discussing, and it seems this discussion has been retracing those steps. This text is not found in the S9 version of the Solaris LLM references earlier, nor in the Solaris 10 FCS version from 2005, but is found in the Solaris 11 FCS version from 2011. That's why I didn't spot it earlier --- my response was based on reading the cited link, and clearly I should have looked at the latest docs as well. However, there's a decent chance that I wrote this addition (between 2005 and 2011, when S11 shipped, so 16-20 years ago), so it's probably useful that we've independently verified what it's saying.
I should have said above that the appearance of that added explanatory text came after the GNU implementation had already been written, so this isn't a case where something obvious was ignored. Rather, it's that documentation was later bolstered to more accurately capture what was intended, but the GNU implementation was already out, and it took a very long time for anyone to notice the mismatch.
(In reply to Michael Matz from comment #35) > Well, citing a Sun specification from the 90's is relevant, because Ali used > the > ARCs to point at the original intent of NDX_LOCAL (and it being more > along the line of "unknown version"), and the docu shows that that wasn't > explicitely put down externally back then (i.e. added only later there). > > Now, whatever the original intent may have been (and I of course agree, it > absolutely makes sense to use '0' as the nothing-known index for mere > references), it wasn't what was written in most places, which only was > > NDX_LOCAL - local scope [full stop] > > . There really isn't much wiggle room in interpreting that single line (and > to remind, the GNU version stuff was designed in large part by getting > inspired by relevant and available Sun docs). Either way, that is what > several binary tools > implemented over a long time, and we can't now willy-nilly change that > without ld and ld.so in glibc never use VER_NDX_LOCAL to change symbol binding.
The master branch has been updated by H.J. Lu <hjl@sourceware.org>: https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;h=e891646a32ac9adcff944d05b14054523f752728 commit e891646a32ac9adcff944d05b14054523f752728 Author: H.J. Lu <hjl.tools@gmail.com> Date: Sat Nov 15 06:59:56 2025 +0800 gold: Treat symbols with version index 0 as unversioned Oracle Solaris 11.4 Linker and Libraries Guide: https://docs.oracle.com/en/operating-systems/solaris/oracle-solaris/11.4/linkers-libraries/version-symbol-section.html defines VER_NDX_LOCAL to 0 with a comment, "Symbol has local scope". This leads to different interpretations by different linker implementations. However Solaris as well as ld and ld.so in glibc always treat symbols with version index 0 as unversioned symbols with global binding. As discussed in https://sourceware.org/bugzilla/show_bug.cgi?id=33577 in hindsight, VER_NDX_NONE might be a better name. Ali from Oracle is working on clarifying what version index 0 really means for unversioned symbols with global binding. In the meantime, update gold to treat symbols with version index 0 as unversioned with global binding. elfcpp/ PR gold/33577 * elfcpp.h (VER_NDX_LOCAL): Update comments. (VER_NDX_GLOBAL): Likewise. gold/ PR gold/33577 * dynobj.cc (Versions::symbol_section_contents): Set unversioned symbol version index to VER_NDX_LOCAL. * symtab.cc (Symbol_table::add_from_dynobj): Don't check VER_NDX_LOCAL. * testsuite/Makefile.am (check_SCRIPTS): Add ver_test_pr33577.sh. (check_DATA): Add ver_test_pr33577a.syms and ver_test_pr33577b.syms. (ver_test_pr33577a.syms): New rule. (ver_test_pr33577.so): Likewise. (ver_test_pr33577b.syms): Likewise. (ver_test_pr33577): Likewise. * testsuite/Makefile.in: Regenerated. * testsuite/ver_matching_test.sh: Updated to checking missing Base version. * testsuite/ver_test_14.sh (check_missing): New. Updated to check missing Base version. * testsuite/ver_test_pr33577.sh: New fille. * testsuite/ver_test_pr33577a.c: Likewise. * testsuite/ver_test_pr33577b.c: Likewise. Signed-off-by: H.J. Lu <hjl.tools@gmail.com>
(In reply to H.J. Lu from comment #38) > (In reply to Michael Matz from comment #35) > > inspired by relevant and available Sun docs). Either way, that is what > > several binary tools > > implemented over a long time, and we can't now willy-nilly change that > > without > > ld and ld.so in glibc never use VER_NDX_LOCAL to change symbol binding. And your point is that these are the only binary tools that exist? Or why do you mention that?
People in the wild already noticed the problem with ld.bfd now producing incorrect output (according to https://refspecs.linuxfoundation.org/LSB_3.0.0/LSB-PDA/LSB-DA.junk/symversion.html , not some Sun document that, strictly speaking, isn't the gosple for the GNU variant of symbol versioning): https://bugzilla.redhat.com/show_bug.cgi?id=2415065 . I find the current way of dealing with this fairly reckless :-/
(In reply to Ali Bahrami from comment #36) > I apologize for not spotting this last week when we > first discussed this, but I think the Solaris docs > do spell out the details for index 0 pretty thoroughly. We noted that here: https://sourceware.org/pipermail/binutils/2025-November/145608.html with reply here: https://sourceware.org/pipermail/binutils/2025-November/145643.html . > The third bullet ("An undefined global symbol") covers the > situation we've been discussing, and it seems this discussion > has been retracing those steps. > > This text is not found in the S9 version of the Solaris LLM > references earlier, nor in the Solaris 10 FCS version from 2005, > but is found in the Solaris 11 FCS version from 2011. That's why > I didn't spot it earlier --- my response was based on reading > the cited link, and clearly I should have looked at the latest > docs as well. However, there's a decent chance that I wrote this > addition (between 2005 and 2011, when S11 shipped, so 16-20 years > ago), so it's probably useful that we've independently verified > what it's saying. I don't think any independendness comes into play here. The ones arguing for changing the GNU versioning implementation ultimately always come from reading the current Solaris docu, i.e. with the clarifications of NDX_LOCAL. Btw, people also try to read into it that it's fine to use NDX_LOCAL also for _defined_ symbols. AFAICS even the clarification in the Solaris>=11 docu only accepts that when there's no verdef section in the defining file. What's your take on that? Is a NDX_LOCAL, defined STB_GLOBAL symbol in a file that has the verdef section (GNU or Sun variant) available to resolve against, or not? Should it be?
(In reply to Michael Matz from comment #42) > We noted that here: > https://sourceware.org/pipermail/binutils/2025-November/145608.html > with reply here: > https://sourceware.org/pipermail/binutils/2025-November/145643.html . I see, and I agree with the comment in the second one that those words were a relatively later addition, though they do reflect how the Solaris ld has always done it. My fuzzy memory of this is that I added them while working on PSARC 2008/603 ELF objects to adopt GNU-style Versym indexes The Sun style originally did not put version indexes for externally defined symbols into the versym section, setting those values all to 0, so while one could deduce that an object had a version dependency on another object, we could not discern specifically which symbols were responsible. We decided to adopt that part of the GNU extensions, and in doing that work, it dawned on me that index 0 has implications beyond local symbols, leading to questions, and a doc update. > I don't think any independendness comes into play here. The ones arguing > for changing the GNU versioning implementation ultimately always come from > reading the current Solaris docu, i.e. with the clarifications of NDX_LOCAL. Great to know. I continue to be grateful for how closely GNU has stuck to ELF, as well as to these Sun additions, but I try not to take it for granted. > Btw, people also try to read into it that it's fine to use NDX_LOCAL also > for _defined_ symbols. AFAICS even the clarification in the Solaris>=11 > docu only accepts that when there's no verdef section in the defining file. The use of NDX_LOCAL for globals is very limited. One might might interpret this as a license: - A global symbol defined within an object that does not have a SHT_SUNW_verdef version definition section. However, I think that rule is intended to describe a dependency that doesn't have a verdef. Note that the text below this list goes on to say: Versions defined by an object are assigned version indexes starting at 1 and incremented by 1 for each version. Index 1 is reserved for the first global version. If the object does not have a SHT_SUNW_verdef version definition section, then all the global symbols defined by the object receive index 1. If the object does have a version definition section, then VER_NDX_GLOBAL simply refers to the first such version Forgive the verbosity, but a concrete example might be useful. This hello world program is not itself versioned, but it links to libc, which is, and so, all of its own definitions end up in version 1 even though it, itself, does not have a verdef, % cc hello.c % elfdump -v a.out Version Needed Section: .SUNW_version index file version [2] libc.so.1 SYSVABI_1.3 % elfdump -sN.dynsym a.out Symbol Table Section: .dynsym index value size type bind oth ver shndx name [0] 0 0 NOTY LOCL D 0 UNDEF [1] 0x8060d7c 0x4 OBJT GLOB X 1 .data __xargc [2] 0x8050968 0 FUNC GLOB D 2 UNDEF printf [3] 0x8050918 0 OBJT GLOB D 1 .plt _PROCEDURE_LINKAGE_TABLE_ [4] 0x8060d5c 0x4 OBJT WEAK X 1 .data environ [5] 0x8060c04 0 OBJT GLOB D 1 .dynamic _DYNAMIC [6] 0x8060db0 0 OBJT GLOB D 1 .data _edata [7] 0x8060d78 0x4 OBJT GLOB X 1 .data ___Argv [8] 0x8050b90 0x1b FUNC GLOB D 1 .init _init [9] 0x8050bc7 0 OBJT GLOB D 1 .fini _etext [10] 0 0 NOTY GLOB D 1 ABS __fsr_init_value [11] 0x8050b60 0x2f FUNC GLOB D 1 .text main [12] 0x8060d5c 0x4 OBJT GLOB X 1 .data _environ [13] 0x8060bc8 0 OBJT GLOB P 1 .got _GLOBAL_OFFSET_TABLE_ [14] 0x8060d80 0x4 OBJT GLOB X 1 .data __xargv [15] 0x8060da8 0x4 OBJT GLOB D 1 .data __xpg4 [16] 0x80508fc 0x4 OBJT GLOB X 1 .rodata _lib_version [17] 0x8050958 0 FUNC GLOB D 2 UNDEF _exit [18] 0x8050948 0 FUNC GLOB D 2 UNDEF atexit [19] 0x8050928 0 FUNC GLOB D 2 UNDEF __fpstart [20] 0x8060dac 0x4 OBJT GLOB D 1 .data __xpg6 [21] 0x8050938 0 FUNC GLOB D 2 UNDEF exit [22] 0x8060db0 0 OBJT GLOB D 1 .data _end [23] 0x8050980 0x86 FUNC GLOB X 1 .text _start [24] 0x8050bac 0x1b FUNC GLOB D 1 .fini _fini [25] 0x8060d60 0x18 OBJT GLOB X 1 .data __environ_lock [26] 0x8060d84 0x4 OBJT GLOB X 1 .data __longdouble_used As long as versioning is in play, global definitions that are not otherwise directed to a different version via a mapfile/linker-script, end up in version 1. And since nearly everything has a dependency on libc, which is versioned, it's hard to make an object on Solaris that doesn't assign these cases to index 1 Note that Rainer manages some modern Solaris systems in the gcc test farm, and it should be possible to get shell access in order to poke at these details if that would be useful. > What's your take on that? Is a NDX_LOCAL, defined STB_GLOBAL symbol in > a file that has the verdef section (GNU or Sun variant) available to resolve > against, or not? Should it be? I'm not sure those symbols are useful. Consider the case that Rainer presented at the start of this, a weak reference, for which no definition was found during the link-edit. If symbol resolution is done for it, nothing will be found. However, it seems that we do support it. % cc -m64 -Kpic -G -o vers33.so vers33.c -Mvers33.map % elfdump -L vers33.so Procedure Linkage Table Section: .plt index addr GOT-index GOT-addr [1] 0x960 [4] 0x100a08 show_undef_weak % dis -F foo vers33.so disassembly for vers33.so foo() foo: 55 pushq %rbp foo+0x1: 48 8b ec movq %rsp,%rbp foo+0x4: 48 8b 05 75 00 10 movq +0x100075(%rip),%rax <0x100a00> 00 foo+0xb: 48 85 c0 testq %rax,%rax foo+0xe: 74 05 je +0x5 <foo+0x15> foo+0x10: e8 cb ff ff ff call -0x35 <0x960> foo+0x15: c9 leave I created a hello world program to call foo(), and debugging diagnostics show that we chase it and fail to find anything: % LD_DEBUG=symbols ./a.out |& grep show_undef 09599: symbol=show_undef_weak; lookup in file=a.out [ ELF ] 09599: symbol=show_undef_weak; lookup in file=./vers33.so [ ELF ] 09599: symbol=show_undef_weak; lookup in file=/lib/64/libc.so.1 [ ELF ] I think this says that such symbols are resolved, but there's likely no resulting benefit. ----- I'd also like to say that I fully agree that these changes to the GNU toolchain do need to be phased in over time, seeding the capability and then waiting for awhile before switching over to using it. The chaos of an abrupt switch for such a small thing just isn't worth the cost, but it would be nice to get there gradually. Our original ask, that is be changed immediately, was due to thinking it was just a minor bookkeeping matter. We've since been educated to its much larger impact.
ld.so in glibc always determines bindings of VER_NDX_LOCAL symbols by st_other. Linux linkers should be the same.
The old spec specifies symbols with version index 0 to local binding. However, it is never implemented on Solaris, ld nor ld.so in Linux. How about adding a linker option to use version index 1 for unversioned defined symbol? In the meantime, all Linux linkers should be changed to follow ld and ld.so.
(In reply to Michael Matz from comment #41) > People in the wild already noticed the problem with ld.bfd now producing > incorrect output (according to > https://refspecs.linuxfoundation.org/LSB_3.0.0/LSB-PDA/LSB-DA.junk/ > symversion.html , not some Sun document that, strictly speaking, > isn't the gosple for the GNU variant of symbol versioning): > https://bugzilla.redhat.com/show_bug.cgi?id=2415065 . > > I find the current way of dealing with this fairly reckless :-/ https://bugzilla.redhat.com/show_bug.cgi?id=2415065 is an instance that GNU ld starts to generates unversioned defined symbols of index 0 in the dynamic symbol table, which gets rejected by LLD (since at least 2018). LLD probably should emit a warning in such a case. This GNU ld behavior change for defined symbols is unexpected (should not emit localized symbols to .dynsym - at least in the general case, is there an exception for ia64 (unsupported by llvm)?) and we should request a revert.
It's especially a problem as new Rust defaults to lld use. Can we have a revert please until we have things figured out?
On behalf of mold users, I’d like to second Sam’s revert request. This is an abrupt change that would break many users’ development environments. I hope we can arrive at a practical decision.
(In reply to Ali Bahrami from comment #43) > % cc hello.c > % elfdump -v a.out > > Symbol Table Section: .dynsym > index value size type bind oth ver shndx name > [11] 0x8050b60 0x2f FUNC GLOB D 1 .text main In the current git of GNU ld, all the above exported symbols (we have to use -E here to export globals from executables) are now VER_NDX_LOCAL: Symbol table '.dynsym' contains 12 entries: Num: Value Size Type Bind Vis Ndx Name ... 11: 0000000000401136 50 FUNC GLOBAL DEFAULT 13 main ... Version symbols section '.gnu.version' contains 12 entries: Addr: 0x00000000004005be Offset: 0x000005be Link: 5 (.dynsym) 000: 0 (*local*) 2 (GLIBC_2.34) 3 (GLIBC_2.2.5) 0 (*local*) 004: 0 (*local*) 0 (*local*) 0 (*local*) 0 (*local*) 008: 0 (*local*) 0 (*local*) 0 (*local*) 0 (*local*) > As long as versioning is in play, global definitions that > are not otherwise directed to a different version via a > mapfile/linker-script, end up in version 1. Agreed. However that's not what the current git implementation of the fix for the initially reported problem here does. It _always_ uses NDX_LOCAL for everything not versioned, undefined or defined, independend of symbols binding. I think there should have been no change whatsoever for defined symbols.
(In reply to H.J. Lu from comment #44) > ld.so in glibc always determines bindings of VER_NDX_LOCAL symbols by > st_other. > Linux linkers should be the same. Or you could just admit that ld.so was incompletely implementing the spec by not looking at the version index when its there. (In reply to H.J. Lu from comment #45) > The old spec specifies symbols with version index 0 to local binding. > However, it is never implemented on Solaris, ld nor ld.so in Linux. But by _everything_ except ld.bfd and glibc ld.so. And > How about adding a linker option to use version index 1 for unversioned > defined symbol? I don't see how a linker option to not produce buggy output is useful. But perhaps one to do produce buggy output. I.e. sure, but default to use NDX_GLOBAL for defined STB_GLOBAL symbols. > In the meantime, all Linux linkers should be changed to follow ld and ld.so. Why? The others follow the spec.
So the below would revert the questionable behaviour, i.e. return back to generating version index VER_NDX_GLOBAL for defined global symbols (using index 0 for undefined symbols, the point of this bug report, remains unchanged). To not regress the testsuite as is, it would also need reversion of 2be0f2da ("readelf: Display the base symbol version as empty string") that always prints "foo@" for VER_NDX_GLOBAL symbols. That could also be done by adjusting some of the testcases to accept that syntax. --- Use version index 1 for defined symbols It's reasonable to use version index 0 for undefined symbols, so let's continue doing that. For defined (global) symbols that aren't otherwise versioned continue using VER_NDX_GLOBAL (partly reverting behaviour introduced in commit f685e395). bfd/ PR ld/33577 * elflink.c (elf_link_output_extsym): Don't set noversion for defined syms. ld/ PR ld/33577 * ld-elfvers/vers16.dsym: Add back the "Base" version for defined syms. diff --git a/bfd/elflink.c b/bfd/elflink.c index ec3ad9735fe..7d284c08c00 100644 --- a/bfd/elflink.c +++ b/bfd/elflink.c @@ -11158,13 +11158,7 @@ elf_link_output_extsym (struct bfd_hash_entry *bh, void *data) else { if (h->verinfo.vertree == NULL) - { - iversym.vs_vers = 1; - if (elf_tdata (flinfo->output_bfd)->cverdefs == 0) - /* Defined symbol has no version if there is no - linker version script. */ - noversion = true; - } + iversym.vs_vers = 1; else iversym.vs_vers = h->verinfo.vertree->vernum + 1; if (flinfo->info->create_default_symver) diff --git a/ld/testsuite/ld-elfvers/vers16.dsym b/ld/testsuite/ld-elfvers/vers16.dsym index 076d0eb07ed..a32abd7aad9 100644 --- a/ld/testsuite/ld-elfvers/vers16.dsym +++ b/ld/testsuite/ld-elfvers/vers16.dsym @@ -1,2 +1,2 @@ -[0-9a-f]+ g +DF (\.text|\.opd|\*ABS\*) [0-9a-f]+ +(0x[0-9a-f]+ )?_?show_bar +[0-9a-f]+ g +DF (\.text|\.opd|\*ABS\*) [0-9a-f]+( +Base +)? +(0x[0-9a-f]+ )?_?show_bar [0-9a-f]+ +DF \*UND\* [0-9a-f]+ +(0x[0-9a-f]+ )?_?show_foo
(In reply to Michael Matz from comment #51) > So the below would revert the questionable behaviour, i.e. return back to > generating version index VER_NDX_GLOBAL for defined global symbols (using > index > 0 for undefined symbols, the point of this bug report, remains unchanged). > > To not regress the testsuite as is, it would also need reversion of > 2be0f2da ("readelf: Display the base symbol version as empty string") > that always prints "foo@" for VER_NDX_GLOBAL symbols. That could also be > done > by adjusting some of the testcases to accept that syntax. That would be best, imo. Okay in that shape.
I also think that we should revert back to the original behaviour. Or ... provide a new linker option that enables the new behaviour, but which defaults to the old behaviour. This default could be changed in the future once agreement is reached and all the linkers are able to handle the new behaviour. H.J. / Michael - please could one of you make this happen ?
https://sourceware.org/pipermail/binutils/2025-November/145783.html for the series (my mailer unfortunately overwrote the message-id and references header, so the four messages don't thread).
The regression: When a shared object uses versioned symbols from its dependencies but omits a version script, its exported defined symbols are incorrectly indexed as 0 (should be index 1). The solution: Michael's patch should resolve this. While reverting the preceding change might be simpler, Michael has done the necessary work to implement the proper code fix. --- I don't think a new option is justified to select index 0 / 1 for unversioned undefined symbols.
The master branch has been updated by H.J. Lu <hjl@sourceware.org>: https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;h=beab972c07d53ba1b0cafc2d16420c40a799bbfd commit beab972c07d53ba1b0cafc2d16420c40a799bbfd Author: H.J. Lu <hjl.tools@gmail.com> Date: Sat Nov 15 11:43:45 2025 +0800 elf: Verify base symbol version works properly Add a testcase to verify that VER_NDX_GLOBAL can be used with asm (".symver foo_base, foo@"); to create the base version to provide an unversioned compatible symbol. PR ld/33577 * testsuite/ld-elfvers/pass.out: New file. * testsuite/ld-elfvers/pr33577-unversioned.c: Likewise. * testsuite/ld-elfvers/pr33577-unversioned.rd: Likewise. * testsuite/ld-elfvers/pr33577-versioned.c: Likewise. * testsuite/ld-elfvers/pr33577-versioned.rd: Likewise. * testsuite/ld-elfvers/pr33577.map: Likewise. * testsuite/ld-elfvers/pr33577a.c: Likewise. * testsuite/ld-elfvers/pr33577b.c: Likewise. * testsuite/ld-elfvers/vers.exp: Run ld/33577 tests. Signed-off-by: H.J. Lu <hjl.tools@gmail.com>
The master branch has been updated by Michael Matz <matz@sourceware.org>: https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;h=78894bcb483d45c4d7be5ef9499ec017a9727079 commit 78894bcb483d45c4d7be5ef9499ec017a9727079 Author: Michael Matz <matz@suse.de> Date: Wed Nov 19 16:32:51 2025 +0100 Use version index 1 for defined symbols It's reasonable to use version index 0 for undefined symbols, so let's continue doing that. For defined (global) symbols that aren't otherwise versioned continue using VER_NDX_GLOBAL (partly reverting behaviour introduced in commit f685e395). bfd/ PR ld/33577 * elflink.c (elf_link_output_extsym): Don't set noversion for defined syms. ld/ PR ld/33577 * ld-elfvers/vers16.dsym: Add back the "Base" version for defined syms.
The master branch has been updated by H.J. Lu <hjl@sourceware.org>: https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;h=94ebb7810ebd966aa857aa55c5bdc34382f4572b commit 94ebb7810ebd966aa857aa55c5bdc34382f4572b Author: H.J. Lu <hjl.tools@gmail.com> Date: Fri Nov 28 08:05:18 2025 +0800 elf: Properly place base symbols in DT_GNU_HASH and DT_HASH The base symbol, a symbol with the empty version string, created by asm (".symver foo_base, foo@"); is used to provide a compatibility symbol in a versioned shared library for binaries linked against the previous unversioned shared library. The dynamic linker will pick the first match to resolve the unversioned symbol reference. If the newest version, VERS_1, asm (".symver foo_v1, foo@@VERS_1"); is picked before the base symbol in DT_GNU_HASH and DT_HASH, foo@@VERS_1, instead of foo@, will be used to resolve the unversioned reference. Properly place base symbols in DT_GNU_HASH and DT_HASH so that they will be picked first. Also check defined function symbol, foo, and undefined function symbol, bar, separately to support different dynamic symbol orders. bfd/ PR ld/33577 PR ld/33673 * elf-bfd.h (elf_link_hash_entry): Add base_symbol. (elf_link_hash_table): Add has_base_symbols. * elflink.c (_bfd_elf_merge_symbol): Set base_symbol and has_base_symbols if the version string is empty. (collect_gnu_hash_codes): Add base_symbol. (elf_gnu_hash_process_symidx): Skip if base symbol doesn't match. (bfd_elf_size_dynsym_hash_dynstr): If there are base symbols, output base symbols first in DT_GNU_HASH. (elf_outext_info): Add base_symbol. (elf_link_output_extsym): Skip if base symbol doesn't match. (_bfd_elf_final_link): If there are base symbols, output base symbols last in DT_HASH. ld/ PR ld/33577 PR ld/33673 * testsuite/ld-elfvers/pr33577-unversioned.rd: Removed. * testsuite/ld-elfvers/pr33577-versioned.rd: Likewise. * testsuite/ld-elfvers/pr33577-unversioned-a.rd: New file. * testsuite/ld-elfvers/pr33577-unversioned-b.rd: Likewise. * testsuite/ld-elfvers/pr33577-versioned-a.rd: Likewise. * testsuite/ld-elfvers/pr33577-versioned-b.rd: Likewise. * ld-elfvers/vers.exp (base_symbol_test): New. Run PR ld/33673 tests. Signed-off-by: H.J. Lu <hjl.tools@gmail.com>
The master branch has been updated by Rainer Orth <ro@sourceware.org>: https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;h=87997f8b4f5d348557fba410c966bd72d3670c1a commit 87997f8b4f5d348557fba410c966bd72d3670c1a Author: Rainer Orth <ro@CeBiTec.Uni-Bielefeld.DE> Date: Sat Jan 24 08:02:14 2026 +0100 ld: testsuite: Skip pr33577 tests with GNU extensions on Solaris [PR33577] Several of the ld-elfvers pr33577 tests FAIL on Solaris, for either or both of two reasons: * Tests using ld --hash-style=gnu cannot work on Solaris: .gnu.hash/SHT_GNU_HASH sections are a GNU extension not supported by Solaris ld.so.1. * Similarly, binding different implementations of the same symbol to different symbol versions is a GNU extension that wasn't in the original Solaris specification of symbol versioning. ld.so.1 doesn't support it and never will. This can be seen in the elfdump output for the .dynsym section: Symbol Table Section: .dynsym index value size type bind oth ver shndx name [8] 0x630 0xd FUNC GLOB D 1H .text foo [10] 0x620 0x6 FUNC GLOB D 2 .text foo foo is bound to both version 1 (the Base version) and version 2 (VERS_1 from pr33577.map). Same for .symtab: Symbol Table Section: .symtab index value size type bind oth ver shndx name [28] 0x620 0x6 FUNC GLOB D 0 .text foo [35] 0x630 0xd FUNC GLOB D 0 .text foo@ As I said, ld.so.1 doesn't support <symbol>@<version> (in this case the Base version) at all. Therefore the tests that employ those extensions are guarded with supports_gnu_osabi. Tested on sparc{,v9}-sun-solaris2.11, sparc{,64}-unknown-linux-gnu, {i386,amd64}-pc-solaris2.11, and {x86_64,i686}-pc-linux-gnu. 2026-01-23 Rainer Orth <ro@CeBiTec.Uni-Bielefeld.DE> ld: PR ld/33577 * testsuite/ld-elfvers/vers.exp (base_symbol_test): Only run pr33577a with libpr33577-versioned.so test on ELFOSABI_GNU systems. Likewise for run base_symbol_tests with --hash-style=gnu.