go/src/cmd/oldlink/internal/objfile/objfile.go
Bryan C. Mills da382a3978 internal/unsafeheader: consolidate stringHeader and sliceHeader declarations into an internal package
The new package "internal/unsafeheader" depends only on "unsafe", and
provides declarations equivalent to reflect.StringHeader and
reflect.SliceHeader but with Data fields of the proper unsafe.Pointer
type (instead of uintptr).

Unlike the types it replaces, the "internal/unsafeheader" package has
a regression test to ensure that its header types remain equivalent to
the declarations provided by the "reflect" package.

Since "internal/unsafeheader" has almost no dependencies, it can be
used in other low-level packages such as "syscall" and "reflect".

This change is based on the corresponding x/sys change in CL 231177.

Fixes #37805
Updates #19367

Change-Id: I7a6d93ef8dd6e235bcab94e7c47270aad047af31
Reviewed-on: https://go-review.googlesource.com/c/go/+/231223
Reviewed-by: Ian Lance Taylor <iant@golang.org>
2020-05-01 02:31:29 +00:00

664 lines
16 KiB
Go

// Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package objfile reads Go object files for the Go linker, cmd/link.
//
// This package is similar to cmd/internal/objfile which also reads
// Go object files.
package objfile
import (
"bufio"
"bytes"
"cmd/internal/bio"
"cmd/internal/dwarf"
"cmd/internal/goobj2"
"cmd/internal/obj"
"cmd/internal/objabi"
"cmd/internal/sys"
"cmd/oldlink/internal/sym"
"fmt"
"internal/unsafeheader"
"io"
"log"
"os"
"strconv"
"strings"
"unsafe"
)
const (
startmagic = "\x00go114ld"
endmagic = "\xffgo114ld"
)
var emptyPkg = []byte(`"".`)
// objReader reads Go object files.
type objReader struct {
rd *bio.Reader
arch *sys.Arch
syms *sym.Symbols
lib *sym.Library
unit *sym.CompilationUnit
pn string
dupSym *sym.Symbol
localSymVersion int
flags int
strictDupMsgs int
dataSize int
// rdBuf is used by readString and readSymName as scratch for reading strings.
rdBuf []byte
// List of symbol references for the file being read.
refs []*sym.Symbol
data []byte
reloc []sym.Reloc
pcdata []sym.Pcdata
funcdata []*sym.Symbol
funcdataoff []int64
file []*sym.Symbol
pkgpref string // objabi.PathToPrefix(r.lib.Pkg) + "."
roObject []byte // from read-only mmap of object file (may be nil)
roOffset int64 // offset into readonly object data examined so far
dataReadOnly bool // whether data is backed by read-only memory
}
// Flags to enable optional behavior during object loading/reading.
const (
NoFlag int = iota
// Sanity-check duplicate symbol contents, issuing warning
// when duplicates have different lengths or contents.
StrictDupsWarnFlag
// Similar to StrictDupsWarnFlag, but issue fatal error.
StrictDupsErrFlag
)
// Load loads an object file f into library lib.
// The symbols loaded are added to syms.
func Load(arch *sys.Arch, syms *sym.Symbols, f *bio.Reader, lib *sym.Library, unit *sym.CompilationUnit, length int64, pn string, flags int) int {
start := f.Offset()
roObject := f.SliceRO(uint64(length))
if roObject != nil {
f.MustSeek(int64(-length), os.SEEK_CUR)
}
r := &objReader{
rd: f,
lib: lib,
unit: unit,
arch: arch,
syms: syms,
pn: pn,
dupSym: &sym.Symbol{Name: ".dup"},
localSymVersion: syms.IncVersion(),
flags: flags,
roObject: roObject,
pkgpref: objabi.PathToPrefix(lib.Pkg) + ".",
}
r.loadObjFile()
if roObject != nil {
if r.roOffset != length {
log.Fatalf("%s: unexpected end at %d, want %d", pn, r.roOffset, start+length)
}
r.rd.MustSeek(int64(length), os.SEEK_CUR)
} else if f.Offset() != start+length {
log.Fatalf("%s: unexpected end at %d, want %d", pn, f.Offset(), start+length)
}
return r.strictDupMsgs
}
func (r *objReader) loadObjFile() {
// Magic header
var buf [8]uint8
r.readFull(buf[:])
if string(buf[:]) != startmagic {
if string(buf[:]) == goobj2.Magic {
log.Fatalf("found object file %s in new format, but -go115newobj is false\nset -go115newobj consistently in all -gcflags, -asmflags, and -ldflags", r.pn)
}
log.Fatalf("%s: invalid file start %x %x %x %x %x %x %x %x", r.pn, buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7])
}
// Version
c, err := r.readByte()
if err != nil || c != 1 {
log.Fatalf("%s: invalid file version number %d", r.pn, c)
}
// Autolib
for {
lib := r.readString()
if lib == "" {
break
}
r.lib.ImportStrings = append(r.lib.ImportStrings, lib)
}
// DWARF strings
count := r.readInt()
r.unit.DWARFFileTable = make([]string, count)
for i := 0; i < count; i++ {
// TODO: This should probably be a call to mkROString.
r.unit.DWARFFileTable[i] = r.readString()
}
// Symbol references
r.refs = []*sym.Symbol{nil} // zeroth ref is nil
for {
c, err := r.peek(1)
if err != nil {
log.Fatalf("%s: peeking: %v", r.pn, err)
}
if c[0] == 0xff {
r.readByte()
break
}
r.readRef()
}
// Lengths
r.readSlices()
// Data section
err = r.readDataSection()
if err != nil {
log.Fatalf("%s: error reading %s", r.pn, err)
}
// Defined symbols
for {
c, err := r.peek(1)
if err != nil {
log.Fatalf("%s: peeking: %v", r.pn, err)
}
if c[0] == 0xff {
break
}
r.readSym()
}
// Magic footer
buf = [8]uint8{}
r.readFull(buf[:])
if string(buf[:]) != endmagic {
log.Fatalf("%s: invalid file end", r.pn)
}
}
func (r *objReader) readSlices() {
r.dataSize = r.readInt()
n := r.readInt()
r.reloc = make([]sym.Reloc, n)
n = r.readInt()
r.pcdata = make([]sym.Pcdata, n)
_ = r.readInt() // TODO: remove on next object file rev (autom count)
n = r.readInt()
r.funcdata = make([]*sym.Symbol, n)
r.funcdataoff = make([]int64, n)
n = r.readInt()
r.file = make([]*sym.Symbol, n)
}
func (r *objReader) readDataSection() (err error) {
if r.roObject != nil {
r.data, r.dataReadOnly, err =
r.roObject[r.roOffset:r.roOffset+int64(r.dataSize)], true, nil
r.roOffset += int64(r.dataSize)
return
}
r.data, r.dataReadOnly, err = r.rd.Slice(uint64(r.dataSize))
return
}
// Symbols are prefixed so their content doesn't get confused with the magic footer.
const symPrefix = 0xfe
func (r *objReader) readSym() {
var c byte
var err error
if c, err = r.readByte(); c != symPrefix || err != nil {
log.Fatalln("readSym out of sync")
}
if c, err = r.readByte(); err != nil {
log.Fatalln("error reading input: ", err)
}
t := sym.AbiSymKindToSymKind[c]
s := r.readSymIndex()
flags := r.readInt()
dupok := flags&1 != 0
local := flags&2 != 0
makeTypelink := flags&4 != 0
size := r.readInt()
typ := r.readSymIndex()
data := r.readData()
nreloc := r.readInt()
isdup := false
var dup *sym.Symbol
if s.Type != 0 && s.Type != sym.SXREF {
if (t == sym.SDATA || t == sym.SBSS || t == sym.SNOPTRBSS) && len(data) == 0 && nreloc == 0 {
if s.Size < int64(size) {
s.Size = int64(size)
}
if typ != nil && s.Gotype == nil {
s.Gotype = typ
}
return
}
if (s.Type == sym.SDATA || s.Type == sym.SBSS || s.Type == sym.SNOPTRBSS) && len(s.P) == 0 && len(s.R) == 0 {
goto overwrite
}
if s.Type != sym.SBSS && s.Type != sym.SNOPTRBSS && !dupok && !s.Attr.DuplicateOK() {
log.Fatalf("duplicate symbol %s (types %d and %d) in %s and %s", s.Name, s.Type, t, s.File, r.pn)
}
if len(s.P) > 0 {
dup = s
s = r.dupSym
isdup = true
}
}
overwrite:
s.File = r.pkgpref[:len(r.pkgpref)-1]
s.Unit = r.unit
if dupok {
s.Attr |= sym.AttrDuplicateOK
}
if t == sym.SXREF {
log.Fatalf("bad sxref")
}
if t == 0 {
log.Fatalf("missing type for %s in %s", s.Name, r.pn)
}
if t == sym.SBSS && (s.Type == sym.SRODATA || s.Type == sym.SNOPTRBSS) {
t = s.Type
}
s.Type = t
if s.Size < int64(size) {
s.Size = int64(size)
}
s.Attr.Set(sym.AttrLocal, local)
s.Attr.Set(sym.AttrMakeTypelink, makeTypelink)
if typ != nil {
s.Gotype = typ
}
if isdup && typ != nil { // if bss sym defined multiple times, take type from any one def
dup.Gotype = typ
}
s.P = data
s.Attr.Set(sym.AttrReadOnly, r.dataReadOnly)
if nreloc > 0 {
s.R = r.reloc[:nreloc:nreloc]
if !isdup {
r.reloc = r.reloc[nreloc:]
}
for i := 0; i < nreloc; i++ {
s.R[i] = sym.Reloc{
Off: r.readInt32(),
Siz: r.readUint8(),
Type: objabi.RelocType(r.readInt32()),
Add: r.readInt64(),
Sym: r.readSymIndex(),
}
}
}
if s.Type == sym.STEXT {
s.FuncInfo = new(sym.FuncInfo)
pc := s.FuncInfo
pc.Args = r.readInt32()
pc.Locals = r.readInt32()
s.Align = r.readInt32()
if r.readUint8() != 0 {
s.Attr |= sym.AttrNoSplit
}
flags := r.readInt()
if flags&(1<<2) != 0 {
s.Attr |= sym.AttrReflectMethod
}
if flags&(1<<3) != 0 {
s.Attr |= sym.AttrShared
}
if flags&(1<<4) != 0 {
s.Attr |= sym.AttrTopFrame
}
n := r.readInt()
if n != 0 {
log.Fatalf("stale object file: autom count nonzero")
}
pc.Pcsp.P = r.readData()
pc.Pcfile.P = r.readData()
pc.Pcline.P = r.readData()
pc.Pcinline.P = r.readData()
n = r.readInt()
pc.Pcdata = r.pcdata[:n:n]
if !isdup {
r.pcdata = r.pcdata[n:]
}
for i := 0; i < n; i++ {
pc.Pcdata[i].P = r.readData()
}
n = r.readInt()
pc.Funcdata = r.funcdata[:n:n]
pc.Funcdataoff = r.funcdataoff[:n:n]
if !isdup {
r.funcdata = r.funcdata[n:]
r.funcdataoff = r.funcdataoff[n:]
}
for i := 0; i < n; i++ {
pc.Funcdata[i] = r.readSymIndex()
}
for i := 0; i < n; i++ {
pc.Funcdataoff[i] = r.readInt64()
}
n = r.readInt()
pc.File = r.file[:n:n]
if !isdup {
r.file = r.file[n:]
}
for i := 0; i < n; i++ {
pc.File[i] = r.readSymIndex()
}
n = r.readInt()
pc.InlTree = make([]sym.InlinedCall, n)
for i := 0; i < n; i++ {
pc.InlTree[i].Parent = r.readInt32()
pc.InlTree[i].File = r.readSymIndex()
pc.InlTree[i].Line = r.readInt32()
pc.InlTree[i].Func = r.readSymIndex().Name
pc.InlTree[i].ParentPC = r.readInt32()
}
if !dupok {
if s.Attr.OnList() {
log.Fatalf("symbol %s listed multiple times", s.Name)
}
s.Attr |= sym.AttrOnList
r.lib.Textp = append(r.lib.Textp, s)
} else {
// there may ba a dup in another package
// put into a temp list and add to text later
if !isdup {
r.lib.DupTextSyms = append(r.lib.DupTextSyms, s)
} else {
r.lib.DupTextSyms = append(r.lib.DupTextSyms, dup)
}
}
}
if s.Type == sym.SDWARFINFO {
r.patchDWARFName(s)
}
if isdup && r.flags&(StrictDupsWarnFlag|StrictDupsErrFlag) != 0 {
// Compare the just-read symbol with the previously read
// symbol of the same name, verifying that they have the same
// payload. If not, issue a warning and possibly an error.
if !bytes.Equal(s.P, dup.P) {
reason := "same length but different contents"
if len(s.P) != len(dup.P) {
reason = fmt.Sprintf("new length %d != old length %d",
len(data), len(dup.P))
}
fmt.Fprintf(os.Stderr, "cmd/link: while reading object for '%v': duplicate symbol '%s', previous def at '%v', with mismatched payload: %s\n", r.lib, dup, dup.Unit.Lib, reason)
// For the moment, whitelist DWARF subprogram DIEs for
// auto-generated wrapper functions. What seems to happen
// here is that we get different line numbers on formal
// params; I am guessing that the pos is being inherited
// from the spot where the wrapper is needed.
whitelist := (strings.HasPrefix(dup.Name, "go.info.go.interface") ||
strings.HasPrefix(dup.Name, "go.info.go.builtin") ||
strings.HasPrefix(dup.Name, "go.isstmt.go.builtin") ||
strings.HasPrefix(dup.Name, "go.debuglines"))
if !whitelist {
r.strictDupMsgs++
}
}
}
}
func (r *objReader) patchDWARFName(s *sym.Symbol) {
// This is kind of ugly. Really the package name should not
// even be included here.
if s.Size < 1 || s.P[0] != dwarf.DW_ABRV_FUNCTION {
return
}
e := bytes.IndexByte(s.P, 0)
if e == -1 {
return
}
p := bytes.Index(s.P[:e], emptyPkg)
if p == -1 {
return
}
pkgprefix := []byte(r.pkgpref)
patched := bytes.Replace(s.P[:e], emptyPkg, pkgprefix, -1)
s.P = append(patched, s.P[e:]...)
delta := int64(len(s.P)) - s.Size
s.Size = int64(len(s.P))
for i := range s.R {
r := &s.R[i]
if r.Off > int32(e) {
r.Off += int32(delta)
}
}
}
func (r *objReader) readFull(b []byte) {
if r.roObject != nil {
copy(b, r.roObject[r.roOffset:])
r.roOffset += int64(len(b))
return
}
_, err := io.ReadFull(r.rd, b)
if err != nil {
log.Fatalf("%s: error reading %s", r.pn, err)
}
}
func (r *objReader) readByte() (byte, error) {
if r.roObject != nil {
b := r.roObject[r.roOffset]
r.roOffset++
return b, nil
}
return r.rd.ReadByte()
}
func (r *objReader) peek(n int) ([]byte, error) {
if r.roObject != nil {
return r.roObject[r.roOffset : r.roOffset+int64(n)], nil
}
return r.rd.Peek(n)
}
func (r *objReader) readRef() {
if c, err := r.readByte(); c != symPrefix || err != nil {
log.Fatalf("readSym out of sync")
}
name := r.readSymName()
var v int
if abi := r.readInt(); abi == -1 {
// Static
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)
r.refs = append(r.refs, s)
if s == nil || v == r.localSymVersion {
return
}
if s.Name[0] == '$' && len(s.Name) > 5 && s.Type == 0 && len(s.P) == 0 {
x, err := strconv.ParseUint(s.Name[5:], 16, 64)
if err != nil {
log.Panicf("failed to parse $-symbol %s: %v", s.Name, err)
}
s.Type = sym.SRODATA
s.Attr |= sym.AttrLocal
switch s.Name[:5] {
case "$f32.":
if uint64(uint32(x)) != x {
log.Panicf("$-symbol %s too large: %d", s.Name, x)
}
s.AddUint32(r.arch, uint32(x))
case "$f64.", "$i64.":
s.AddUint64(r.arch, x)
default:
log.Panicf("unrecognized $-symbol: %s", s.Name)
}
s.Attr.Set(sym.AttrReachable, false)
}
if strings.HasPrefix(s.Name, "runtime.gcbits.") {
s.Attr |= sym.AttrLocal
}
}
func (r *objReader) readInt64() int64 {
uv := uint64(0)
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
log.Fatalf("corrupt input")
}
c, err := r.readByte()
if err != nil {
log.Fatalln("error reading input: ", err)
}
uv |= uint64(c&0x7F) << shift
if c&0x80 == 0 {
break
}
}
return int64(uv>>1) ^ (int64(uv<<63) >> 63)
}
func (r *objReader) readInt() int {
n := r.readInt64()
if int64(int(n)) != n {
log.Panicf("%v out of range for int", n)
}
return int(n)
}
func (r *objReader) readInt32() int32 {
n := r.readInt64()
if int64(int32(n)) != n {
log.Panicf("%v out of range for int32", n)
}
return int32(n)
}
func (r *objReader) readInt16() int16 {
n := r.readInt64()
if int64(int16(n)) != n {
log.Panicf("%v out of range for int16", n)
}
return int16(n)
}
func (r *objReader) readUint8() uint8 {
n := r.readInt64()
if int64(uint8(n)) != n {
log.Panicf("%v out of range for uint8", n)
}
return uint8(n)
}
func (r *objReader) readString() string {
n := r.readInt()
if cap(r.rdBuf) < n {
r.rdBuf = make([]byte, 2*n)
}
r.readFull(r.rdBuf[:n])
return string(r.rdBuf[:n])
}
func (r *objReader) readData() []byte {
n := r.readInt()
p := r.data[:n:n]
r.data = r.data[n:]
return p
}
func mkROString(rodata []byte) string {
if len(rodata) == 0 {
return ""
}
var s string
hdr := (*unsafeheader.String)(unsafe.Pointer(&s))
hdr.Data = unsafe.Pointer(&rodata[0])
hdr.Len = len(rodata)
return s
}
// readSymName reads a symbol name, replacing all "". with pkg.
func (r *objReader) readSymName() string {
n := r.readInt()
if n == 0 {
r.readInt64()
return ""
}
if cap(r.rdBuf) < n {
r.rdBuf = make([]byte, 2*n)
}
sOffset := r.roOffset
origName, err := r.peek(n)
if err == bufio.ErrBufferFull {
// Long symbol names are rare but exist. One source is type
// symbols for types with long string forms. See #15104.
origName = make([]byte, n)
r.readFull(origName)
} else if err != nil {
log.Fatalf("%s: error reading symbol: %v", r.pn, err)
}
adjName := r.rdBuf[:0]
nPkgRefs := 0
for {
i := bytes.Index(origName, emptyPkg)
if i == -1 {
var s string
if r.roObject != nil && nPkgRefs == 0 {
s = mkROString(r.roObject[sOffset : sOffset+int64(n)])
} else {
s = string(append(adjName, origName...))
}
// Read past the peeked origName, now that we're done with it,
// using the rfBuf (also no longer used) as the scratch space.
// TODO: use bufio.Reader.Discard if available instead?
if err == nil {
r.readFull(r.rdBuf[:n])
}
r.rdBuf = adjName[:0] // in case 2*n wasn't enough
return s
}
nPkgRefs++
adjName = append(adjName, origName[:i]...)
adjName = append(adjName, r.pkgpref[:len(r.pkgpref)-1]...)
adjName = append(adjName, '.')
origName = origName[i+len(emptyPkg):]
}
}
// Reads the index of a symbol reference and resolves it to a symbol
func (r *objReader) readSymIndex() *sym.Symbol {
i := r.readInt()
return r.refs[i]
}