Re: Bug#649241: binutils: Either gas or ld is doing the wrong thing with R_SPARC_WDISP22 relocations on sparc
(Hooray for investigating long-standing bug reports?)
On Sat, Nov 19, 2011 at 09:41:21AM +0100, Mike Hommey wrote:
> Package: binutils
> Version: 2.21.90.20111025-1
> Severity: important
>
> In Iceweasel 9, there is a piece of assembly that can be simplified as
> the following:
>
> .text
> .global foo
> .type foo, #function
> foo:
> ba bar
> nop
>
> .global bar
> .type bar, #function
> bar:
> call exit
>
> When compiling the above, the resulting binary ends up with a runtime
> R_SPARC_WDISP22 relocation that ld.so doesn't know about:
>
> $ gcc -o test.so -shared test.s
> $ LD_PRELOAD=$(pwd)/test.so cat
> cat: error while loading shared libraries: /home/glandium/test.so: unexpected reloc type 0x08
>
> One would expect ld to actually care about this relocation itself at
> (static) link time. Or gas to emit a relocation that ld knows about.
So, the problem here is that you're making a shared library and thus
bar, being a global symbol, is by default preemptible (if you make it
non-global then the runtime linker is happy, but you can't preempt it).
In the case of using a normal "call" instruction for which the assembler
generates an R_SPARC_WPLT30 relocation (when given -KPIC), the linker
generates a PLT entry and everything is as you'd expect. However, none
of the branches have corresponding WPLT relocations available. Gold is
rather more helpful here, giving:
> error: /tmp/ccgupDIX.o: requires unsupported dynamic reloc; recompile with -fPIC
which, while not especially informative as to *which* relocation was
bad, at least stops it from producing an output the runtime linker will
crash on (and one which has evil text relocations...).
The canonical way to perform a tail-call on SPARC like this is:
> or %o7, %g0, %REG
> call bar
> or %REG, %g0, %o7
where REG is whatever you have free. This obviously has to be done in
the *same* register window as the caller (otherwise when bar returns
there'll still be an extra window to pop). This saves the return
address, does a call-and-link (clobbering %o7 with PC+8) and then
restores %o7 in the handy delay slot, emulating a call-without-link.
In fact, ld is smart enough to recognise this pattern as not being a
real call, and turns it into the following:
> or %o7, %g0, %REG
> b bar@plt
> nop
Gold does the same but uses the V9 branch instead. Unfortunately there's
still a wasted instruction there, but it's better than it was.
So to summarise, I guess there are two issues here:
1. bfd should give an error when trying to use an instruction other
than call to branch to a preemptible function (currently there are
no places in elfxx-sparc.c where the "recompile with -fPIC" string
appears, so there are likely other things like this).
2. Perhaps new R_SPARC_WPLT{10,16,19,22} should be introduced to allow
shorter tail-call sequences like this? Glancing at bfd it should be
fairly straightforward, and I imagine the same is true for gold.
Regards,
James
Reply to: