Bad MIPS address arithmetic

Paul Koning
Wed May 12 14:14:00 GMT 2010

> 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(v0)

(v0) not (sp) -- I transcribed this wrong when I first wrote the note.

> > The problem is that this produces wrong addresses in machines with
> > bit registers, if the current sp is 0x7fff0000 or higher.  If so,
> > 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
> from KUSEG to KSEG0 this way.  This is undefined behaviour -- the
> 31-bit KUSEG space ends at 0x7fffffff.

N32 and O64 are the same from the point of view of address handling.  

The "undefined behavior" is exactly the point I'm complaining about.
The assembler is generating code that fails (exercises undefined
behavior) for some values of SP that are perfectly legal.
>  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.
> ...
>  Have I made it clear enough?

I think so.  It looks like N32 and O64 require the kernel to do one of
two things:

1. Set PX in the status register and leave UX clear, or
2. Leave the upper 64k of useg unmapped (so, for example, the stack
would start at 0x7fff0000 or below).

The code produces ffffffff8000nnnn in v0.  With 64 bit address
arithmetic, the memory reference to -25536(v0) produces an address of
the form ffffffff7fffnnnn which is what causes the trouble.  I take it
from what you're saying that setting PX and clearing UX will cause the
processor to ignore the upper 32 bits in that address calculation and
instead use the sign extended lower 32 bits, so I get 000000007fffnnnn
which is the right answer.  Section 4.9 in the MIPS64 spec volume 3
describes this reasonably clearly.

My kernel is currently setting UX (it doesn't have a symbolic name for
PX at all) but my platform does support PX so that seems like the
simplest solution.  Alternatively, I can move the initial SP, that isn't
all that hard either.

Thanks for the help.


More information about the Binutils mailing list