Commit 7d4cca07 authored by Lynn Boger's avatar Lynn Boger

cmd/asm: detect invalid DS form offsets for ppc64x

While debugging a recent regression it was discovered that
the assembler for ppc64x was not always generating the correct
instruction for DS form loads and stores.  When an instruction
is DS form then the offset must be a multiple of 4, and if it
isn't then bits outside the offset field were being incorrectly
set resulting in unexpected and incorrect instructions.

This change adds a check to determine when the opcode is DS form
and then verifies that the offset is a multiple of 4 before
generating the instruction, otherwise logs an error.

This also changes a few asm files that were using unaligned offsets
for DS form loads and stores.  In the runtime package these were
instructions intended to cause a crash so using aligned or unaligned
offsets doesn't change that behavior.

Change-Id: Ie3a7e1e65dcc9933b54de7a46a054da8459cb56f
Reviewed-on: https://go-review.googlesource.com/40476Reviewed-by: default avatarMichael Hudson-Doyle <michael.hudson@canonical.com>
parent 16db1892
...@@ -24,7 +24,7 @@ TEXT foo(SB),DUPOK|NOSPLIT,$0 ...@@ -24,7 +24,7 @@ TEXT foo(SB),DUPOK|NOSPLIT,$0
// { // {
// outcode(int($1), &$2, 0, &$4); // outcode(int($1), &$2, 0, &$4);
// } // }
MOVW foo<>+3(SB), R2 MOVW foo<>+4(SB), R2
MOVW 16(R1), R2 MOVW 16(R1), R2
// LMOVW regaddr ',' rreg // LMOVW regaddr ',' rreg
...@@ -61,7 +61,7 @@ TEXT foo(SB),DUPOK|NOSPLIT,$0 ...@@ -61,7 +61,7 @@ TEXT foo(SB),DUPOK|NOSPLIT,$0
// { // {
// outcode(int($1), &$2, 0, &$4); // outcode(int($1), &$2, 0, &$4);
// } // }
FMOVD foo<>+3(SB), F2 FMOVD foo<>+4(SB), F2
FMOVD 16(R1), F2 FMOVD 16(R1), F2
// LFMOV regaddr ',' freg // LFMOV regaddr ',' freg
...@@ -86,7 +86,7 @@ TEXT foo(SB),DUPOK|NOSPLIT,$0 ...@@ -86,7 +86,7 @@ TEXT foo(SB),DUPOK|NOSPLIT,$0
// { // {
// outcode(int($1), &$2, 0, &$4); // outcode(int($1), &$2, 0, &$4);
// } // }
FMOVD F2, foo<>+3(SB) FMOVD F2, foo<>+4(SB)
FMOVD F2, 16(R1) FMOVD F2, 16(R1)
// LFMOV freg ',' regaddr // LFMOV freg ',' regaddr
...@@ -132,7 +132,7 @@ TEXT foo(SB),DUPOK|NOSPLIT,$0 ...@@ -132,7 +132,7 @@ TEXT foo(SB),DUPOK|NOSPLIT,$0
// { // {
// outcode(int($1), &$2, 0, &$4); // outcode(int($1), &$2, 0, &$4);
// } // }
FMOVD F1, foo<>+3(SB) FMOVD F1, foo<>+4(SB)
FMOVD F1, 16(R2) FMOVD F1, 16(R2)
// LMOVW freg ',' regaddr // LMOVW freg ',' regaddr
......
...@@ -1985,29 +1985,48 @@ const ( ...@@ -1985,29 +1985,48 @@ const (
DS_FORM DS_FORM
) )
// opform returns the form (D_FORM or DS_FORM) of an instruction. Used to decide on // This function determines when a non-indexed load or store is D or
// which relocation to use with a load or store and only supports the needed // DS form for use in finding the size of the offset field in the instruction.
// instructions. // The size is needed when setting the offset value in the instruction
// and when generating relocation for that field.
// DS form instructions include: ld, ldu, lwa, std, stdu. All other
// loads and stores with an offset field are D form. This function should
// only be called with the same opcodes as are handled by opstore and opload.
func (c *ctxt9) opform(insn uint32) int { func (c *ctxt9) opform(insn uint32) int {
switch insn { switch insn {
default: default:
c.ctxt.Diag("bad insn in loadform: %x", insn) c.ctxt.Diag("bad insn in loadform: %x", insn)
case OPVCC(58, 0, 0, 0), // ld case OPVCC(58, 0, 0, 0), // ld
OPVCC(58, 0, 0, 1), // ldu
OPVCC(58, 0, 0, 0) | 1<<1, // lwa OPVCC(58, 0, 0, 0) | 1<<1, // lwa
OPVCC(62, 0, 0, 0): // std OPVCC(62, 0, 0, 0), // std
OPVCC(62, 0, 0, 1): //stdu
return DS_FORM return DS_FORM
case OP_ADDI, // add case OP_ADDI, // add
OPVCC(32, 0, 0, 0), // lwz OPVCC(32, 0, 0, 0), // lwz
OPVCC(42, 0, 0, 0), // lha OPVCC(33, 0, 0, 0), // lwzu
OPVCC(40, 0, 0, 0), // lhz
OPVCC(34, 0, 0, 0), // lbz OPVCC(34, 0, 0, 0), // lbz
OPVCC(50, 0, 0, 0), // lfd OPVCC(35, 0, 0, 0), // lbzu
OPVCC(40, 0, 0, 0), // lhz
OPVCC(41, 0, 0, 0), // lhzu
OPVCC(42, 0, 0, 0), // lha
OPVCC(43, 0, 0, 0), // lhau
OPVCC(46, 0, 0, 0), // lmw
OPVCC(48, 0, 0, 0), // lfs OPVCC(48, 0, 0, 0), // lfs
OPVCC(49, 0, 0, 0), // lfsu
OPVCC(50, 0, 0, 0), // lfd
OPVCC(51, 0, 0, 0), // lfdu
OPVCC(36, 0, 0, 0), // stw OPVCC(36, 0, 0, 0), // stw
OPVCC(44, 0, 0, 0), // sth OPVCC(37, 0, 0, 0), // stwu
OPVCC(38, 0, 0, 0), // stb OPVCC(38, 0, 0, 0), // stb
OPVCC(39, 0, 0, 0), // stbu
OPVCC(44, 0, 0, 0), // sth
OPVCC(45, 0, 0, 0), // sthu
OPVCC(47, 0, 0, 0), // stmw
OPVCC(52, 0, 0, 0), // stfs
OPVCC(53, 0, 0, 0), // stfsu
OPVCC(54, 0, 0, 0), // stfd OPVCC(54, 0, 0, 0), // stfd
OPVCC(52, 0, 0, 0): // stfs OPVCC(55, 0, 0, 0): // stfdu
return D_FORM return D_FORM
} }
return 0 return 0
...@@ -2268,7 +2287,12 @@ func (c *ctxt9) asmout(p *obj.Prog, o *Optab, out []uint32) { ...@@ -2268,7 +2287,12 @@ func (c *ctxt9) asmout(p *obj.Prog, o *Optab, out []uint32) {
if int32(int16(v)) != v { if int32(int16(v)) != v {
log.Fatalf("mishandled instruction %v", p) log.Fatalf("mishandled instruction %v", p)
} }
o1 = AOP_IRR(c.opstore(p.As), uint32(p.From.Reg), uint32(r), uint32(v)) // Offsets in DS form stores must be a multiple of 4
inst := c.opstore(p.As)
if c.opform(inst) == DS_FORM && v&0x3 != 0 {
log.Fatalf("invalid offset for DS form load/store %v", p)
}
o1 = AOP_IRR(inst, uint32(p.From.Reg), uint32(r), uint32(v))
} }
case 8: /* mov soreg, r ==> lbz/lhz/lwz o(r) */ case 8: /* mov soreg, r ==> lbz/lhz/lwz o(r) */
...@@ -2294,7 +2318,12 @@ func (c *ctxt9) asmout(p *obj.Prog, o *Optab, out []uint32) { ...@@ -2294,7 +2318,12 @@ func (c *ctxt9) asmout(p *obj.Prog, o *Optab, out []uint32) {
if int32(int16(v)) != v { if int32(int16(v)) != v {
log.Fatalf("mishandled instruction %v", p) log.Fatalf("mishandled instruction %v", p)
} }
o1 = AOP_IRR(c.opload(p.As), uint32(p.To.Reg), uint32(r), uint32(v)) // Offsets in DS form loads must be a multiple of 4
inst := c.opload(p.As)
if c.opform(inst) == DS_FORM && v&0x3 != 0 {
log.Fatalf("invalid offset for DS form load/store %v", p)
}
o1 = AOP_IRR(inst, uint32(p.To.Reg), uint32(r), uint32(v))
} }
case 9: /* movb soreg, r ==> lbz o(r),r2; extsb r2,r2 */ case 9: /* movb soreg, r ==> lbz o(r),r2; extsb r2,r2 */
...@@ -2789,8 +2818,13 @@ func (c *ctxt9) asmout(p *obj.Prog, o *Optab, out []uint32) { ...@@ -2789,8 +2818,13 @@ func (c *ctxt9) asmout(p *obj.Prog, o *Optab, out []uint32) {
if r == 0 { if r == 0 {
r = int(o.param) r = int(o.param)
} }
// Offsets in DS form stores must be a multiple of 4
inst := c.opstore(p.As)
if c.opform(inst) == DS_FORM && v&0x3 != 0 {
log.Fatalf("invalid offset for DS form load/store %v", p)
}
o1 = AOP_IRR(OP_ADDIS, REGTMP, uint32(r), uint32(high16adjusted(v))) o1 = AOP_IRR(OP_ADDIS, REGTMP, uint32(r), uint32(high16adjusted(v)))
o2 = AOP_IRR(c.opstore(p.As), uint32(p.From.Reg), REGTMP, uint32(v)) o2 = AOP_IRR(inst, uint32(p.From.Reg), REGTMP, uint32(v))
case 36: /* mov bz/h/hz lext/lauto/lreg,r ==> lbz/lha/lhz etc */ case 36: /* mov bz/h/hz lext/lauto/lreg,r ==> lbz/lha/lhz etc */
v := c.regoff(&p.From) v := c.regoff(&p.From)
...@@ -3120,19 +3154,34 @@ func (c *ctxt9) asmout(p *obj.Prog, o *Optab, out []uint32) { ...@@ -3120,19 +3154,34 @@ func (c *ctxt9) asmout(p *obj.Prog, o *Optab, out []uint32) {
/* relocation operations */ /* relocation operations */
case 74: case 74:
v := c.vregoff(&p.To) v := c.vregoff(&p.To)
o1, o2 = c.symbolAccess(p.To.Sym, v, p.From.Reg, c.opstore(p.As)) // Offsets in DS form stores must be a multiple of 4
inst := c.opstore(p.As)
if c.opform(inst) == DS_FORM && v&0x3 != 0 {
log.Fatalf("invalid offset for DS form load/store %v", p)
}
o1, o2 = c.symbolAccess(p.To.Sym, v, p.From.Reg, inst)
//if(dlm) reloc(&p->to, p->pc, 1); //if(dlm) reloc(&p->to, p->pc, 1);
case 75: case 75:
v := c.vregoff(&p.From) v := c.vregoff(&p.From)
o1, o2 = c.symbolAccess(p.From.Sym, v, p.To.Reg, c.opload(p.As)) // Offsets in DS form loads must be a multiple of 4
inst := c.opload(p.As)
if c.opform(inst) == DS_FORM && v&0x3 != 0 {
log.Fatalf("invalid offset for DS form load/store %v", p)
}
o1, o2 = c.symbolAccess(p.From.Sym, v, p.To.Reg, inst)
//if(dlm) reloc(&p->from, p->pc, 1); //if(dlm) reloc(&p->from, p->pc, 1);
case 76: case 76:
v := c.vregoff(&p.From) v := c.vregoff(&p.From)
o1, o2 = c.symbolAccess(p.From.Sym, v, p.To.Reg, c.opload(p.As)) // Offsets in DS form loads must be a multiple of 4
inst := c.opload(p.As)
if c.opform(inst) == DS_FORM && v&0x3 != 0 {
log.Fatalf("invalid offset for DS form load/store %v", p)
}
o1, o2 = c.symbolAccess(p.From.Sym, v, p.To.Reg, inst)
o3 = LOP_RRR(OP_EXTSB, uint32(p.To.Reg), uint32(p.To.Reg), 0) o3 = LOP_RRR(OP_EXTSB, uint32(p.To.Reg), uint32(p.To.Reg), 0)
//if(dlm) reloc(&p->from, p->pc, 1); //if(dlm) reloc(&p->from, p->pc, 1);
......
...@@ -85,14 +85,14 @@ nocgo: ...@@ -85,14 +85,14 @@ nocgo:
// start this M // start this M
BL runtime·mstart(SB) BL runtime·mstart(SB)
MOVD R0, 1(R0) MOVD R0, 0(R0)
RET RET
DATA runtime·mainPC+0(SB)/8,$runtime·main(SB) DATA runtime·mainPC+0(SB)/8,$runtime·main(SB)
GLOBL runtime·mainPC(SB),RODATA,$8 GLOBL runtime·mainPC(SB),RODATA,$8
TEXT runtime·breakpoint(SB),NOSPLIT|NOFRAME,$0-0 TEXT runtime·breakpoint(SB),NOSPLIT|NOFRAME,$0-0
MOVD R0, 2(R0) // TODO: TD MOVD R0, 0(R0) // TODO: TD
RET RET
TEXT runtime·asminit(SB),NOSPLIT|NOFRAME,$0-0 TEXT runtime·asminit(SB),NOSPLIT|NOFRAME,$0-0
......
...@@ -189,7 +189,7 @@ TEXT runtime·rtsigprocmask(SB),NOSPLIT|NOFRAME,$0-28 ...@@ -189,7 +189,7 @@ TEXT runtime·rtsigprocmask(SB),NOSPLIT|NOFRAME,$0-28
MOVW size+24(FP), R6 MOVW size+24(FP), R6
SYSCALL $SYS_rt_sigprocmask SYSCALL $SYS_rt_sigprocmask
BVC 2(PC) BVC 2(PC)
MOVD R0, 0xf1(R0) // crash MOVD R0, 0xf0(R0) // crash
RET RET
TEXT runtime·rt_sigaction(SB),NOSPLIT|NOFRAME,$0-36 TEXT runtime·rt_sigaction(SB),NOSPLIT|NOFRAME,$0-36
...@@ -273,7 +273,7 @@ TEXT runtime·munmap(SB),NOSPLIT|NOFRAME,$0 ...@@ -273,7 +273,7 @@ TEXT runtime·munmap(SB),NOSPLIT|NOFRAME,$0
MOVD n+8(FP), R4 MOVD n+8(FP), R4
SYSCALL $SYS_munmap SYSCALL $SYS_munmap
BVC 2(PC) BVC 2(PC)
MOVD R0, 0xf3(R0) MOVD R0, 0xf0(R0)
RET RET
TEXT runtime·madvise(SB),NOSPLIT|NOFRAME,$0 TEXT runtime·madvise(SB),NOSPLIT|NOFRAME,$0
...@@ -366,7 +366,7 @@ TEXT runtime·sigaltstack(SB),NOSPLIT|NOFRAME,$0 ...@@ -366,7 +366,7 @@ TEXT runtime·sigaltstack(SB),NOSPLIT|NOFRAME,$0
MOVD old+8(FP), R4 MOVD old+8(FP), R4
SYSCALL $SYS_sigaltstack SYSCALL $SYS_sigaltstack
BVC 2(PC) BVC 2(PC)
MOVD R0, 0xf1(R0) // crash MOVD R0, 0xf0(R0) // crash
RET RET
TEXT runtime·osyield(SB),NOSPLIT|NOFRAME,$0 TEXT runtime·osyield(SB),NOSPLIT|NOFRAME,$0
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment