mirror of
https://github.com/golang/go
synced 2024-11-20 00:44:45 -07: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
@ -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() {
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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 (
|
||||
|
@ -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)
|
||||
|
@ -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'])
|
||||
|
||||
|
@ -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.
|
||||
|
@ -235,6 +235,7 @@ const (
|
||||
R_PLT2
|
||||
R_USEFIELD
|
||||
R_POWER_TOC
|
||||
R_GOTPCREL
|
||||
)
|
||||
|
||||
// Reloc.variant
|
||||
|
@ -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
|
||||
|
@ -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))
|
||||
|
||||
|
@ -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
|
||||
|
@ -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) {
|
||||
|
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…
Reference in New Issue
Block a user