return int(a.Class) - 1
}
-// add R_ADDRPOWER relocation to symbol s with addend d
-func addaddrreloc(ctxt *obj.Link, s *obj.LSym, d int64) {
+const (
+ D_FORM = iota
+ DS_FORM
+)
+
+// opform returns the form (D_FORM or DS_FORM) of an instruction. Used to decide on
+// which relocation to use with a load or store and only supports the needed
+// instructions.
+func opform(ctxt *obj.Link, insn int32) int {
+ switch uint32(insn) {
+ default:
+ ctxt.Diag("bad insn in loadform: %x", insn)
+ case OPVCC(58, 0, 0, 0), // ld
+ OPVCC(58, 0, 0, 0) | 1<<1, // lwa
+ OPVCC(62, 0, 0, 0): // std
+ return DS_FORM
+ case OP_ADDI, // add
+ OPVCC(32, 0, 0, 0), // lwz
+ OPVCC(42, 0, 0, 0), // lha
+ OPVCC(40, 0, 0, 0), // lhz
+ OPVCC(34, 0, 0, 0), // lbz
+ OPVCC(50, 0, 0, 0), // lfd
+ OPVCC(48, 0, 0, 0), // lfs
+ OPVCC(36, 0, 0, 0), // stw
+ OPVCC(44, 0, 0, 0), // sth
+ OPVCC(38, 0, 0, 0), // stb
+ OPVCC(54, 0, 0, 0), // stfd
+ OPVCC(52, 0, 0, 0): // stfs
+ return D_FORM
+ }
+ return 0
+}
+
+// Encode instructions and create relocation for accessing s+d according to the
+// instruction op with source or destination (as appropriate) register reg.
+func symbolAccess(ctxt *obj.Link, s *obj.LSym, d int64, reg int16, op int32) (o1, o2 uint32) {
+ form := opform(ctxt, op)
+ o1 = AOP_IRR(OP_ADDIS, REGTMP, REGZERO, 0)
+ o2 = AOP_IRR(uint32(op), uint32(reg), REGTMP, 0)
rel := obj.Addrel(ctxt.Cursym)
rel.Off = int32(ctxt.Pc)
rel.Siz = 8
rel.Sym = s
rel.Add = d
- rel.Type = obj.R_ADDRPOWER
+ switch form {
+ case D_FORM:
+ rel.Type = obj.R_ADDRPOWER
+ case DS_FORM:
+ rel.Type = obj.R_ADDRPOWER_DS
+ }
+ return
}
/*
o1 = loadu32(int(p.To.Reg), d)
o2 = LOP_IRR(OP_ORI, uint32(p.To.Reg), uint32(p.To.Reg), uint32(int32(d)))
} else {
- o1 = AOP_IRR(OP_ADDIS, REGTMP, REGZERO, 0)
- o2 = AOP_IRR(OP_ADDI, uint32(p.To.Reg), REGTMP, 0)
- addaddrreloc(ctxt, p.From.Sym, d)
+ o1, o2 = symbolAccess(ctxt, p.From.Sym, d, p.To.Reg, OP_ADDI)
}
//if(dlm) reloc(&p->from, p->pc, 0);
/* relocation operations */
case 74:
v := vregoff(ctxt, &p.To)
-
- o1 = AOP_IRR(OP_ADDIS, REGTMP, REGZERO, 0)
- o2 = AOP_IRR(uint32(opstore(ctxt, int(p.As))), uint32(p.From.Reg), REGTMP, 0)
- addaddrreloc(ctxt, p.To.Sym, v)
+ o1, o2 = symbolAccess(ctxt, p.To.Sym, v, p.From.Reg, opstore(ctxt, int(p.As)))
//if(dlm) reloc(&p->to, p->pc, 1);
case 75:
v := vregoff(ctxt, &p.From)
- o1 = AOP_IRR(OP_ADDIS, REGTMP, REGZERO, 0)
- o2 = AOP_IRR(uint32(opload(ctxt, int(p.As))), uint32(p.To.Reg), REGTMP, 0)
- addaddrreloc(ctxt, p.From.Sym, v)
+ o1, o2 = symbolAccess(ctxt, p.From.Sym, v, p.To.Reg, opload(ctxt, int(p.As)))
//if(dlm) reloc(&p->from, p->pc, 1);
case 76:
v := vregoff(ctxt, &p.From)
- o1 = AOP_IRR(OP_ADDIS, REGTMP, REGZERO, 0)
- o2 = AOP_IRR(uint32(opload(ctxt, int(p.As))), uint32(p.To.Reg), REGTMP, 0)
- addaddrreloc(ctxt, p.From.Sym, v)
+ o1, o2 = symbolAccess(ctxt, p.From.Sym, v, p.To.Reg, opload(ctxt, int(p.As)))
o3 = LOP_RRR(OP_EXTSB, uint32(p.To.Reg), uint32(p.To.Reg), 0)
//if(dlm) reloc(&p->from, p->pc, 1);
return toc.Value
}
+func archrelocaddr(r *ld.Reloc, s *ld.LSym, val *int64) int {
+ var o1, o2 uint32
+ if ld.Ctxt.Arch.ByteOrder == binary.BigEndian {
+ o1 = uint32(*val >> 32)
+ o2 = uint32(*val)
+ } else {
+ o1 = uint32(*val)
+ o2 = uint32(*val >> 32)
+ }
+
+ // We are spreading a 31-bit address across two instructions, putting the
+ // high (adjusted) part in the low 16 bits of the first instruction and the
+ // low part in the low 16 bits of the second instruction, or, in the DS case,
+ // bits 15-2 (inclusive) of the address into bits 15-2 of the second
+ // instruction (it is an error in this case if the low 2 bits of the address
+ // are non-zero).
+
+ t := ld.Symaddr(r.Sym) + r.Add
+ if t < 0 || t >= 1<<31 {
+ ld.Ctxt.Diag("relocation for %s is too big (>=2G): %d", s.Name, ld.Symaddr(r.Sym))
+ }
+ if t&0x8000 != 0 {
+ t += 0x10000
+ }
+
+ switch r.Type {
+ case obj.R_ADDRPOWER:
+ o1 |= (uint32(t) >> 16) & 0xffff
+ o2 |= uint32(t) & 0xffff
+
+ case obj.R_ADDRPOWER_DS:
+ o1 |= (uint32(t) >> 16) & 0xffff
+ if t&3 != 0 {
+ ld.Ctxt.Diag("bad DS reloc for %s: %d", s.Name, ld.Symaddr(r.Sym))
+ }
+ o2 |= uint32(t) & 0xfffc
+
+ default:
+ return -1
+ }
+
+ if ld.Ctxt.Arch.ByteOrder == binary.BigEndian {
+ *val = int64(o1)<<32 | int64(o2)
+ } else {
+ *val = int64(o2)<<32 | int64(o1)
+ }
+ return 0
+}
+
func archreloc(r *ld.Reloc, s *ld.LSym, val *int64) int {
if ld.Linkmode == ld.LinkExternal {
// TODO(minux): translate R_ADDRPOWER and R_CALLPOWER into standard ELF relocations.
*val = ld.Symaddr(r.Sym) + r.Add - ld.Symaddr(ld.Linklookup(ld.Ctxt, ".got", 0))
return 0
- case obj.R_ADDRPOWER:
- // We are spreading a 31-bit address across two instructions,
- // putting the high (adjusted) part in the low 16 bits of the
- // first instruction and the low part in the low 16 bits of the
- // second instruction.
- t := ld.Symaddr(r.Sym) + r.Add
- if t < 0 || t >= 1<<31 {
- ld.Ctxt.Diag("relocation for %s is too big (>=2G): %d", s.Name, ld.Symaddr(r.Sym))
- }
- var o1, o2 uint32
- if ld.Ctxt.Arch.ByteOrder == binary.BigEndian {
- o1 = uint32(*val >> 32)
- o2 = uint32(*val)
- } else {
- o1 = uint32(*val)
- o2 = uint32(*val >> 32)
- }
- if t&0x8000 != 0 {
- t += 0x10000
- }
- // There is an almost-bug here. When R_ADDRPOWER is relocating a
- // load, the two instructions are addi and then a load. addi and
- // almost all loads are "D-form" instructions, which have a
- // 16-bit immediate in the lower 16-bits of the instruction
- // word. But the load doubleword instruction is a "DS-form"
- // instruction: the immediate only occupies bits 16-29 of the
- // instruction and is implicity padded with zeros on the
- // right. The reason the belows isn't a bug is because we only
- // ever use immediates that have zeros on in their lower bits
- // with ld, and we combine the immediate with | so bits 30 and
- // 31 are preserved.
- o1 |= (uint32(t) >> 16) & 0xffff
- o2 |= uint32(t) & 0xffff
-
- if ld.Ctxt.Arch.ByteOrder == binary.BigEndian {
- *val = int64(o1)<<32 | int64(o2)
- } else {
- *val = int64(o2)<<32 | int64(o1)
- }
- return 0
+ case obj.R_ADDRPOWER, obj.R_ADDRPOWER_DS:
+ return archrelocaddr(r, s, val)
case obj.R_CALLPOWER:
// Bits 6 through 29 = (S + A - P) >> 2