Consider a.out with the following dependency: readelf -Ws a.out | grep foo 5: 0000000000000000 0 FUNC WEAK DEFAULT UND foo@FOO_1 (3) 54: 0000000000000000 0 FUNC WEAK DEFAULT UND foo@@FOO_1 Since this is a weak dependency, one may expect the program to run, but it doesn't: ./a.out: ./foo-run.so: no version information available (required by ./a.out) ./a.out: relocation error: ./a.out: symbol foo version FOO_1 not defined in file ./foo-run.so with link time reference Repro steps: // foo-link.c int foo() { return 42; } int bar() { return 24; } // foo-run.c int bar() { return 1; } // main.c #include <stdio.h> extern int foo() __attribute__((weak)); int main() { if (&foo == NULL) printf("foo is not defined\n"); else printf("foo returns %d\n", foo()); return 0; } // foo-link.lds FOO_1 { global: foo; bar; }; gcc -fPIC -shared -o foo-link1.so foo-link.c -Wl,--soname=./foo-run.so gcc -fPIC -shared -o foo-link2.so foo-link.c -Wl,--version-script=foo-link.lds -Wl,--soname=./foo-run.so gcc -fPIC -shared -o foo-run.so foo-run.c gcc main.c ./foo-link1.so -o a.out1 gcc main.c ./foo-link2.so -o a.out2 ./a.out1 foo is not defined ./a.out2 ./a.out2: ./foo-run.so: no version information available (required by ./a.out) ./a.out2: relocation error: ./a.out: symbol foo version FOO_1 not defined in file ./foo-run.so with link time reference
Did your linker emit a weak version reference (as opposed to a weak symbol reference)?
(In reply to Florian Weimer from comment #1) > Did your linker emit a weak version reference (as opposed to a weak symbol > reference)? Probably not. How would I check? The linker used is "GNU ld (GNU Binutils for Debian) 2.30". readelf --all a.out2 | grep FOO 000000200fe0 000500000006 R_X86_64_GLOB_DAT 0000000000000000 foo@FOO_1 + 0 5: 0000000000000000 0 FUNC WEAK DEFAULT UND foo@FOO_1 (3) 54: 0000000000000000 0 FUNC WEAK DEFAULT UND foo@@FOO_1 004: 2 (GLIBC_2.2.5) 3 (FOO_1) 0 (*local*) 0 (*local*) 0x0010: Name: FOO_1 Flags: none Version: 3
(In reply to Paul Pluzhnikov from comment #0) > Consider a.out with the following dependency: > > readelf -Ws a.out | grep foo > 5: 0000000000000000 0 FUNC WEAK DEFAULT UND foo@FOO_1 (3) This is .dynsym reference. This is the important part for binding. You have a weak versioned reference (as Florian notes). > 54: 0000000000000000 0 FUNC WEAK DEFAULT UND foo@@FOO_1 This is the .symtab reference. Not generally useful for binding decisions. The other important information is in the symbol version requirements section: Version needs section '.gnu.version_r' contains 2 entries: Addr: 0x0000000000400428 Offset: 0x000428 Link: 6 (.dynstr) 000000: Version: 1 File: ./foo-run.so Cnt: 1 0x0010: Name: FOO_1 Flags: none Version: 3 0x0020: Version: 1 File: libc.so.6 Cnt: 1 0x0030: Name: GLIBC_2.2.5 Flags: none Version: 2 Which says you *must* have a FOO_1 version defined. > > Since this is a weak dependency, one may expect the program to run, but it > doesn't: What you see has nothing to do with a weak dependency, it has to do with version information and how we encode it and what ld.so requires once you have version information. However, I understand your point being that weak bindings should not fail at runtime. Unfortunately this is not how ELF dynamic binding works. The symbol weakness is no longer relevant at runtime binding. > ./a.out: ./foo-run.so: no version information available (required by ./a.out) > ./a.out: relocation error: ./a.out: symbol foo version FOO_1 not defined in > file ./foo-run.so with link time reference This is exactly the error you should expect for GNU versioned symbol support. Keep in mind this error is not 100% required, we emit the error because of the following: * The binary has version requirements. * The library does not meet those version requirements. Given that it looks like you linked against a new versioned library, and then ran on an old unversioned library, we issue the error to avoid the use case of a user running on an old library and failing. If you have a use case that needs this to work you should argue this on libc-alpha and discuss the situation and why we should remove this restriction. The alternative implementation would be to special case the weak symbols in some way, and not trigger the error for them. However, at runtime weak symbols are not relevant, either the symbol is defined or not is what gives the value of 0 or not-0. It's only during static link that the weak symbols are treated with their particular semantics. We have a legacy interpretation of the ELF standard in glibc which meant we used to consider weakness at runtime in some cases, but this has been dropped and can be enabled with LD_DYNAMIC_WEAK, thought it doesn't impact your case. I'm just pointing out that we no longer want to consider weakness at runtime. > > Repro steps: > > // foo-link.c > int foo() { return 42; } > int bar() { return 24; } > > // foo-run.c > int bar() { return 1; } > > // main.c > #include <stdio.h> > > extern int foo() __attribute__((weak)); > > int main() > { > if (&foo == NULL) > printf("foo is not defined\n"); > else > printf("foo returns %d\n", foo()); > > return 0; > } > > // foo-link.lds > FOO_1 { > global: > foo; > bar; > }; > > > gcc -fPIC -shared -o foo-link1.so foo-link.c -Wl,--soname=./foo-run.so > gcc -fPIC -shared -o foo-link2.so foo-link.c > -Wl,--version-script=foo-link.lds -Wl,--soname=./foo-run.so > gcc -fPIC -shared -o foo-run.so foo-run.c > gcc main.c ./foo-link1.so -o a.out1 > gcc main.c ./foo-link2.so -o a.out2 > > > ./a.out1 > foo is not defined How did you get this result? Is this x86_64? I would have expected a segfault. The weak-ref-and-check idiom doesn't work in executables. It works in libraries or -fPIC -pie binaries. In your case the symbol will always have an address value because it's the value of the reserved PLT entry. So it should immediately segfault because &foo is <foo@plt> but it has a value of zero when called (undefined symbol). This may be an x86_64 quirk, but we can't change it now. I'm surprised you get "foo is not defined", could you please double check your results? e.g. [carlos@koi swbz24718]$ ./a.out1 Segmentation fault (core dumped) (gdb) p &foo $1 = (<text variable, no debug info> *) 0x401050 <foo@plt> Dump of assembler code for function foo@plt: => 0x0000000000401050 <+0>: jmpq *0x2fd2(%rip) # 0x404028 <foo@got.plt> 0x0000000000401056 <+6>: pushq $0x2 0x000000000040105b <+11>: jmpq 0x401020 (Used LD_BIND_NOW=1 to get early binding to see which got slot has the value) (gdb) x/8x 0x404028 0x404028 <foo@got.plt>: 0x00000000 0x00000000 0x00000000 0x00000000 0x404038: 0x00000000 0x00000000 0x00000000 0x00000000 Again, weak-ref-and-check only works in a library, or in a binary only with -fPIC -pie. If you link the binary against foo-run.so, which lacks the foo definition, the situation is simpler because no definition exists the linker always assign a value of zero (no possible future interposition is considered). > > ./a.out2 > ./a.out2: ./foo-run.so: no version information available (required by > ./a.out) > ./a.out2: relocation error: ./a.out: symbol foo version FOO_1 not defined in > file ./foo-run.so with link time reference Completely expected. Once you have version information you can't run without it. In summary: - The weak status of symbol is not relevant to ELF dynamic binding. - Version requirements must be met or errors are raised by the dynamic loader. Does that answer your questions?
(In reply to Carlos O'Donell from comment #3) > What you see has nothing to do with a weak dependency, it has to do with > version information and how we encode it and what ld.so requires once you > have version information. Perhaps the static linker shouldn't demand FOO_1 version IFF the dependency on that symbol is weak? > If you have a use case that needs this to work you should argue this on > libc-alpha and discuss the situation and why we should remove this > restriction. The use case here is that we unintentionally versioned a symbol that we added. Our binaries intended to check at runtime whether the symbol is defined or not, and only call it if it is. This worked, until we added the version. Once we did, the binaries hard-fail at startup. > How did you get this result? Is this x86_64? I would have expected a > segfault. This is x86_64, and my compiler defaults to PIE. But I just tried with -no-pie, and it still worked. It also worked when built for i686, with and without -no-pie. > In your case the symbol will always have an address value because it's the > value of the reserved PLT entry. So it should immediately segfault because > &foo is <foo@plt> but it has a value of zero when called (undefined symbol). But I am not calling it; only checking its address.
(In reply to Paul Pluzhnikov from comment #4) > (In reply to Carlos O'Donell from comment #3) > > > What you see has nothing to do with a weak dependency, it has to do with > > version information and how we encode it and what ld.so requires once you > > have version information. > > Perhaps the static linker shouldn't demand FOO_1 version IFF the dependency > on that symbol is weak? I don't think that's a good idea. You would lose the version information which is required to correctly resolve the final symbol if multiple versions are present. > > If you have a use case that needs this to work you should argue this on > > libc-alpha and discuss the situation and why we should remove this > > restriction. > > The use case here is that we unintentionally versioned a symbol that we > added. You are stuck with that. It is your new ABI. Congratulations :-) > Our binaries intended to check at runtime whether the symbol is defined or > not, and only call it if it is. > This worked, until we added the version. Once we did, the binaries hard-fail > at startup. > > > How did you get this result? Is this x86_64? I would have expected a > > segfault. > > This is x86_64, and my compiler defaults to PIE. I'm still surprised this works. What compiler and binutils are you using? > But I just tried with -no-pie, and it still worked. Can you provide the output of objdump -ldr for main? > It also worked when built for i686, with and without -no-pie. I wouldn't expect it. > > In your case the symbol will always have an address value because it's the > > value of the reserved PLT entry. So it should immediately segfault because > > &foo is <foo@plt> but it has a value of zero when called (undefined symbol). > > But I am not calling it; only checking its address. If the address is always non-null, because the address of the symbol is the address of the PLT slot then you *do* call it via "printf("foo returns %d\n", foo());", and that call sequence jumps to a NULL address stored in the got and crashes. I don't like the existing x86_64/i686 implementation of pointing the address of the symbol to the PLT slot in the binary, but that's what I expect it to do. I'll have to go back and look at older versions of binutils to see if this has historically changed. In 2.31.1 I get: /home/carlos/support/swbz24718/main.c:7 40114a: b8 50 10 40 00 mov $0x401050,%eax 40114f: 48 85 c0 test %rax,%rax 401152: 75 0c jne 401160 <main+0x1a> 0000000000401050 <foo@plt>: 401050: ff 25 d2 2f 00 00 jmpq *0x2fd2(%rip) # 404028 <foo> 401056: 68 02 00 00 00 pushq $0x2 40105b: e9 c0 ff ff ff jmpq 401020 <.plt> Very clearly loading the address of the PLT into %eax to test it (non-PIE). I can't compiler just -pie, because &foo is a R_X86_64_32 direct reloc against the address of the symbol and can't be resolved. /usr/bin/ld: main.o: relocation R_X86_64_32 against symbol `foo' can not be used when making a PIE object; recompile with -fPIC /usr/bin/ld: final link failed: nonrepresentable section on output collect2: error: ld returned 1 exit status And with -fPIC -pie it's a GOTPCRELX reloc: /home/carlos/support/swbz24718/main.c:7 115d: 48 8b 05 84 2e 00 00 mov 0x2e84(%rip),%rax # 3fe8 <foo> 1164: 48 85 c0 test %rax,%rax 1167: 75 0e jne 1177 <main+0x1e> And 0x3fe8 is the .got slot for foo. That would work as expected.
(In reply to Paul Pluzhnikov from comment #2) > (In reply to Florian Weimer from comment #1) > > Did your linker emit a weak version reference (as opposed to a weak symbol > > reference)? > > Probably not. How would I check? > > The linker used is "GNU ld (GNU Binutils for Debian) 2.30". > > readelf --all a.out2 | grep FOO > 000000200fe0 000500000006 R_X86_64_GLOB_DAT 0000000000000000 foo@FOO_1 + 0 > 5: 0000000000000000 0 FUNC WEAK DEFAULT UND foo@FOO_1 (3) > 54: 0000000000000000 0 FUNC WEAK DEFAULT UND foo@@FOO_1 > 004: 2 (GLIBC_2.2.5) 3 (FOO_1) 0 (*local*) 0 (*local*) > > 0x0010: Name: FOO_1 Flags: none Version: 3 I think readelf is supposed to print the flag if it is present, there is code for that. I also looked at a hexdump of the section, and the flag is just not there. I don't see code in binutils which would set the VER_FLG_WEAK flag on symbol references. I would say this is a static link editor limitation. It has to mark symbol versions as weak if they are only referenced by weak symbols. Recovering this information at run time is very costly: a complete scan of the dynamic symbol table is required.
$ gcc main.c ./foo-link1.so -o a.out1 -no-pie -fno-pie && ./a.out1 Segmentation fault $ gcc main.c ./foo-link1.so -o a.out1 -no-pie && ./a.out1 foo is not defined So I didn't compile with -fno-pie in the case that printed "foo is not defined".
(In reply to Florian Weimer from comment #6) > I would say this is a static link editor limitation. It has to mark symbol > versions as weak if they are only referenced by weak symbols. Ok, I'll update product to "binutils".
(In reply to Paul Pluzhnikov from comment #8) > (In reply to Florian Weimer from comment #6) > > > I would say this is a static link editor limitation. It has to mark symbol > > versions as weak if they are only referenced by weak symbols. > > Ok, I'll update product to "binutils". Again, I'll comment for the record that the "weak" status of the symbol should make no difference at runtime. Please also note that VER_FLG_WEAK was not for weak symbols, but for "weak version definitions" : https://docs.oracle.com/cd/E19957-01/806-0641/6j9vuqujg/index.html#chapter5-16371 That is to say: * If you have a bug fix / performance improvement, which is externally invisible to the binary, you can expose a weak version definition for the bug fix e.g. GLIBC_BZ24718. * Normal binaries record the weak version definition, but if it's not present at runtime they operate normally, and the lack of this weak version definition is ignored. * Binaries that need the bug fix can depend explicitly on the weak version definition which promotes it to a strong version definition and it becomes _required_ at runtime. In summary: - weak symbols are not relevant to dynamic linking. - VER_FLG_WEAK is not for weak symbols but for weak version definitions, and the traditional role for those in Solaris is to demarcate bug fixes and performance changes or other library internal changes. Using it for optional "weak" functions could have unintended behavioural consequences that would need to be reviewed.
(In reply to Paul Pluzhnikov from comment #8) > (In reply to Florian Weimer from comment #6) > > > I would say this is a static link editor limitation. It has to mark symbol > > versions as weak if they are only referenced by weak symbols. > > Ok, I'll update product to "binutils". What do you expect binutils to change?
> VER_FLG_WEAK was not for weak symbols, but for "weak version definitions" That's my understanding too. I was making the same comment when Carlos' comment #9 landed.
<https://www.akkadia.org/drepper/symbol-versioning> says this: vna_flags Bitmask of flags. Currently the following are defined: VER_FLG_WEAK the reference to this version is weak. I believe this is what is implemented in glibc, but it's not bean tested so far because binutils doesn't set this flag. It should be quite safe to introduce this feature in binutils (obviously for GNU only). The Solaris documentation is not helpful at all in this case because our implementation is so very different. Based on reading that documentation, Solaris does not bind individual symbols to versions, only shared object dependencies as a whole. Therefore, the problem that weak symbol references try to solve in Ulrich's approach does not actually arise on Solaris. Instead, you can just instruct the link editor to omit certain symbol version references from the resulting binary, keep a weak (unversioned) symbol reference, and apply a run-time check, as with any weak symbol. Since it is not possible to define multiple symbol versions for the same symbol name on Solaris, restricting the set of bound symbol versions at static link time is safe. (In the GNU implementation, this is unsafe because different versioned symbols can require different declarations and have different behavior in general, so deferring this decision to the link editor does not work.)
(In reply to Florian Weimer from comment #12) > <https://www.akkadia.org/drepper/symbol-versioning> says this: > > vna_flags > Bitmask of flags. Currently the following are defined: > > VER_FLG_WEAK the reference to this version is weak. This specification was written and implemented before the glibc community corrected their interpretation of the ELF standard and introduced LD_DYNAMIC_WEAK to undo similar weak-style changes. That is to say that the VER_FLG_WEAK description in "symbol-vresioning" is based on an incorrect interpretation of the ELF standard. It was based on the idea that you would scan all loaded shared objects for a potentially strong definition that overrode the weak one, and you can see this means lazy loading would be useless in this case because even one weak symbol means you need to load *everything* to confirm no strong symbols override. See "Weak symbols that aren't" https://lists.x.org/archives/xorg/2006-August/017341.html https://sourceware.org/ml/libc-hacker/2000-06/msg00029.html Weak Symbol - Limitations https://en.wikipedia.org/wiki/Weak_symbol#Limitations Thus the only use left for VER_FLG_WEAK is the usage as described in the Solaris documentation. > I believe this is what is implemented in glibc, but it's not bean tested so > far because binutils doesn't set this flag. It should be quite safe to > introduce this feature in binutils (obviously for GNU only). It is because it is a historical implementation. We could deprecate LD_DYANMIC_WEAK and remove lots of code and simplify things. > The Solaris documentation is not helpful at all in this case because our > implementation is so very different. Based on reading that documentation, > Solaris does not bind individual symbols to versions, only shared object > dependencies as a whole. Correct, and if we wanted we could implement the Solaris meaning of VER_FLG_WEAK for "weak version definitions." I don't know how we would instruct a binding between the binary and the "weak version definition", some new linker file construct like Solaris has. > Therefore, the problem that weak symbol references > try to solve in Ulrich's approach does not actually arise on Solaris. Correct, because weak *symbol* references don't exist in Solaris, only references to versions, and those version definitions can be weak. I think the original "symbol-versioning" design simply tried to carry the static linker semantics of weak symbol version processing to the dynamic linker without consideration for the impact it would have on lazy binding. Namely that if you have an undefined weak reference to a symbol, you have to search every object looking for a potential strong reference, instead of stopping at the first definition (weak or strong) that you find based on search order. > Instead, you can just instruct the link editor to omit certain symbol > version references from the resulting binary, keep a weak (unversioned) > symbol reference, and apply a run-time check, as with any weak symbol. > Since it is not possible to define multiple symbol versions for the same > symbol name on Solaris, restricting the set of bound symbol versions at > static link time is safe. (In the GNU implementation, this is unsafe > because different versioned symbols can require different declarations and > have different behavior in general, so deferring this decision to the link > editor does not work.) I don't follow what you're trying to state here. Can you expand on this with an exmaple please?
(In reply to Carlos O'Donell from comment #13) > (In reply to Florian Weimer from comment #12) > > <https://www.akkadia.org/drepper/symbol-versioning> says this: > > > > vna_flags > > Bitmask of flags. Currently the following are defined: > > > > VER_FLG_WEAK the reference to this version is weak. > > This specification was written and implemented before the glibc community > corrected their interpretation of the ELF standard and introduced > LD_DYNAMIC_WEAK to undo similar weak-style changes. That is to say that the > VER_FLG_WEAK description in "symbol-vresioning" is based on an incorrect > interpretation of the ELF standard. It was based on the idea that you would > scan all loaded shared objects for a potentially strong definition that > overrode the weak one, and you can see this means lazy loading would be > useless in this case because even one weak symbol means you need to load > *everything* to confirm no strong symbols override. Wasn't that disagreement about weak *definitions*? For weak references, we still have a long-standing ambiguity if the link editor encounters both weak and non-weak references. I don't think any of this matters at the versioning level because the version reference would be weak if all references are weak. This means that limitations due to relocation types available for a particular architectures etc. do not come into play (which impact what you can implement with the actual symbol bindings). > > I believe this is what is implemented in glibc, but it's not bean tested so > > far because binutils doesn't set this flag. It should be quite safe to > > introduce this feature in binutils (obviously for GNU only). > > It is because it is a historical implementation. We could deprecate > LD_DYANMIC_WEAK and remove lots of code and simplify things. I think it's totally unrelated. LD_DYANMIC_WEAK only affects weak definitions in shared objects. The example does not contain a weak definition. > > The Solaris documentation is not helpful at all in this case because our > > implementation is so very different. Based on reading that documentation, > > Solaris does not bind individual symbols to versions, only shared object > > dependencies as a whole. > > Correct, and if we wanted we could implement the Solaris meaning of > VER_FLG_WEAK for "weak version definitions." I don't know how we would > instruct a binding between the binary and the "weak version definition", > some new linker file construct like Solaris has. We have the SHT_GNU_verneed section referenced from the dynamic segment. The glibc loader processes that, in much the same way as the Solaris loader does with their data structure. The key difference is that our link editor puts only symbol version references there for symbols which are actually referenced by the link. Without that, you would be able to run glibc 2.30 binaries only on systems having glibc 2.30 or later, for example. > I think the original "symbol-versioning" design simply tried to carry the > static linker semantics of weak symbol version processing to the dynamic > linker without consideration for the impact it would have on lazy binding. > Namely that if you have an undefined weak reference to a symbol, you have to > search every object looking for a potential strong reference, instead of > stopping at the first definition (weak or strong) that you find based on > search order. You always have to search all objects for an undefined weak symbol. There is no defined symbol at which the search can stop. > > Instead, you can just instruct the link editor to omit certain symbol > > version references from the resulting binary, keep a weak (unversioned) > > symbol reference, and apply a run-time check, as with any weak symbol. > > Since it is not possible to define multiple symbol versions for the same > > symbol name on Solaris, restricting the set of bound symbol versions at > > static link time is safe. (In the GNU implementation, this is unsafe > > because different versioned symbols can require different declarations and > > have different behavior in general, so deferring this decision to the link > > editor does not work.) > > I don't follow what you're trying to state here. Can you expand on this with > an exmaple please? Solaris has a capability to specify the symbol versions you want to require from a specific soname. See “Specifying a Version Binding” in the documentation you cited, and in particular “If a shared object has been versioned over several software releases, application developers can restrict themselves to the interfaces that were available in a previous software release. Thus, an application can be built using the latest release of the shared object in the knowledge that the application's interface requirements can be met by a previous release of the shared object.”. Since individual symbols do not carry versions in the Solaris implementation, programmers can do this to get rid of the load failure, and avoid relying on specific weak symbols if some version probing (outside the specification) indicates that the installed version does not provide support. (Due to relocation issue, directly probing the weak symbol itself can be problematic, but this is not the point here.) On glibc, nothing like this is possible. If you use .symver to bind to an earlier version of a symbol, then you need to make sure that you meet all the different requirements of that earlier symbol version manually, such as different parameter lists, different struct sizes, and so on.
(In reply to Florian Weimer from comment #14) > (In reply to Carlos O'Donell from comment #13) > > (In reply to Florian Weimer from comment #12) > > > <https://www.akkadia.org/drepper/symbol-versioning> says this: > > > > > > vna_flags > > > Bitmask of flags. Currently the following are defined: > > > > > > VER_FLG_WEAK the reference to this version is weak. > > > > This specification was written and implemented before the glibc community > > corrected their interpretation of the ELF standard and introduced > > LD_DYNAMIC_WEAK to undo similar weak-style changes. That is to say that the > > VER_FLG_WEAK description in "symbol-vresioning" is based on an incorrect > > interpretation of the ELF standard. It was based on the idea that you would > > scan all loaded shared objects for a potentially strong definition that > > overrode the weak one, and you can see this means lazy loading would be > > useless in this case because even one weak symbol means you need to load > > *everything* to confirm no strong symbols override. > > Wasn't that disagreement about weak *definitions*? I think we talked past eachother here. Thank you for responding to my other points. I am going to try and stay on point here for this issue. We should discuss semantics of dynamic bindings on libc-alpha, sorry for going off topic earlier. I raised the issue of LD_DYNAMIC_WEAK only as an instructive reference that discussions about weak symbols in dynamic binding are not correct. Both weak version symbol references and weak version symbol definitions should not be used when discussing dynamic binding. In this particular bug, in this particular example, you have an undefined reference to foo being bound to a non-weak version definition of foo in foo-link2.so. The weak attribute in the source C file only tells the linker to create a new symbol named foo if one doesn't already exist and mark it weak. The static linker does the actual binding from the executable to foo-link2.so, and the version symbol definition is non-weak. The only time VER_FLG_WEAK should be set in vernaux vna_flags is to indicate that the original binding was against a weak version definition. Something which doesn't happen in this example. Even if we adjust the sources to make it happen, binutils doesn't record it as a being weak. So the first bug is that weak version symbol definitions don't mark the version definition as VER_FLG_WEAK. I can inject the flag: Version definition section '.gnu.version_d' contains 2 entries: Addr: 0x00000000000003e8 Offset: 0x0003e8 Link: 4 (.dynstr) 000000: Rev: 1 Flags: BASE Index: 1 Cnt: 1 Name: ./foo-run.so 0x001c: Rev: 1 Flags: WEAK Index: 2 Cnt: 1 Name: FOO_1 And *then* we get the correct WEAK version reference: Version needs section '.gnu.version_r' contains 2 entries: Addr: 0x0000000000400428 Offset: 0x000428 Link: 6 (.dynstr) 000000: Version: 1 File: ./foo-run.so Cnt: 1 0x0010: Name: FOO_1 Flags: WEAK Version: 3 0x0020: Version: 1 File: libc.so.6 Cnt: 1 0x0030: Name: GLIBC_2.2.5 Flags: none Version: 2 but we still fail: ./a.out2 ./a.out2: ./foo-run.so: no version information available (required by ./a.out2) ./a.out2: relocation error: ./a.out2: symbol foo version FOO_1 not defined in file ./foo-run.so with link time reference So we have a recorded binding in the binary saying that originally we were bound to a weak version definition. The enhancement we can make to binutils and glibc is to do exactly as Solaris does, which is to ignore any reference to a weak version declaration, and we know this is the case because the version reference is weak which indicates it was bound at static link time to a weak version definition. The language in "symbol-versioning", particularly this language: ~~~ There is one situation where a missing symbol definition is not an error. This is when the vna_flags in the Elfxx_Vernaux entry has the VER_FLG_WEAK bit set. In this case only a warning is issued and the object is used normally. ~~~ I believe this is trying to set out the same semantics as in Solaris. At runtime when you use a slightly older version of the library without the optional performance fix, you want to still be able to run but perhaps do something different. Therefore I think the ask is for glibc *and* binutils to do this: - Implement Solaris weak version definition handling: - Binutils to record in .gnu.version_d that if all symbol definitions are weak then the version is WEAK. - glibc to avoid issuing an error if VER_FLG_WEAK is set on a version reference because it indicates that the original static linkage was against a weak version definition and it should be ignored at runtime. The subject should be flipped the other way around I think: "BFD ld does not set VER_FLG_WEAK on version definition if all symbols are weak." > For weak references, we still have a long-standing ambiguity if the link > editor encounters both weak and non-weak references. This should promote to a non-weak reference IMO. You must ensure all references are weak if you want weak semantics. I hope this clarifies my position. Again, we aren't talking about symbols as being weak or non-weak, we are only talking about version references and version definitions. We are in effect asking to implement the Solaris semantics for weak version definitions. Once we fix this in glibc, then Paul's fix is to hexedit his binaries to have WEAK version references to FOO_1, because otherwise it won't work.
(In reply to Carlos O'Donell from comment #15) > I can inject the flag: > > Version definition section '.gnu.version_d' contains 2 entries: > Addr: 0x00000000000003e8 Offset: 0x0003e8 Link: 4 (.dynstr) > 000000: Rev: 1 Flags: BASE Index: 1 Cnt: 1 Name: ./foo-run.so > 0x001c: Rev: 1 Flags: WEAK Index: 2 Cnt: 1 Name: FOO_1 > > And *then* we get the correct WEAK version reference: > > Version needs section '.gnu.version_r' contains 2 entries: > Addr: 0x0000000000400428 Offset: 0x000428 Link: 6 (.dynstr) > 000000: Version: 1 File: ./foo-run.so Cnt: 1 > 0x0010: Name: FOO_1 Flags: WEAK Version: 3 > 0x0020: Version: 1 File: libc.so.6 Cnt: 1 > 0x0030: Name: GLIBC_2.2.5 Flags: none Version: 2 Really? That's an odd link editor behavior for our implementation of symbol versioning. Shouldn't the WEAK version flag be inherited from the weak property of the symbol reference. > but we still fail: > > ./a.out2 > ./a.out2: ./foo-run.so: no version information available (required by > ./a.out2) > ./a.out2: relocation error: ./a.out2: symbol foo version FOO_1 not defined > in file ./foo-run.so with link time reference I think you botched your patching somehow. With a WEAK symbol version on the reference side, I get this: ./a.out2: ./foo-run.so: weak version `FOO_1' not found (required by ./a.out2) ./a.out2: relocation error: ./a.out2: symbol foo version FOO_1 not defined in file ./foo-run.so with link time reference Note the changed first message. The second part is the soname consistency check which causes us a lot of headache in glibc, e.g. the broken IFUNC-based forwarders in libpthread (bug 20188, bug 19861). I think we can remove the soname consistency check from the glibc loader without any ill effect, and then weak version references should work. Although we should not print the warning message when we encounter them, I think, like we don't do this for undefined weak 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=4bed253f22c9bb9b0823a78f310afeff4f25bb40 commit 4bed253f22c9bb9b0823a78f310afeff4f25bb40 Author: H.J. Lu <hjl.tools@gmail.com> Date: Mon Jan 24 08:20:13 2022 -0800 elf: Check symbol version without any symbols VER_FLG_WEAK doesn't indicate that all symbol references of the symbol version have STB_WEAK. VER_FLG_WEAK indicates a weak symbol version definition with no symbols associated with it. It is used to verify the existence of a particular implementation without any symbol references to the weak symbol version. PR ld/24718 * testsuite/ld-elf/pr24718-1.d: New file. * testsuite/ld-elf/pr24718-1.s: Likewise. * testsuite/ld-elf/pr24718-1.t: Likewise.
There is no bug since VER_FLG_WEAK doesn't indicate that all symbol references of the symbol version have STB_WEAK. VER_FLG_WEAK indicates a weak symbol version definition with no symbols associated with it.
This came up recently in the context of CPython and RPM: https://lists.fedoraproject.org/archives/list/devel@lists.fedoraproject.org/message/NLAFVQYXKRU7UQVF3M6SPCVPHLHZXNFY/ (https://discuss.python.org/t/moving-expat-version-or-symbol-checks-from-buildtime-to-runtime/105769)
(In reply to Sam James from comment #19) > This came up recently in the context of CPython and RPM: > https://lists.fedoraproject.org/archives/list/devel@lists.fedoraproject.org/ > message/NLAFVQYXKRU7UQVF3M6SPCVPHLHZXNFY/ > (https://discuss.python.org/t/moving-expat-version-or-symbol-checks-from- > buildtime-to-runtime/105769) https://maskray.me/blog/2020-11-26-all-about-symbol-versioning describes this example. I've now enhanced it after checking the latest glibc rtld behavior. [glibc 2.30 ld.so: Support moving versioned symbols between sonames [BZ #24741]](https://sourceware.org/git/gitweb.cgi?p=glibc.git;h=f0b2132b35248c1f4a80f62a2c38cddcc802aa8c) has a side benefit for weak references. Previously, if `b.so` had a versioned weak reference `foo@v1` (where `v1` referenced `c.so`), rtld would error with `symbol %s version %s not defined in file %s with link time reference` when `c.so` lacked `foo@@v1` or `foo@v1`—contrary to weak reference semantics. Newer rtld tolerates this as long as the runtime `c.so` defines version `v1`: ```sh echo '#include <stdio.h>\nvoid fb(); int main() { fb(); puts("a"); }' > a.c echo '__attribute__((weak)) void foo(); void fb() { if (foo) foo(); }' > b.c echo 'v1 { foo; };' > c-link.ver echo '#include <stdio.h>\nvoid foo() { puts("foo"); }' > c.c echo 'v1 { };' > c.ver echo 'v2 { };' > c2.ver sed 's/^ /\t/' > Makefile <<'eof' .MAKE.MODE := meta curdirOk=1 CFLAGS := -fpic a: a.c b.so c-link.so c.so c2.so $(LINK.c) a.c b.so -Wl,-rpath=$$PWD -o $@ b.so: b.c c-link.so $(LINK.c) -shared $> -Wl,-rpath=$$PWD -o $@ c-link.so: c.c c.ver $(LINK.c) -shared -Wl,-soname=c.so,--version-script=c-link.ver c.c -o $@ c.so: c.c c.ver $(LINK.c) -shared -Wl,-soname=c.so,--version-script=c.ver -Dfoo=foo1 c.c -o $@ c2.so: c.c c2.ver $(LINK.c) -shared -Wl,-soname=c.so,--version-script=c2.ver -Dfoo=foo1 c.c -o $@ clean: rm -f a *.so *.o *.meta eof ``` If `c.so` lacks the required version entirely, rtld still reports a fatal error: ``` % ./a a % cp c2.so c.so % ./a ./a: /tmp/t/v2/c.so: version `v1' not found (required by /tmp/t/v2/b.so) ``` The [BZ24718#319 comment](https://sourceware.org/bugzilla/show_bug.cgi?id=24718#c19) in 2026 asked `VER_FLG_WEAK` in `Vernaux::vna_flags` extension, allowing rtld to continue execution (with a verbose-mode warning `weak version ... not found`) instead of failing.
> Status: RESOLVED INVALID I’d like to suggest we revisit the previous rejection. I have actually created a vda_flags VER_FLG_WEAK patch for lld https://github.com/llvm/llvm-project/pull/176673