]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/link: optimize TLS IE to LE in build mode PIE
authorDavid Crawshaw <crawshaw@golang.org>
Tue, 6 Sep 2016 11:46:59 +0000 (07:46 -0400)
committerDavid Crawshaw <crawshaw@golang.org>
Sun, 11 Sep 2016 21:13:42 +0000 (21:13 +0000)
When cmd/compile generates position-independent code on linux
(the -shared flag), it refers to runtime.tlsg as a TLS IE variable.

When cmd/link is linking a PIE executable internally, all TLS IE
relocations are generated by cmd/compile, and the variable they
refer to, runtime.tlsg, is local to the binary. This means we can
optimize this particular IE case to LE, and thus implement IE
support when internally linking.

To do this optimization in the linker, we need to rewrite the
PC-relative MOVD to a constant load. This may seem like an
unconscionable act born of enthusiasm, but it turns out this is
standard operating procedure for linkers. GNU gold does exactly
the same optimization. I spent some time reading it and documented
at least one missing feature from this version.

Part of adding PIE internal linking on linux/amd64.

Change-Id: I1eb068d0ec774724944c6b308aa5084582ecde0b
Reviewed-on: https://go-review.googlesource.com/28540
Reviewed-by: Ian Lance Taylor <iant@golang.org>
Run-TryBot: Ian Lance Taylor <iant@golang.org>
Run-TryBot: David Crawshaw <crawshaw@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>

src/cmd/link/internal/amd64/asm.go
src/cmd/link/internal/amd64/obj.go
src/cmd/link/internal/ld/data.go
src/cmd/link/internal/ld/lib.go

index ea99e7f25975db5142d24eec1cd8ca8303da4bd5..9dd55727a0a1e51de017f62bad93b37f4f8eb490 100644 (file)
@@ -789,3 +789,36 @@ func asmb(ctxt *ld.Link) {
 
        ld.Cflush()
 }
+
+func tlsIEtoLE(s *ld.Symbol, off, size int) {
+       // Transform the PC-relative instruction into a constant load.
+       // That is,
+       //
+       //      MOVQ X(IP), REG  ->  MOVQ $Y, REG
+       //
+       // To determine the instruction and register, we study the op codes.
+       // Consult an AMD64 instruction encoding guide to decipher this.
+       op := s.P[off-3 : off]
+       reg := op[2] >> 3
+
+       if op[1] == 0x8b || reg == 4 {
+               // MOVQ
+               if op[0] == 0x4c {
+                       op[0] = 0x49
+               } else if size == 4 && op[0] == 0x44 {
+                       op[0] = 0x41
+               }
+               if op[1] == 0x8b {
+                       op[1] = 0xc7
+               } else {
+                       op[1] = 0x81 // special case for SP
+               }
+               op[2] = 0xc0 | reg
+       } else {
+               // An alternate op is ADDQ. This is handled by GNU gold,
+               // but right now is not generated by the Go compiler:
+               //      ADDQ X(IP), REG  ->  ADDQ $Y, REG
+               // Consider adding support for it here.
+               log.Fatalf("expected TLS IE op to be MOVQ, got %v", op)
+       }
+}
index 4b815c771dbe98f7847dc6e16c17002739ef56e2..0494050d8642b926aaf78907b4c3f5a8d239827c 100644 (file)
@@ -73,6 +73,7 @@ func linkarchinit() {
        ld.Thearch.Append16 = ld.Append16l
        ld.Thearch.Append32 = ld.Append32l
        ld.Thearch.Append64 = ld.Append64l
+       ld.Thearch.TLSIEtoLE = tlsIEtoLE
 
        ld.Thearch.Linuxdynld = "/lib64/ld-linux-x86-64.so.2"
        ld.Thearch.Freebsddynld = "/libexec/ld-elf.so.1"
index 73e2717ed991ec59fef299e6b07e4ea396cab919..fd536181d76f940e96d682337d3eb189b0704ceb 100644 (file)
@@ -448,7 +448,17 @@ func relocsym(ctxt *Link, s *Symbol) {
                                }
                                break
                        }
-                       log.Fatalf("cannot handle R_TLS_IE when linking internally")
+                       if Buildmode == BuildmodePIE && Iself {
+                               // We are linking the final executable, so we
+                               // can optimize any TLS IE relocation to LE.
+                               if Thearch.TLSIEtoLE == nil {
+                                       log.Fatalf("internal linking of TLS IE not supported on %s", SysArch.Family)
+                               }
+                               Thearch.TLSIEtoLE(s, int(off), int(r.Siz))
+                               o = int64(ctxt.Tlsoffset)
+                       } else {
+                               log.Fatalf("cannot handle R_TLS_IE (sym %s) when linking internally", s.Name)
+                       }
 
                case obj.R_ADDR:
                        if Linkmode == LinkExternal && r.Sym.Type != obj.SCONST {
index 76047905be8167995039d9f41d9f46ae1075d6d6..7750f1dc68f30ab3da39ee3eb6a05c9997bf6db8 100644 (file)
@@ -111,6 +111,14 @@ type Arch struct {
        Append16         func(b []byte, v uint16) []byte
        Append32         func(b []byte, v uint32) []byte
        Append64         func(b []byte, v uint64) []byte
+
+       // TLSIEtoLE converts a TLS Initial Executable relocation to
+       // a TLS Local Executable relocation.
+       //
+       // This is possible when a TLS IE relocation refers to a local
+       // symbol in an executable, which is typical when internally
+       // linking PIE binaries.
+       TLSIEtoLE func(s *Symbol, off, size int)
 }
 
 var (