blob: 13dde21809c81d1deeba6c4de7df22c44aa9ddb0 [file] [log] [blame]
// 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 ld
// Reading of Go object files.
import (
"bufio"
"bytes"
"cmd/internal/bio"
"cmd/internal/dwarf"
"cmd/internal/objabi"
"crypto/sha1"
"encoding/base64"
"io"
"log"
"strconv"
"strings"
)
const (
startmagic = "\x00\x00go19ld"
endmagic = "\xff\xffgo19ld"
)
var emptyPkg = []byte(`"".`)
// objReader reads Go object files.
type objReader struct {
rd *bufio.Reader
ctxt *Link
lib *Library
pn string
dupSym *Symbol
localSymVersion 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 []*Symbol
data []byte
reloc []Reloc
pcdata []Pcdata
autom []Auto
funcdata []*Symbol
funcdataoff []int64
file []*Symbol
}
func LoadObjFile(ctxt *Link, f *bio.Reader, lib *Library, length int64, pn string) {
start := f.Offset()
r := &objReader{
rd: f.Reader,
lib: lib,
ctxt: ctxt,
pn: pn,
dupSym: &Symbol{Name: ".dup"},
localSymVersion: ctxt.Syms.IncVersion(),
}
r.loadObjFile()
if f.Offset() != start+length {
log.Fatalf("%s: unexpected end at %d, want %d", pn, f.Offset(), start+length)
}
}
func (r *objReader) loadObjFile() {
pkg := objabi.PathToPrefix(r.lib.Pkg)
// Magic header
var buf [8]uint8
r.readFull(buf[:])
if string(buf[:]) != startmagic {
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.rd.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
}
l := addlib(r.ctxt, pkg, r.pn, lib)
if l != nil {
r.lib.imports = append(r.lib.imports, l)
}
}
// Symbol references
r.refs = []*Symbol{nil} // zeroth ref is nil
for {
c, err := r.rd.Peek(1)
if err != nil {
log.Fatalf("%s: peeking: %v", r.pn, err)
}
if c[0] == 0xff {
r.rd.ReadByte()
break
}
r.readRef()
}
// Lengths
r.readSlices()
// Data section
r.readFull(r.data)
// Defined symbols
for {
c, err := r.rd.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() {
n := r.readInt()
r.data = make([]byte, n)
n = r.readInt()
r.reloc = make([]Reloc, n)
n = r.readInt()
r.pcdata = make([]Pcdata, n)
n = r.readInt()
r.autom = make([]Auto, n)
n = r.readInt()
r.funcdata = make([]*Symbol, n)
r.funcdataoff = make([]int64, n)
n = r.readInt()
r.file = make([]*Symbol, n)
}
// 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.rd.ReadByte(); c != symPrefix || err != nil {
log.Fatalln("readSym out of sync")
}
if c, err = r.rd.ReadByte(); err != nil {
log.Fatalln("error reading input: ", err)
}
t := 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()
pkg := objabi.PathToPrefix(r.lib.Pkg)
isdup := false
var dup *Symbol
if s.Type != 0 && s.Type != SXREF {
if (t == SDATA || t == SBSS || t == 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 == SDATA || s.Type == SBSS || s.Type == SNOPTRBSS) && len(s.P) == 0 && len(s.R) == 0 {
goto overwrite
}
if s.Type != SBSS && s.Type != 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 = pkg
if dupok {
s.Attr |= AttrDuplicateOK
}
if t == SXREF {
log.Fatalf("bad sxref")
}
if t == 0 {
log.Fatalf("missing type for %s in %s", s.Name, r.pn)
}
if t == SBSS && (s.Type == SRODATA || s.Type == SNOPTRBSS) {
t = s.Type
}
s.Type = t
if s.Size < int64(size) {
s.Size = int64(size)
}
s.Attr.Set(AttrLocal, local)
s.Attr.Set(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
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] = Reloc{
Off: r.readInt32(),
Siz: r.readUint8(),
Type: objabi.RelocType(r.readInt32()),
Add: r.readInt64(),
Sym: r.readSymIndex(),
}
}
}
if s.Type == STEXT {
s.FuncInfo = new(FuncInfo)
pc := s.FuncInfo
pc.Args = r.readInt32()
pc.Locals = r.readInt32()
if r.readUint8() != 0 {
s.Attr |= AttrNoSplit
}
flags := r.readInt()
if flags&(1<<2) != 0 {
s.Attr |= AttrReflectMethod
}
if flags&(1<<3) != 0 {
s.Attr |= AttrShared
}
n := r.readInt()
pc.Autom = r.autom[:n:n]
if !isdup {
r.autom = r.autom[n:]
}
for i := 0; i < n; i++ {
pc.Autom[i] = Auto{
Asym: r.readSymIndex(),
Aoffset: r.readInt32(),
Name: r.readInt16(),
Gotype: r.readSymIndex(),
}
}
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([]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()
}
if !dupok {
if s.Attr.OnList() {
log.Fatalf("symbol %s listed multiple times", s.Name)
}
s.Attr |= 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 == SDWARFINFO {
r.patchDWARFName(s)
}
}
func (r *objReader) patchDWARFName(s *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(objabi.PathToPrefix(r.lib.Pkg) + ".")
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) {
_, err := io.ReadFull(r.rd, b)
if err != nil {
log.Fatalf("%s: error reading %s", r.pn, err)
}
}
func (r *objReader) readRef() {
if c, err := r.rd.ReadByte(); c != symPrefix || err != nil {
log.Fatalf("readSym out of sync")
}
name := r.readSymName()
v := r.readInt()
if v != 0 && v != 1 {
log.Fatalf("invalid symbol version for %q: %d", name, v)
}
if v == 1 {
v = r.localSymVersion
}
s := r.ctxt.Syms.Lookup(name, v)
r.refs = append(r.refs, s)
if s == nil || v != 0 {
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 = SRODATA
s.Attr |= AttrLocal
switch s.Name[:5] {
case "$f32.":
if uint64(uint32(x)) != x {
log.Panicf("$-symbol %s too large: %d", s.Name, x)
}
Adduint32(r.ctxt, s, uint32(x))
case "$f64.", "$i64.":
Adduint64(r.ctxt, s, x)
default:
log.Panicf("unrecognized $-symbol: %s", s.Name)
}
s.Attr.Set(AttrReachable, false)
}
if strings.HasPrefix(s.Name, "runtime.gcbits.") {
s.Attr |= 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.rd.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
}
// readSymName reads a symbol name, replacing all "". with pkg.
func (r *objReader) readSymName() string {
pkg := objabi.PathToPrefix(r.lib.Pkg)
n := r.readInt()
if n == 0 {
r.readInt64()
return ""
}
if cap(r.rdBuf) < n {
r.rdBuf = make([]byte, 2*n)
}
origName, err := r.rd.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]
for {
i := bytes.Index(origName, emptyPkg)
if i == -1 {
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
if Buildmode == BuildmodeShared || *FlagLinkshared {
// These types are included in the symbol
// table when dynamically linking. To keep
// binary size down, we replace the names
// with SHA-1 prefixes.
//
// Keep the type.. prefix, which parts of the
// linker (like the DWARF generator) know means
// the symbol is not decodable.
//
// Leave type.runtime. symbols alone, because
// other parts of the linker manipulates them,
// and also symbols whose names would not be
// shortened by this process.
if len(s) > 14 && strings.HasPrefix(s, "type.") && !strings.HasPrefix(s, "type.runtime.") {
hash := sha1.Sum([]byte(s))
prefix := "type."
if s[5] == '.' {
prefix = "type.."
}
s = prefix + base64.StdEncoding.EncodeToString(hash[:6])
}
}
return s
}
adjName = append(adjName, origName[:i]...)
adjName = append(adjName, pkg...)
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() *Symbol {
i := r.readInt()
return r.refs[i]
}