From b4efd09d1880793e33fbb191ccfe1657bfeba0c9 Mon Sep 17 00:00:00 2001 From: Lynn Boger Date: Thu, 25 Aug 2016 11:07:33 -0500 Subject: cmd/link: split large elf text sections on ppc64x Some applications built with Go on ppc64x with external linking can fail to link with relocation truncation errors if the elf text section that is generated is larger than 2^26 bytes and that section contains a call instruction (bl) which calls a function beyond the limit addressable by the 24 bit field in the instruction. This solution consists of generating multiple text sections where each is small enough to allow the GNU linker to resolve the calls by generating long branch code where needed. Other changes were added to handle differences in processing when multiple text sections exist. Some adjustments were required to the computation of a method's address when using the method offset table when there are multiple text sections. The number of possible section headers was increased to allow for up to 128 text sections. A test case was also added. Fixes #15823. Change-Id: If8117b0e0afb058cbc072258425a35aef2363c92 Reviewed-on: https://go-review.googlesource.com/27790 Run-TryBot: Ian Lance Taylor TryBot-Result: Gobot Gobot Reviewed-by: Ian Lance Taylor --- src/runtime/symtab.go | 34 +++++++++++++++++++++++++++------- src/runtime/type.go | 25 ++++++++++++++++++++++++- 2 files changed, 51 insertions(+), 8 deletions(-) (limited to 'src/runtime') diff --git a/src/runtime/symtab.go b/src/runtime/symtab.go index 87b478a885..c1cca7037d 100644 --- a/src/runtime/symtab.go +++ b/src/runtime/symtab.go @@ -195,8 +195,9 @@ type moduledata struct { end, gcdata, gcbss uintptr types, etypes uintptr - typelinks []int32 // offsets from types - itablinks []*itab + textsectmap []textsect + typelinks []int32 // offsets from types + itablinks []*itab ptab []ptabEntry @@ -228,6 +229,14 @@ type functab struct { funcoff uintptr } +// Mapping information for secondary text sections + +type textsect struct { + vaddr uintptr // prelinked section vaddr + length uintptr // section length + baseaddr uintptr // relocated section address +} + const minfunc = 16 // minimum function size const pcbucketsize = 256 * minfunc // size of bucket in the pc->func lookup table @@ -370,12 +379,23 @@ func findfunc(pc uintptr) *_func { ffb := (*findfuncbucket)(add(unsafe.Pointer(datap.findfunctab), b*unsafe.Sizeof(findfuncbucket{}))) idx := ffb.idx + uint32(ffb.subbuckets[i]) if pc < datap.ftab[idx].entry { - throw("findfunc: bad findfunctab entry") - } - // linear search to find func with pc >= entry. - for datap.ftab[idx+1].entry <= pc { - idx++ + // If there are multiple text sections then the buckets for the secondary + // text sections will be off because the addresses in those text sections + // were relocated to higher addresses. Search back to find it. + + for datap.ftab[idx].entry > pc && idx > 0 { + idx-- + } + if idx == 0 { + throw("findfunc: bad findfunctab entry idx") + } + } else { + + // linear search to find func with pc >= entry. + for datap.ftab[idx+1].entry <= pc { + idx++ + } } return (*_func)(unsafe.Pointer(&datap.pclntable[datap.ftab[idx].funcoff])) } diff --git a/src/runtime/type.go b/src/runtime/type.go index 0467c77400..7f7849d5a0 100644 --- a/src/runtime/type.go +++ b/src/runtime/type.go @@ -257,7 +257,30 @@ func (t *_type) textOff(off textOff) unsafe.Pointer { } return res } - res := md.text + uintptr(off) + res := uintptr(0) + + // The text, or instruction stream is generated as one large buffer. The off (offset) for a method is + // its offset within this buffer. If the total text size gets too large, there can be issues on platforms like ppc64 if + // the target of calls are too far for the call instruction. To resolve the large text issue, the text is split + // into multiple text sections to allow the linker to generate long calls when necessary. When this happens, the vaddr + // for each text section is set to its offset within the text. Each method's offset is compared against the section + // vaddrs and sizes to determine the containing section. Then the section relative offset is added to the section's + // relocated baseaddr to compute the method addess. + + if len(md.textsectmap) > 1 { + for i := range md.textsectmap { + sectaddr := md.textsectmap[i].vaddr + sectlen := md.textsectmap[i].length + if uintptr(off) >= sectaddr && uintptr(off) <= sectaddr+sectlen { + res = md.textsectmap[i].baseaddr + uintptr(off) - uintptr(md.textsectmap[i].vaddr) + break + } + } + } else { + // single text section + res = md.text + uintptr(off) + } + if res > md.etext { println("runtime: textOff", hex(off), "out of range", hex(md.text), "-", hex(md.etext)) throw("runtime: text offset out of range") -- cgit v1.3