o = Symaddr(r.Sym) + r.Add - int64(r.Sym.Sect.Vaddr)
case obj.R_ADDROFF:
- o = Symaddr(r.Sym) - int64(r.Sym.Sect.Vaddr) + r.Add
+ // The method offset tables using this relocation expect the offset to be relative
+ // to the start of the first text section, even if there are multiple.
+
+ if r.Sym.Sect.Name == ".text" {
+ o = Symaddr(r.Sym) - int64(Segtext.Vaddr) + r.Add
+ } else {
+ o = Symaddr(r.Sym) - int64(r.Sym.Sect.Vaddr) + r.Add
+ }
// r->sym can be null when CALL $(constant) is transformed from absolute PC to relative PC call.
case obj.R_CALL, obj.R_GOTPCREL, obj.R_PCREL:
sect.Align = int32(Funcalign)
ctxt.Syms.Lookup("runtime.text", 0).Sect = sect
- ctxt.Syms.Lookup("runtime.etext", 0).Sect = sect
if Headtype == obj.Hwindows || Headtype == obj.Hwindowsgui {
ctxt.Syms.Lookup(".text", 0).Sect = sect
}
va := uint64(*FlagTextAddr)
+ n := 1
sect.Vaddr = va
for _, sym := range ctxt.Textp {
sym.Sect = sect
for sub := sym; sub != nil; sub = sub.Sub {
sub.Value += int64(va)
}
- if sym.Size < MINFUNC {
- va += MINFUNC // spacing required for findfunctab
- } else {
- va += uint64(sym.Size)
+ funcsize := uint64(MINFUNC) // spacing required for findfunctab
+ if sym.Size > MINFUNC {
+ funcsize = uint64(sym.Size)
}
+
+ // On ppc64x a text section should not be larger than 2^26 bytes due to the size of
+ // call target offset field in the bl instruction. Splitting into smaller text
+ // sections smaller than this limit allows the GNU linker to modify the long calls
+ // appropriately. The limit allows for the space needed for tables inserted by the linker.
+
+ // If this function doesn't fit in the current text section, then create a new one.
+
+ // Only break at outermost syms.
+
+ if SysArch.InFamily(sys.PPC64) && sym.Outer == nil && Iself && Linkmode == LinkExternal && va-sect.Vaddr+funcsize > 0x1c00000 {
+
+ // Set the length for the previous text section
+ sect.Length = va - sect.Vaddr
+
+ // Create new section, set the starting Vaddr
+ sect = addsection(&Segtext, ".text", 05)
+ sect.Vaddr = va
+
+ // Create a symbol for the start of the secondary text sections
+ ctxt.Syms.Lookup(fmt.Sprintf("runtime.text.%d", n), 0).Sect = sect
+ n++
+ }
+ va += funcsize
}
sect.Length = va - sect.Vaddr
+ ctxt.Syms.Lookup("runtime.etext", 0).Sect = sect
}
// assign addresses
pclntab = ctxt.Syms.Lookup("runtime.pclntab", 0).Sect
types = ctxt.Syms.Lookup("runtime.types", 0).Sect
)
+ lasttext := text
+ // Could be multiple .text sections
+ for sect := text.Next; sect != nil && sect.Name == ".text"; sect = sect.Next {
+ lasttext = sect
+ }
for _, s := range datap {
if s.Sect != nil {
}
ctxt.xdefine("runtime.text", obj.STEXT, int64(text.Vaddr))
- ctxt.xdefine("runtime.etext", obj.STEXT, int64(text.Vaddr+text.Length))
+ ctxt.xdefine("runtime.etext", obj.STEXT, int64(lasttext.Vaddr+lasttext.Length))
if Headtype == obj.Hwindows || Headtype == obj.Hwindowsgui {
ctxt.xdefine(".text", obj.STEXT, int64(text.Vaddr))
}
+
+ // If there are multiple text sections, create runtime.text.n for
+ // their section Vaddr, using n for index
+ n := 1
+ for sect := Segtext.Sect.Next; sect != nil && sect.Name == ".text"; sect = sect.Next {
+ symname := fmt.Sprintf("runtime.text.%d", n)
+ ctxt.xdefine(symname, obj.STEXT, int64(sect.Vaddr))
+ n++
+ }
+
ctxt.xdefine("runtime.rodata", obj.SRODATA, int64(rodata.Vaddr))
ctxt.xdefine("runtime.erodata", obj.SRODATA, int64(rodata.Vaddr+rodata.Length))
ctxt.xdefine("runtime.types", obj.SRODATA, int64(types.Vaddr))
* written in the 32-bit format on the 32-bit machines.
*/
const (
- NSECT = 48
+ NSECT = 400
)
var (
return nil
}
+// Create an ElfShdr for the section with name.
+// Create a duplicate if one already exists with that name
+func elfshnamedup(name string) *ElfShdr {
+ var off int
+ var sh *ElfShdr
+
+ for i := 0; i < nelfstr; i++ {
+ if name == elfstr[i].s {
+ off = elfstr[i].off
+ sh = newElfShdr(int64(off))
+ return sh
+ }
+ }
+
+ Errorf(nil, "cannot find elf name %s", name)
+ errorexit()
+ return nil
+}
+
func elfshalloc(sect *Section) *ElfShdr {
sh := elfshname(sect.Name)
sect.Elfsect = sh
}
func elfshbits(sect *Section) *ElfShdr {
- sh := elfshalloc(sect)
+ var sh *ElfShdr
+
+ if sect.Name == ".text" {
+ if sect.Elfsect == nil {
+ sect.Elfsect = elfshnamedup(sect.Name)
+ }
+ sh = sect.Elfsect
+ } else {
+ sh = elfshalloc(sect)
+ }
+
// If this section has already been set up as a note, we assume type_ and
// flags are already correct, but the other fields still need filling in.
if sh.type_ == SHT_NOTE {
}
sh := elfshname(elfRelType + sect.Name)
+ // There could be multiple text sections but each needs
+ // its own .rela.text.
+
+ if sect.Name == ".text" {
+ if sh.info != 0 && sh.info != uint32(sect.Elfsect.shnum) {
+ sh = elfshnamedup(elfRelType + sect.Name)
+ }
+ }
+
sh.type_ = uint32(typ)
sh.entsize = uint64(SysArch.RegSize) * 2
if typ == SHT_RELA {
Cput(0)
}
- elfrelocsect(ctxt, Segtext.Sect, ctxt.Textp)
- for sect := Segtext.Sect.Next; sect != nil; sect = sect.Next {
- elfrelocsect(ctxt, sect, datap)
+ for sect := Segtext.Sect; sect != nil; sect = sect.Next {
+ if sect.Name == ".text" {
+ elfrelocsect(ctxt, sect, ctxt.Textp)
+ } else {
+ elfrelocsect(ctxt, sect, datap)
+ }
}
+
for sect := Segrodata.Sect; sect != nil; sect = sect.Next {
elfrelocsect(ctxt, sect, datap)
}
elfshname("")
for sect := Segtext.Sect; sect != nil; sect = sect.Next {
- elfshalloc(sect)
+ // There could be multiple .text sections. Instead check the Elfsect
+ // field to determine if already has an ElfShdr and if not, create one.
+ if sect.Name == ".text" {
+ if sect.Elfsect == nil {
+ sect.Elfsect = elfshnamedup(sect.Name)
+ }
+ } else {
+ elfshalloc(sect)
+ }
}
for sect := Segrodata.Sect; sect != nil; sect = sect.Next {
elfshalloc(sect)
}
elfreserve := int64(ELFRESERVE)
+
+ numtext := int64(0)
+ for sect := Segtext.Sect; sect != nil; sect = sect.Next {
+ if sect.Name == ".text" {
+ numtext++
+ }
+ }
+
+ // If there are multiple text sections, extra space is needed
+ // in the elfreserve for the additional .text and .rela.text
+ // section headers. It can handle 4 extra now. Headers are
+ // 64 bytes.
+
+ if numtext > 4 {
+ elfreserve += elfreserve + numtext*64*2
+ }
+
startva := *FlagTextAddr - int64(HEADR)
resoff := elfreserve
}
if a > elfreserve {
- Errorf(nil, "ELFRESERVE too small: %d > %d", a, elfreserve)
+ Errorf(nil, "ELFRESERVE too small: %d > %d with %d text sections", a, elfreserve, numtext)
}
}
if s.Type == obj.STEXT {
put(ctxt, s, s.Name, TextSym, s.Value, nil)
}
+
+ n := 0
+
+ // Generate base addresses for all text sections if there are multiple
+ for sect := Segtext.Sect; sect != nil; sect = sect.Next {
+ if n == 0 {
+ n++
+ continue
+ }
+ if sect.Name != ".text" {
+ break
+ }
+ s = ctxt.Syms.ROLookup(fmt.Sprintf("runtime.text.%d", n), 0)
+ if s == nil {
+ break
+ }
+ if s.Type == obj.STEXT {
+ put(ctxt, s, s.Name, TextSym, s.Value, nil)
+ }
+ n++
+ }
+
s = ctxt.Syms.Lookup("runtime.etext", 0)
if s.Type == obj.STEXT {
put(ctxt, s, s.Name, TextSym, s.Value, nil)
libs[a], libs[b] = libs[b], libs[a]
}
+// Create a table with information on the text sections.
+
+func textsectionmap(ctxt *Link) uint32 {
+
+ t := ctxt.Syms.Lookup("runtime.textsectionmap", 0)
+ t.Type = obj.SRODATA
+ t.Attr |= AttrReachable
+ nsections := int64(0)
+
+ for sect := Segtext.Sect; sect != nil; sect = sect.Next {
+ if sect.Name == ".text" {
+ nsections++
+ } else {
+ break
+ }
+ }
+ Symgrow(t, nsections*(2*int64(SysArch.IntSize)+int64(SysArch.PtrSize)))
+
+ off := int64(0)
+ n := 0
+
+ // The vaddr for each text section is the difference between the section's
+ // Vaddr and the Vaddr for the first text section as determined at compile
+ // time.
+
+ // The symbol for the first text section is named runtime.text as before.
+ // Additional text sections are named runtime.text.n where n is the
+ // order of creation starting with 1. These symbols provide the section's
+ // address after relocation by the linker.
+
+ textbase := Segtext.Sect.Vaddr
+ for sect := Segtext.Sect; sect != nil; sect = sect.Next {
+ if sect.Name != ".text" {
+ break
+ }
+ off = setuintxx(ctxt, t, off, sect.Vaddr-textbase, int64(SysArch.IntSize))
+ off = setuintxx(ctxt, t, off, sect.Length, int64(SysArch.IntSize))
+ if n == 0 {
+ s := ctxt.Syms.ROLookup("runtime.text", 0)
+ if s == nil {
+ Errorf(nil, "Unable to find symbol runtime.text\n")
+ }
+ off = setaddr(ctxt, t, off, s)
+
+ } else {
+ s := ctxt.Syms.Lookup(fmt.Sprintf("runtime.text.%d", n), 0)
+ if s == nil {
+ Errorf(nil, "Unable to find symbol runtime.text.%d\n", n)
+ }
+ off = setaddr(ctxt, t, off, s)
+ }
+ n++
+ }
+ return uint32(n)
+}
+
func (ctxt *Link) symtab() {
dosymtype(ctxt)
adduint(ctxt, abihashgostr, uint64(hashsym.Size))
}
+ nsections := textsectionmap(ctxt)
+
// Information about the layout of the executable image for the
// runtime to use. Any changes here must be matched by changes to
// the definition of moduledata in runtime/symtab.go.
Addaddr(ctxt, moduledata, ctxt.Syms.Lookup("runtime.gcbss", 0))
Addaddr(ctxt, moduledata, ctxt.Syms.Lookup("runtime.types", 0))
Addaddr(ctxt, moduledata, ctxt.Syms.Lookup("runtime.etypes", 0))
+
+ // text section information
+ Addaddr(ctxt, moduledata, ctxt.Syms.Lookup("runtime.textsectionmap", 0))
+ adduint(ctxt, moduledata, uint64(nsections))
+ adduint(ctxt, moduledata, uint64(nsections))
+
// The typelinks slice
Addaddr(ctxt, moduledata, ctxt.Syms.Lookup("runtime.typelink", 0))
adduint(ctxt, moduledata, uint64(ntypelinks))
ld.Asmbelfsetup()
}
- sect := ld.Segtext.Sect
- ld.Cseek(int64(sect.Vaddr - ld.Segtext.Vaddr + ld.Segtext.Fileoff))
- ld.Codeblk(ctxt, int64(sect.Vaddr), int64(sect.Length))
- for sect = sect.Next; sect != nil; sect = sect.Next {
+ for sect := ld.Segtext.Sect; sect != nil; sect = sect.Next {
ld.Cseek(int64(sect.Vaddr - ld.Segtext.Vaddr + ld.Segtext.Fileoff))
- ld.Datblk(ctxt, int64(sect.Vaddr), int64(sect.Length))
+ // Handle additional text sections with Codeblk
+ if sect.Name == ".text" {
+ ld.Codeblk(ctxt, int64(sect.Vaddr), int64(sect.Length))
+ } else {
+ ld.Datblk(ctxt, int64(sect.Vaddr), int64(sect.Length))
+ }
}
if ld.Segrodata.Filelen > 0 {
--- /dev/null
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This program generates a test to verify that a program can be
+// successfully linked even when there are very large text
+// sections present.
+
+package main
+
+import (
+ "bytes"
+ "cmd/internal/obj"
+ "fmt"
+ "io/ioutil"
+ "os"
+ "os/exec"
+ "testing"
+)
+
+func TestLargeText(t *testing.T) {
+
+ var w bytes.Buffer
+
+ if testing.Short() || (obj.GOARCH != "ppc64le" && obj.GOARCH != "ppc64") {
+ t.Skip("Skipping large text section test in short mode or if not ppc64x")
+ }
+ const FN = 4
+ tmpdir, err := ioutil.TempDir("", "bigtext")
+
+ defer os.RemoveAll(tmpdir)
+
+ // Generate the scenario where the total amount of text exceeds the
+ // limit for the bl instruction, on RISC architectures like ppc64le,
+ // which is 2^26. When that happens the call requires special trampolines or
+ // long branches inserted by the linker where supported.
+
+ // Multiple .s files are generated instead of one.
+
+ for j := 0; j < FN; j++ {
+ testname := fmt.Sprintf("bigfn%d", j)
+ fmt.Fprintf(&w, "TEXT ยท%s(SB),$0\n", testname)
+ for i := 0; i < 2200000; i++ {
+ fmt.Fprintf(&w, "\tMOVD\tR0,R3\n")
+ }
+ fmt.Fprintf(&w, "\tRET\n")
+ err := ioutil.WriteFile(tmpdir+"/"+testname+".s", w.Bytes(), 0666)
+ if err != nil {
+ t.Fatalf("can't write output: %v\n", err)
+ }
+ w.Reset()
+ }
+ fmt.Fprintf(&w, "package main\n")
+ fmt.Fprintf(&w, "\nimport (\n")
+ fmt.Fprintf(&w, "\t\"os\"\n")
+ fmt.Fprintf(&w, "\t\"fmt\"\n")
+ fmt.Fprintf(&w, ")\n\n")
+
+ for i := 0; i < FN; i++ {
+ fmt.Fprintf(&w, "func bigfn%d()\n", i)
+ }
+ fmt.Fprintf(&w, "\nfunc main() {\n")
+
+ // There are lots of dummy code generated in the .s files just to generate a lot
+ // of text. Link them in but guard their call so their code is not executed but
+ // the main part of the program can be run.
+
+ fmt.Fprintf(&w, "\tif os.Getenv(\"LINKTESTARG\") != \"\" {\n")
+ for i := 0; i < FN; i++ {
+ fmt.Fprintf(&w, "\t\tbigfn%d()\n", i)
+ }
+ fmt.Fprintf(&w, "\t}\n")
+ fmt.Fprintf(&w, "\tfmt.Printf(\"PASS\\n\")\n")
+ fmt.Fprintf(&w, "}")
+ err = ioutil.WriteFile(tmpdir+"/bigfn.go", w.Bytes(), 0666)
+ if err != nil {
+ t.Fatalf("can't write output: %v\n", err)
+ }
+
+ os.Chdir(tmpdir)
+ cmd := exec.Command("go", "build", "-o", "bigtext", "-ldflags", "'-linkmode=external'")
+ out, err := cmd.CombinedOutput()
+ if err != nil {
+ t.Fatalf("Build of big text program failed: %v, output: %s", err, out)
+ }
+ cmd = exec.Command(tmpdir + "/bigtext")
+ out, err = cmd.CombinedOutput()
+ if err != nil {
+ t.Fatalf("Program failed with err %v, output: %s", err, out)
+ }
+}
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
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
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]))
}
}
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")