mirror of
https://github.com/golang/go.git
synced 2025-11-01 01:00:56 +00:00
cmd/internal/obj/x86, cmd/internal/ld, cmd/6l: 6g/asm -dynlink accesses global data via a GOT
Change-Id: I49862e177045369d6c94d6a58afbdace4f13cc96 Reviewed-on: https://go-review.googlesource.com/8237 Reviewed-by: Ian Lance Taylor <iant@golang.org>
This commit is contained in:
parent
969f10140c
commit
84207a2500
13 changed files with 292 additions and 7 deletions
|
|
@ -61,6 +61,9 @@ func betypeinit() {
|
||||||
typedefs[2].Sameas = gc.TUINT32
|
typedefs[2].Sameas = gc.TUINT32
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if gc.Ctxt.Flag_dynlink {
|
||||||
|
gc.Thearch.ReservedRegs = append(gc.Thearch.ReservedRegs, x86.REG_R15)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
|
|
||||||
|
|
@ -123,7 +123,9 @@ func BtoR(b uint64) int {
|
||||||
// BP is part of the calling convention if framepointer_enabled.
|
// BP is part of the calling convention if framepointer_enabled.
|
||||||
b &^= (1 << (x86.REG_BP - x86.REG_AX))
|
b &^= (1 << (x86.REG_BP - x86.REG_AX))
|
||||||
}
|
}
|
||||||
|
if gc.Ctxt.Flag_dynlink {
|
||||||
|
b &^= (1 << (x86.REG_R15 - x86.REG_AX))
|
||||||
|
}
|
||||||
if b == 0 {
|
if b == 0 {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -332,6 +332,13 @@ func elfreloc1(r *ld.Reloc, sectoff int64) int {
|
||||||
return -1
|
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:
|
case ld.R_TLS:
|
||||||
if r.Siz == 4 {
|
if r.Siz == 4 {
|
||||||
if ld.Buildmode == ld.BuildmodeCShared {
|
if ld.Buildmode == ld.BuildmodeCShared {
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ var (
|
||||||
PrintOut = flag.Bool("S", false, "print assembly and machine code")
|
PrintOut = flag.Bool("S", false, "print assembly and machine code")
|
||||||
TrimPath = flag.String("trimpath", "", "remove prefix from recorded source file paths")
|
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")
|
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 (
|
var (
|
||||||
|
|
|
||||||
|
|
@ -41,7 +41,8 @@ func main() {
|
||||||
ctxt.Debugasm = 1
|
ctxt.Debugasm = 1
|
||||||
}
|
}
|
||||||
ctxt.Trimpath = *flags.TrimPath
|
ctxt.Trimpath = *flags.TrimPath
|
||||||
if *flags.Shared {
|
ctxt.Flag_dynlink = *flags.Dynlink
|
||||||
|
if *flags.Shared || *flags.Dynlink {
|
||||||
ctxt.Flag_shared = 1
|
ctxt.Flag_shared = 1
|
||||||
}
|
}
|
||||||
ctxt.Bso = obj.Binitw(os.Stdout)
|
ctxt.Bso = obj.Binitw(os.Stdout)
|
||||||
|
|
|
||||||
|
|
@ -222,15 +222,22 @@ func Main() {
|
||||||
obj.Flagcount("x", "debug lexer", &Debug['x'])
|
obj.Flagcount("x", "debug lexer", &Debug['x'])
|
||||||
obj.Flagcount("y", "debug declarations in canned imports (with -d)", &Debug['y'])
|
obj.Flagcount("y", "debug declarations in canned imports (with -d)", &Debug['y'])
|
||||||
var flag_shared int
|
var flag_shared int
|
||||||
|
var flag_dynlink bool
|
||||||
if Thearch.Thechar == '6' {
|
if Thearch.Thechar == '6' {
|
||||||
obj.Flagcount("largemodel", "generate code that assumes a large memory model", &flag_largemodel)
|
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)
|
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("cpuprofile", "file: write cpu profile to file", &cpuprofile)
|
||||||
obj.Flagstr("memprofile", "file: write memory profile to file", &memprofile)
|
obj.Flagstr("memprofile", "file: write memory profile to file", &memprofile)
|
||||||
obj.Flagparse(usage)
|
obj.Flagparse(usage)
|
||||||
|
|
||||||
|
if flag_dynlink {
|
||||||
|
flag_shared = 1
|
||||||
|
}
|
||||||
Ctxt.Flag_shared = int32(flag_shared)
|
Ctxt.Flag_shared = int32(flag_shared)
|
||||||
|
Ctxt.Flag_dynlink = flag_dynlink
|
||||||
|
|
||||||
Ctxt.Debugasm = int32(Debug['S'])
|
Ctxt.Debugasm = int32(Debug['S'])
|
||||||
Ctxt.Debugvlog = int32(Debug['v'])
|
Ctxt.Debugvlog = int32(Debug['v'])
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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.
|
// r->sym can be null when CALL $(constant) is transformed from absolute PC to relative PC call.
|
||||||
case R_CALL, R_PCREL:
|
case R_CALL, R_GOTPCREL, R_PCREL:
|
||||||
if Linkmode == LinkExternal && r.Sym != nil && r.Sym.Type != SCONST && r.Sym.Sect != Ctxt.Cursym.Sect {
|
if Linkmode == LinkExternal && r.Sym != nil && r.Sym.Type != SCONST && (r.Sym.Sect != Ctxt.Cursym.Sect || r.Type == R_GOTPCREL) {
|
||||||
r.Done = 0
|
r.Done = 0
|
||||||
|
|
||||||
// set up addend for eventual relocation via outer symbol.
|
// set up addend for eventual relocation via outer symbol.
|
||||||
|
|
|
||||||
|
|
@ -235,6 +235,7 @@ const (
|
||||||
R_PLT2
|
R_PLT2
|
||||||
R_USEFIELD
|
R_USEFIELD
|
||||||
R_POWER_TOC
|
R_POWER_TOC
|
||||||
|
R_GOTPCREL
|
||||||
)
|
)
|
||||||
|
|
||||||
// Reloc.variant
|
// Reloc.variant
|
||||||
|
|
|
||||||
|
|
@ -173,6 +173,9 @@ const (
|
||||||
NAME_STATIC
|
NAME_STATIC
|
||||||
NAME_AUTO
|
NAME_AUTO
|
||||||
NAME_PARAM
|
NAME_PARAM
|
||||||
|
// A reference to name@GOT(SB) is a reference to the entry in the global offset
|
||||||
|
// table for 'name'.
|
||||||
|
NAME_GOTREF
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
|
@ -380,6 +383,7 @@ const (
|
||||||
R_PLT2
|
R_PLT2
|
||||||
R_USEFIELD
|
R_USEFIELD
|
||||||
R_POWER_TOC
|
R_POWER_TOC
|
||||||
|
R_GOTPCREL
|
||||||
)
|
)
|
||||||
|
|
||||||
type Auto struct {
|
type Auto struct {
|
||||||
|
|
@ -431,6 +435,7 @@ type Link struct {
|
||||||
Debugdivmod int32
|
Debugdivmod int32
|
||||||
Debugpcln int32
|
Debugpcln int32
|
||||||
Flag_shared int32
|
Flag_shared int32
|
||||||
|
Flag_dynlink bool
|
||||||
Bso *Biobuf
|
Bso *Biobuf
|
||||||
Pathname string
|
Pathname string
|
||||||
Windows int32
|
Windows int32
|
||||||
|
|
|
||||||
|
|
@ -477,6 +477,9 @@ func Mconv(a *Addr) string {
|
||||||
case NAME_EXTERN:
|
case NAME_EXTERN:
|
||||||
str = fmt.Sprintf("%s%s(SB)", a.Sym.Name, offConv(a.Offset))
|
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:
|
case NAME_STATIC:
|
||||||
str = fmt.Sprintf("%s<>%s(SB)", a.Sym.Name, offConv(a.Offset))
|
str = fmt.Sprintf("%s<>%s(SB)", a.Sym.Name, offConv(a.Offset))
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2028,6 +2028,7 @@ func oclass(ctxt *obj.Link, p *obj.Prog, a *obj.Addr) int {
|
||||||
case obj.TYPE_ADDR:
|
case obj.TYPE_ADDR:
|
||||||
switch a.Name {
|
switch a.Name {
|
||||||
case obj.NAME_EXTERN,
|
case obj.NAME_EXTERN,
|
||||||
|
obj.NAME_GOTREF,
|
||||||
obj.NAME_STATIC:
|
obj.NAME_STATIC:
|
||||||
if a.Sym != nil && isextern(a.Sym) || p.Mode == 32 {
|
if a.Sym != nil && isextern(a.Sym) || p.Mode == 32 {
|
||||||
return Yi32
|
return Yi32
|
||||||
|
|
@ -2437,6 +2438,7 @@ func vaddr(ctxt *obj.Link, p *obj.Prog, a *obj.Addr, r *obj.Reloc) int64 {
|
||||||
|
|
||||||
switch a.Name {
|
switch a.Name {
|
||||||
case obj.NAME_STATIC,
|
case obj.NAME_STATIC,
|
||||||
|
obj.NAME_GOTREF,
|
||||||
obj.NAME_EXTERN:
|
obj.NAME_EXTERN:
|
||||||
s := a.Sym
|
s := a.Sym
|
||||||
if r == nil {
|
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")
|
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.Siz = 4
|
||||||
r.Type = obj.R_ADDR
|
r.Type = obj.R_ADDR
|
||||||
} else {
|
} 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)
|
base := int(a.Reg)
|
||||||
switch a.Name {
|
switch a.Name {
|
||||||
case obj.NAME_EXTERN,
|
case obj.NAME_EXTERN,
|
||||||
|
obj.NAME_GOTREF,
|
||||||
obj.NAME_STATIC:
|
obj.NAME_STATIC:
|
||||||
if !isextern(a.Sym) && p.Mode == 64 {
|
if !isextern(a.Sym) && p.Mode == 64 {
|
||||||
goto bad
|
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)
|
base = int(a.Reg)
|
||||||
switch a.Name {
|
switch a.Name {
|
||||||
case obj.NAME_STATIC,
|
case obj.NAME_STATIC,
|
||||||
|
obj.NAME_GOTREF,
|
||||||
obj.NAME_EXTERN:
|
obj.NAME_EXTERN:
|
||||||
if a.Sym == nil {
|
if a.Sym == nil {
|
||||||
ctxt.Diag("bad addr: %v", p)
|
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
|
ctxt.Rexflag |= regrex[base]&Rxb | rex
|
||||||
if base == REG_NONE || (REG_CS <= base && base <= REG_GS) || base == REG_TLS {
|
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[0] = byte(0<<6 | 5<<0 | r<<3)
|
||||||
ctxt.Andptr = ctxt.Andptr[1:]
|
ctxt.Andptr = ctxt.Andptr[1:]
|
||||||
goto putrelv
|
goto putrelv
|
||||||
|
|
|
||||||
|
|
@ -297,6 +297,84 @@ func progedit(ctxt *obj.Link, p *obj.Prog) {
|
||||||
p.From.Offset = 0
|
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) {
|
func nacladdr(ctxt *obj.Link, p *obj.Prog, a *obj.Addr) {
|
||||||
|
|
|
||||||
167
src/cmd/internal/obj/x86/obj6_test.go
Normal file
167
src/cmd/internal/obj/x86/obj6_test.go
Normal file
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue