diff options
| author | Austin Clements <austin@google.com> | 2018-10-19 16:24:59 -0400 |
|---|---|---|
| committer | Austin Clements <austin@google.com> | 2018-11-12 20:46:33 +0000 |
| commit | ba2e8a629b36e43cc27b23470b631a1dfee0900f (patch) | |
| tree | c266ff31c4e0b9f4a18f5d1ac55afe2d580c456e /src/cmd/asm/internal | |
| parent | 52b222055908a3ae23c2a9185e53919fe56b54db (diff) | |
| download | go-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.go | 43 | ||||
| -rw-r--r-- | src/cmd/asm/internal/asm/parse.go | 81 | ||||
| -rw-r--r-- | src/cmd/asm/internal/flags/flags.go | 1 |
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 ( |
