2016-03-01 22:57:46 +00:00
|
|
|
// Copyright 2013 The Go Authors. All rights reserved.
|
2015-02-27 22:57:28 -05:00
|
|
|
// Use of this source code is governed by a BSD-style
|
|
|
|
|
// license that can be found in the LICENSE file.
|
|
|
|
|
|
|
|
|
|
package ld
|
|
|
|
|
|
2016-03-14 22:57:58 +02:00
|
|
|
// Reading of Go object files.
|
2015-05-18 15:50:00 -04:00
|
|
|
|
2015-02-27 22:57:28 -05:00
|
|
|
import (
|
2016-03-31 18:34:02 +03:00
|
|
|
"bufio"
|
2015-02-27 22:57:28 -05:00
|
|
|
"bytes"
|
2016-04-06 21:45:29 -07:00
|
|
|
"cmd/internal/bio"
|
2016-07-28 13:04:41 -04:00
|
|
|
"cmd/internal/dwarf"
|
2017-04-18 12:53:25 -07:00
|
|
|
"cmd/internal/objabi"
|
2016-04-06 13:09:06 -04:00
|
|
|
"crypto/sha1"
|
|
|
|
|
"encoding/base64"
|
2016-03-31 18:34:02 +03:00
|
|
|
"io"
|
2015-02-27 22:57:28 -05:00
|
|
|
"log"
|
|
|
|
|
"strconv"
|
|
|
|
|
"strings"
|
|
|
|
|
)
|
|
|
|
|
|
2015-03-06 11:42:53 -08:00
|
|
|
const (
|
2017-03-06 10:51:37 -08:00
|
|
|
startmagic = "\x00\x00go19ld"
|
|
|
|
|
endmagic = "\xff\xffgo19ld"
|
2015-03-06 11:42:53 -08:00
|
|
|
)
|
2015-02-27 22:57:28 -05:00
|
|
|
|
2016-03-31 18:34:02 +03:00
|
|
|
var emptyPkg = []byte(`"".`)
|
|
|
|
|
|
|
|
|
|
// objReader reads Go object files.
|
|
|
|
|
type objReader struct {
|
2016-09-20 15:46:37 +12:00
|
|
|
rd *bufio.Reader
|
|
|
|
|
ctxt *Link
|
2016-09-14 14:47:12 -04:00
|
|
|
lib *Library
|
2016-09-20 15:46:37 +12:00
|
|
|
pn string
|
|
|
|
|
dupSym *Symbol
|
|
|
|
|
localSymVersion int
|
2016-03-31 18:34:02 +03:00
|
|
|
|
|
|
|
|
// rdBuf is used by readString and readSymName as scratch for reading strings.
|
|
|
|
|
rdBuf []byte
|
|
|
|
|
|
2016-09-20 15:46:37 +12:00
|
|
|
// List of symbol references for the file being read.
|
2016-08-19 11:35:54 -04:00
|
|
|
refs []*Symbol
|
2016-03-31 18:34:02 +03:00
|
|
|
data []byte
|
|
|
|
|
reloc []Reloc
|
|
|
|
|
pcdata []Pcdata
|
|
|
|
|
autom []Auto
|
2016-08-19 11:35:54 -04:00
|
|
|
funcdata []*Symbol
|
2016-03-31 18:34:02 +03:00
|
|
|
funcdataoff []int64
|
2016-08-19 11:35:54 -04:00
|
|
|
file []*Symbol
|
2016-03-31 18:34:02 +03:00
|
|
|
}
|
|
|
|
|
|
2016-09-14 14:47:12 -04:00
|
|
|
func LoadObjFile(ctxt *Link, f *bio.Reader, lib *Library, length int64, pn string) {
|
|
|
|
|
|
2016-04-08 19:14:03 +10:00
|
|
|
start := f.Offset()
|
2016-03-31 18:34:02 +03:00
|
|
|
r := &objReader{
|
2016-09-20 15:46:37 +12:00
|
|
|
rd: f.Reader,
|
2016-09-14 14:47:12 -04:00
|
|
|
lib: lib,
|
2016-09-20 15:46:37 +12:00
|
|
|
ctxt: ctxt,
|
|
|
|
|
pn: pn,
|
|
|
|
|
dupSym: &Symbol{Name: ".dup"},
|
|
|
|
|
localSymVersion: ctxt.Syms.IncVersion(),
|
2016-03-31 18:34:02 +03:00
|
|
|
}
|
|
|
|
|
r.loadObjFile()
|
2016-04-08 19:14:03 +10:00
|
|
|
if f.Offset() != start+length {
|
|
|
|
|
log.Fatalf("%s: unexpected end at %d, want %d", pn, f.Offset(), start+length)
|
2016-03-31 18:34:02 +03:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (r *objReader) loadObjFile() {
|
2017-04-17 18:46:09 -05:00
|
|
|
pkg := objabi.PathToPrefix(r.lib.Pkg)
|
2016-03-31 18:34:02 +03:00
|
|
|
|
|
|
|
|
// Magic header
|
2015-03-02 14:22:05 -05:00
|
|
|
var buf [8]uint8
|
2016-03-31 18:34:02 +03:00
|
|
|
r.readFull(buf[:])
|
2015-02-27 22:57:28 -05:00
|
|
|
if string(buf[:]) != startmagic {
|
2016-03-31 18:34:02 +03:00
|
|
|
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])
|
2015-02-27 22:57:28 -05:00
|
|
|
}
|
2016-03-31 18:34:02 +03:00
|
|
|
|
|
|
|
|
// Version
|
|
|
|
|
c, err := r.rd.ReadByte()
|
|
|
|
|
if err != nil || c != 1 {
|
|
|
|
|
log.Fatalf("%s: invalid file version number %d", r.pn, c)
|
2015-02-27 22:57:28 -05:00
|
|
|
}
|
|
|
|
|
|
2016-03-31 18:34:02 +03:00
|
|
|
// Autolib
|
2015-02-27 22:57:28 -05:00
|
|
|
for {
|
2016-03-31 18:34:02 +03:00
|
|
|
lib := r.readString()
|
2015-02-27 22:57:28 -05:00
|
|
|
if lib == "" {
|
|
|
|
|
break
|
|
|
|
|
}
|
2016-09-14 14:47:12 -04:00
|
|
|
l := addlib(r.ctxt, pkg, r.pn, lib)
|
|
|
|
|
if l != nil {
|
|
|
|
|
r.lib.imports = append(r.lib.imports, l)
|
|
|
|
|
}
|
2015-02-27 22:57:28 -05:00
|
|
|
}
|
|
|
|
|
|
2016-03-31 18:34:02 +03:00
|
|
|
// Symbol references
|
2016-08-19 11:35:54 -04:00
|
|
|
r.refs = []*Symbol{nil} // zeroth ref is nil
|
2016-03-14 22:57:58 +02:00
|
|
|
for {
|
2016-03-31 18:34:02 +03:00
|
|
|
c, err := r.rd.Peek(1)
|
2016-03-14 22:57:58 +02:00
|
|
|
if err != nil {
|
2016-03-31 18:34:02 +03:00
|
|
|
log.Fatalf("%s: peeking: %v", r.pn, err)
|
2016-03-14 22:57:58 +02:00
|
|
|
}
|
|
|
|
|
if c[0] == 0xff {
|
2016-03-31 18:34:02 +03:00
|
|
|
r.rd.ReadByte()
|
2016-03-14 22:57:58 +02:00
|
|
|
break
|
|
|
|
|
}
|
2016-03-31 18:34:02 +03:00
|
|
|
r.readRef()
|
2016-03-14 22:57:58 +02:00
|
|
|
}
|
|
|
|
|
|
2016-03-31 18:34:02 +03:00
|
|
|
// Lengths
|
|
|
|
|
r.readSlices()
|
2016-03-23 00:44:07 +02:00
|
|
|
|
2016-03-31 18:34:02 +03:00
|
|
|
// Data section
|
|
|
|
|
r.readFull(r.data)
|
2016-03-21 10:55:20 +13:00
|
|
|
|
2016-03-31 18:34:02 +03:00
|
|
|
// Defined symbols
|
2015-02-27 22:57:28 -05:00
|
|
|
for {
|
2016-03-31 18:34:02 +03:00
|
|
|
c, err := r.rd.Peek(1)
|
2015-04-27 12:53:34 -04:00
|
|
|
if err != nil {
|
2016-03-31 18:34:02 +03:00
|
|
|
log.Fatalf("%s: peeking: %v", r.pn, err)
|
2015-04-27 12:53:34 -04:00
|
|
|
}
|
|
|
|
|
if c[0] == 0xff {
|
2015-02-27 22:57:28 -05:00
|
|
|
break
|
|
|
|
|
}
|
2016-03-31 18:34:02 +03:00
|
|
|
r.readSym()
|
2015-02-27 22:57:28 -05:00
|
|
|
}
|
|
|
|
|
|
2016-03-31 18:34:02 +03:00
|
|
|
// Magic footer
|
2015-02-27 22:57:28 -05:00
|
|
|
buf = [8]uint8{}
|
2016-03-31 18:34:02 +03:00
|
|
|
r.readFull(buf[:])
|
2015-02-27 22:57:28 -05:00
|
|
|
if string(buf[:]) != endmagic {
|
2016-03-31 18:34:02 +03:00
|
|
|
log.Fatalf("%s: invalid file end", r.pn)
|
2015-02-27 22:57:28 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-03-31 18:34:02 +03:00
|
|
|
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()
|
2016-08-19 11:35:54 -04:00
|
|
|
r.funcdata = make([]*Symbol, n)
|
2016-03-31 18:34:02 +03:00
|
|
|
r.funcdataoff = make([]int64, n)
|
|
|
|
|
n = r.readInt()
|
2016-08-19 11:35:54 -04:00
|
|
|
r.file = make([]*Symbol, n)
|
2016-03-23 00:44:07 +02:00
|
|
|
}
|
|
|
|
|
|
2016-03-31 18:34:02 +03:00
|
|
|
// Symbols are prefixed so their content doesn't get confused with the magic footer.
|
|
|
|
|
const symPrefix = 0xfe
|
2016-03-23 00:44:07 +02:00
|
|
|
|
2016-03-31 18:34:02 +03:00
|
|
|
func (r *objReader) readSym() {
|
|
|
|
|
if c, err := r.rd.ReadByte(); c != symPrefix || err != nil {
|
|
|
|
|
log.Fatalln("readSym out of sync")
|
2015-02-27 22:57:28 -05:00
|
|
|
}
|
2017-04-19 15:15:35 +12:00
|
|
|
t := abiSymKindToSymKind[r.readInt()]
|
2016-03-31 18:34:02 +03:00
|
|
|
s := r.readSymIndex()
|
|
|
|
|
flags := r.readInt()
|
2016-03-02 07:59:49 -05:00
|
|
|
dupok := flags&1 != 0
|
|
|
|
|
local := flags&2 != 0
|
2016-10-19 07:33:16 +03:00
|
|
|
makeTypelink := flags&4 != 0
|
2016-03-31 18:34:02 +03:00
|
|
|
size := r.readInt()
|
|
|
|
|
typ := r.readSymIndex()
|
|
|
|
|
data := r.readData()
|
|
|
|
|
nreloc := r.readInt()
|
2017-04-17 18:46:09 -05:00
|
|
|
pkg := objabi.PathToPrefix(r.lib.Pkg)
|
2016-03-23 00:44:07 +02:00
|
|
|
isdup := false
|
2015-02-27 22:57:28 -05:00
|
|
|
|
2016-08-19 11:35:54 -04:00
|
|
|
var dup *Symbol
|
2017-04-19 15:15:35 +12:00
|
|
|
if s.Type != 0 && s.Type != SXREF {
|
|
|
|
|
if (t == SDATA || t == SBSS || t == SNOPTRBSS) && len(data) == 0 && nreloc == 0 {
|
2015-02-27 22:57:28 -05:00
|
|
|
if s.Size < int64(size) {
|
|
|
|
|
s.Size = int64(size)
|
|
|
|
|
}
|
|
|
|
|
if typ != nil && s.Gotype == nil {
|
|
|
|
|
s.Gotype = typ
|
|
|
|
|
}
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
2017-04-19 15:15:35 +12:00
|
|
|
if (s.Type == SDATA || s.Type == SBSS || s.Type == SNOPTRBSS) && len(s.P) == 0 && len(s.R) == 0 {
|
2015-02-27 22:57:28 -05:00
|
|
|
goto overwrite
|
|
|
|
|
}
|
2017-04-19 15:15:35 +12:00
|
|
|
if s.Type != SBSS && s.Type != SNOPTRBSS && !dupok && !s.Attr.DuplicateOK() {
|
2016-03-31 18:34:02 +03:00
|
|
|
log.Fatalf("duplicate symbol %s (types %d and %d) in %s and %s", s.Name, s.Type, t, s.File, r.pn)
|
2015-02-27 22:57:28 -05:00
|
|
|
}
|
|
|
|
|
if len(s.P) > 0 {
|
|
|
|
|
dup = s
|
2016-03-31 18:34:02 +03:00
|
|
|
s = r.dupSym
|
2016-03-23 00:44:07 +02:00
|
|
|
isdup = true
|
2015-02-27 22:57:28 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
overwrite:
|
2016-09-14 14:47:12 -04:00
|
|
|
s.File = pkg
|
2016-03-02 07:59:49 -05:00
|
|
|
if dupok {
|
|
|
|
|
s.Attr |= AttrDuplicateOK
|
|
|
|
|
}
|
2017-04-19 15:15:35 +12:00
|
|
|
if t == SXREF {
|
2015-02-27 22:57:28 -05:00
|
|
|
log.Fatalf("bad sxref")
|
|
|
|
|
}
|
|
|
|
|
if t == 0 {
|
2016-03-31 18:34:02 +03:00
|
|
|
log.Fatalf("missing type for %s in %s", s.Name, r.pn)
|
2015-02-27 22:57:28 -05:00
|
|
|
}
|
2017-04-19 15:15:35 +12:00
|
|
|
if t == SBSS && (s.Type == SRODATA || s.Type == SNOPTRBSS) {
|
2016-09-07 14:45:27 -04:00
|
|
|
t = s.Type
|
2015-02-27 22:57:28 -05:00
|
|
|
}
|
2016-09-07 14:45:27 -04:00
|
|
|
s.Type = t
|
2015-02-27 22:57:28 -05:00
|
|
|
if s.Size < int64(size) {
|
|
|
|
|
s.Size = int64(size)
|
|
|
|
|
}
|
2016-03-02 07:59:49 -05:00
|
|
|
s.Attr.Set(AttrLocal, local)
|
2016-10-19 07:33:16 +03:00
|
|
|
s.Attr.Set(AttrMakeTypelink, makeTypelink)
|
2016-03-21 17:29:29 +13:00
|
|
|
if typ != nil {
|
2015-02-27 22:57:28 -05:00
|
|
|
s.Gotype = typ
|
|
|
|
|
}
|
2016-03-23 00:44:07 +02:00
|
|
|
if isdup && typ != nil { // if bss sym defined multiple times, take type from any one def
|
2015-02-27 22:57:28 -05:00
|
|
|
dup.Gotype = typ
|
|
|
|
|
}
|
|
|
|
|
s.P = data
|
|
|
|
|
if nreloc > 0 {
|
2016-03-31 18:34:02 +03:00
|
|
|
s.R = r.reloc[:nreloc:nreloc]
|
2016-03-23 00:44:07 +02:00
|
|
|
if !isdup {
|
2016-03-31 18:34:02 +03:00
|
|
|
r.reloc = r.reloc[nreloc:]
|
2016-03-23 00:44:07 +02:00
|
|
|
}
|
|
|
|
|
|
2015-03-02 12:35:15 -05:00
|
|
|
for i := 0; i < nreloc; i++ {
|
2016-03-31 18:34:02 +03:00
|
|
|
s.R[i] = Reloc{
|
|
|
|
|
Off: r.readInt32(),
|
|
|
|
|
Siz: r.readUint8(),
|
2017-04-18 12:53:25 -07:00
|
|
|
Type: objabi.RelocType(r.readInt32()),
|
2016-03-31 18:34:02 +03:00
|
|
|
Add: r.readInt64(),
|
|
|
|
|
Sym: r.readSymIndex(),
|
|
|
|
|
}
|
2015-02-27 22:57:28 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-04-19 15:15:35 +12:00
|
|
|
if s.Type == STEXT {
|
2016-04-12 23:18:47 +03:00
|
|
|
s.FuncInfo = new(FuncInfo)
|
|
|
|
|
pc := s.FuncInfo
|
2016-04-11 22:19:34 +03:00
|
|
|
|
|
|
|
|
pc.Args = r.readInt32()
|
|
|
|
|
pc.Locals = r.readInt32()
|
2016-03-31 18:34:02 +03:00
|
|
|
if r.readUint8() != 0 {
|
2016-03-02 07:59:49 -05:00
|
|
|
s.Attr |= AttrNoSplit
|
|
|
|
|
}
|
2016-03-31 18:34:02 +03:00
|
|
|
flags := r.readInt()
|
2016-03-10 16:15:26 -05:00
|
|
|
if flags&(1<<2) != 0 {
|
|
|
|
|
s.Attr |= AttrReflectMethod
|
|
|
|
|
}
|
2016-03-31 18:34:02 +03:00
|
|
|
n := r.readInt()
|
2016-04-11 22:19:34 +03:00
|
|
|
pc.Autom = r.autom[:n:n]
|
2016-03-23 00:44:07 +02:00
|
|
|
if !isdup {
|
2016-03-31 18:34:02 +03:00
|
|
|
r.autom = r.autom[n:]
|
2016-03-23 00:44:07 +02:00
|
|
|
}
|
|
|
|
|
|
2015-03-02 12:35:15 -05:00
|
|
|
for i := 0; i < n; i++ {
|
2016-04-11 22:19:34 +03:00
|
|
|
pc.Autom[i] = Auto{
|
2016-03-31 18:34:02 +03:00
|
|
|
Asym: r.readSymIndex(),
|
|
|
|
|
Aoffset: r.readInt32(),
|
|
|
|
|
Name: r.readInt16(),
|
|
|
|
|
Gotype: r.readSymIndex(),
|
2016-03-02 22:38:42 -05:00
|
|
|
}
|
2015-02-27 22:57:28 -05:00
|
|
|
}
|
|
|
|
|
|
2016-03-31 18:34:02 +03:00
|
|
|
pc.Pcsp.P = r.readData()
|
|
|
|
|
pc.Pcfile.P = r.readData()
|
|
|
|
|
pc.Pcline.P = r.readData()
|
cmd/compile,link: generate PC-value tables with inlining information
In order to generate accurate tracebacks, the runtime needs to know the
inlined call stack for a given PC. This creates two tables per function
for this purpose. The first table is the inlining tree (stored in the
function's funcdata), which has a node containing the file, line, and
function name for every inlined call. The second table is a PC-value
table that maps each PC to a node in the inlining tree (or -1 if the PC
is not the result of inlining).
To give the appearance that inlining hasn't happened, the runtime also
needs the original source position information of inlined AST nodes.
Previously the compiler plastered over the line numbers of inlined AST
nodes with the line number of the call. This meant that the PC-line
table mapped each PC to line number of the outermost call in its inlined
call stack, with no way to access the innermost line number.
Now the compiler retains line numbers of inlined AST nodes and writes
the innermost source position information to the PC-line and PC-file
tables. Some tools and tests expect to see outermost line numbers, so we
provide the OutermostLine function for displaying line info.
To keep track of the inlined call stack for an AST node, we extend the
src.PosBase type with an index into a global inlining tree. Every time
the compiler inlines a call, it creates a node in the global inlining
tree for the call, and writes its index to the PosBase of every inlined
AST node. The parent of this node is the inlining tree index of the
call. -1 signifies no parent.
For each function, the compiler creates a local inlining tree and a
PC-value table mapping each PC to an index in the local tree. These are
written to an object file, which is read by the linker. The linker
re-encodes these tables compactly by deduplicating function names and
file names.
This change increases the size of binaries by 4-5%. For example, this is
how the go1 benchmark binary is impacted by this change:
section old bytes new bytes delta
.text 3.49M ± 0% 3.49M ± 0% +0.06%
.rodata 1.12M ± 0% 1.21M ± 0% +8.21%
.gopclntab 1.50M ± 0% 1.68M ± 0% +11.89%
.debug_line 338k ± 0% 435k ± 0% +28.78%
Total 9.21M ± 0% 9.58M ± 0% +4.01%
Updates #19348.
Change-Id: Ic4f180c3b516018138236b0c35e0218270d957d3
Reviewed-on: https://go-review.googlesource.com/37231
Run-TryBot: David Lazar <lazard@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Austin Clements <austin@google.com>
2017-02-17 12:28:05 -05:00
|
|
|
pc.Pcinline.P = r.readData()
|
2016-03-31 18:34:02 +03:00
|
|
|
n = r.readInt()
|
|
|
|
|
pc.Pcdata = r.pcdata[:n:n]
|
2016-03-23 00:44:07 +02:00
|
|
|
if !isdup {
|
2016-03-31 18:34:02 +03:00
|
|
|
r.pcdata = r.pcdata[n:]
|
2016-03-23 00:44:07 +02:00
|
|
|
}
|
2015-03-02 12:35:15 -05:00
|
|
|
for i := 0; i < n; i++ {
|
2016-03-31 18:34:02 +03:00
|
|
|
pc.Pcdata[i].P = r.readData()
|
2015-02-27 22:57:28 -05:00
|
|
|
}
|
2016-03-31 18:34:02 +03:00
|
|
|
n = r.readInt()
|
|
|
|
|
pc.Funcdata = r.funcdata[:n:n]
|
|
|
|
|
pc.Funcdataoff = r.funcdataoff[:n:n]
|
2016-03-23 00:44:07 +02:00
|
|
|
if !isdup {
|
2016-03-31 18:34:02 +03:00
|
|
|
r.funcdata = r.funcdata[n:]
|
|
|
|
|
r.funcdataoff = r.funcdataoff[n:]
|
2016-03-23 00:44:07 +02:00
|
|
|
}
|
2015-03-02 12:35:15 -05:00
|
|
|
for i := 0; i < n; i++ {
|
2016-03-31 18:34:02 +03:00
|
|
|
pc.Funcdata[i] = r.readSymIndex()
|
2015-02-27 22:57:28 -05:00
|
|
|
}
|
2015-03-02 12:35:15 -05:00
|
|
|
for i := 0; i < n; i++ {
|
2016-03-31 18:34:02 +03:00
|
|
|
pc.Funcdataoff[i] = r.readInt64()
|
2015-02-27 22:57:28 -05:00
|
|
|
}
|
2016-03-31 18:34:02 +03:00
|
|
|
n = r.readInt()
|
|
|
|
|
pc.File = r.file[:n:n]
|
2016-03-23 00:44:07 +02:00
|
|
|
if !isdup {
|
2016-03-31 18:34:02 +03:00
|
|
|
r.file = r.file[n:]
|
2016-03-23 00:44:07 +02:00
|
|
|
}
|
2015-03-02 12:35:15 -05:00
|
|
|
for i := 0; i < n; i++ {
|
2016-03-31 18:34:02 +03:00
|
|
|
pc.File[i] = r.readSymIndex()
|
2015-02-27 22:57:28 -05:00
|
|
|
}
|
cmd/compile,link: generate PC-value tables with inlining information
In order to generate accurate tracebacks, the runtime needs to know the
inlined call stack for a given PC. This creates two tables per function
for this purpose. The first table is the inlining tree (stored in the
function's funcdata), which has a node containing the file, line, and
function name for every inlined call. The second table is a PC-value
table that maps each PC to a node in the inlining tree (or -1 if the PC
is not the result of inlining).
To give the appearance that inlining hasn't happened, the runtime also
needs the original source position information of inlined AST nodes.
Previously the compiler plastered over the line numbers of inlined AST
nodes with the line number of the call. This meant that the PC-line
table mapped each PC to line number of the outermost call in its inlined
call stack, with no way to access the innermost line number.
Now the compiler retains line numbers of inlined AST nodes and writes
the innermost source position information to the PC-line and PC-file
tables. Some tools and tests expect to see outermost line numbers, so we
provide the OutermostLine function for displaying line info.
To keep track of the inlined call stack for an AST node, we extend the
src.PosBase type with an index into a global inlining tree. Every time
the compiler inlines a call, it creates a node in the global inlining
tree for the call, and writes its index to the PosBase of every inlined
AST node. The parent of this node is the inlining tree index of the
call. -1 signifies no parent.
For each function, the compiler creates a local inlining tree and a
PC-value table mapping each PC to an index in the local tree. These are
written to an object file, which is read by the linker. The linker
re-encodes these tables compactly by deduplicating function names and
file names.
This change increases the size of binaries by 4-5%. For example, this is
how the go1 benchmark binary is impacted by this change:
section old bytes new bytes delta
.text 3.49M ± 0% 3.49M ± 0% +0.06%
.rodata 1.12M ± 0% 1.21M ± 0% +8.21%
.gopclntab 1.50M ± 0% 1.68M ± 0% +11.89%
.debug_line 338k ± 0% 435k ± 0% +28.78%
Total 9.21M ± 0% 9.58M ± 0% +4.01%
Updates #19348.
Change-Id: Ic4f180c3b516018138236b0c35e0218270d957d3
Reviewed-on: https://go-review.googlesource.com/37231
Run-TryBot: David Lazar <lazard@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Austin Clements <austin@google.com>
2017-02-17 12:28:05 -05:00
|
|
|
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()
|
|
|
|
|
}
|
2015-02-27 22:57:28 -05:00
|
|
|
|
2016-09-14 14:47:12 -04:00
|
|
|
if !dupok {
|
2016-03-02 07:59:49 -05:00
|
|
|
if s.Attr.OnList() {
|
2015-02-27 22:57:28 -05:00
|
|
|
log.Fatalf("symbol %s listed multiple times", s.Name)
|
|
|
|
|
}
|
2016-03-02 07:59:49 -05:00
|
|
|
s.Attr |= AttrOnList
|
2016-09-14 14:47:12 -04:00
|
|
|
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)
|
|
|
|
|
}
|
2015-02-27 22:57:28 -05:00
|
|
|
}
|
|
|
|
|
}
|
2017-04-19 15:15:35 +12:00
|
|
|
if s.Type == SDWARFINFO {
|
2016-07-28 13:04:41 -04:00
|
|
|
r.patchDWARFName(s)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-08-19 11:35:54 -04:00
|
|
|
func (r *objReader) patchDWARFName(s *Symbol) {
|
2016-07-28 13:04:41 -04:00
|
|
|
// 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
|
|
|
|
|
}
|
2017-04-17 18:46:09 -05:00
|
|
|
pkgprefix := []byte(objabi.PathToPrefix(r.lib.Pkg) + ".")
|
2016-07-28 13:04:41 -04:00
|
|
|
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)
|
|
|
|
|
}
|
|
|
|
|
}
|
2015-02-27 22:57:28 -05:00
|
|
|
}
|
|
|
|
|
|
2016-03-31 18:34:02 +03:00
|
|
|
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")
|
2016-03-14 22:57:58 +02:00
|
|
|
}
|
2016-03-31 18:34:02 +03:00
|
|
|
name := r.readSymName()
|
|
|
|
|
v := r.readInt()
|
2016-03-14 22:57:58 +02:00
|
|
|
if v != 0 && v != 1 {
|
2017-04-14 06:35:53 -07:00
|
|
|
log.Fatalf("invalid symbol version for %q: %d", name, v)
|
2016-03-14 22:57:58 +02:00
|
|
|
}
|
|
|
|
|
if v == 1 {
|
2016-09-20 15:46:37 +12:00
|
|
|
v = r.localSymVersion
|
2016-03-14 22:57:58 +02:00
|
|
|
}
|
cmd/link: use ctxt.{Lookup,ROLookup} in favour of function versions of same
Done with two eg templates:
package p
import (
"cmd/link/internal/ld"
)
func before(ctxt *ld.Link, name string, v int) *ld.Symbol {
return ld.Linklookup(ctxt, name, v)
}
func after(ctxt *ld.Link, name string, v int) *ld.Symbol {
return ctxt.Syms.Lookup(name, v)
}
package p
import (
"cmd/link/internal/ld"
)
func before(ctxt *ld.Link, name string, v int) *ld.Symbol {
return ld.Linkrlookup(ctxt, name, v)
}
func after(ctxt *ld.Link, name string, v int) *ld.Symbol {
return ctxt.Syms.ROLookup(name, v)
}
Change-Id: I00647dbf62294557bd24c29ad1f108fc786335f1
Reviewed-on: https://go-review.googlesource.com/29343
Run-TryBot: Michael Hudson-Doyle <michael.hudson@canonical.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Ian Lance Taylor <iant@golang.org>
2016-09-20 15:06:08 +12:00
|
|
|
s := r.ctxt.Syms.Lookup(name, v)
|
2016-03-31 18:34:02 +03:00
|
|
|
r.refs = append(r.refs, s)
|
2016-03-20 17:22:57 +02:00
|
|
|
|
|
|
|
|
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)
|
|
|
|
|
}
|
2017-04-19 15:15:35 +12:00
|
|
|
s.Type = SRODATA
|
2016-03-20 17:22:57 +02:00
|
|
|
s.Attr |= AttrLocal
|
|
|
|
|
switch s.Name[:5] {
|
|
|
|
|
case "$f32.":
|
|
|
|
|
if uint64(uint32(x)) != x {
|
|
|
|
|
log.Panicf("$-symbol %s too large: %d", s.Name, x)
|
|
|
|
|
}
|
2016-03-31 18:34:02 +03:00
|
|
|
Adduint32(r.ctxt, s, uint32(x))
|
2016-03-20 17:22:57 +02:00
|
|
|
case "$f64.", "$i64.":
|
2016-03-31 18:34:02 +03:00
|
|
|
Adduint64(r.ctxt, s, x)
|
2016-03-20 17:22:57 +02:00
|
|
|
default:
|
|
|
|
|
log.Panicf("unrecognized $-symbol: %s", s.Name)
|
|
|
|
|
}
|
|
|
|
|
s.Attr.Set(AttrReachable, false)
|
|
|
|
|
}
|
|
|
|
|
if strings.HasPrefix(s.Name, "runtime.gcbits.") {
|
|
|
|
|
s.Attr |= AttrLocal
|
|
|
|
|
}
|
2016-03-14 22:57:58 +02:00
|
|
|
}
|
|
|
|
|
|
2016-03-31 18:34:02 +03:00
|
|
|
func (r *objReader) readInt64() int64 {
|
2015-03-02 12:35:15 -05:00
|
|
|
uv := uint64(0)
|
2016-03-19 23:27:41 +02:00
|
|
|
for shift := uint(0); ; shift += 7 {
|
2015-02-27 22:57:28 -05:00
|
|
|
if shift >= 64 {
|
|
|
|
|
log.Fatalf("corrupt input")
|
|
|
|
|
}
|
2016-03-31 18:34:02 +03:00
|
|
|
c, err := r.rd.ReadByte()
|
2016-03-19 23:27:41 +02:00
|
|
|
if err != nil {
|
|
|
|
|
log.Fatalln("error reading input: ", err)
|
|
|
|
|
}
|
|
|
|
|
uv |= uint64(c&0x7F) << shift
|
2015-02-27 22:57:28 -05:00
|
|
|
if c&0x80 == 0 {
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-04-14 19:04:45 -07:00
|
|
|
return int64(uv>>1) ^ (int64(uv<<63) >> 63)
|
2015-02-27 22:57:28 -05:00
|
|
|
}
|
|
|
|
|
|
2016-03-31 18:34:02 +03:00
|
|
|
func (r *objReader) readInt() int {
|
|
|
|
|
n := r.readInt64()
|
2015-07-10 14:38:35 -07:00
|
|
|
if int64(int(n)) != n {
|
|
|
|
|
log.Panicf("%v out of range for int", n)
|
|
|
|
|
}
|
|
|
|
|
return int(n)
|
|
|
|
|
}
|
|
|
|
|
|
2016-03-31 18:34:02 +03:00
|
|
|
func (r *objReader) readInt32() int32 {
|
|
|
|
|
n := r.readInt64()
|
2015-07-10 14:38:35 -07:00
|
|
|
if int64(int32(n)) != n {
|
|
|
|
|
log.Panicf("%v out of range for int32", n)
|
|
|
|
|
}
|
|
|
|
|
return int32(n)
|
|
|
|
|
}
|
|
|
|
|
|
2016-03-31 18:34:02 +03:00
|
|
|
func (r *objReader) readInt16() int16 {
|
|
|
|
|
n := r.readInt64()
|
2015-07-10 14:38:35 -07:00
|
|
|
if int64(int16(n)) != n {
|
|
|
|
|
log.Panicf("%v out of range for int16", n)
|
|
|
|
|
}
|
|
|
|
|
return int16(n)
|
|
|
|
|
}
|
|
|
|
|
|
2016-03-31 18:34:02 +03:00
|
|
|
func (r *objReader) readUint8() uint8 {
|
|
|
|
|
n := r.readInt64()
|
2015-07-10 14:38:35 -07:00
|
|
|
if int64(uint8(n)) != n {
|
|
|
|
|
log.Panicf("%v out of range for uint8", n)
|
|
|
|
|
}
|
|
|
|
|
return uint8(n)
|
|
|
|
|
}
|
|
|
|
|
|
2016-03-31 18:34:02 +03:00
|
|
|
func (r *objReader) readString() string {
|
|
|
|
|
n := r.readInt()
|
2016-04-04 22:36:43 +03:00
|
|
|
if cap(r.rdBuf) < n {
|
|
|
|
|
r.rdBuf = make([]byte, 2*n)
|
2016-02-28 15:59:33 -09:00
|
|
|
}
|
2016-03-31 18:34:02 +03:00
|
|
|
r.readFull(r.rdBuf[:n])
|
|
|
|
|
return string(r.rdBuf[:n])
|
2015-02-27 22:57:28 -05:00
|
|
|
}
|
|
|
|
|
|
2016-03-31 18:34:02 +03:00
|
|
|
func (r *objReader) readData() []byte {
|
|
|
|
|
n := r.readInt()
|
|
|
|
|
p := r.data[:n:n]
|
|
|
|
|
r.data = r.data[n:]
|
2015-04-24 20:24:49 -07:00
|
|
|
return p
|
2015-02-27 22:57:28 -05:00
|
|
|
}
|
|
|
|
|
|
2016-03-31 18:34:02 +03:00
|
|
|
// readSymName reads a symbol name, replacing all "". with pkg.
|
|
|
|
|
func (r *objReader) readSymName() string {
|
2017-04-17 18:46:09 -05:00
|
|
|
pkg := objabi.PathToPrefix(r.lib.Pkg)
|
2016-03-31 18:34:02 +03:00
|
|
|
n := r.readInt()
|
2015-02-27 22:57:28 -05:00
|
|
|
if n == 0 {
|
2016-03-31 18:34:02 +03:00
|
|
|
r.readInt64()
|
2016-02-28 15:59:33 -09:00
|
|
|
return ""
|
2015-02-27 22:57:28 -05:00
|
|
|
}
|
2016-04-04 22:36:43 +03:00
|
|
|
if cap(r.rdBuf) < n {
|
|
|
|
|
r.rdBuf = make([]byte, 2*n)
|
2016-04-03 18:27:17 +00:00
|
|
|
}
|
2016-04-06 07:11:24 -04:00
|
|
|
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 {
|
2016-05-12 15:13:22 +03:00
|
|
|
log.Fatalf("%s: error reading symbol: %v", r.pn, err)
|
2016-04-06 07:11:24 -04:00
|
|
|
}
|
2016-04-03 18:27:17 +00:00
|
|
|
adjName := r.rdBuf[:0]
|
2016-02-28 15:59:33 -09:00
|
|
|
for {
|
|
|
|
|
i := bytes.Index(origName, emptyPkg)
|
|
|
|
|
if i == -1 {
|
2016-04-03 18:27:17 +00:00
|
|
|
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?
|
2016-04-06 07:11:24 -04:00
|
|
|
if err == nil {
|
|
|
|
|
r.readFull(r.rdBuf[:n])
|
|
|
|
|
}
|
2016-04-04 22:36:43 +03:00
|
|
|
r.rdBuf = adjName[:0] // in case 2*n wasn't enough
|
2016-04-06 13:09:06 -04:00
|
|
|
|
2016-08-25 21:58:45 -04:00
|
|
|
if Buildmode == BuildmodeShared || *FlagLinkshared {
|
2016-04-06 13:09:06 -04:00
|
|
|
// 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
|
2016-08-12 10:31:17 +12:00
|
|
|
// 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.") {
|
2016-04-06 13:09:06 -04:00
|
|
|
hash := sha1.Sum([]byte(s))
|
|
|
|
|
prefix := "type."
|
|
|
|
|
if s[5] == '.' {
|
|
|
|
|
prefix = "type.."
|
|
|
|
|
}
|
|
|
|
|
s = prefix + base64.StdEncoding.EncodeToString(hash[:6])
|
|
|
|
|
}
|
|
|
|
|
}
|
2016-04-03 18:27:17 +00:00
|
|
|
return s
|
2016-02-28 15:59:33 -09:00
|
|
|
}
|
|
|
|
|
adjName = append(adjName, origName[:i]...)
|
|
|
|
|
adjName = append(adjName, pkg...)
|
|
|
|
|
adjName = append(adjName, '.')
|
|
|
|
|
origName = origName[i+len(emptyPkg):]
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-03-31 18:34:02 +03:00
|
|
|
// Reads the index of a symbol reference and resolves it to a symbol
|
2016-08-19 11:35:54 -04:00
|
|
|
func (r *objReader) readSymIndex() *Symbol {
|
2016-03-31 18:34:02 +03:00
|
|
|
i := r.readInt()
|
|
|
|
|
return r.refs[i]
|
2015-02-27 22:57:28 -05:00
|
|
|
}
|