From: Austin Clements Date: Fri, 19 Oct 2018 20:24:59 +0000 (-0400) Subject: cmd/asm: add mode to collect symbol ABIs X-Git-Tag: go1.12beta1~395 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=ba2e8a629b36e43cc27b23470b631a1dfee0900f;p=gostls13.git 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 TryBot-Result: Gobot Gobot Reviewed-by: David Chase Reviewed-by: Cherry Zhang --- 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 ( diff --git a/src/cmd/asm/main.go b/src/cmd/asm/main.go index 04f56f9646..55ae94a6de 100644 --- a/src/cmd/asm/main.go +++ b/src/cmd/asm/main.go @@ -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 {