mirror of
https://github.com/golang/go
synced 2024-11-12 08:10:21 -07:00
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>
This commit is contained in:
parent
52b2220559
commit
ba2e8a629b
@ -122,6 +122,49 @@ func TestS390XOperandParser(t *testing.T) {
|
|||||||
testOperandParser(t, parser, s390xOperandTests)
|
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 {
|
type operandTest struct {
|
||||||
input, output string
|
input, output string
|
||||||
}
|
}
|
||||||
|
@ -116,6 +116,22 @@ func (p *Parser) Parse() (*obj.Prog, bool) {
|
|||||||
return p.firstProg, true
|
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
|
// line consumes a single assembly line from p.lex of the form
|
||||||
//
|
//
|
||||||
// {label:} WORD[.cond] [ arg {, arg} ] (';' | '\n')
|
// {label:} WORD[.cond] [ arg {, arg} ] (';' | '\n')
|
||||||
@ -258,6 +274,42 @@ func (p *Parser) pseudo(word string, operands [][]lex.Token) bool {
|
|||||||
return true
|
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) {
|
func (p *Parser) start(operand []lex.Token) {
|
||||||
p.input = operand
|
p.input = operand
|
||||||
p.inputPos = 0
|
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.
|
// 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)
|
// 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).
|
// where R1 may be a simple register or register pair R:R or (R, R) or (R+R).
|
||||||
|
@ -22,6 +22,7 @@ var (
|
|||||||
Shared = flag.Bool("shared", false, "generate code that can be linked into a shared library")
|
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")
|
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")
|
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 (
|
var (
|
||||||
|
@ -53,8 +53,10 @@ func main() {
|
|||||||
defer bio.MustClose(out)
|
defer bio.MustClose(out)
|
||||||
buf := bufio.NewWriter(bio.MustWriter(out))
|
buf := bufio.NewWriter(bio.MustWriter(out))
|
||||||
|
|
||||||
fmt.Fprintf(buf, "go object %s %s %s\n", objabi.GOOS, objabi.GOARCH, objabi.Version)
|
if !*flags.SymABIs {
|
||||||
fmt.Fprintf(buf, "!\n")
|
fmt.Fprintf(buf, "go object %s %s %s\n", objabi.GOOS, objabi.GOARCH, objabi.Version)
|
||||||
|
fmt.Fprintf(buf, "!\n")
|
||||||
|
}
|
||||||
|
|
||||||
var ok, diag bool
|
var ok, diag bool
|
||||||
var failedFile string
|
var failedFile string
|
||||||
@ -65,16 +67,22 @@ func main() {
|
|||||||
diag = true
|
diag = true
|
||||||
log.Printf(format, args...)
|
log.Printf(format, args...)
|
||||||
}
|
}
|
||||||
pList := new(obj.Plist)
|
if *flags.SymABIs {
|
||||||
pList.Firstpc, ok = parser.Parse()
|
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 {
|
if !ok {
|
||||||
failedFile = f
|
failedFile = f
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
// reports errors to parser.Errorf
|
|
||||||
obj.Flushplist(ctxt, pList, nil, "")
|
|
||||||
}
|
}
|
||||||
if ok {
|
if ok && !*flags.SymABIs {
|
||||||
obj.WriteObjFile(ctxt, buf)
|
obj.WriteObjFile(ctxt, buf)
|
||||||
}
|
}
|
||||||
if !ok || diag {
|
if !ok || diag {
|
||||||
|
Loading…
Reference in New Issue
Block a user