// Convert the direct jump relocation r to refer to a trampoline if the target is too far func trampoline(ctxt *ld.Link, r *ld.Reloc, s *ld.Symbol) { switch r.Type { case obj.R_CALLARM: // r.Add is the instruction // low 24-bit encodes the target address t := (ld.Symaddr(r.Sym) + int64(signext24(r.Add&0xffffff)*4) - (s.Value + int64(r.Off))) / 4 if t > 0x7fffff || t < -0x800000 || (*ld.FlagDebugTramp > 1 && s.File != r.Sym.File) { // direct call too far, need to insert trampoline offset := (signext24(r.Add&0xffffff) + 2) * 4 var tramp *ld.Symbol for i := 0; ; i++ { name := r.Sym.Name + fmt.Sprintf("%+d-tramp%d", offset, i) tramp = ctxt.Syms.Lookup(name, int(r.Sym.Version)) if tramp.Value == 0 { // either the trampoline does not exist -- we need to create one, // or found one the address which is not assigned -- this will be // laid down immediately after the current function. use this one. break } t = (ld.Symaddr(tramp) - 8 - (s.Value + int64(r.Off))) / 4 if t >= -0x800000 && t < 0x7fffff { // found an existing trampoline that is not too far // we can just use it break } } if tramp.Type == 0 { // trampoline does not exist, create one ctxt.AddTramp(tramp) tramp.Size = 12 // 3 instructions tramp.P = make([]byte, tramp.Size) t = ld.Symaddr(r.Sym) + int64(offset) o1 := uint32(0xe5900000 | 11<<12 | 15<<16) // MOVW (R15), R11 // R15 is actual pc + 8 o2 := uint32(0xe12fff10 | 11) // JMP (R11) o3 := uint32(t) // WORD $target ld.SysArch.ByteOrder.PutUint32(tramp.P, o1) ld.SysArch.ByteOrder.PutUint32(tramp.P[4:], o2) ld.SysArch.ByteOrder.PutUint32(tramp.P[8:], o3) } // modify reloc to point to tramp, which will be resolved later r.Sym = tramp r.Add = r.Add&0xff000000 | 0xfffffe // clear the offset embedded in the instruction r.Done = 0 } default: ld.Errorf(s, "trampoline called with non-jump reloc: %v", r.Type) } }
// generate a trampoline to target+offset in position independent code func gentramppic(tramp, target *ld.Symbol, offset int64) { tramp.Size = 16 // 4 instructions tramp.P = make([]byte, tramp.Size) o1 := uint32(0xe5900000 | 11<<12 | 15<<16 | 4) // MOVW 4(R15), R11 // R15 is actual pc + 8 o2 := uint32(0xe0800000 | 11<<12 | 15<<16 | 11) // ADD R15, R11, R11 o3 := uint32(0xe12fff10 | 11) // JMP (R11) o4 := uint32(0) // WORD $(target-pc) // filled in with relocation ld.SysArch.ByteOrder.PutUint32(tramp.P, o1) ld.SysArch.ByteOrder.PutUint32(tramp.P[4:], o2) ld.SysArch.ByteOrder.PutUint32(tramp.P[8:], o3) ld.SysArch.ByteOrder.PutUint32(tramp.P[12:], o4) r := ld.Addrel(tramp) r.Off = 12 r.Type = obj.R_PCREL r.Siz = 4 r.Sym = target r.Add = offset + 4 }
// generate a trampoline to target+offset func gentramp(tramp, target *ld.Symbol, offset int64) { tramp.Size = 12 // 3 instructions tramp.P = make([]byte, tramp.Size) t := ld.Symaddr(target) + int64(offset) o1 := uint32(0xe5900000 | 11<<12 | 15<<16) // MOVW (R15), R11 // R15 is actual pc + 8 o2 := uint32(0xe12fff10 | 11) // JMP (R11) o3 := uint32(t) // WORD $target ld.SysArch.ByteOrder.PutUint32(tramp.P, o1) ld.SysArch.ByteOrder.PutUint32(tramp.P[4:], o2) ld.SysArch.ByteOrder.PutUint32(tramp.P[8:], o3) if ld.Linkmode == ld.LinkExternal { r := ld.Addrel(tramp) r.Off = 8 r.Type = obj.R_ADDR r.Siz = 4 r.Sym = target r.Add = offset } }
// generate a trampoline to target+offset in dynlink mode (using GOT) func gentrampdyn(tramp, target *ld.Symbol, offset int64) { tramp.Size = 20 // 5 instructions o1 := uint32(0xe5900000 | 11<<12 | 15<<16 | 8) // MOVW 8(R15), R11 // R15 is actual pc + 8 o2 := uint32(0xe0800000 | 11<<12 | 15<<16 | 11) // ADD R15, R11, R11 o3 := uint32(0xe5900000 | 11<<12 | 11<<16) // MOVW (R11), R11 o4 := uint32(0xe12fff10 | 11) // JMP (R11) o5 := uint32(0) // WORD $target@GOT // filled in with relocation o6 := uint32(0) if offset != 0 { // insert an instruction to add offset tramp.Size = 24 // 6 instructions o6 = o5 o5 = o4 o4 = uint32(0xe2800000 | 11<<12 | 11<<16 | immrot(uint32(offset))) // ADD $offset, R11, R11 o1 = uint32(0xe5900000 | 11<<12 | 15<<16 | 12) // MOVW 12(R15), R11 } tramp.P = make([]byte, tramp.Size) ld.SysArch.ByteOrder.PutUint32(tramp.P, o1) ld.SysArch.ByteOrder.PutUint32(tramp.P[4:], o2) ld.SysArch.ByteOrder.PutUint32(tramp.P[8:], o3) ld.SysArch.ByteOrder.PutUint32(tramp.P[12:], o4) ld.SysArch.ByteOrder.PutUint32(tramp.P[16:], o5) if offset != 0 { ld.SysArch.ByteOrder.PutUint32(tramp.P[20:], o6) } r := ld.Addrel(tramp) r.Off = 16 r.Type = obj.R_GOTPCREL r.Siz = 4 r.Sym = target r.Add = 8 if offset != 0 { // increase reloc offset by 4 as we inserted an ADD instruction r.Off = 20 r.Add = 12 } }
// resolve direct jump relocation r in s, and add trampoline if necessary func trampoline(ctxt *ld.Link, r *ld.Reloc, s *ld.Symbol) { t := ld.Symaddr(r.Sym) + r.Add - (s.Value + int64(r.Off)) switch r.Type { case obj.R_CALLPOWER: // If branch offset is too far then create a trampoline. if int64(int32(t<<6)>>6) != t || (*ld.FlagDebugTramp > 1 && s.File != r.Sym.File) { var tramp *ld.Symbol for i := 0; ; i++ { // Using r.Add as part of the name is significant in functions like duffzero where the call // target is at some offset within the function. Calls to duff+8 and duff+256 must appear as // distinct trampolines. name := r.Sym.Name if r.Add == 0 { name = name + fmt.Sprintf("-tramp%d", i) } else { name = name + fmt.Sprintf("%+x-tramp%d", r.Add, i) } // Look up the trampoline in case it already exists tramp = ctxt.Syms.Lookup(name, int(r.Sym.Version)) if tramp.Value == 0 { break } t = ld.Symaddr(tramp) + r.Add - (s.Value + int64(r.Off)) // If the offset of the trampoline that has been found is within range, use it. if int64(int32(t<<6)>>6) == t { break } } if tramp.Type == 0 { ctxt.AddTramp(tramp) tramp.Size = 16 // 4 instructions tramp.P = make([]byte, tramp.Size) t = ld.Symaddr(r.Sym) + r.Add f := t & 0xffff0000 o1 := uint32(0x3fe00000 | (f >> 16)) // lis r31,trampaddr hi (r31 is temp reg) f = t & 0xffff o2 := uint32(0x63ff0000 | f) // ori r31,trampaddr lo o3 := uint32(0x7fe903a6) // mtctr o4 := uint32(0x4e800420) // bctr ld.SysArch.ByteOrder.PutUint32(tramp.P, o1) ld.SysArch.ByteOrder.PutUint32(tramp.P[4:], o2) ld.SysArch.ByteOrder.PutUint32(tramp.P[8:], o3) ld.SysArch.ByteOrder.PutUint32(tramp.P[12:], o4) } r.Sym = tramp r.Add = 0 // This was folded into the trampoline target address r.Done = 0 } default: ld.Errorf(s, "trampoline called with non-jump reloc: %v", r.Type) } }