[PATCH] as: Replace the removed symbol with the versioned symbol

Fangrui Song i@maskray.me
Fri Aug 13 22:29:15 GMT 2021


On 2021-08-13, H.J. Lu wrote:
>On Thu, Aug 12, 2021 at 3:52 PM Fangrui Song <i@maskray.me> wrote:
>>
>>
>> On 2021-08-12, H.J. Lu wrote:
>> >On Wed, Aug 11, 2021 at 6:11 PM Fangrui Song <i@maskray.me> wrote:
>> >>
>> >>
>> >> On 2021-08-11, H.J. Lu wrote:
>> >> >On Tue, Aug 10, 2021 at 2:56 PM Fangrui Song <i@maskray.me> wrote:
>> >> >>
>> >> >> On 2021-08-01, H.J. Lu via Binutils wrote:
>> >> >> >When a symbol removed by .symver is used in relocation and there is one
>> >> >> >and only one versioned symbol, don't remove the symbol.  Instead, mark
>> >> >> >it to be removed and replace the removed symbol used in relocation with
>> >> >> >the versioned symbol before generating relocation.
>> >> >>
>> >> >> Thanks for the patch.
>> >> >> The behavior looks good.
>> >> >>
>> >> >> .symver foo, foo@v1, remove
>> >> >> .globl foo
>> >> >> foo:
>> >> >>    call foo   # R_X86_64_PLT32 foo@v1
>> >> >>
>> >> >>
>> >> >>
>> >> >> I think this can be extended to non-remove one-@ as well.
>> >> >> The unadorned symbol and relocations referencing it just add complexity
>> >> >> to linker internals.
>> >> >>
>> >> >> .symver foo, foo@v1
>> >> >
>> >> >This usage is perfectly fine and quite normal.
>> >>
>> >> It isn't.
>> >>
>> >> cat > a.s <<eof
>> >> .symver foo, foo@v1
>> >> .globl foo
>> >> foo:
>> >> eof
>> >
>> >We must keep foo since there may be relocations
>> >against foo in this file as well as in other files.
>>
>> In practice, no project uses this.
>> (OK, I am less confident on glibc but I am hoping it mostly does the right thing.)
>
>This is the standard usage in glibc.
>
>> Unversioned 'foo' references from other files should bind the default versioned definition,
>> not foo@v1.
>
>Unversioned 'foo' references from other files will be resolved to the
>unversioned
>foo definition which will be turned into a local symbol.

If you think about it, keeping the 'foo' definition is highly
error-prone.

First, it is unusual for a non-default version definition
to be referenced from outside the TU.

Second, if glibc defines foo@GLIBC_2.3 in one TU and has a version node
GLIBC_2.4 { foo; };, there will be a foo@@GLIBC_2.4 beside
foo@GLIBC_2.3, and the unadorned reference will be silently bound to
foo@@GLIBC_2.4. There is no error reporting.

The sensible behavior is

.symver foo, foo@@@v2 only defines foo@@v2
.symver foo, foo@v1, remove only defines foo_v1.

An unadorned reference cannot be bound to a default-version definition.
Non-default version references must use .symver .

Linkers have well established rules that a foo@@v2 definition can
satisfy foo.


I want to check what on earth could break in glibc
if I add `, remove` but I cannot test currently because
relocations don't work.
With this patch we will be able to test, and hopefully the result isn't
that bad.

>>
>> If 'foo' does not have a default version definition, the references
>> should be versioned (`.symver foo, foo@@@v1`).
>>
>> >> cat > a.ver <<eof
>> >> v1 {};
>> >> v2 { foo*; };
>> >> eof
>> >> cc -c a.s
>> >>
>> >> % ld.bfd --version-script=a.ver -shared a.o -o a.so && nm -D a.so
>> >> 0000000000001000 R foo@v1
>> >> 0000000000000000 A v1
>> >> 0000000000000000 A v2
>> >
>> >This looks normal.
>>
>> I'd argue that the good design should leave a dynamic `foo@@v2`,
>> matching the case below.
>> Linkers would have been doing this if gas had done the right thing in
>> the first place.
>>
>> >>
>> >> If
>> >> .symver foo_v1, foo@v1
>> >>
>> >> % ld.bfd --version-script=a.ver -shared a.o -o a.so && nm -D a.so
>> >> 0000000000001000 R foo@v1
>> >> 0000000000001000 R foo_v1@@v2
>> >> 0000000000000000 A v1
>> >> 0000000000000000 A v2
>> >
>> >This looks normal.
>>
>> This is indeed normal.
>>
>> >>
>> >> Merging (unadorned) foo and (non-default versioned) foo@v1 is a hack, in all of GNU ld, gold, and ld.lld.
>> >
>> >
>> >
>> >--
>> >H.J.
>
>
>
>-- 
>H.J.


More information about the Binutils mailing list