fix(goobj): external package and symbol indices and arm64 pair relocations
Assisted-by: GLM 5.3
This commit is contained in:
+72
-32
@@ -40,8 +40,11 @@ func exportPath(importPath string) (string, error) {
|
||||
//
|
||||
// refs maps package import paths to the symbol names referenced from that
|
||||
// package. The returned pkgIdx maps each import path to its position in
|
||||
// the blkPkgIdx table (0-based), and symIdx gives each symbol's index within
|
||||
// its package.
|
||||
// the blkPkgIdx table, which reserves index 0 for the dummy invalid
|
||||
// package (cmd/internal/obj/sym.go: "0 is invalid index"; the loader's
|
||||
// reader loop starts at 1), so package i sits at block index i+1 and its
|
||||
// relocations carry i+1. symIdx gives each symbol's index within its
|
||||
// package.
|
||||
func resolveExternalGOOBJ(refs map[string][]string) (pkgIdx map[string]int, symIdx map[string]int, err error) {
|
||||
pkgIdx = make(map[string]int, len(refs))
|
||||
symIdx = make(map[string]int)
|
||||
@@ -50,7 +53,9 @@ func resolveExternalGOOBJ(refs map[string][]string) (pkgIdx map[string]int, symI
|
||||
packages := sortedPkgRefs(refs)
|
||||
|
||||
for i, pkg := range packages {
|
||||
pkgIdx[pkg.path] = i
|
||||
// Block index 0 is the dummy invalid package; the first real
|
||||
// package starts at 1.
|
||||
pkgIdx[pkg.path] = i + 1
|
||||
exp, err := exportPath(pkg.path)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
@@ -145,40 +150,65 @@ func parseArDecimal(b []byte) int {
|
||||
}
|
||||
|
||||
// goobjFile is a parsed GOOBJ file: the string table and the symbol-definition
|
||||
// block.
|
||||
// blocks. The hashed blocks are kept raw: their symbols carry no names, only
|
||||
// the loader needs their counts.
|
||||
type goobjFile struct {
|
||||
strTab []byte // string table, at headerSize + n
|
||||
symdef []byte // blkSymdef raw block
|
||||
npdef []byte // blkNonpkgdef raw block
|
||||
strTab []byte // string table, at headerSize + n
|
||||
symdef []byte // blkSymdef raw block
|
||||
hashed64 []byte // blkHashed64def raw block
|
||||
hashed []byte // blkHasheddef raw block
|
||||
npdef []byte // blkNonpkgdef raw block
|
||||
}
|
||||
|
||||
// symbols returns all symbol names in definition order by scanning the
|
||||
// symdef and nonpkgdef blocks and resolving each name through the string
|
||||
// table. Package definitions (blkSymdef) use fully-qualified names like
|
||||
// "runtime.morestack"; non-package definitions (blkNonpkgdef) use bare
|
||||
// names like "morestack". This combined list matches the index the
|
||||
// linker expects for cross-package references.
|
||||
// loaderIndexBase returns the index the first nonpkgdef symbol occupies in the
|
||||
// loader's per-object symbol array. cmd/link lays the definition blocks out as
|
||||
// symdef, hashed64def, hasheddef, nonpkgdef, nonpkgref (loader.go: preloadSyms
|
||||
// fills r.syms in exactly that order, and resolve() indexes PkgIdxNone and
|
||||
// cross-package SymIdx into it), so a symbol found in blkNonpkgdef carries the
|
||||
// three leading blocks' symbol counts as its base.
|
||||
func (f *goobjFile) loaderIndexBase() int {
|
||||
return len(f.symdef)/recSymSize + len(f.hashed64)/recSymSize + len(f.hashed)/recSymSize
|
||||
}
|
||||
|
||||
// symbols returns the names of the symdef and nonpkgdef blocks in
|
||||
// definition order. Package definitions (blkSymdef) use fully-qualified
|
||||
// names like "runtime.morestack"; non-package definitions (blkNonpkgdef)
|
||||
// use bare names like "morestack". For lookups by index prefer
|
||||
// findSymbol: it adds the hashed blocks' count the loader's array
|
||||
// interleaves between the two.
|
||||
func (f *goobjFile) symbols() []string {
|
||||
return append(f.defNames(), f.npdefNames()...)
|
||||
}
|
||||
|
||||
// findSymbol returns the index of a symbol within the combined symbol list,
|
||||
// or -1 if not found. It first tries the fully-qualified name (pkg.name),
|
||||
// then the bare name.
|
||||
// findSymbol returns the index of a symbol within the loader's per-object
|
||||
// symbol array, or -1 if not found. It first tries the fully-qualified
|
||||
// name (pkg.name), then the bare name (assembly objects store dotless
|
||||
// names, e.g. runtime's "gogo", for symbols other packages reach through
|
||||
// a linkname).
|
||||
func (f *goobjFile) findSymbol(pkg, name string) int {
|
||||
base := f.loaderIndexBase()
|
||||
qualified := pkg + "." + name
|
||||
syms := f.symbols()
|
||||
for i, s := range syms {
|
||||
for i, s := range f.defNames() {
|
||||
if s == qualified {
|
||||
return i
|
||||
}
|
||||
}
|
||||
// Try bare name (for non-package definitions).
|
||||
for i, s := range syms {
|
||||
for i, s := range f.npdefNames() {
|
||||
if s == qualified {
|
||||
return base + i
|
||||
}
|
||||
}
|
||||
// Try bare name (for dotless assembly definitions).
|
||||
for i, s := range f.defNames() {
|
||||
if s == name {
|
||||
return i
|
||||
}
|
||||
}
|
||||
for i, s := range f.npdefNames() {
|
||||
if s == name {
|
||||
return base + i
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
@@ -192,12 +222,16 @@ func (f *goobjFile) npdefNames() []string {
|
||||
return f.readSymNames(f.npdef)
|
||||
}
|
||||
|
||||
// recSymSize is the size of one Sym record in the definition blocks
|
||||
// (goobj.SymSize: stringRefSize + 2 + 1 + 1 + 1 + 4 + 4).
|
||||
const recSymSize = 21
|
||||
|
||||
// readSymNames reads symbol names from a symdef/nonpkgdef block. Each record
|
||||
// is 21 bytes: nameLen (u32), nameOff (u32), abi (u16), typ, flag, flag2,
|
||||
// size (u32), align (u32). nameOff is an absolute offset into the string
|
||||
// table.
|
||||
func (f *goobjFile) readSymNames(block []byte) []string {
|
||||
const recSize = 21
|
||||
const recSize = recSymSize
|
||||
if len(block) < recSize {
|
||||
return nil
|
||||
}
|
||||
@@ -247,16 +281,18 @@ func parseGOOBJ(data []byte) (*goobjFile, error) {
|
||||
// [16:20] flags
|
||||
// [20:96] 19 × uint32 offsets
|
||||
var offs [blkEnd + 1]uint32
|
||||
for i := 0; i <= blkEnd; i++ {
|
||||
for i := range blkEnd + 1 {
|
||||
offs[i] = binary.LittleEndian.Uint32(payload[20+4*i:])
|
||||
}
|
||||
// The string table lives at headerSize.
|
||||
strTabStart := uint32(goobjHeaderSize)
|
||||
|
||||
f := &goobjFile{
|
||||
strTab: payload[strTabStart:offs[0]],
|
||||
symdef: blockSlice(payload, offs, blkSymdef, blkSymdef+1),
|
||||
npdef: blockSlice(payload, offs, blkNonpkgdef, blkNonpkgdef+1),
|
||||
strTab: payload[strTabStart:offs[0]],
|
||||
symdef: blockSlice(payload, offs, blkSymdef, blkSymdef+1),
|
||||
hashed64: blockSlice(payload, offs, blkHashed64def, blkHashed64def+1),
|
||||
hashed: blockSlice(payload, offs, blkHasheddef, blkHasheddef+1),
|
||||
npdef: blockSlice(payload, offs, blkNonpkgdef, blkNonpkgdef+1),
|
||||
}
|
||||
return f, nil
|
||||
}
|
||||
@@ -306,22 +342,26 @@ func resolveExternalSymbols(externals []string) (pkgTable []string, pkgIdxMap ma
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
|
||||
// Build the package table in pkgIdx order.
|
||||
// Build the package table in pkgIdx order. The indices are 1-based
|
||||
// (0 is the dummy invalid package, written by the emitter itself), so
|
||||
// the table without the dummy is indexed one below.
|
||||
pkgTable = make([]string, len(pkgIdx1))
|
||||
for pkg, idx := range pkgIdx1 {
|
||||
pkgTable[idx] = pkg
|
||||
pkgTable[idx-1] = pkg
|
||||
}
|
||||
|
||||
return pkgTable, pkgIdx1, symIdx1, nil
|
||||
}
|
||||
|
||||
// splitQualified splits a qualified Go symbol name (pkgpath·name) into its
|
||||
// package path and local name. The separator is the middle dot (U+00B7).
|
||||
// If no separator is found, the symbol is assumed to be in the current
|
||||
// package (empty pkg).
|
||||
// package path and local name. The separator is the middle dot (U+00B7),
|
||||
// whose UTF-8 encoding is two bytes, so the search must be string-based:
|
||||
// IndexByte would match only the second byte and leave the lead byte on
|
||||
// the package path. If no separator is found, the symbol is assumed to be
|
||||
// in the current package (empty pkg).
|
||||
func splitQualified(full string) (pkg, name string) {
|
||||
if idx := strings.IndexByte(full, '\u00b7'); idx >= 0 {
|
||||
return full[:idx], full[idx+len("\u00b7"):]
|
||||
if before, after, ok := strings.Cut(full, "\u00b7"); ok {
|
||||
return before, after
|
||||
}
|
||||
if before, after, ok := strings.Cut(full, "."); ok {
|
||||
return before, after
|
||||
|
||||
Reference in New Issue
Block a user