nim / rpms / golang

Forked from rpms/golang 5 years ago
Clone
d978616
From d6beea7f9ea1aa2ae5abca7fccb252767820aa13 Mon Sep 17 00:00:00 2001
d978616
From: Lynn Boger <laboger@linux.vnet.ibm.com>
d978616
Date: Tue, 26 Jul 2016 08:51:10 -0500
d978616
Subject: [PATCH] cmd/link: split large elf text sections for ppc64x
d978616
d978616
Some applications built with Go on ppc64x with
d978616
external linking can fail to link with relocation
d978616
truncation errors, due to the way that the golang
d978616
compiler generates a single go.o file containing
d978616
a single large text section to send to the GNU
d978616
linker.  If the size of the single text section is
d978616
greater than 2^26, this can lead to link errors
d978616
due to 24 bit offset field in the bl (call)
d978616
instruction.
d978616
d978616
This fix solves the problem by splitting into
d978616
multiple text sections when this limit is reached.
d978616
When this is done then the GNU linker can fix the
d978616
long calls and insert jump tables where needed.
d978616
---
d978616
 src/cmd/link/internal/ld/data.go   | 52 +++++++++++++++++++++++++++++--
d978616
 src/cmd/link/internal/ld/elf.go    | 60 ++++++++++++++++++++++++++++++++---
d978616
 src/cmd/link/internal/ld/lib.go    | 20 ++++++++++++
d978616
 src/cmd/link/internal/ld/symtab.go | 64 ++++++++++++++++++++++++++++++++++++++
d978616
 src/cmd/link/internal/ppc64/asm.go | 12 ++++---
d978616
 src/runtime/symtab.go              | 34 +++++++++++++++-----
d978616
 src/runtime/type.go                | 16 +++++++++-
d978616
 7 files changed, 237 insertions(+), 21 deletions(-)
d978616
d978616
diff --git a/src/cmd/link/internal/ld/data.go b/src/cmd/link/internal/ld/data.go
d978616
index 57a0dad..58ce18c 100644
d978616
--- a/src/cmd/link/internal/ld/data.go
d978616
+++ b/src/cmd/link/internal/ld/data.go
d978616
@@ -527,7 +527,15 @@ func relocsym(s *LSym) {
d978616
 			o = Symaddr(r.Sym) + r.Add - int64(r.Sym.Sect.Vaddr)
d978616
 
d978616
 		case obj.R_ADDROFF:
d978616
-			o = Symaddr(r.Sym) - int64(r.Sym.Sect.Vaddr) + r.Add
d978616
+
d978616
+			// The method offset tables using this relocation expect the offset to be relative
d978616
+			// to the start of the first text section, even if there are multiple.
d978616
+
d978616
+			if Linkmode == LinkExternal && r.Sym.Sect.Name == ".text" && r.Sym.Sect.Vaddr != Segtext.Vaddr {
d978616
+				o = Symaddr(r.Sym) - int64(Segtext.Vaddr) + r.Add
d978616
+			} else {
d978616
+				o = Symaddr(r.Sym) - int64(r.Sym.Sect.Vaddr) + r.Add
d978616
+			}
d978616
 
d978616
 			// r->sym can be null when CALL $(constant) is transformed from absolute PC to relative PC call.
d978616
 		case obj.R_CALL, obj.R_GOTPCREL, obj.R_PCREL:
d978616
@@ -1926,6 +1934,7 @@ func textaddress() {
d978616
 	}
d978616
 	va := uint64(INITTEXT)
d978616
 	sect.Vaddr = va
d978616
+	n := 1
d978616
 	for _, sym := range Ctxt.Textp {
d978616
 		sym.Sect = sect
d978616
 		if sym.Type&obj.SSUB != 0 {
d978616
@@ -1948,9 +1957,30 @@ func textaddress() {
d978616
 		} else {
d978616
 			va += uint64(sym.Size)
d978616
 		}
d978616
+		// On ppc64x a text section should not be larger than 2^26 bytes due to the size of
d978616
+		// call target offset field in the bl instruction.  Splitting into text
d978616
+		// sections smaller than this limit allows the GNU linker to modify the long calls
d978616
+		// appropriately.  The limit allows for the space for linker tables.
d978616
+
d978616
+		// Only break at outermost syms.
d978616
+
d978616
+		if sym.Outer == nil && Iself && Linkmode == LinkExternal && SysArch.InFamily(sys.PPC64) && va-sect.Vaddr > uint64(0x1c00000) {
d978616
+
d978616
+			// Set the length for the previous text section
d978616
+			sect.Length = va - sect.Vaddr
d978616
+
d978616
+			// Create new section, set the starting Vaddr
d978616
+			sect = addsection(&Segtext, ".text", 05)
d978616
+			sect.Vaddr = va
d978616
+
d978616
+			// Create a symbol for the start and end of the secondary text section
d978616
+			Linklookup(Ctxt, fmt.Sprintf("runtime.text.%d", n), 0).Sect = sect
d978616
+			n++
d978616
+		}
d978616
 	}
d978616
 
d978616
 	sect.Length = va - sect.Vaddr
d978616
+	Linklookup(Ctxt, "runtime.etext", 0).Sect = sect
d978616
 }
d978616
 
d978616
 // assign addresses
d978616
@@ -2057,11 +2087,27 @@ func address() {
d978616
 	Segdwarf.Filelen = va - Segdwarf.Vaddr
d978616
 
d978616
 	text := Segtext.Sect
d978616
+	lasttext := text
d978616
 	var rodata *Section
d978616
 	if Segrodata.Sect != nil {
d978616
 		rodata = Segrodata.Sect
d978616
 	} else {
d978616
-		rodata = text.Next
d978616
+		// Could be multiple .text sections
d978616
+		n := 1
d978616
+		for sect := Segtext.Sect.Next; sect != nil; sect = sect.Next {
d978616
+			if sect.Name != ".text" {
d978616
+				break
d978616
+			}
d978616
+			lasttext = sect
d978616
+			symname := fmt.Sprintf("runtime.text.%d", n)
d978616
+			xdefine(symname, obj.STEXT, int64(sect.Vaddr))
d978616
+			n++
d978616
+		}
d978616
+
d978616
+		rodata = lasttext.Next
d978616
+		if rodata != nil && rodata.Name != ".rodata" {
d978616
+			Diag("Unexpected section order in text segment")
d978616
+		}
d978616
 	}
d978616
 	var relrodata *Section
d978616
 	typelink := rodata.Next
d978616
@@ -2108,7 +2154,7 @@ func address() {
d978616
 	}
d978616
 
d978616
 	xdefine("runtime.text", obj.STEXT, int64(text.Vaddr))
d978616
-	xdefine("runtime.etext", obj.STEXT, int64(text.Vaddr+text.Length))
d978616
+	xdefine("runtime.etext", obj.STEXT, int64(lasttext.Vaddr+lasttext.Length))
d978616
 	if HEADTYPE == obj.Hwindows {
d978616
 		xdefine(".text", obj.STEXT, int64(text.Vaddr))
d978616
 	}
d978616
diff --git a/src/cmd/link/internal/ld/elf.go b/src/cmd/link/internal/ld/elf.go
d978616
index 39d3609..ecb00c2 100644
d978616
--- a/src/cmd/link/internal/ld/elf.go
d978616
+++ b/src/cmd/link/internal/ld/elf.go
d978616
@@ -1623,6 +1623,25 @@ func elfshname(name string) *ElfShdr {
d978616
 	return nil
d978616
 }
d978616
 
d978616
+// Create an ElfShdr for the section with name.
d978616
+// A new one is created even if one already exists with
d978616
+// the same name.
d978616
+func elfshnamedup(name string) *ElfShdr {
d978616
+	var off int
d978616
+	var sh *ElfShdr
d978616
+
d978616
+	for i := 0; i < nelfstr; i++ {
d978616
+		if name == elfstr[i].s {
d978616
+			off = elfstr[i].off
d978616
+			sh = newElfShdr(int64(off))
d978616
+			return sh
d978616
+		}
d978616
+	}
d978616
+	Diag("cannot find elf name %s", name)
d978616
+	errorexit()
d978616
+	return nil
d978616
+}
d978616
+
d978616
 func elfshalloc(sect *Section) *ElfShdr {
d978616
 	sh := elfshname(sect.Name)
d978616
 	sect.Elfsect = sh
d978616
@@ -1630,7 +1649,17 @@ func elfshalloc(sect *Section) *ElfShdr {
d978616
 }
d978616
 
d978616
 func elfshbits(sect *Section) *ElfShdr {
d978616
-	sh := elfshalloc(sect)
d978616
+	var sh *ElfShdr
d978616
+
d978616
+	if sect.Name == ".text" {
d978616
+		if sect.Elfsect == nil {
d978616
+			sect.Elfsect = elfshnamedup(sect.Name)
d978616
+		}
d978616
+		sh = sect.Elfsect
d978616
+	} else {
d978616
+		sh = elfshalloc(sect)
d978616
+	}
d978616
+
d978616
 	// If this section has already been set up as a note, we assume type_ and
d978616
 	// flags are already correct, but the other fields still need filling in.
d978616
 	if sh.type_ == SHT_NOTE {
d978616
@@ -1706,6 +1735,15 @@ func elfshreloc(sect *Section) *ElfShdr {
d978616
 	}
d978616
 
d978616
 	sh := elfshname(elfRelType + sect.Name)
d978616
+
d978616
+	// There could be multiple text sections but each needs
d978616
+	// its own .rela.text.
d978616
+	if sect.Name == ".text" {
d978616
+		if sh.info != 0 && sh.info != uint32(sect.Elfsect.shnum) {
d978616
+			sh = elfshnamedup(elfRelType + sect.Name)
d978616
+		}
d978616
+	}
d978616
+
d978616
 	sh.type_ = uint32(typ)
d978616
 	sh.entsize = uint64(SysArch.RegSize) * 2
d978616
 	if typ == SHT_RELA {
d978616
@@ -1776,9 +1814,12 @@ func Elfemitreloc() {
d978616
 		Cput(0)
d978616
 	}
d978616
 
d978616
-	elfrelocsect(Segtext.Sect, Ctxt.Textp)
d978616
-	for sect := Segtext.Sect.Next; sect != nil; sect = sect.Next {
d978616
-		elfrelocsect(sect, datap)
d978616
+	for sect := Segtext.Sect; sect != nil; sect = sect.Next {
d978616
+		if sect.Name == ".text" {
d978616
+			elfrelocsect(sect, Ctxt.Textp)
d978616
+		} else {
d978616
+			elfrelocsect(sect, datap)
d978616
+		}
d978616
 	}
d978616
 	for sect := Segrodata.Sect; sect != nil; sect = sect.Next {
d978616
 		elfrelocsect(sect, datap)
d978616
@@ -2109,7 +2150,16 @@ func Asmbelfsetup() {
d978616
 	elfshname("")
d978616
 
d978616
 	for sect := Segtext.Sect; sect != nil; sect = sect.Next {
d978616
-		elfshalloc(sect)
d978616
+
d978616
+		// There could be multiple .text sections. Instead check the Elfsect
d978616
+		// field to determine if already has an ElfShdr and if not, create one.
d978616
+		if sect.Name == ".text" {
d978616
+			if sect.Elfsect == nil {
d978616
+				sect.Elfsect = elfshnamedup(sect.Name)
d978616
+			}
d978616
+		} else {
d978616
+			elfshalloc(sect)
d978616
+		}
d978616
 	}
d978616
 	for sect := Segrodata.Sect; sect != nil; sect = sect.Next {
d978616
 		elfshalloc(sect)
d978616
diff --git a/src/cmd/link/internal/ld/lib.go b/src/cmd/link/internal/ld/lib.go
d978616
index 14f4fa9..709e7ca 100644
d978616
--- a/src/cmd/link/internal/ld/lib.go
d978616
+++ b/src/cmd/link/internal/ld/lib.go
d978616
@@ -1956,6 +1956,26 @@ func genasmsym(put func(*LSym, string, int, int64, int64, int, *LSym)) {
d978616
 	if s.Type == obj.STEXT {
d978616
 		put(s, s.Name, 'T', s.Value, s.Size, int(s.Version), nil)
d978616
 	}
d978616
+	n := 0
d978616
+
d978616
+	// Generate base addresses for all text sections if there are multiple
d978616
+	for sect := Segtext.Sect; sect != nil; sect = sect.Next {
d978616
+		if n == 0 {
d978616
+			n++
d978616
+			continue
d978616
+		}
d978616
+		if sect.Name != ".text" {
d978616
+			break
d978616
+		}
d978616
+		s = Linkrlookup(Ctxt, fmt.Sprintf("runtime.text.%d", n), 0)
d978616
+		if s == nil {
d978616
+			break
d978616
+		}
d978616
+		if s.Type == obj.STEXT {
d978616
+			put(s, s.Name, 'T', s.Value, s.Size, int(s.Version), nil)
d978616
+		}
d978616
+		n++
d978616
+	}
d978616
 	s = Linklookup(Ctxt, "runtime.etext", 0)
d978616
 	if s.Type == obj.STEXT {
d978616
 		put(s, s.Name, 'T', s.Value, s.Size, int(s.Version), nil)
d978616
diff --git a/src/cmd/link/internal/ld/symtab.go b/src/cmd/link/internal/ld/symtab.go
d978616
index 06d7792..80eb33d 100644
d978616
--- a/src/cmd/link/internal/ld/symtab.go
d978616
+++ b/src/cmd/link/internal/ld/symtab.go
d978616
@@ -315,6 +315,62 @@ func (libs byPkg) Swap(a, b int) {
d978616
 	libs[a], libs[b] = libs[b], libs[a]
d978616
 }
d978616
 
d978616
+// Create a table with information on the text sections.
d978616
+
d978616
+func textsectionmap() uint32 {
d978616
+
d978616
+	t := Linklookup(Ctxt, "runtime.textsectionmap", 0)
d978616
+	t.Type = obj.SRODATA
d978616
+	t.Attr |= AttrReachable
d978616
+	nsections := int64(0)
d978616
+
d978616
+	for sect := Segtext.Sect; sect != nil; sect = sect.Next {
d978616
+		if sect.Name == ".text" {
d978616
+			nsections++
d978616
+		} else {
d978616
+			break
d978616
+		}
d978616
+	}
d978616
+	Symgrow(Ctxt, t, nsections*3*8)
d978616
+
d978616
+	off := int64(0)
d978616
+	n := 0
d978616
+
d978616
+	// The vaddr for each text section is the difference between the section's
d978616
+	// Vaddr and the Vaddr for the first text section as determined at compile
d978616
+	// time.
d978616
+
d978616
+	// The symbol name for the start address of the first text section is
d978616
+	// runtime.text.  Additional text sections are named runtime.text.n where n is the
d978616
+	// order of creation starting with 1. These symbols provide the section's
d978616
+	// start address after relocation by the linker.
d978616
+
d978616
+	textbase := Segtext.Sect.Vaddr
d978616
+	for sect := Segtext.Sect; sect != nil; sect = sect.Next {
d978616
+		if sect.Name != ".text" {
d978616
+			break
d978616
+		}
d978616
+		off = setuintxx(Ctxt, t, off, sect.Vaddr-textbase, int64(SysArch.IntSize))
d978616
+		off = setuintxx(Ctxt, t, off, sect.Length, int64(SysArch.IntSize))
d978616
+		if n == 0 {
d978616
+			s := Linkrlookup(Ctxt, "runtime.text", 0)
d978616
+			if s == nil {
d978616
+				Diag("Unable to find symbol runtime.text\n")
d978616
+			}
d978616
+			off = setaddr(Ctxt, t, off, s)
d978616
+
d978616
+		} else {
d978616
+			s := Linklookup(Ctxt, fmt.Sprintf("runtime.text.%d", n), 0)
d978616
+			if s == nil {
d978616
+				Diag("Unable to find symbol runtime.text.%d\n", n)
d978616
+			}
d978616
+			off = setaddr(Ctxt, t, off, s)
d978616
+		}
d978616
+		n++
d978616
+	}
d978616
+	return uint32(n)
d978616
+}
d978616
+
d978616
 func symtab() {
d978616
 	dosymtype()
d978616
 
d978616
@@ -489,6 +545,8 @@ func symtab() {
d978616
 		adduint(Ctxt, abihashgostr, uint64(hashsym.Size))
d978616
 	}
d978616
 
d978616
+	nsections := textsectionmap()
d978616
+
d978616
 	// Information about the layout of the executable image for the
d978616
 	// runtime to use. Any changes here must be matched by changes to
d978616
 	// the definition of moduledata in runtime/symtab.go.
d978616
@@ -527,6 +585,12 @@ func symtab() {
d978616
 	Addaddr(Ctxt, moduledata, Linklookup(Ctxt, "runtime.gcbss", 0))
d978616
 	Addaddr(Ctxt, moduledata, Linklookup(Ctxt, "runtime.types", 0))
d978616
 	Addaddr(Ctxt, moduledata, Linklookup(Ctxt, "runtime.etypes", 0))
d978616
+
d978616
+	// text section information
d978616
+	Addaddr(Ctxt, moduledata, Linklookup(Ctxt, "runtime.textsectionmap", 0))
d978616
+	adduint(Ctxt, moduledata, uint64(nsections))
d978616
+	adduint(Ctxt, moduledata, uint64(nsections))
d978616
+
d978616
 	// The typelinks slice
d978616
 	Addaddr(Ctxt, moduledata, Linklookup(Ctxt, "runtime.typelink", 0))
d978616
 	adduint(Ctxt, moduledata, uint64(ntypelinks))
d978616
diff --git a/src/cmd/link/internal/ppc64/asm.go b/src/cmd/link/internal/ppc64/asm.go
d978616
index bd2e23f..ab59fa8 100644
d978616
--- a/src/cmd/link/internal/ppc64/asm.go
d978616
+++ b/src/cmd/link/internal/ppc64/asm.go
d978616
@@ -813,12 +813,14 @@ func asmb() {
d978616
 		ld.Asmbelfsetup()
d978616
 	}
d978616
 
d978616
-	sect := ld.Segtext.Sect
d978616
-	ld.Cseek(int64(sect.Vaddr - ld.Segtext.Vaddr + ld.Segtext.Fileoff))
d978616
-	ld.Codeblk(int64(sect.Vaddr), int64(sect.Length))
d978616
-	for sect = sect.Next; sect != nil; sect = sect.Next {
d978616
+	for sect := ld.Segtext.Sect; sect != nil; sect = sect.Next {
d978616
 		ld.Cseek(int64(sect.Vaddr - ld.Segtext.Vaddr + ld.Segtext.Fileoff))
d978616
-		ld.Datblk(int64(sect.Vaddr), int64(sect.Length))
d978616
+		// Might have multiple text sections
d978616
+		if sect.Name == ".text" {
d978616
+			ld.Codeblk(int64(sect.Vaddr), int64(sect.Length))
d978616
+		} else {
d978616
+			ld.Datblk(int64(sect.Vaddr), int64(sect.Length))
d978616
+		}
d978616
 	}
d978616
 
d978616
 	if ld.Segrodata.Filelen > 0 {
d978616
diff --git a/src/runtime/symtab.go b/src/runtime/symtab.go
d978616
index 4f6fae2..23e80ce 100644
d978616
--- a/src/runtime/symtab.go
d978616
+++ b/src/runtime/symtab.go
d978616
@@ -195,8 +195,9 @@ type moduledata struct {
d978616
 	end, gcdata, gcbss    uintptr
d978616
 	types, etypes         uintptr
d978616
 
d978616
-	typelinks []int32 // offsets from types
d978616
-	itablinks []*itab
d978616
+	textsectmap []textsect
d978616
+	typelinks   []int32 // offsets from types
d978616
+	itablinks   []*itab
d978616
 
d978616
 	modulename   string
d978616
 	modulehashes []modulehash
d978616
@@ -226,6 +227,14 @@ type functab struct {
d978616
 	funcoff uintptr
d978616
 }
d978616
 
d978616
+// Mapping information for secondary text sections
d978616
+
d978616
+type textsect struct {
d978616
+	vaddr    uint64  // prelinked section vaddr
d978616
+	length   uint64  // section length
d978616
+	baseaddr uintptr // relocated section address
d978616
+}
d978616
+
d978616
 const minfunc = 16                 // minimum function size
d978616
 const pcbucketsize = 256 * minfunc // size of bucket in the pc->func lookup table
d978616
 
d978616
@@ -368,12 +377,23 @@ func findfunc(pc uintptr) *_func {
d978616
 	ffb := (*findfuncbucket)(add(unsafe.Pointer(datap.findfunctab), b*unsafe.Sizeof(findfuncbucket{})))
d978616
 	idx := ffb.idx + uint32(ffb.subbuckets[i])
d978616
 	if pc < datap.ftab[idx].entry {
d978616
-		throw("findfunc: bad findfunctab entry")
d978616
-	}
d978616
 
d978616
-	// linear search to find func with pc >= entry.
d978616
-	for datap.ftab[idx+1].entry <= pc {
d978616
-		idx++
d978616
+		// If there are multiple text sections then the buckets for the secondary
d978616
+		// text sections will be off because the addresses in those text sections
d978616
+		// were relocated to higher addresses.  Search back to find it.
d978616
+
d978616
+		for datap.ftab[idx].entry > pc && idx > 0 {
d978616
+			idx--
d978616
+		}
d978616
+		if idx == 0 {
d978616
+			throw("findfunc: bad findfunctab entry idx")
d978616
+		}
d978616
+	} else {
d978616
+
d978616
+		// linear search to find func with pc >= entry.
d978616
+		for datap.ftab[idx+1].entry <= pc {
d978616
+			idx++
d978616
+		}
d978616
 	}
d978616
 	return (*_func)(unsafe.Pointer(&datap.pclntable[datap.ftab[idx].funcoff]))
d978616
 }
d978616
diff --git a/src/runtime/type.go b/src/runtime/type.go
d978616
index 5ef11a4..f641adc 100644
d978616
--- a/src/runtime/type.go
d978616
+++ b/src/runtime/type.go
d978616
@@ -257,7 +257,21 @@ func (t *_type) textOff(off textOff) unsafe.Pointer {
d978616
 		}
d978616
 		return res
d978616
 	}
d978616
-	res := md.text + uintptr(off)
d978616
+	res := uintptr(0)
d978616
+
d978616
+	// Find the text section range that contains the offset to determine the section's base
d978616
+	// address.  In cases where there are multiple text sections, the base address might be
d978616
+	// relocated by the linker.
d978616
+
d978616
+	for i := 0; i < len(md.textsectmap); i++ {
d978616
+		sectaddr := md.textsectmap[i].vaddr
d978616
+		sectlen := md.textsectmap[i].length
d978616
+		if uint64(off) >= sectaddr && uint64(off) <= sectaddr+sectlen {
d978616
+			res = md.textsectmap[i].baseaddr + uintptr(off) - uintptr(md.textsectmap[i].vaddr)
d978616
+			break
d978616
+		}
d978616
+	}
d978616
+
d978616
 	if res > md.etext {
d978616
 		println("runtime: textOff", hex(off), "out of range", hex(md.text), "-", hex(md.etext))
d978616
 		throw("runtime: text offset out of range")