aboutsummaryrefslogtreecommitdiff
path: root/src/cmd/asm/internal
diff options
context:
space:
mode:
authorAustin Clements <austin@google.com>2018-10-19 16:24:59 -0400
committerAustin Clements <austin@google.com>2018-11-12 20:46:33 +0000
commitba2e8a629b36e43cc27b23470b631a1dfee0900f (patch)
treec266ff31c4e0b9f4a18f5d1ac55afe2d580c456e /src/cmd/asm/internal
parent52b222055908a3ae23c2a9185e53919fe56b54db (diff)
downloadgo-ba2e8a629b36e43cc27b23470b631a1dfee0900f.tar.xz
cmd/asm: add mode to collect symbol ABIs
This adds a -symabis flag that runs the assembler in a special mode that outputs symbol definition and reference ABIs rather than assembling the code. This uses a fast and somewhat lax parser because the go_asm.h definitions may not be available. For #27539. Change-Id: I248ba0ebab7cc75dcb2a90e82a82eb445da7e88e Reviewed-on: https://go-review.googlesource.com/c/147098 Run-TryBot: Austin Clements <austin@google.com> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: David Chase <drchase@google.com> Reviewed-by: Cherry Zhang <cherryyz@google.com>
Diffstat (limited to 'src/cmd/asm/internal')
-rw-r--r--src/cmd/asm/internal/asm/operand_test.go43
-rw-r--r--src/cmd/asm/internal/asm/parse.go81
-rw-r--r--src/cmd/asm/internal/flags/flags.go1
3 files changed, 125 insertions, 0 deletions
diff --git a/src/cmd/asm/internal/asm/operand_test.go b/src/cmd/asm/internal/asm/operand_test.go
index 69393b6b20..2ba3fd73df 100644
--- a/src/cmd/asm/internal/asm/operand_test.go
+++ b/src/cmd/asm/internal/asm/operand_test.go
@@ -122,6 +122,49 @@ func TestS390XOperandParser(t *testing.T) {
testOperandParser(t, parser, s390xOperandTests)
}
+func TestFuncAddress(t *testing.T) {
+ type subtest struct {
+ arch string
+ tests []operandTest
+ }
+ for _, sub := range []subtest{
+ {"amd64", amd64OperandTests},
+ {"386", x86OperandTests},
+ {"arm", armOperandTests},
+ {"arm64", arm64OperandTests},
+ {"ppc64", ppc64OperandTests},
+ {"mips", mipsOperandTests},
+ {"mips64", mips64OperandTests},
+ {"s390x", s390xOperandTests},
+ } {
+ t.Run(sub.arch, func(t *testing.T) {
+ parser := newParser(sub.arch)
+ for _, test := range sub.tests {
+ parser.start(lex.Tokenize(test.input))
+ name, ok := parser.funcAddress()
+
+ isFuncSym := strings.HasSuffix(test.input, "(SB)") &&
+ // Ignore static symbols.
+ !strings.Contains(test.input, "<>") &&
+ // Ignore symbols with offsets.
+ !strings.Contains(test.input, "+")
+
+ wantName := ""
+ if isFuncSym {
+ // Strip $|* and (SB).
+ wantName = test.output[:len(test.output)-4]
+ if strings.HasPrefix(wantName, "$") || strings.HasPrefix(wantName, "*") {
+ wantName = wantName[1:]
+ }
+ }
+ if ok != isFuncSym || name != wantName {
+ t.Errorf("fail at %s as function address: got %s, %v; expected %s, %v", test.input, name, ok, wantName, isFuncSym)
+ }
+ }
+ })
+ }
+}
+
type operandTest struct {
input, output string
}
diff --git a/src/cmd/asm/internal/asm/parse.go b/src/cmd/asm/internal/asm/parse.go
index 3620e31320..346976ef48 100644
--- a/src/cmd/asm/internal/asm/parse.go
+++ b/src/cmd/asm/internal/asm/parse.go
@@ -116,6 +116,22 @@ func (p *Parser) Parse() (*obj.Prog, bool) {
return p.firstProg, true
}
+// ParseSymABIs parses p's assembly code to find text symbol
+// definitions and references and writes a symabis file to w.
+func (p *Parser) ParseSymABIs(w io.Writer) bool {
+ operands := make([][]lex.Token, 0, 3)
+ for {
+ word, _, operands1, ok := p.line(operands)
+ if !ok {
+ break
+ }
+ operands = operands1
+
+ p.symDefRef(w, word, operands)
+ }
+ return p.errorCount == 0
+}
+
// line consumes a single assembly line from p.lex of the form
//
// {label:} WORD[.cond] [ arg {, arg} ] (';' | '\n')
@@ -258,6 +274,42 @@ func (p *Parser) pseudo(word string, operands [][]lex.Token) bool {
return true
}
+// symDefRef scans a line for potential text symbol definitions and
+// references and writes symabis information to w.
+//
+// The symabis format is documented at
+// cmd/compile/internal/gc.readSymABIs.
+func (p *Parser) symDefRef(w io.Writer, word string, operands [][]lex.Token) {
+ switch word {
+ case "TEXT":
+ // Defines text symbol in operands[0].
+ if len(operands) > 0 {
+ p.start(operands[0])
+ if name, ok := p.funcAddress(); ok {
+ fmt.Fprintf(w, "def %s ABI0\n", name)
+ }
+ }
+ return
+ case "GLOBL", "PCDATA":
+ // No text definitions or symbol references.
+ case "DATA", "FUNCDATA":
+ // For DATA, operands[0] is defined symbol.
+ // For FUNCDATA, operands[0] is an immediate constant.
+ // Remaining operands may have references.
+ if len(operands) < 2 {
+ return
+ }
+ operands = operands[1:]
+ }
+ // Search for symbol references.
+ for _, op := range operands {
+ p.start(op)
+ if name, ok := p.funcAddress(); ok {
+ fmt.Fprintf(w, "ref %s ABI0\n", name)
+ }
+ }
+}
+
func (p *Parser) start(operand []lex.Token) {
p.input = operand
p.inputPos = 0
@@ -746,6 +798,35 @@ func (p *Parser) setPseudoRegister(addr *obj.Addr, reg string, isStatic bool, pr
}
}
+// funcAddress parses an external function address. This is a
+// constrained form of the operand syntax that's always SB-based,
+// non-static, and has no additional offsets:
+//
+// [$|*]sym(SB)
+func (p *Parser) funcAddress() (string, bool) {
+ switch p.peek() {
+ case '$', '*':
+ // Skip prefix.
+ p.next()
+ }
+
+ tok := p.next()
+ name := tok.String()
+ if tok.ScanToken != scanner.Ident || p.atStartOfRegister(name) {
+ return "", false
+ }
+ if p.next().ScanToken != '(' {
+ return "", false
+ }
+ if reg := p.next(); reg.ScanToken != scanner.Ident || reg.String() != "SB" {
+ return "", false
+ }
+ if p.next().ScanToken != ')' || p.peek() != scanner.EOF {
+ return "", false
+ }
+ return name, true
+}
+
// registerIndirect parses the general form of a register indirection.
// It is can be (R1), (R2*scale), (R1)(R2*scale), (R1)(R2.SXTX<<3) or (R1)(R2<<3)
// where R1 may be a simple register or register pair R:R or (R, R) or (R+R).
diff --git a/src/cmd/asm/internal/flags/flags.go b/src/cmd/asm/internal/flags/flags.go
index 6acde29432..752a1d4526 100644
--- a/src/cmd/asm/internal/flags/flags.go
+++ b/src/cmd/asm/internal/flags/flags.go
@@ -22,6 +22,7 @@ var (
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")
AllErrors = flag.Bool("e", false, "no limit on number of errors reported")
+ SymABIs = flag.Bool("symabis", false, "write symbol ABI information to output file, don't assemble")
)
var (