mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
cmd/link: insert trampolines for too-far jumps on ARM
ARM direct CALL/JMP instruction has 24 bit offset, which can only
encodes jumps within +/-32M. When the target is too far, the top
bits get truncated and the program jumps wild.
This CL detects too-far jumps and automatically insert trampolines,
currently only internal linking on ARM.
It is necessary to make the following changes to the linker:
- Resolve direct jump relocs when assigning addresses to functions.
this allows trampoline insertion without moving all code that
already laid down.
- Lay down packages in dependency order, so that when resolving a
inter-package direct jump reloc, the target address is already
known. Intra-package jumps are assumed never too far.
- a linker flag -debugtramp is added for debugging trampolines:
"-debugtramp=1 -v" prints trampoline debug message
"-debugtramp=2" forces all inter-package jump to use
trampolines (currently ARM only)
"-debugtramp=2 -v" does both
- Some data structures are changed for bookkeeping.
On ARM, pseudo DIV/DIVU/MOD/MODU instructions now clobber R8
(unfortunate). In the standard library there is no ARM assembly
code that uses these instructions, and the compiler no longer emits
them (CL 29390).
all.bash passes with -debugtramp=2, except a disassembly test (this
is unavoidable as we changed the instruction).
TBD: debug info of trampolines?
Fixes #17028.
Change-Id: Idcce347ea7e0af77c4079041a160b2f6e114b474
Reviewed-on: https://go-review.googlesource.com/29397
Reviewed-by: David Crawshaw <crawshaw@golang.org>
Run-TryBot: Cherry Zhang <cherryyz@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
This commit is contained in:
parent
d03e8b226c
commit
7c431cb7f9
16 changed files with 359 additions and 112 deletions
|
|
@ -99,6 +99,7 @@ type Arch struct {
|
|||
Archinit func(*Link)
|
||||
Archreloc func(*Link, *Reloc, *Symbol, *int64) int
|
||||
Archrelocvariant func(*Link, *Reloc, *Symbol, int64) int64
|
||||
Trampoline func(*Link, *Reloc, *Symbol)
|
||||
Asmb func(*Link)
|
||||
Elfreloc1 func(*Link, *Reloc, int64) int
|
||||
Elfsetupplt func(*Link)
|
||||
|
|
@ -331,8 +332,7 @@ func errorexit() {
|
|||
Exit(0)
|
||||
}
|
||||
|
||||
func loadinternal(ctxt *Link, name string) {
|
||||
found := 0
|
||||
func loadinternal(ctxt *Link, name string) *Library {
|
||||
for i := 0; i < len(ctxt.Libdir); i++ {
|
||||
if *FlagLinkshared {
|
||||
shlibname := filepath.Join(ctxt.Libdir[i], name+".shlibname")
|
||||
|
|
@ -340,9 +340,7 @@ func loadinternal(ctxt *Link, name string) {
|
|||
ctxt.Logf("searching for %s.a in %s\n", name, shlibname)
|
||||
}
|
||||
if _, err := os.Stat(shlibname); err == nil {
|
||||
addlibpath(ctxt, "internal", "internal", "", name, shlibname)
|
||||
found = 1
|
||||
break
|
||||
return addlibpath(ctxt, "internal", "internal", "", name, shlibname)
|
||||
}
|
||||
}
|
||||
pname := filepath.Join(ctxt.Libdir[i], name+".a")
|
||||
|
|
@ -350,15 +348,12 @@ func loadinternal(ctxt *Link, name string) {
|
|||
ctxt.Logf("searching for %s.a in %s\n", name, pname)
|
||||
}
|
||||
if _, err := os.Stat(pname); err == nil {
|
||||
addlibpath(ctxt, "internal", "internal", pname, name, "")
|
||||
found = 1
|
||||
break
|
||||
return addlibpath(ctxt, "internal", "internal", pname, name, "")
|
||||
}
|
||||
}
|
||||
|
||||
if found == 0 {
|
||||
ctxt.Logf("warning: unable to find %s.a\n", name)
|
||||
}
|
||||
ctxt.Logf("warning: unable to find %s.a\n", name)
|
||||
return nil
|
||||
}
|
||||
|
||||
// findLibPathCmd uses cmd command to find gcc library libname.
|
||||
|
|
@ -617,6 +612,38 @@ func (ctxt *Link) loadlib() {
|
|||
}
|
||||
|
||||
importcycles()
|
||||
|
||||
// put symbols into Textp
|
||||
// do it in postorder so that packages are laid down in dependency order
|
||||
// internal first, then everything else
|
||||
ctxt.Library = postorder(ctxt.Library)
|
||||
for _, doInternal := range [2]bool{true, false} {
|
||||
for _, lib := range ctxt.Library {
|
||||
if isRuntimeDepPkg(lib.Pkg) != doInternal {
|
||||
continue
|
||||
}
|
||||
ctxt.Textp = append(ctxt.Textp, lib.textp...)
|
||||
for _, s := range lib.dupTextSyms {
|
||||
if !s.Attr.OnList() {
|
||||
ctxt.Textp = append(ctxt.Textp, s)
|
||||
s.Attr |= AttrOnList
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(ctxt.Shlibs) > 0 {
|
||||
// We might have overwritten some functions above (this tends to happen for the
|
||||
// autogenerated type equality/hashing functions) and we don't want to generated
|
||||
// pcln table entries for these any more so remove them from Textp.
|
||||
textp := make([]*Symbol, 0, len(ctxt.Textp))
|
||||
for _, s := range ctxt.Textp {
|
||||
if s.Type != obj.SDYNIMPORT {
|
||||
textp = append(textp, s)
|
||||
}
|
||||
}
|
||||
ctxt.Textp = textp
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -671,7 +698,7 @@ func objfile(ctxt *Link, lib *Library) {
|
|||
l := f.Seek(0, 2)
|
||||
|
||||
f.Seek(0, 0)
|
||||
ldobj(ctxt, f, pkg, l, lib.File, lib.File, FileObj)
|
||||
ldobj(ctxt, f, lib, l, lib.File, lib.File, FileObj)
|
||||
f.Close()
|
||||
|
||||
return
|
||||
|
|
@ -733,7 +760,7 @@ func objfile(ctxt *Link, lib *Library) {
|
|||
|
||||
pname = fmt.Sprintf("%s(%s)", lib.File, arhdr.name)
|
||||
l = atolwhex(arhdr.size)
|
||||
ldobj(ctxt, f, pkg, l, pname, lib.File, ArchiveObj)
|
||||
ldobj(ctxt, f, lib, l, pname, lib.File, ArchiveObj)
|
||||
}
|
||||
|
||||
out:
|
||||
|
|
@ -1219,9 +1246,10 @@ func hostlinkArchArgs() []string {
|
|||
// ldobj loads an input object. If it is a host object (an object
|
||||
// compiled by a non-Go compiler) it returns the Hostobj pointer. If
|
||||
// it is a Go object, it returns nil.
|
||||
func ldobj(ctxt *Link, f *bio.Reader, pkg string, length int64, pn string, file string, whence int) *Hostobj {
|
||||
eof := f.Offset() + length
|
||||
func ldobj(ctxt *Link, f *bio.Reader, lib *Library, length int64, pn string, file string, whence int) *Hostobj {
|
||||
pkg := pathtoprefix(lib.Pkg)
|
||||
|
||||
eof := f.Offset() + length
|
||||
start := f.Offset()
|
||||
c1 := bgetc(f)
|
||||
c2 := bgetc(f)
|
||||
|
|
@ -1308,7 +1336,7 @@ func ldobj(ctxt *Link, f *bio.Reader, pkg string, length int64, pn string, file
|
|||
ldpkg(ctxt, f, pkg, import1-import0-2, pn, whence) // -2 for !\n
|
||||
f.Seek(import1, 0)
|
||||
|
||||
LoadObjFile(ctxt, f, pkg, eof-f.Offset(), pn)
|
||||
LoadObjFile(ctxt, f, lib, eof-f.Offset(), pn)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -1476,17 +1504,6 @@ func ldshlibsyms(ctxt *Link, shlib string) {
|
|||
}
|
||||
}
|
||||
|
||||
// We might have overwritten some functions above (this tends to happen for the
|
||||
// autogenerated type equality/hashing functions) and we don't want to generated
|
||||
// pcln table entries for these any more so remove them from Textp.
|
||||
textp := make([]*Symbol, 0, len(ctxt.Textp))
|
||||
for _, s := range ctxt.Textp {
|
||||
if s.Type != obj.SDYNIMPORT {
|
||||
textp = append(textp, s)
|
||||
}
|
||||
}
|
||||
ctxt.Textp = textp
|
||||
|
||||
ctxt.Shlibs = append(ctxt.Shlibs, Shlib{Path: libpath, Hash: hash, Deps: deps, File: f, gcdataAddresses: gcdataAddresses})
|
||||
}
|
||||
|
||||
|
|
@ -2084,3 +2101,34 @@ func bgetc(r *bio.Reader) int {
|
|||
}
|
||||
return int(c)
|
||||
}
|
||||
|
||||
type markKind uint8 // for postorder traversal
|
||||
const (
|
||||
unvisited markKind = iota
|
||||
visiting
|
||||
visited
|
||||
)
|
||||
|
||||
func postorder(libs []*Library) []*Library {
|
||||
order := make([]*Library, 0, len(libs)) // hold the result
|
||||
mark := make(map[*Library]markKind, len(libs))
|
||||
for _, lib := range libs {
|
||||
dfs(lib, mark, &order)
|
||||
}
|
||||
return order
|
||||
}
|
||||
|
||||
func dfs(lib *Library, mark map[*Library]markKind, order *[]*Library) {
|
||||
if mark[lib] == visited {
|
||||
return
|
||||
}
|
||||
if mark[lib] == visiting {
|
||||
panic("found import cycle while visiting " + lib.Pkg)
|
||||
}
|
||||
mark[lib] = visiting
|
||||
for _, i := range lib.imports {
|
||||
dfs(i, mark, order)
|
||||
}
|
||||
mark[lib] = visited
|
||||
*order = append(*order, lib)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue