mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
cmd/asm: allow def/ref of func ABI when compiling runtime
Function symbols defined and referenced by assembly source currently
always default to ABI0; this patch adds preliminary support for
accepting an explicit ABI selector clause for func defs/refs. This
functionality is currently only enabled when compiling runtime-related
packages (runtime, syscall, reflect). Examples:
TEXT ·DefinedAbi0Symbol<ABI0>(SB),NOSPLIT,$0
RET
TEXT ·DefinedAbi1Symbol<ABIInternal>(SB),NOSPLIT,$0
CALL ·AbiZerolSym<ABI0>(SB)
...
JMP ·AbiInternalSym<ABIInternal>(SB)
RET
Also included is a small change to the code in the compiler that reads
the symabis file emitted by the assembler.
New behavior is currently gated under GOEXPERIMENT=regabi.
Updates #27539, #40724.
Change-Id: Ia22221fe26df0fa002191cfb13bdfaaa38d7df38
Reviewed-on: https://go-review.googlesource.com/c/go/+/260477
Run-TryBot: Than McIntosh <thanm@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Cherry Zhang <cherryyz@google.com>
Trust: Than McIntosh <thanm@google.com>
This commit is contained in:
parent
9499a2e108
commit
dd58239dd2
11 changed files with 207 additions and 65 deletions
|
|
@ -31,7 +31,7 @@ func testEndToEnd(t *testing.T, goarch, file string) {
|
||||||
architecture, ctxt := setArch(goarch)
|
architecture, ctxt := setArch(goarch)
|
||||||
architecture.Init(ctxt)
|
architecture.Init(ctxt)
|
||||||
lexer := lex.NewLexer(input)
|
lexer := lex.NewLexer(input)
|
||||||
parser := NewParser(ctxt, architecture, lexer)
|
parser := NewParser(ctxt, architecture, lexer, false)
|
||||||
pList := new(obj.Plist)
|
pList := new(obj.Plist)
|
||||||
var ok bool
|
var ok bool
|
||||||
testOut = new(bytes.Buffer) // The assembler writes test output to this buffer.
|
testOut = new(bytes.Buffer) // The assembler writes test output to this buffer.
|
||||||
|
|
@ -273,7 +273,7 @@ func testErrors(t *testing.T, goarch, file string) {
|
||||||
input := filepath.Join("testdata", file+".s")
|
input := filepath.Join("testdata", file+".s")
|
||||||
architecture, ctxt := setArch(goarch)
|
architecture, ctxt := setArch(goarch)
|
||||||
lexer := lex.NewLexer(input)
|
lexer := lex.NewLexer(input)
|
||||||
parser := NewParser(ctxt, architecture, lexer)
|
parser := NewParser(ctxt, architecture, lexer, false)
|
||||||
pList := new(obj.Plist)
|
pList := new(obj.Plist)
|
||||||
var ok bool
|
var ok bool
|
||||||
testOut = new(bytes.Buffer) // The assembler writes test output to this buffer.
|
testOut = new(bytes.Buffer) // The assembler writes test output to this buffer.
|
||||||
|
|
|
||||||
|
|
@ -57,7 +57,7 @@ var exprTests = []exprTest{
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestExpr(t *testing.T) {
|
func TestExpr(t *testing.T) {
|
||||||
p := NewParser(nil, nil, nil) // Expression evaluation uses none of these fields of the parser.
|
p := NewParser(nil, nil, nil, false) // Expression evaluation uses none of these fields of the parser.
|
||||||
for i, test := range exprTests {
|
for i, test := range exprTests {
|
||||||
p.start(lex.Tokenize(test.input))
|
p.start(lex.Tokenize(test.input))
|
||||||
result := int64(p.expr())
|
result := int64(p.expr())
|
||||||
|
|
@ -113,7 +113,7 @@ func TestBadExpr(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func runBadTest(i int, test badExprTest, t *testing.T) (err error) {
|
func runBadTest(i int, test badExprTest, t *testing.T) (err error) {
|
||||||
p := NewParser(nil, nil, nil) // Expression evaluation uses none of these fields of the parser.
|
p := NewParser(nil, nil, nil, false) // Expression evaluation uses none of these fields of the parser.
|
||||||
p.start(lex.Tokenize(test.input))
|
p.start(lex.Tokenize(test.input))
|
||||||
return tryParse(t, func() {
|
return tryParse(t, func() {
|
||||||
p.expr()
|
p.expr()
|
||||||
|
|
|
||||||
|
|
@ -39,7 +39,7 @@ func testBadInstParser(t *testing.T, goarch string, tests []badInstTest) {
|
||||||
for i, test := range tests {
|
for i, test := range tests {
|
||||||
arch, ctxt := setArch(goarch)
|
arch, ctxt := setArch(goarch)
|
||||||
tokenizer := lex.NewTokenizer("", strings.NewReader(test.input+"\n"), nil)
|
tokenizer := lex.NewTokenizer("", strings.NewReader(test.input+"\n"), nil)
|
||||||
parser := NewParser(ctxt, arch, tokenizer)
|
parser := NewParser(ctxt, arch, tokenizer, false)
|
||||||
|
|
||||||
err := tryParse(t, func() {
|
err := tryParse(t, func() {
|
||||||
parser.Parse()
|
parser.Parse()
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@ func setArch(goarch string) (*arch.Arch, *obj.Link) {
|
||||||
|
|
||||||
func newParser(goarch string) *Parser {
|
func newParser(goarch string) *Parser {
|
||||||
architecture, ctxt := setArch(goarch)
|
architecture, ctxt := setArch(goarch)
|
||||||
return NewParser(ctxt, architecture, nil)
|
return NewParser(ctxt, architecture, nil, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// tryParse executes parse func in panicOnError=true context.
|
// tryParse executes parse func in panicOnError=true context.
|
||||||
|
|
@ -75,7 +75,12 @@ func testOperandParser(t *testing.T, parser *Parser, tests []operandTest) {
|
||||||
parser.start(lex.Tokenize(test.input))
|
parser.start(lex.Tokenize(test.input))
|
||||||
addr := obj.Addr{}
|
addr := obj.Addr{}
|
||||||
parser.operand(&addr)
|
parser.operand(&addr)
|
||||||
result := obj.Dconv(&emptyProg, &addr)
|
var result string
|
||||||
|
if parser.compilingRuntime {
|
||||||
|
result = obj.DconvWithABIDetail(&emptyProg, &addr)
|
||||||
|
} else {
|
||||||
|
result = obj.Dconv(&emptyProg, &addr)
|
||||||
|
}
|
||||||
if result != test.output {
|
if result != test.output {
|
||||||
t.Errorf("fail at %s: got %s; expected %s\n", test.input, result, test.output)
|
t.Errorf("fail at %s: got %s; expected %s\n", test.input, result, test.output)
|
||||||
}
|
}
|
||||||
|
|
@ -86,6 +91,9 @@ func TestAMD64OperandParser(t *testing.T) {
|
||||||
parser := newParser("amd64")
|
parser := newParser("amd64")
|
||||||
testOperandParser(t, parser, amd64OperandTests)
|
testOperandParser(t, parser, amd64OperandTests)
|
||||||
testBadOperandParser(t, parser, amd64BadOperandTests)
|
testBadOperandParser(t, parser, amd64BadOperandTests)
|
||||||
|
parser.compilingRuntime = true
|
||||||
|
testOperandParser(t, parser, amd64RuntimeOperandTests)
|
||||||
|
testBadOperandParser(t, parser, amd64BadOperandRuntimeTests)
|
||||||
}
|
}
|
||||||
|
|
||||||
func Test386OperandParser(t *testing.T) {
|
func Test386OperandParser(t *testing.T) {
|
||||||
|
|
@ -141,7 +149,7 @@ func TestFuncAddress(t *testing.T) {
|
||||||
parser := newParser(sub.arch)
|
parser := newParser(sub.arch)
|
||||||
for _, test := range sub.tests {
|
for _, test := range sub.tests {
|
||||||
parser.start(lex.Tokenize(test.input))
|
parser.start(lex.Tokenize(test.input))
|
||||||
name, ok := parser.funcAddress()
|
name, _, ok := parser.funcAddress()
|
||||||
|
|
||||||
isFuncSym := strings.HasSuffix(test.input, "(SB)") &&
|
isFuncSym := strings.HasSuffix(test.input, "(SB)") &&
|
||||||
// Ignore static symbols.
|
// Ignore static symbols.
|
||||||
|
|
@ -298,6 +306,11 @@ var amd64OperandTests = []operandTest{
|
||||||
{"[):[o-FP", ""}, // Issue 12469 - asm hung parsing the o-FP range on non ARM platforms.
|
{"[):[o-FP", ""}, // Issue 12469 - asm hung parsing the o-FP range on non ARM platforms.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var amd64RuntimeOperandTests = []operandTest{
|
||||||
|
{"$bar<ABI0>(SB)", "$bar<ABI0>(SB)"},
|
||||||
|
{"$foo<ABIInternal>(SB)", "$foo<ABIInternal>(SB)"},
|
||||||
|
}
|
||||||
|
|
||||||
var amd64BadOperandTests = []badOperandTest{
|
var amd64BadOperandTests = []badOperandTest{
|
||||||
{"[", "register list: expected ']', found EOF"},
|
{"[", "register list: expected ']', found EOF"},
|
||||||
{"[4", "register list: bad low register in `[4`"},
|
{"[4", "register list: bad low register in `[4`"},
|
||||||
|
|
@ -311,6 +324,11 @@ var amd64BadOperandTests = []badOperandTest{
|
||||||
{"[X0-X1-X2]", "register list: expected ']' after `[X0-X1`, found '-'"},
|
{"[X0-X1-X2]", "register list: expected ']' after `[X0-X1`, found '-'"},
|
||||||
{"[X0,X3]", "register list: expected '-' after `[X0`, found ','"},
|
{"[X0,X3]", "register list: expected '-' after `[X0`, found ','"},
|
||||||
{"[X0,X1,X2,X3]", "register list: expected '-' after `[X0`, found ','"},
|
{"[X0,X1,X2,X3]", "register list: expected '-' after `[X0`, found ','"},
|
||||||
|
{"$foo<ABI0>", "ABI selector only permitted when compiling runtime, reference was to \"foo\""},
|
||||||
|
}
|
||||||
|
|
||||||
|
var amd64BadOperandRuntimeTests = []badOperandTest{
|
||||||
|
{"$foo<bletch>", "malformed ABI selector \"bletch\" in reference to \"foo\""},
|
||||||
}
|
}
|
||||||
|
|
||||||
var x86OperandTests = []operandTest{
|
var x86OperandTests = []operandTest{
|
||||||
|
|
|
||||||
|
|
@ -43,6 +43,7 @@ type Parser struct {
|
||||||
lastProg *obj.Prog
|
lastProg *obj.Prog
|
||||||
dataAddr map[string]int64 // Most recent address for DATA for this symbol.
|
dataAddr map[string]int64 // Most recent address for DATA for this symbol.
|
||||||
isJump bool // Instruction being assembled is a jump.
|
isJump bool // Instruction being assembled is a jump.
|
||||||
|
compilingRuntime bool
|
||||||
errorWriter io.Writer
|
errorWriter io.Writer
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -51,7 +52,7 @@ type Patch struct {
|
||||||
label string
|
label string
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewParser(ctxt *obj.Link, ar *arch.Arch, lexer lex.TokenReader) *Parser {
|
func NewParser(ctxt *obj.Link, ar *arch.Arch, lexer lex.TokenReader, compilingRuntime bool) *Parser {
|
||||||
return &Parser{
|
return &Parser{
|
||||||
ctxt: ctxt,
|
ctxt: ctxt,
|
||||||
arch: ar,
|
arch: ar,
|
||||||
|
|
@ -59,6 +60,7 @@ func NewParser(ctxt *obj.Link, ar *arch.Arch, lexer lex.TokenReader) *Parser {
|
||||||
labels: make(map[string]*obj.Prog),
|
labels: make(map[string]*obj.Prog),
|
||||||
dataAddr: make(map[string]int64),
|
dataAddr: make(map[string]int64),
|
||||||
errorWriter: os.Stderr,
|
errorWriter: os.Stderr,
|
||||||
|
compilingRuntime: compilingRuntime,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -310,8 +312,8 @@ func (p *Parser) symDefRef(w io.Writer, word string, operands [][]lex.Token) {
|
||||||
// Defines text symbol in operands[0].
|
// Defines text symbol in operands[0].
|
||||||
if len(operands) > 0 {
|
if len(operands) > 0 {
|
||||||
p.start(operands[0])
|
p.start(operands[0])
|
||||||
if name, ok := p.funcAddress(); ok {
|
if name, abi, ok := p.funcAddress(); ok {
|
||||||
fmt.Fprintf(w, "def %s ABI0\n", name)
|
fmt.Fprintf(w, "def %s %s\n", name, abi)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
|
|
@ -329,8 +331,8 @@ func (p *Parser) symDefRef(w io.Writer, word string, operands [][]lex.Token) {
|
||||||
// Search for symbol references.
|
// Search for symbol references.
|
||||||
for _, op := range operands {
|
for _, op := range operands {
|
||||||
p.start(op)
|
p.start(op)
|
||||||
if name, ok := p.funcAddress(); ok {
|
if name, abi, ok := p.funcAddress(); ok {
|
||||||
fmt.Fprintf(w, "ref %s ABI0\n", name)
|
fmt.Fprintf(w, "ref %s %s\n", name, abi)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -765,20 +767,19 @@ func (p *Parser) symbolReference(a *obj.Addr, name string, prefix rune) {
|
||||||
case '*':
|
case '*':
|
||||||
a.Type = obj.TYPE_INDIR
|
a.Type = obj.TYPE_INDIR
|
||||||
}
|
}
|
||||||
// Weirdness with statics: Might now have "<>".
|
|
||||||
isStatic := false
|
// Parse optional <> (indicates a static symbol) or
|
||||||
if p.peek() == '<' {
|
// <ABIxxx> (selecting text symbol with specific ABI).
|
||||||
isStatic = true
|
doIssueError := true
|
||||||
p.next()
|
isStatic, abi := p.symRefAttrs(name, doIssueError)
|
||||||
p.get('>')
|
|
||||||
}
|
|
||||||
if p.peek() == '+' || p.peek() == '-' {
|
if p.peek() == '+' || p.peek() == '-' {
|
||||||
a.Offset = int64(p.expr())
|
a.Offset = int64(p.expr())
|
||||||
}
|
}
|
||||||
if isStatic {
|
if isStatic {
|
||||||
a.Sym = p.ctxt.LookupStatic(name)
|
a.Sym = p.ctxt.LookupStatic(name)
|
||||||
} else {
|
} else {
|
||||||
a.Sym = p.ctxt.Lookup(name)
|
a.Sym = p.ctxt.LookupABI(name, abi)
|
||||||
}
|
}
|
||||||
if p.peek() == scanner.EOF {
|
if p.peek() == scanner.EOF {
|
||||||
if prefix == 0 && p.isJump {
|
if prefix == 0 && p.isJump {
|
||||||
|
|
@ -823,12 +824,60 @@ func (p *Parser) setPseudoRegister(addr *obj.Addr, reg string, isStatic bool, pr
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// symRefAttrs parses an optional function symbol attribute clause for
|
||||||
|
// the function symbol 'name', logging an error for a malformed
|
||||||
|
// attribute clause if 'issueError' is true. The return value is a
|
||||||
|
// (boolean, ABI) pair indicating that the named symbol is either
|
||||||
|
// static or a particular ABI specification.
|
||||||
|
//
|
||||||
|
// The expected form of the attribute clause is:
|
||||||
|
//
|
||||||
|
// empty, yielding (false, obj.ABI0)
|
||||||
|
// "<>", yielding (true, obj.ABI0)
|
||||||
|
// "<ABI0>" yielding (false, obj.ABI0)
|
||||||
|
// "<ABIInternal>" yielding (false, obj.ABIInternal)
|
||||||
|
//
|
||||||
|
// Anything else beginning with "<" logs an error if issueError is
|
||||||
|
// true, otherwise returns (false, obj.ABI0).
|
||||||
|
//
|
||||||
|
func (p *Parser) symRefAttrs(name string, issueError bool) (bool, obj.ABI) {
|
||||||
|
abi := obj.ABI0
|
||||||
|
isStatic := false
|
||||||
|
if p.peek() != '<' {
|
||||||
|
return isStatic, abi
|
||||||
|
}
|
||||||
|
p.next()
|
||||||
|
tok := p.peek()
|
||||||
|
if tok == '>' {
|
||||||
|
isStatic = true
|
||||||
|
} else if tok == scanner.Ident {
|
||||||
|
abistr := p.get(scanner.Ident).String()
|
||||||
|
if !p.compilingRuntime {
|
||||||
|
if issueError {
|
||||||
|
p.errorf("ABI selector only permitted when compiling runtime, reference was to %q", name)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
theabi, valid := obj.ParseABI(abistr)
|
||||||
|
if !valid {
|
||||||
|
if issueError {
|
||||||
|
p.errorf("malformed ABI selector %q in reference to %q",
|
||||||
|
abistr, name)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
abi = theabi
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
p.get('>')
|
||||||
|
return isStatic, abi
|
||||||
|
}
|
||||||
|
|
||||||
// funcAddress parses an external function address. This is a
|
// funcAddress parses an external function address. This is a
|
||||||
// constrained form of the operand syntax that's always SB-based,
|
// constrained form of the operand syntax that's always SB-based,
|
||||||
// non-static, and has at most a simple integer offset:
|
// non-static, and has at most a simple integer offset:
|
||||||
//
|
//
|
||||||
// [$|*]sym[+Int](SB)
|
// [$|*]sym[<abi>][+Int](SB)
|
||||||
func (p *Parser) funcAddress() (string, bool) {
|
func (p *Parser) funcAddress() (string, obj.ABI, bool) {
|
||||||
switch p.peek() {
|
switch p.peek() {
|
||||||
case '$', '*':
|
case '$', '*':
|
||||||
// Skip prefix.
|
// Skip prefix.
|
||||||
|
|
@ -838,25 +887,32 @@ func (p *Parser) funcAddress() (string, bool) {
|
||||||
tok := p.next()
|
tok := p.next()
|
||||||
name := tok.String()
|
name := tok.String()
|
||||||
if tok.ScanToken != scanner.Ident || p.atStartOfRegister(name) {
|
if tok.ScanToken != scanner.Ident || p.atStartOfRegister(name) {
|
||||||
return "", false
|
return "", obj.ABI0, false
|
||||||
|
}
|
||||||
|
// Parse optional <> (indicates a static symbol) or
|
||||||
|
// <ABIxxx> (selecting text symbol with specific ABI).
|
||||||
|
noErrMsg := false
|
||||||
|
isStatic, abi := p.symRefAttrs(name, noErrMsg)
|
||||||
|
if isStatic {
|
||||||
|
return "", obj.ABI0, false // This function rejects static symbols.
|
||||||
}
|
}
|
||||||
tok = p.next()
|
tok = p.next()
|
||||||
if tok.ScanToken == '+' {
|
if tok.ScanToken == '+' {
|
||||||
if p.next().ScanToken != scanner.Int {
|
if p.next().ScanToken != scanner.Int {
|
||||||
return "", false
|
return "", obj.ABI0, false
|
||||||
}
|
}
|
||||||
tok = p.next()
|
tok = p.next()
|
||||||
}
|
}
|
||||||
if tok.ScanToken != '(' {
|
if tok.ScanToken != '(' {
|
||||||
return "", false
|
return "", obj.ABI0, false
|
||||||
}
|
}
|
||||||
if reg := p.next(); reg.ScanToken != scanner.Ident || reg.String() != "SB" {
|
if reg := p.next(); reg.ScanToken != scanner.Ident || reg.String() != "SB" {
|
||||||
return "", false
|
return "", obj.ABI0, false
|
||||||
}
|
}
|
||||||
if p.next().ScanToken != ')' || p.peek() != scanner.EOF {
|
if p.next().ScanToken != ')' || p.peek() != scanner.EOF {
|
||||||
return "", false
|
return "", obj.ABI0, false
|
||||||
}
|
}
|
||||||
return name, true
|
return name, abi, true
|
||||||
}
|
}
|
||||||
|
|
||||||
// registerIndirect parses the general form of a register indirection.
|
// registerIndirect parses the general form of a register indirection.
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,7 @@ func TestErroneous(t *testing.T) {
|
||||||
{"TEXT", "$0É:0, 0, $1", "expected end of operand, found É"}, // Issue #12467.
|
{"TEXT", "$0É:0, 0, $1", "expected end of operand, found É"}, // Issue #12467.
|
||||||
{"TEXT", "$:0:(SB, 0, $1", "expected '(', found 0"}, // Issue 12468.
|
{"TEXT", "$:0:(SB, 0, $1", "expected '(', found 0"}, // Issue 12468.
|
||||||
{"TEXT", "@B(SB),0,$0", "expected '(', found B"}, // Issue 23580.
|
{"TEXT", "@B(SB),0,$0", "expected '(', found B"}, // Issue 23580.
|
||||||
|
{"TEXT", "foo<ABIInternal>(SB),0", "ABI selector only permitted when compiling runtime, reference was to \"foo\""},
|
||||||
{"FUNCDATA", "", "expect two operands for FUNCDATA"},
|
{"FUNCDATA", "", "expect two operands for FUNCDATA"},
|
||||||
{"FUNCDATA", "(SB ", "expect two operands for FUNCDATA"},
|
{"FUNCDATA", "(SB ", "expect two operands for FUNCDATA"},
|
||||||
{"DATA", "", "expect two operands for DATA"},
|
{"DATA", "", "expect two operands for DATA"},
|
||||||
|
|
|
||||||
|
|
@ -52,6 +52,7 @@ func main() {
|
||||||
case "all", "ret":
|
case "all", "ret":
|
||||||
ctxt.Retpoline = true
|
ctxt.Retpoline = true
|
||||||
}
|
}
|
||||||
|
compilingRuntime := objabi.IsRuntimePackagePath(*flags.Importpath)
|
||||||
|
|
||||||
ctxt.Bso = bufio.NewWriter(os.Stdout)
|
ctxt.Bso = bufio.NewWriter(os.Stdout)
|
||||||
defer ctxt.Bso.Flush()
|
defer ctxt.Bso.Flush()
|
||||||
|
|
@ -74,7 +75,7 @@ func main() {
|
||||||
var failedFile string
|
var failedFile string
|
||||||
for _, f := range flag.Args() {
|
for _, f := range flag.Args() {
|
||||||
lexer := lex.NewLexer(f)
|
lexer := lex.NewLexer(f)
|
||||||
parser := asm.NewParser(ctxt, architecture, lexer)
|
parser := asm.NewParser(ctxt, architecture, lexer, compilingRuntime)
|
||||||
ctxt.DiagFunc = func(format string, args ...interface{}) {
|
ctxt.DiagFunc = func(format string, args ...interface{}) {
|
||||||
diag = true
|
diag = true
|
||||||
log.Printf(format, args...)
|
log.Printf(format, args...)
|
||||||
|
|
|
||||||
|
|
@ -968,9 +968,10 @@ func readSymABIs(file, myimportpath string) {
|
||||||
if len(parts) != 3 {
|
if len(parts) != 3 {
|
||||||
log.Fatalf(`%s:%d: invalid symabi: syntax is "%s sym abi"`, file, lineNum, parts[0])
|
log.Fatalf(`%s:%d: invalid symabi: syntax is "%s sym abi"`, file, lineNum, parts[0])
|
||||||
}
|
}
|
||||||
sym, abi := parts[1], parts[2]
|
sym, abistr := parts[1], parts[2]
|
||||||
if abi != "ABI0" { // Only supported external ABI right now
|
abi, valid := obj.ParseABI(abistr)
|
||||||
log.Fatalf(`%s:%d: invalid symabi: unknown abi "%s"`, file, lineNum, abi)
|
if !valid {
|
||||||
|
log.Fatalf(`%s:%d: invalid symabi: unknown abi "%s"`, file, lineNum, abistr)
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the symbol is already prefixed with
|
// If the symbol is already prefixed with
|
||||||
|
|
@ -983,9 +984,9 @@ func readSymABIs(file, myimportpath string) {
|
||||||
|
|
||||||
// Record for later.
|
// Record for later.
|
||||||
if parts[0] == "def" {
|
if parts[0] == "def" {
|
||||||
symabiDefs[sym] = obj.ABI0
|
symabiDefs[sym] = abi
|
||||||
} else {
|
} else {
|
||||||
symabiRefs[sym] = obj.ABI0
|
symabiRefs[sym] = abi
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
log.Fatalf(`%s:%d: invalid symabi type "%s"`, file, lineNum, parts[0])
|
log.Fatalf(`%s:%d: invalid symabi type "%s"`, file, lineNum, parts[0])
|
||||||
|
|
|
||||||
|
|
@ -502,6 +502,20 @@ const (
|
||||||
ABICount
|
ABICount
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// ParseABI converts from a string representation in 'abistr' to the
|
||||||
|
// corresponding ABI value. Second return value is TRUE if the
|
||||||
|
// abi string is recognized, FALSE otherwise.
|
||||||
|
func ParseABI(abistr string) (ABI, bool) {
|
||||||
|
switch abistr {
|
||||||
|
default:
|
||||||
|
return ABI0, false
|
||||||
|
case "ABI0":
|
||||||
|
return ABI0, true
|
||||||
|
case "ABIInternal":
|
||||||
|
return ABIInternal, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Attribute is a set of symbol attributes.
|
// Attribute is a set of symbol attributes.
|
||||||
type Attribute uint32
|
type Attribute uint32
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -210,13 +210,30 @@ func (ctxt *Link) CanReuseProgs() bool {
|
||||||
return ctxt.Debugasm == 0
|
return ctxt.Debugasm == 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Dconv accepts an argument 'a' within a prog 'p' and returns a string
|
||||||
|
// with a formatted version of the argument.
|
||||||
func Dconv(p *Prog, a *Addr) string {
|
func Dconv(p *Prog, a *Addr) string {
|
||||||
buf := new(bytes.Buffer)
|
buf := new(bytes.Buffer)
|
||||||
WriteDconv(buf, p, a)
|
writeDconv(buf, p, a, false)
|
||||||
return buf.String()
|
return buf.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DconvDconvWithABIDetail accepts an argument 'a' within a prog 'p'
|
||||||
|
// and returns a string with a formatted version of the argument, in
|
||||||
|
// which text symbols are rendered with explicit ABI selectors.
|
||||||
|
func DconvWithABIDetail(p *Prog, a *Addr) string {
|
||||||
|
buf := new(bytes.Buffer)
|
||||||
|
writeDconv(buf, p, a, true)
|
||||||
|
return buf.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// WriteDconv accepts an argument 'a' within a prog 'p'
|
||||||
|
// and writes a formatted version of the arg to the writer.
|
||||||
func WriteDconv(w io.Writer, p *Prog, a *Addr) {
|
func WriteDconv(w io.Writer, p *Prog, a *Addr) {
|
||||||
|
writeDconv(w, p, a, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeDconv(w io.Writer, p *Prog, a *Addr, abiDetail bool) {
|
||||||
switch a.Type {
|
switch a.Type {
|
||||||
default:
|
default:
|
||||||
fmt.Fprintf(w, "type=%d", a.Type)
|
fmt.Fprintf(w, "type=%d", a.Type)
|
||||||
|
|
@ -250,7 +267,7 @@ func WriteDconv(w io.Writer, p *Prog, a *Addr) {
|
||||||
|
|
||||||
case TYPE_BRANCH:
|
case TYPE_BRANCH:
|
||||||
if a.Sym != nil {
|
if a.Sym != nil {
|
||||||
fmt.Fprintf(w, "%s(SB)", a.Sym.Name)
|
fmt.Fprintf(w, "%s%s(SB)", a.Sym.Name, abiDecorate(a, abiDetail))
|
||||||
} else if a.Target() != nil {
|
} else if a.Target() != nil {
|
||||||
fmt.Fprint(w, a.Target().Pc)
|
fmt.Fprint(w, a.Target().Pc)
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -259,7 +276,7 @@ func WriteDconv(w io.Writer, p *Prog, a *Addr) {
|
||||||
|
|
||||||
case TYPE_INDIR:
|
case TYPE_INDIR:
|
||||||
io.WriteString(w, "*")
|
io.WriteString(w, "*")
|
||||||
a.WriteNameTo(w)
|
a.writeNameTo(w, abiDetail)
|
||||||
|
|
||||||
case TYPE_MEM:
|
case TYPE_MEM:
|
||||||
a.WriteNameTo(w)
|
a.WriteNameTo(w)
|
||||||
|
|
@ -299,7 +316,7 @@ func WriteDconv(w io.Writer, p *Prog, a *Addr) {
|
||||||
|
|
||||||
case TYPE_ADDR:
|
case TYPE_ADDR:
|
||||||
io.WriteString(w, "$")
|
io.WriteString(w, "$")
|
||||||
a.WriteNameTo(w)
|
a.writeNameTo(w, abiDetail)
|
||||||
|
|
||||||
case TYPE_SHIFT:
|
case TYPE_SHIFT:
|
||||||
v := int(a.Offset)
|
v := int(a.Offset)
|
||||||
|
|
@ -335,6 +352,11 @@ func WriteDconv(w io.Writer, p *Prog, a *Addr) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *Addr) WriteNameTo(w io.Writer) {
|
func (a *Addr) WriteNameTo(w io.Writer) {
|
||||||
|
a.writeNameTo(w, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Addr) writeNameTo(w io.Writer, abiDetail bool) {
|
||||||
|
|
||||||
switch a.Name {
|
switch a.Name {
|
||||||
default:
|
default:
|
||||||
fmt.Fprintf(w, "name=%d", a.Name)
|
fmt.Fprintf(w, "name=%d", a.Name)
|
||||||
|
|
@ -356,7 +378,7 @@ func (a *Addr) WriteNameTo(w io.Writer) {
|
||||||
reg = Rconv(int(a.Reg))
|
reg = Rconv(int(a.Reg))
|
||||||
}
|
}
|
||||||
if a.Sym != nil {
|
if a.Sym != nil {
|
||||||
fmt.Fprintf(w, "%s%s(%s)", a.Sym.Name, offConv(a.Offset), reg)
|
fmt.Fprintf(w, "%s%s%s(%s)", a.Sym.Name, abiDecorate(a, abiDetail), offConv(a.Offset), reg)
|
||||||
} else {
|
} else {
|
||||||
fmt.Fprintf(w, "%s(%s)", offConv(a.Offset), reg)
|
fmt.Fprintf(w, "%s(%s)", offConv(a.Offset), reg)
|
||||||
}
|
}
|
||||||
|
|
@ -596,3 +618,10 @@ func Bool2int(b bool) int {
|
||||||
}
|
}
|
||||||
return i
|
return i
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func abiDecorate(a *Addr, abiDetail bool) string {
|
||||||
|
if !abiDetail || a.Sym == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("<%s>", a.Sym.ABI())
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -39,3 +39,25 @@ func PathToPrefix(s string) string {
|
||||||
|
|
||||||
return string(p)
|
return string(p)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsRuntimePackagePath examines 'pkgpath' and returns TRUE if it
|
||||||
|
// belongs to the collection of "runtime-related" packages, including
|
||||||
|
// "runtime" itself, "reflect", "syscall", and the
|
||||||
|
// "runtime/internal/*" packages. The compiler and/or assembler in
|
||||||
|
// some cases need to be aware of when they are building such a
|
||||||
|
// package, for example to enable features such as ABI selectors in
|
||||||
|
// assembly sources.
|
||||||
|
func IsRuntimePackagePath(pkgpath string) bool {
|
||||||
|
rval := false
|
||||||
|
switch pkgpath {
|
||||||
|
case "runtime":
|
||||||
|
rval = true
|
||||||
|
case "reflect":
|
||||||
|
rval = true
|
||||||
|
case "syscall":
|
||||||
|
rval = true
|
||||||
|
default:
|
||||||
|
rval = strings.HasPrefix(pkgpath, "runtime/internal")
|
||||||
|
}
|
||||||
|
return rval
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue