mirror of
				https://github.com/golang/go.git
				synced 2025-10-31 16:50:58 +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
	
	 Michael Hudson-Doyle
						Michael Hudson-Doyle