]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/internal/obj/x86, cmd/internal/ld, cmd/6l: 6g/asm -dynlink accesses global data...
authorMichael Hudson-Doyle <michael.hudson@canonical.com>
Mon, 30 Mar 2015 00:49:25 +0000 (00:49 +0000)
committerIan Lance Taylor <iant@golang.org>
Fri, 10 Apr 2015 03:47:47 +0000 (03:47 +0000)
Change-Id: I49862e177045369d6c94d6a58afbdace4f13cc96
Reviewed-on: https://go-review.googlesource.com/8237
Reviewed-by: Ian Lance Taylor <iant@golang.org>
13 files changed:
src/cmd/6g/galign.go
src/cmd/6g/reg.go
src/cmd/6l/asm.go
src/cmd/asm/internal/flags/flags.go
src/cmd/asm/main.go
src/cmd/internal/gc/lex.go
src/cmd/internal/ld/data.go
src/cmd/internal/ld/link.go
src/cmd/internal/obj/link.go
src/cmd/internal/obj/util.go
src/cmd/internal/obj/x86/asm6.go
src/cmd/internal/obj/x86/obj6.go
src/cmd/internal/obj/x86/obj6_test.go [new file with mode: 0644]

index 74be60e5ee49f8c77d11e050ce0abfb489a0f06d..fb31710a148c3d4376df8b214291912bb71725a0 100644 (file)
@@ -61,6 +61,9 @@ func betypeinit() {
                typedefs[2].Sameas = gc.TUINT32
        }
 
+       if gc.Ctxt.Flag_dynlink {
+               gc.Thearch.ReservedRegs = append(gc.Thearch.ReservedRegs, x86.REG_R15)
+       }
 }
 
 func main() {
index dd06bc54d5f9d2551d54d9ff51cb768d15ac1bc9..7ad31f924a671a260673ccf8ecd0a13a10f7949b 100644 (file)
@@ -123,7 +123,9 @@ func BtoR(b uint64) int {
                // BP is part of the calling convention if framepointer_enabled.
                b &^= (1 << (x86.REG_BP - x86.REG_AX))
        }
-
+       if gc.Ctxt.Flag_dynlink {
+               b &^= (1 << (x86.REG_R15 - x86.REG_AX))
+       }
        if b == 0 {
                return 0
        }
index 07bb7c418f4f27e63997b8b42da145924f9f344b..329eb657a94dd7b34a202d2163c069372cbeb8a2 100644 (file)
@@ -332,6 +332,13 @@ func elfreloc1(r *ld.Reloc, sectoff int64) int {
                        return -1
                }
 
+       case ld.R_GOTPCREL:
+               if r.Siz == 4 {
+                       ld.Thearch.Vput(ld.R_X86_64_GOTPCREL | uint64(elfsym)<<32)
+               } else {
+                       return -1
+               }
+
        case ld.R_TLS:
                if r.Siz == 4 {
                        if ld.Buildmode == ld.BuildmodeCShared {
index 0fa997f06ece667bff053224b414cd1513874178..c74f26974a2fdfe54374ae62cb95115dcecf8666 100644 (file)
@@ -19,6 +19,7 @@ var (
        PrintOut   = flag.Bool("S", false, "print assembly and machine code")
        TrimPath   = flag.String("trimpath", "", "remove prefix from recorded source file paths")
        Shared     = flag.Bool("shared", false, "generate code that can be linked into a shared library")
+       Dynlink    = flag.Bool("dynlink", false, "support references to Go symbols defined in other shared libraries")
 )
 
 var (
index 690ec2ef893024ec7e641595947cb3ec247e47df..9b07dd22e13eb0631ff56699a9c0714915d3f0be 100644 (file)
@@ -41,7 +41,8 @@ func main() {
                ctxt.Debugasm = 1
        }
        ctxt.Trimpath = *flags.TrimPath
-       if *flags.Shared {
+       ctxt.Flag_dynlink = *flags.Dynlink
+       if *flags.Shared || *flags.Dynlink {
                ctxt.Flag_shared = 1
        }
        ctxt.Bso = obj.Binitw(os.Stdout)
index 774b9a62451e69a00540c1da76d3ab5a7f21a057..1f0e88375cc99b4865963c8aba31aa0a3a0d80fa 100644 (file)
@@ -222,15 +222,22 @@ func Main() {
        obj.Flagcount("x", "debug lexer", &Debug['x'])
        obj.Flagcount("y", "debug declarations in canned imports (with -d)", &Debug['y'])
        var flag_shared int
+       var flag_dynlink bool
        if Thearch.Thechar == '6' {
                obj.Flagcount("largemodel", "generate code that assumes a large memory model", &flag_largemodel)
                obj.Flagcount("shared", "generate code that can be linked into a shared library", &flag_shared)
+               flag.BoolVar(&flag_dynlink, "dynlink", false, "support references to Go symbols defined in other shared libraries")
        }
-
        obj.Flagstr("cpuprofile", "file: write cpu profile to file", &cpuprofile)
        obj.Flagstr("memprofile", "file: write memory profile to file", &memprofile)
        obj.Flagparse(usage)
+
+       if flag_dynlink {
+               flag_shared = 1
+       }
        Ctxt.Flag_shared = int32(flag_shared)
+       Ctxt.Flag_dynlink = flag_dynlink
+
        Ctxt.Debugasm = int32(Debug['S'])
        Ctxt.Debugvlog = int32(Debug['v'])
 
index 39dfea910f08da05f1f8eac313af786762dabd3f..e9a890d84fd856fca429af7df014aaf45b137a39 100644 (file)
@@ -476,8 +476,8 @@ func relocsym(s *LSym) {
                        }
 
                        // r->sym can be null when CALL $(constant) is transformed from absolute PC to relative PC call.
-               case R_CALL, R_PCREL:
-                       if Linkmode == LinkExternal && r.Sym != nil && r.Sym.Type != SCONST && r.Sym.Sect != Ctxt.Cursym.Sect {
+               case R_CALL, R_GOTPCREL, R_PCREL:
+                       if Linkmode == LinkExternal && r.Sym != nil && r.Sym.Type != SCONST && (r.Sym.Sect != Ctxt.Cursym.Sect || r.Type == R_GOTPCREL) {
                                r.Done = 0
 
                                // set up addend for eventual relocation via outer symbol.
index 47af2ae77ff1a88aae98663b15aa0c4416a1c44d..83cfe283f4fe08f6674b35520b9f97fac5aafa24 100644 (file)
@@ -235,6 +235,7 @@ const (
        R_PLT2
        R_USEFIELD
        R_POWER_TOC
+       R_GOTPCREL
 )
 
 // Reloc.variant
index a714057028fd19f2b018724a9cbcf5dec5a9279b..7d4026c312c0576abf7e149747bf496282596025 100644 (file)
@@ -173,6 +173,9 @@ const (
        NAME_STATIC
        NAME_AUTO
        NAME_PARAM
+       // A reference to name@GOT(SB) is a reference to the entry in the global offset
+       // table for 'name'.
+       NAME_GOTREF
 )
 
 const (
@@ -380,6 +383,7 @@ const (
        R_PLT2
        R_USEFIELD
        R_POWER_TOC
+       R_GOTPCREL
 )
 
 type Auto struct {
@@ -431,6 +435,7 @@ type Link struct {
        Debugdivmod        int32
        Debugpcln          int32
        Flag_shared        int32
+       Flag_dynlink       bool
        Bso                *Biobuf
        Pathname           string
        Windows            int32
index b0d10b5fd5c029269cdc6f21d1646696d35c78a0..f76c9362bde596af1fc04eaa9a0716ca82abba3f 100644 (file)
@@ -477,6 +477,9 @@ func Mconv(a *Addr) string {
        case NAME_EXTERN:
                str = fmt.Sprintf("%s%s(SB)", a.Sym.Name, offConv(a.Offset))
 
+       case NAME_GOTREF:
+               str = fmt.Sprintf("%s%s@GOT(SB)", a.Sym.Name, offConv(a.Offset))
+
        case NAME_STATIC:
                str = fmt.Sprintf("%s<>%s(SB)", a.Sym.Name, offConv(a.Offset))
 
index e0869722c6b6ffbd27e4d16d2372b46e033fd961..dfabfd4124ae79d928ee32040d404f57660bf07a 100644 (file)
@@ -2028,6 +2028,7 @@ func oclass(ctxt *obj.Link, p *obj.Prog, a *obj.Addr) int {
        case obj.TYPE_ADDR:
                switch a.Name {
                case obj.NAME_EXTERN,
+                       obj.NAME_GOTREF,
                        obj.NAME_STATIC:
                        if a.Sym != nil && isextern(a.Sym) || p.Mode == 32 {
                                return Yi32
@@ -2437,6 +2438,7 @@ func vaddr(ctxt *obj.Link, p *obj.Prog, a *obj.Addr, r *obj.Reloc) int64 {
 
        switch a.Name {
        case obj.NAME_STATIC,
+               obj.NAME_GOTREF,
                obj.NAME_EXTERN:
                s := a.Sym
                if r == nil {
@@ -2444,7 +2446,10 @@ func vaddr(ctxt *obj.Link, p *obj.Prog, a *obj.Addr, r *obj.Reloc) int64 {
                        log.Fatalf("reloc")
                }
 
-               if isextern(s) || p.Mode != 64 {
+               if a.Name == obj.NAME_GOTREF {
+                       r.Siz = 4
+                       r.Type = obj.R_GOTPCREL
+               } else if isextern(s) || p.Mode != 64 {
                        r.Siz = 4
                        r.Type = obj.R_ADDR
                } else {
@@ -2519,6 +2524,7 @@ func asmandsz(ctxt *obj.Link, p *obj.Prog, a *obj.Addr, r int, rex int, m64 int)
                base := int(a.Reg)
                switch a.Name {
                case obj.NAME_EXTERN,
+                       obj.NAME_GOTREF,
                        obj.NAME_STATIC:
                        if !isextern(a.Sym) && p.Mode == 64 {
                                goto bad
@@ -2564,6 +2570,7 @@ func asmandsz(ctxt *obj.Link, p *obj.Prog, a *obj.Addr, r int, rex int, m64 int)
        base = int(a.Reg)
        switch a.Name {
        case obj.NAME_STATIC,
+               obj.NAME_GOTREF,
                obj.NAME_EXTERN:
                if a.Sym == nil {
                        ctxt.Diag("bad addr: %v", p)
@@ -2582,7 +2589,10 @@ func asmandsz(ctxt *obj.Link, p *obj.Prog, a *obj.Addr, r int, rex int, m64 int)
 
        ctxt.Rexflag |= regrex[base]&Rxb | rex
        if base == REG_NONE || (REG_CS <= base && base <= REG_GS) || base == REG_TLS {
-               if (a.Sym == nil || !isextern(a.Sym)) && base == REG_NONE && (a.Name == obj.NAME_STATIC || a.Name == obj.NAME_EXTERN) || p.Mode != 64 {
+               if (a.Sym == nil || !isextern(a.Sym)) && base == REG_NONE && (a.Name == obj.NAME_STATIC || a.Name == obj.NAME_EXTERN || a.Name == obj.NAME_GOTREF) || p.Mode != 64 {
+                       if a.Name == obj.NAME_GOTREF && (a.Offset != 0 || a.Index != 0 || a.Scale != 0) {
+                               ctxt.Diag("%v has offset against gotref", p)
+                       }
                        ctxt.Andptr[0] = byte(0<<6 | 5<<0 | r<<3)
                        ctxt.Andptr = ctxt.Andptr[1:]
                        goto putrelv
index 05a966a772d2c574b87386979af00e0ee4b384ff..2d30e9ebd4ed7e0dd1ca8477a6d1b163df6f06af 100644 (file)
@@ -297,6 +297,84 @@ func progedit(ctxt *obj.Link, p *obj.Prog) {
                        p.From.Offset = 0
                }
        }
+
+       if ctxt.Flag_dynlink {
+               if p.As == ALEAQ && p.From.Type == obj.TYPE_MEM && p.From.Name == obj.NAME_EXTERN {
+                       p.As = AMOVQ
+                       p.From.Type = obj.TYPE_ADDR
+               }
+               if p.From.Type == obj.TYPE_ADDR && p.From.Name == obj.NAME_EXTERN {
+                       if p.As != AMOVQ {
+                               ctxt.Diag("do not know how to handle TYPE_ADDR in %v with -dynlink", p)
+                       }
+                       if p.To.Type != obj.TYPE_REG {
+                               ctxt.Diag("do not know how to handle LEAQ-type insn to non-register in %v with -dynlink", p)
+                       }
+                       p.From.Type = obj.TYPE_MEM
+                       p.From.Name = obj.NAME_GOTREF
+                       if p.From.Offset != 0 {
+                               q := obj.Appendp(ctxt, p)
+                               q.As = AADDQ
+                               q.From.Type = obj.TYPE_CONST
+                               q.From.Offset = p.From.Offset
+                               q.To = p.To
+                               p.From.Offset = 0
+                       }
+               }
+               if p.From3.Name == obj.NAME_EXTERN {
+                       ctxt.Diag("don't know how to handle %v with -dynlink", p)
+               }
+               if p.To2.Name == obj.NAME_EXTERN {
+                       ctxt.Diag("don't know how to handle %v with -dynlink", p)
+               }
+               var source *obj.Addr
+               if p.From.Name == obj.NAME_EXTERN {
+                       if p.To.Name == obj.NAME_EXTERN {
+                               ctxt.Diag("cannot handle NAME_EXTERN on both sides in %v with -dynlink", p)
+                       }
+                       source = &p.From
+               } else if p.To.Name == obj.NAME_EXTERN {
+                       source = &p.To
+               } else {
+                       return
+               }
+               if p.As == obj.ATEXT || p.As == obj.AFUNCDATA || p.As == obj.ACALL || p.As == obj.ARET || p.As == obj.AJMP {
+                       return
+               }
+               if source.Type != obj.TYPE_MEM {
+                       ctxt.Diag("don't know how to handle %v with -dynlink", p)
+               }
+               p1 := obj.Appendp(ctxt, p)
+               p2 := obj.Appendp(ctxt, p1)
+
+               p1.As = AMOVQ
+               p1.From.Type = obj.TYPE_MEM
+               p1.From.Sym = source.Sym
+               p1.From.Name = obj.NAME_GOTREF
+               p1.To.Type = obj.TYPE_REG
+               p1.To.Reg = REG_R15
+
+               p2.As = p.As
+               p2.From = p.From
+               p2.To = p.To
+               if p.From.Name == obj.NAME_EXTERN {
+                       p2.From.Reg = REG_R15
+                       p2.From.Name = obj.NAME_NONE
+                       p2.From.Sym = nil
+               } else if p.To.Name == obj.NAME_EXTERN {
+                       p2.To.Reg = REG_R15
+                       p2.To.Name = obj.NAME_NONE
+                       p2.To.Sym = nil
+               } else {
+                       return
+               }
+               l := p.Link
+               l2 := p2.Link
+               *p = *p1
+               *p1 = *p2
+               p.Link = l
+               p1.Link = l2
+       }
 }
 
 func nacladdr(ctxt *obj.Link, p *obj.Prog, a *obj.Addr) {
diff --git a/src/cmd/internal/obj/x86/obj6_test.go b/src/cmd/internal/obj/x86/obj6_test.go
new file mode 100644 (file)
index 0000000..93f8f3c
--- /dev/null
@@ -0,0 +1,167 @@
+package x86_test
+
+import (
+       "bufio"
+       "bytes"
+       "fmt"
+       "go/build"
+       "io/ioutil"
+       "os"
+       "os/exec"
+       "path/filepath"
+       "regexp"
+       "runtime"
+       "strconv"
+       "strings"
+       "testing"
+)
+
+const testdata = `
+MOVQ AX, AX -> MOVQ AX, AX
+
+LEAQ name(SB), AX -> MOVQ name@GOT(SB), AX
+LEAQ name+10(SB), AX -> MOVQ name@GOT(SB), AX; ADDQ $10, AX
+MOVQ $name(SB), AX -> MOVQ name@GOT(SB), AX
+MOVQ $name+10(SB), AX -> MOVQ name@GOT(SB), AX; ADDQ $10, AX
+
+MOVQ name(SB), AX -> MOVQ name@GOT(SB), R15; MOVQ (R15), AX
+MOVQ name+10(SB), AX -> MOVQ name@GOT(SB), R15; MOVQ 10(R15), AX
+
+CMPQ name(SB), $0 -> MOVQ name@GOT(SB), R15; CMPQ (R15), $0
+
+MOVQ $1, name(SB) -> MOVQ name@GOT(SB), R15; MOVQ $1, (R15)
+MOVQ $1, name+10(SB) -> MOVQ name@GOT(SB), R15; MOVQ $1, 10(R15)
+`
+
+type ParsedTestData struct {
+       input              string
+       marks              []int
+       marker_to_input    map[int][]string
+       marker_to_expected map[int][]string
+       marker_to_output   map[int][]string
+}
+
+const marker_start = 1234
+
+func parseTestData(t *testing.T) *ParsedTestData {
+       r := &ParsedTestData{}
+       scanner := bufio.NewScanner(strings.NewReader(testdata))
+       r.marker_to_input = make(map[int][]string)
+       r.marker_to_expected = make(map[int][]string)
+       marker := marker_start
+       input_insns := []string{}
+       for scanner.Scan() {
+               line := scanner.Text()
+               if len(strings.TrimSpace(line)) == 0 {
+                       continue
+               }
+               parts := strings.Split(line, "->")
+               if len(parts) != 2 {
+                       t.Fatalf("malformed line %v", line)
+               }
+               r.marks = append(r.marks, marker)
+               marker_insn := fmt.Sprintf("MOVQ $%d, AX", marker)
+               input_insns = append(input_insns, marker_insn)
+               for _, input_insn := range strings.Split(parts[0], ";") {
+                       input_insns = append(input_insns, input_insn)
+                       r.marker_to_input[marker] = append(r.marker_to_input[marker], normalize(input_insn))
+               }
+               for _, expected_insn := range strings.Split(parts[1], ";") {
+                       r.marker_to_expected[marker] = append(r.marker_to_expected[marker], normalize(expected_insn))
+               }
+               marker++
+       }
+       r.input = "TEXT ·foo(SB),$0\n" + strings.Join(input_insns, "\n") + "\n"
+       return r
+}
+
+var spaces_re *regexp.Regexp = regexp.MustCompile("\\s+")
+var marker_re *regexp.Regexp = regexp.MustCompile("MOVQ \\$([0-9]+), AX")
+
+func normalize(s string) string {
+       return spaces_re.ReplaceAllLiteralString(strings.TrimSpace(s), " ")
+}
+
+func asmOutput(t *testing.T, s string) []byte {
+       tmpdir, err := ioutil.TempDir("", "progedittest")
+       if err != nil {
+               t.Fatal(err)
+       }
+       defer os.RemoveAll(tmpdir)
+       tmpfile, err := os.Create(filepath.Join(tmpdir, "input.s"))
+       if err != nil {
+               t.Fatal(err)
+       }
+       defer tmpfile.Close()
+       _, err = tmpfile.WriteString(s)
+       if err != nil {
+               t.Fatal(err)
+       }
+       cmd := exec.Command(
+               build.Default.GOROOT+"/bin/go", "tool", "asm", "-S", "-dynlink",
+               "-o", filepath.Join(tmpdir, "output.6"), tmpfile.Name())
+
+       var env []string
+       for _, v := range os.Environ() {
+               if !strings.HasPrefix(v, "GOARCH=") {
+                       env = append(env, v)
+               }
+       }
+       cmd.Env = append(env, "GOARCH=amd64")
+       asmout, err := cmd.CombinedOutput()
+       if err != nil {
+               t.Fatalf("error %s output %s", err, asmout)
+       }
+       return asmout
+}
+
+func parseOutput(t *testing.T, td *ParsedTestData, asmout []byte) {
+       scanner := bufio.NewScanner(bytes.NewReader(asmout))
+       marker := regexp.MustCompile("MOVQ \\$([0-9]+), AX")
+       mark := -1
+       td.marker_to_output = make(map[int][]string)
+       for scanner.Scan() {
+               line := scanner.Text()
+               if line[0] != '\t' {
+                       continue
+               }
+               parts := strings.SplitN(line, "\t", 3)
+               if len(parts) != 3 {
+                       continue
+               }
+               n := normalize(parts[2])
+               mark_matches := marker.FindStringSubmatch(n)
+               if mark_matches != nil {
+                       mark, _ = strconv.Atoi(mark_matches[1])
+                       if _, ok := td.marker_to_input[mark]; !ok {
+                               t.Fatalf("unexpected marker %d", mark)
+                       }
+               } else if mark != -1 {
+                       td.marker_to_output[mark] = append(td.marker_to_output[mark], n)
+               }
+       }
+}
+
+func TestDynlink(t *testing.T) {
+       if runtime.GOOS == "nacl" || runtime.GOOS == "android" || (runtime.GOOS == "darwin" && runtime.GOARCH == "arm") {
+               // iOS and nacl cannot fork
+               t.Skipf("skipping on %s/%s", runtime.GOOS, runtime.GOARCH)
+       }
+       testdata := parseTestData(t)
+       asmout := asmOutput(t, testdata.input)
+       parseOutput(t, testdata, asmout)
+       for _, m := range testdata.marks {
+               i := strings.Join(testdata.marker_to_input[m], "; ")
+               o := strings.Join(testdata.marker_to_output[m], "; ")
+               e := strings.Join(testdata.marker_to_expected[m], "; ")
+               if o != e {
+                       if o == i {
+                               t.Errorf("%s was unchanged; should have become %s", i, e)
+                       } else {
+                               t.Errorf("%s became %s; should have become %s", i, o, e)
+                       }
+               } else if i != e {
+                       t.Logf("%s correctly became %s", i, o)
+               }
+       }
+}