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) {
--- /dev/null
+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)
+ }
+ }
+}