]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/asm: add mode to collect symbol ABIs
authorAustin Clements <austin@google.com>
Fri, 19 Oct 2018 20:24:59 +0000 (16:24 -0400)
committerAustin Clements <austin@google.com>
Mon, 12 Nov 2018 20:46:33 +0000 (20:46 +0000)
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>
src/cmd/asm/internal/asm/operand_test.go
src/cmd/asm/internal/asm/parse.go
src/cmd/asm/internal/flags/flags.go
src/cmd/asm/main.go

index 69393b6b2052a6fbf74fd40230b7c4e4c6880ac8..2ba3fd73df83c9db0e57b94750c9f7a06d56d51b 100644 (file)
@@ -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
 }
index 3620e3132090321bd9006217ccb5e896ef0c9f60..346976ef48b67b1d55d605e79f774c0b3ddd4fe7 100644 (file)
@@ -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).
index 6acde29432675f6f635641d9ef9c7c3d6cf60e27..752a1d45265d3f72c7b4fcfc4b5656d389fd6d2a 100644 (file)
@@ -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 (
index 04f56f96467f8ba5fa2e96f888997fad6fcd81e7..55ae94a6de5a603ca4719cc3911ea2fd9f302d50 100644 (file)
@@ -53,8 +53,10 @@ func main() {
        defer bio.MustClose(out)
        buf := bufio.NewWriter(bio.MustWriter(out))
 
-       fmt.Fprintf(buf, "go object %s %s %s\n", objabi.GOOS, objabi.GOARCH, objabi.Version)
-       fmt.Fprintf(buf, "!\n")
+       if !*flags.SymABIs {
+               fmt.Fprintf(buf, "go object %s %s %s\n", objabi.GOOS, objabi.GOARCH, objabi.Version)
+               fmt.Fprintf(buf, "!\n")
+       }
 
        var ok, diag bool
        var failedFile string
@@ -65,16 +67,22 @@ func main() {
                        diag = true
                        log.Printf(format, args...)
                }
-               pList := new(obj.Plist)
-               pList.Firstpc, ok = parser.Parse()
+               if *flags.SymABIs {
+                       ok = parser.ParseSymABIs(buf)
+               } else {
+                       pList := new(obj.Plist)
+                       pList.Firstpc, ok = parser.Parse()
+                       // reports errors to parser.Errorf
+                       if ok {
+                               obj.Flushplist(ctxt, pList, nil, "")
+                       }
+               }
                if !ok {
                        failedFile = f
                        break
                }
-               // reports errors to parser.Errorf
-               obj.Flushplist(ctxt, pList, nil, "")
        }
-       if ok {
+       if ok && !*flags.SymABIs {
                obj.WriteObjFile(ctxt, buf)
        }
        if !ok || diag {