Commit c5718b6b authored by Austin Clements's avatar Austin Clements

cmd/internal/obj, cmd/link: record ABIs and aliases in Go obj files

This repurposes the "version" field of a symbol reference in the Go
object file format to be an ABI field. Currently, this is just 0 or 1
depending on whether the symbol is static (the linker turns it into a
different internal version number), so it's already only tenuously a
symbol version. We change this to be -1 for static symbols and
otherwise by the ABI number.

This also adds a separate list of ABI alias symbols to be recorded in
the object file. The ABI aliases must be a separate list and not just
part of the symbol definitions because it's possible to have a symbol
defined in one package and the alias "defined" in a different package.
For example, this can happen if a symbol is defined in assembly in one
package and stubbed in a different package. The stub triggers the
generation of the ABI alias, but in a different package from the
definition.

For #27539.

Change-Id: I015c9fe54690c027de6ef77e22b5585976a01587
Reviewed-on: https://go-review.googlesource.com/c/147157
Run-TryBot: Austin Clements <austin@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: default avatarDavid Chase <drchase@google.com>
parent 07544c7e
...@@ -288,18 +288,31 @@ func (r *objReader) readSymID() SymID { ...@@ -288,18 +288,31 @@ func (r *objReader) readSymID() SymID {
} }
func (r *objReader) readRef() { func (r *objReader) readRef() {
name, vers := r.readString(), r.readInt() name, abiOrStatic := r.readString(), r.readInt()
// In a symbol name in an object file, "". denotes the // In a symbol name in an object file, "". denotes the
// prefix for the package in which the object file has been found. // prefix for the package in which the object file has been found.
// Expand it. // Expand it.
name = strings.ReplaceAll(name, `"".`, r.pkgprefix) name = strings.ReplaceAll(name, `"".`, r.pkgprefix)
// An individual object file only records version 0 (extern) or 1 (static). // The ABI field records either the ABI or -1 for static symbols.
// To make static symbols unique across all files being read, we //
// replace version 1 with the version corresponding to the current // To distinguish different static symbols with the same name,
// file number. The number is incremented on each call to parseObject. // we use the symbol "version". Version 0 corresponds to
if vers != 0 { // global symbols, and each file has a unique version > 0 for
// all of its static symbols. The version is incremented on
// each call to parseObject.
//
// For global symbols, we currently ignore the ABI.
//
// TODO(austin): Record the ABI in SymID. Since this is a
// public API, we'll have to keep Version as 0 and record the
// ABI in a new field (which differs from how the linker does
// this, but that's okay). Show the ABI in things like
// objdump.
var vers int64
if abiOrStatic == -1 {
// Static symbol
vers = r.p.MaxVersion vers = r.p.MaxVersion
} }
r.p.SymRefs = append(r.p.SymRefs, SymID{name, vers}) r.p.SymRefs = append(r.p.SymRefs, SymID{name, vers})
...@@ -487,7 +500,7 @@ func (r *objReader) parseObject(prefix []byte) error { ...@@ -487,7 +500,7 @@ func (r *objReader) parseObject(prefix []byte) error {
// TODO: extract OS + build ID if/when we need it // TODO: extract OS + build ID if/when we need it
r.readFull(r.tmp[:8]) r.readFull(r.tmp[:8])
if !bytes.Equal(r.tmp[:8], []byte("\x00\x00go19ld")) { if !bytes.Equal(r.tmp[:8], []byte("\x00go112ld")) {
return r.error(errCorruptObject) return r.error(errCorruptObject)
} }
...@@ -602,7 +615,7 @@ func (r *objReader) parseObject(prefix []byte) error { ...@@ -602,7 +615,7 @@ func (r *objReader) parseObject(prefix []byte) error {
} }
r.readFull(r.tmp[:7]) r.readFull(r.tmp[:7])
if !bytes.Equal(r.tmp[:7], []byte("\xffgo19ld")) { if !bytes.Equal(r.tmp[:7], []byte("go112ld")) {
return r.error(errCorruptObject) return r.error(errCorruptObject)
} }
......
...@@ -432,7 +432,7 @@ const ( ...@@ -432,7 +432,7 @@ const (
) )
// Attribute is a set of symbol attributes. // Attribute is a set of symbol attributes.
type Attribute int16 type Attribute uint16
const ( const (
AttrDuplicateOK Attribute = 1 << iota AttrDuplicateOK Attribute = 1 << iota
...@@ -468,6 +468,13 @@ const ( ...@@ -468,6 +468,13 @@ const (
// For function symbols; indicates that the specified function was the // For function symbols; indicates that the specified function was the
// target of an inline during compilation // target of an inline during compilation
AttrWasInlined AttrWasInlined
// attrABIBase is the value at which the ABI is encoded in
// Attribute. This must be last; all bits after this are
// assumed to be an ABI value.
//
// MUST BE LAST since all bits above this comprise the ABI.
attrABIBase
) )
func (a Attribute) DuplicateOK() bool { return a&AttrDuplicateOK != 0 } func (a Attribute) DuplicateOK() bool { return a&AttrDuplicateOK != 0 }
...@@ -493,6 +500,12 @@ func (a *Attribute) Set(flag Attribute, value bool) { ...@@ -493,6 +500,12 @@ func (a *Attribute) Set(flag Attribute, value bool) {
} }
} }
func (a Attribute) ABI() ABI { return ABI(a / attrABIBase) }
func (a *Attribute) SetABI(abi ABI) {
const mask = 1 // Only one ABI bit for now.
*a = (*a &^ (mask * attrABIBase)) | Attribute(abi)*attrABIBase
}
var textAttrStrings = [...]struct { var textAttrStrings = [...]struct {
bit Attribute bit Attribute
s string s string
...@@ -524,6 +537,12 @@ func (a Attribute) TextAttrString() string { ...@@ -524,6 +537,12 @@ func (a Attribute) TextAttrString() string {
a &^= x.bit a &^= x.bit
} }
} }
switch a.ABI() {
case ABI0:
case ABIInternal:
s += "ABIInternal|"
a.SetABI(0) // Clear ABI so we don't print below.
}
if a != 0 { if a != 0 {
s += fmt.Sprintf("UnknownAttribute(%d)|", a) s += fmt.Sprintf("UnknownAttribute(%d)|", a)
} }
...@@ -606,6 +625,16 @@ type Link struct { ...@@ -606,6 +625,16 @@ type Link struct {
// state for writing objects // state for writing objects
Text []*LSym Text []*LSym
Data []*LSym Data []*LSym
// ABIAliases are text symbols that should be aliased to all
// ABIs. These symbols may only be referenced and not defined
// by this object, since the need for an alias may appear in a
// different object than the definition. Hence, this
// information can't be carried in the symbol definition.
//
// TODO(austin): Replace this with ABI wrappers once the ABIs
// actually diverge.
ABIAliases []*LSym
} }
func (ctxt *Link) Diag(format string, args ...interface{}) { func (ctxt *Link) Diag(format string, args ...interface{}) {
......
...@@ -82,7 +82,7 @@ func WriteObjFile(ctxt *Link, b *bufio.Writer) { ...@@ -82,7 +82,7 @@ func WriteObjFile(ctxt *Link, b *bufio.Writer) {
w := newObjWriter(ctxt, b) w := newObjWriter(ctxt, b)
// Magic header // Magic header
w.wr.WriteString("\x00\x00go19ld") w.wr.WriteString("\x00go112ld")
// Version // Version
w.wr.WriteByte(1) w.wr.WriteByte(1)
...@@ -102,6 +102,10 @@ func WriteObjFile(ctxt *Link, b *bufio.Writer) { ...@@ -102,6 +102,10 @@ func WriteObjFile(ctxt *Link, b *bufio.Writer) {
w.writeRefs(s) w.writeRefs(s)
w.addLengths(s) w.addLengths(s)
} }
for _, s := range ctxt.ABIAliases {
w.writeRefs(s)
w.addLengths(s)
}
// End symbol references // End symbol references
w.wr.WriteByte(0xff) w.wr.WriteByte(0xff)
...@@ -137,9 +141,12 @@ func WriteObjFile(ctxt *Link, b *bufio.Writer) { ...@@ -137,9 +141,12 @@ func WriteObjFile(ctxt *Link, b *bufio.Writer) {
for _, s := range ctxt.Data { for _, s := range ctxt.Data {
w.writeSym(s) w.writeSym(s)
} }
for _, s := range ctxt.ABIAliases {
w.writeSym(s)
}
// Magic footer // Magic footer
w.wr.WriteString("\xff\xffgo19ld") w.wr.WriteString("\xffgo112ld")
} }
// Symbols are prefixed so their content doesn't get confused with the magic footer. // Symbols are prefixed so their content doesn't get confused with the magic footer.
...@@ -155,8 +162,12 @@ func (w *objWriter) writeRef(s *LSym, isPath bool) { ...@@ -155,8 +162,12 @@ func (w *objWriter) writeRef(s *LSym, isPath bool) {
} else { } else {
w.writeString(s.Name) w.writeString(s.Name)
} }
// Write "version". // Write ABI/static information.
w.writeBool(s.Static()) abi := int64(s.ABI())
if s.Static() {
abi = -1
}
w.writeInt(abi)
w.nRefs++ w.nRefs++
s.RefIdx = w.nRefs s.RefIdx = w.nRefs
} }
......
...@@ -22,7 +22,7 @@ ...@@ -22,7 +22,7 @@
// //
// The file format is: // The file format is:
// //
// - magic header: "\x00\x00go19ld" // - magic header: "\x00go112ld"
// - byte 1 - version number // - byte 1 - version number
// - sequence of strings giving dependencies (imported packages) // - sequence of strings giving dependencies (imported packages)
// - empty string (marks end of sequence) // - empty string (marks end of sequence)
...@@ -38,7 +38,7 @@ ...@@ -38,7 +38,7 @@
// - data, the content of the defined symbols // - data, the content of the defined symbols
// - sequence of defined symbols // - sequence of defined symbols
// - byte 0xff (marks end of sequence) // - byte 0xff (marks end of sequence)
// - magic footer: "\xff\xffgo19ld" // - magic footer: "\xffgo112ld"
// //
// All integers are stored in a zigzag varint format. // All integers are stored in a zigzag varint format.
// See golang.org/s/go12symtab for a definition. // See golang.org/s/go12symtab for a definition.
...@@ -46,7 +46,7 @@ ...@@ -46,7 +46,7 @@
// Data blocks and strings are both stored as an integer // Data blocks and strings are both stored as an integer
// followed by that many bytes. // followed by that many bytes.
// //
// A symbol reference is a string name followed by a version. // A symbol reference is a string name followed by an ABI or -1 for static.
// //
// A symbol points to other symbols using an index into the symbol // A symbol points to other symbols using an index into the symbol
// reference sequence. Index 0 corresponds to a nil symbol pointer. // reference sequence. Index 0 corresponds to a nil symbol pointer.
...@@ -57,7 +57,7 @@ ...@@ -57,7 +57,7 @@
// //
// - byte 0xfe (sanity check for synchronization) // - byte 0xfe (sanity check for synchronization)
// - type [byte] // - type [byte]
// - name & version [symref index] // - name & ABI [symref index]
// - flags [int] // - flags [int]
// 1<<0 dupok // 1<<0 dupok
// 1<<1 local // 1<<1 local
......
...@@ -13,6 +13,7 @@ import ( ...@@ -13,6 +13,7 @@ import (
"bytes" "bytes"
"cmd/internal/bio" "cmd/internal/bio"
"cmd/internal/dwarf" "cmd/internal/dwarf"
"cmd/internal/obj"
"cmd/internal/objabi" "cmd/internal/objabi"
"cmd/internal/sys" "cmd/internal/sys"
"cmd/link/internal/sym" "cmd/link/internal/sym"
...@@ -23,8 +24,8 @@ import ( ...@@ -23,8 +24,8 @@ import (
) )
const ( const (
startmagic = "\x00\x00go19ld" startmagic = "\x00go112ld"
endmagic = "\xff\xffgo19ld" endmagic = "\xffgo112ld"
) )
var emptyPkg = []byte(`"".`) var emptyPkg = []byte(`"".`)
...@@ -382,17 +383,20 @@ func (r *objReader) readRef() { ...@@ -382,17 +383,20 @@ func (r *objReader) readRef() {
log.Fatalf("readSym out of sync") log.Fatalf("readSym out of sync")
} }
name := r.readSymName() name := r.readSymName()
v := r.readInt() var v int
if v != 0 && v != 1 { if abi := r.readInt(); abi == -1 {
log.Fatalf("invalid symbol version for %q: %d", name, v) // Static
}
if v == 1 {
v = r.localSymVersion v = r.localSymVersion
} else if abiver := sym.ABIToVersion(obj.ABI(abi)); abiver != -1 {
// Note that data symbols are "ABI0", which maps to version 0.
v = abiver
} else {
log.Fatalf("invalid symbol ABI for %q: %d", name, abi)
} }
s := r.syms.Lookup(name, v) s := r.syms.Lookup(name, v)
r.refs = append(r.refs, s) r.refs = append(r.refs, s)
if s == nil || v != 0 { if s == nil || v == r.localSymVersion {
return return
} }
if s.Name[0] == '$' && len(s.Name) > 5 && s.Type == 0 && len(s.P) == 0 { if s.Name[0] == '$' && len(s.Name) > 5 && s.Type == 0 && len(s.P) == 0 {
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
package sym package sym
import ( import (
"cmd/internal/obj"
"cmd/internal/objabi" "cmd/internal/objabi"
"cmd/internal/sys" "cmd/internal/sys"
"debug/elf" "debug/elf"
...@@ -52,9 +53,21 @@ type AuxSymbol struct { ...@@ -52,9 +53,21 @@ type AuxSymbol struct {
} }
const ( const (
SymVerABI0 = 0
SymVerABIInternal = 1
SymVerStatic = 10 // Minimum version used by static (file-local) syms SymVerStatic = 10 // Minimum version used by static (file-local) syms
) )
func ABIToVersion(abi obj.ABI) int {
switch abi {
case obj.ABI0:
return SymVerABI0
case obj.ABIInternal:
return SymVerABIInternal
}
return -1
}
func (s *Symbol) String() string { func (s *Symbol) String() string {
if s.Version == 0 { if s.Version == 0 {
return s.Name return s.Name
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment