This is the mail archive of the
binutils@sourceware.org
mailing list for the binutils project.
Re: Bad MIPS address arithmetic
- From: "Maciej W. Rozycki" <macro at linux-mips dot org>
- To: Paul Koning <Paul_Koning at Dell dot com>
- Cc: binutils at sourceware dot org
- Date: Wed, 12 May 2010 02:12:04 +0100 (BST)
- Subject: Re: Bad MIPS address arithmetic
- References: <D8CEBB6AE9D43848BD2220619A43F3265BD194@M31.equallogic.com>
On Mon, 10 May 2010, Paul Koning wrote:
> I spotted this in binutils 2.18.
>
> Given the source file:
>
> foo: ld $v0,40000($sp)
> jr $ra
>
> The resulting code is:
>
> lui v0, 1
> addu v0, v0, sp
> jr ra
> ld v0, -25536(sp)
>
> The problem is that this produces wrong addresses in machines with 64
> bit registers, if the current sp is 0x7fff0000 or higher. If so, the
> addu produces 0xffffffff8000nnnn in v0, and the ld then references
> 0xffffffff7fffnnnn which is not likely to be a valid address.
>
> It seems that daddu rather than addu should be used here, for O64 (and
> probably N32) ABIs.
I dare not speak of the o64 ABI as its details have always been a mystery
to me. But with n32 the code generated is correct. You can't just offset
an address of "0x7fff0000 or higher" by 40000, because you are crossing
from KUSEG to KSEG0 this way. This is undefined behaviour -- the n32's
31-bit KUSEG space ends at 0x7fffffff.
Now I agree there is a problem, namely with legacy 64-bit MIPS hardware
that did not properly support address space truncation that would
correctly support the 31-bit user address space suitable for n32
processes. The only controlling bit for the 64-bit mode was CP0.Status.UX
that enabled both 64-bit operations and 64-bit addresses at the same time.
This was corrected in the MIPS64 ISA with the addition of the
CP0.Status.PX that only enables 64-bit operations, but keeps addresses
truncated to 32-bit segments. With CP0.Status.PX set the LD instruction
above correctly refers to 0xffffffff8000nnnn.
The difference is however somewhat academic -- at the kernel level with
legacy processors you get an unhandled TLBL exception rather than the
expected AdEL one. A properly written OS kernel will kill the offender
with a signal in both cases.
Even if you're advanced enough to handle the signal sent, then you don't
really need to take any special precautions -- the faulting address passed
to the handler has to be properly sign-extended from 32 bits by user
software (typically by using LW to retrieve) and (at least with Linux) you
won't notice a difference as a SIGSEGV is sent in response to both
exceptions under these circumstances.
Have I made it clear enough?
Maciej