2016-03-01 22:57:46 +00:00
|
|
|
// Copyright 2013 The Go Authors. All rights reserved.
|
2015-01-19 14:34:58 -05:00
|
|
|
// Use of this source code is governed by a BSD-style
|
|
|
|
|
// license that can be found in the LICENSE file.
|
|
|
|
|
|
2015-03-05 13:57:36 -05:00
|
|
|
// Writing of Go object files.
|
|
|
|
|
//
|
|
|
|
|
// Originally, Go object files were Plan 9 object files, but no longer.
|
|
|
|
|
// Now they are more like standard object files, in that each symbol is defined
|
|
|
|
|
// by an associated memory image (bytes) and a list of relocations to apply
|
|
|
|
|
// during linking. We do not (yet?) use a standard file format, however.
|
|
|
|
|
// For now, the format is chosen to be as simple as possible to read and write.
|
|
|
|
|
// It may change for reasons of efficiency, or we may even switch to a
|
|
|
|
|
// standard file format if there are compelling benefits to doing so.
|
|
|
|
|
// See golang.org/s/go13linker for more background.
|
|
|
|
|
//
|
|
|
|
|
// The file format is:
|
|
|
|
|
//
|
2017-03-06 10:51:37 -08:00
|
|
|
// - magic header: "\x00\x00go19ld"
|
2015-03-05 13:57:36 -05:00
|
|
|
// - byte 1 - version number
|
|
|
|
|
// - sequence of strings giving dependencies (imported packages)
|
|
|
|
|
// - empty string (marks end of sequence)
|
2016-03-21 10:55:20 +13:00
|
|
|
// - sequence of symbol references used by the defined symbols
|
2016-03-14 22:57:58 +02:00
|
|
|
// - byte 0xff (marks end of sequence)
|
2016-03-23 00:44:07 +02:00
|
|
|
// - sequence of integer lengths:
|
|
|
|
|
// - total data length
|
|
|
|
|
// - total number of relocations
|
|
|
|
|
// - total number of pcdata
|
|
|
|
|
// - total number of automatics
|
|
|
|
|
// - total number of funcdata
|
|
|
|
|
// - total number of files
|
2016-03-21 10:55:20 +13:00
|
|
|
// - data, the content of the defined symbols
|
2015-03-05 13:57:36 -05:00
|
|
|
// - sequence of defined symbols
|
|
|
|
|
// - byte 0xff (marks end of sequence)
|
2017-03-06 10:51:37 -08:00
|
|
|
// - magic footer: "\xff\xffgo19ld"
|
2015-03-05 13:57:36 -05:00
|
|
|
//
|
|
|
|
|
// All integers are stored in a zigzag varint format.
|
|
|
|
|
// See golang.org/s/go12symtab for a definition.
|
|
|
|
|
//
|
|
|
|
|
// Data blocks and strings are both stored as an integer
|
|
|
|
|
// followed by that many bytes.
|
|
|
|
|
//
|
|
|
|
|
// A symbol reference is a string name followed by a version.
|
2016-03-14 22:57:58 +02:00
|
|
|
//
|
|
|
|
|
// A symbol points to other symbols using an index into the symbol
|
|
|
|
|
// reference sequence. Index 0 corresponds to a nil LSym* pointer.
|
|
|
|
|
// In the symbol layout described below "symref index" stands for this
|
|
|
|
|
// index.
|
2015-03-05 13:57:36 -05:00
|
|
|
//
|
|
|
|
|
// Each symbol is laid out as the following fields (taken from LSym*):
|
|
|
|
|
//
|
|
|
|
|
// - byte 0xfe (sanity check for synchronization)
|
|
|
|
|
// - type [int]
|
2016-03-14 22:57:58 +02:00
|
|
|
// - name & version [symref index]
|
2015-03-05 13:57:36 -05:00
|
|
|
// - flags [int]
|
2016-10-19 07:33:16 +03:00
|
|
|
// 1<<0 dupok
|
|
|
|
|
// 1<<1 local
|
|
|
|
|
// 1<<2 add to typelink table
|
2015-03-05 13:57:36 -05:00
|
|
|
// - size [int]
|
2016-03-14 22:57:58 +02:00
|
|
|
// - gotype [symref index]
|
2015-03-05 13:57:36 -05:00
|
|
|
// - p [data block]
|
|
|
|
|
// - nr [int]
|
|
|
|
|
// - r [nr relocations, sorted by off]
|
|
|
|
|
//
|
|
|
|
|
// If type == STEXT, there are a few more fields:
|
|
|
|
|
//
|
|
|
|
|
// - args [int]
|
|
|
|
|
// - locals [int]
|
|
|
|
|
// - nosplit [int]
|
|
|
|
|
// - flags [int]
|
2016-03-14 22:57:58 +02:00
|
|
|
// 1<<0 leaf
|
|
|
|
|
// 1<<1 C function
|
|
|
|
|
// 1<<2 function may call reflect.Type.Method
|
2015-03-05 13:57:36 -05:00
|
|
|
// - nlocal [int]
|
|
|
|
|
// - local [nlocal automatics]
|
|
|
|
|
// - pcln [pcln table]
|
|
|
|
|
//
|
|
|
|
|
// Each relocation has the encoding:
|
|
|
|
|
//
|
|
|
|
|
// - off [int]
|
|
|
|
|
// - siz [int]
|
|
|
|
|
// - type [int]
|
|
|
|
|
// - add [int]
|
2016-03-14 22:57:58 +02:00
|
|
|
// - sym [symref index]
|
2015-03-05 13:57:36 -05:00
|
|
|
//
|
|
|
|
|
// Each local has the encoding:
|
|
|
|
|
//
|
2016-03-14 22:57:58 +02:00
|
|
|
// - asym [symref index]
|
2015-03-05 13:57:36 -05:00
|
|
|
// - offset [int]
|
|
|
|
|
// - type [int]
|
2016-03-14 22:57:58 +02:00
|
|
|
// - gotype [symref index]
|
2015-03-05 13:57:36 -05:00
|
|
|
//
|
|
|
|
|
// The pcln table has the encoding:
|
|
|
|
|
//
|
|
|
|
|
// - pcsp [data block]
|
|
|
|
|
// - pcfile [data block]
|
|
|
|
|
// - pcline [data block]
|
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
|
|
|
// - pcinline [data block]
|
2015-03-05 13:57:36 -05:00
|
|
|
// - npcdata [int]
|
|
|
|
|
// - pcdata [npcdata data blocks]
|
|
|
|
|
// - nfuncdata [int]
|
2016-03-14 22:57:58 +02:00
|
|
|
// - funcdata [nfuncdata symref index]
|
2015-03-05 13:57:36 -05:00
|
|
|
// - funcdatasym [nfuncdata ints]
|
|
|
|
|
// - nfile [int]
|
2016-03-14 22:57:58 +02:00
|
|
|
// - file [nfile symref index]
|
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
|
|
|
// - ninlinedcall [int]
|
|
|
|
|
// - inlinedcall [ninlinedcall int symref int symref]
|
2015-03-05 13:57:36 -05:00
|
|
|
//
|
|
|
|
|
// The file layout and meaning of type integers are architecture-independent.
|
|
|
|
|
//
|
|
|
|
|
// TODO(rsc): The file format is good for a first pass but needs work.
|
|
|
|
|
// - There are SymID in the object file that should really just be strings.
|
|
|
|
|
|
2015-01-19 14:34:58 -05:00
|
|
|
package obj
|
|
|
|
|
|
|
|
|
|
import (
|
2016-03-31 12:59:05 +03:00
|
|
|
"bufio"
|
2016-07-28 13:04:41 -04:00
|
|
|
"cmd/internal/dwarf"
|
2016-04-06 12:01:40 -07:00
|
|
|
"cmd/internal/sys"
|
2015-01-19 14:34:58 -05:00
|
|
|
"fmt"
|
|
|
|
|
"log"
|
|
|
|
|
"path/filepath"
|
2016-03-13 10:13:03 -07:00
|
|
|
"sort"
|
2015-01-19 14:34:58 -05:00
|
|
|
)
|
|
|
|
|
|
2016-03-31 12:59:05 +03:00
|
|
|
// objWriter writes Go object files.
|
|
|
|
|
type objWriter struct {
|
|
|
|
|
wr *bufio.Writer
|
|
|
|
|
ctxt *Link
|
|
|
|
|
// Temporary buffer for zigzag int writing.
|
|
|
|
|
varintbuf [10]uint8
|
|
|
|
|
|
2017-03-05 09:14:38 -08:00
|
|
|
// Provide the index of a symbol reference by symbol name.
|
2016-03-31 12:59:05 +03:00
|
|
|
// One map for versioned symbols and one for unversioned symbols.
|
|
|
|
|
// Used for deduplicating the symbol reference list.
|
|
|
|
|
refIdx map[string]int
|
|
|
|
|
vrefIdx map[string]int
|
|
|
|
|
|
|
|
|
|
// Number of objects written of each type.
|
|
|
|
|
nRefs int
|
|
|
|
|
nData int
|
|
|
|
|
nReloc int
|
|
|
|
|
nPcdata int
|
|
|
|
|
nAutom int
|
|
|
|
|
nFuncdata int
|
|
|
|
|
nFile int
|
2016-03-23 00:44:07 +02:00
|
|
|
}
|
|
|
|
|
|
2016-03-31 12:59:05 +03:00
|
|
|
func (w *objWriter) addLengths(s *LSym) {
|
|
|
|
|
w.nData += len(s.P)
|
|
|
|
|
w.nReloc += len(s.R)
|
2016-03-23 00:44:07 +02:00
|
|
|
|
|
|
|
|
if s.Type != STEXT {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
2017-03-03 16:45:21 -08:00
|
|
|
pc := &s.Pcln
|
2016-03-23 00:44:07 +02:00
|
|
|
|
|
|
|
|
data := 0
|
|
|
|
|
data += len(pc.Pcsp.P)
|
|
|
|
|
data += len(pc.Pcfile.P)
|
|
|
|
|
data += len(pc.Pcline.P)
|
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
|
|
|
data += len(pc.Pcinline.P)
|
2016-03-23 00:44:07 +02:00
|
|
|
for i := 0; i < len(pc.Pcdata); i++ {
|
|
|
|
|
data += len(pc.Pcdata[i].P)
|
|
|
|
|
}
|
|
|
|
|
|
2016-03-31 12:59:05 +03:00
|
|
|
w.nData += data
|
|
|
|
|
w.nPcdata += len(pc.Pcdata)
|
2016-03-23 00:44:07 +02:00
|
|
|
|
2017-03-03 16:45:21 -08:00
|
|
|
w.nAutom += len(s.Autom)
|
2016-03-31 12:59:05 +03:00
|
|
|
w.nFuncdata += len(pc.Funcdataoff)
|
|
|
|
|
w.nFile += len(pc.File)
|
2016-03-23 00:44:07 +02:00
|
|
|
}
|
|
|
|
|
|
2016-03-31 12:59:05 +03:00
|
|
|
func (w *objWriter) writeLengths() {
|
|
|
|
|
w.writeInt(int64(w.nData))
|
|
|
|
|
w.writeInt(int64(w.nReloc))
|
|
|
|
|
w.writeInt(int64(w.nPcdata))
|
|
|
|
|
w.writeInt(int64(w.nAutom))
|
|
|
|
|
w.writeInt(int64(w.nFuncdata))
|
|
|
|
|
w.writeInt(int64(w.nFile))
|
2016-03-23 00:44:07 +02:00
|
|
|
}
|
|
|
|
|
|
2016-04-12 17:58:46 -07:00
|
|
|
func newObjWriter(ctxt *Link, b *bufio.Writer) *objWriter {
|
2016-03-31 12:59:05 +03:00
|
|
|
return &objWriter{
|
|
|
|
|
ctxt: ctxt,
|
2016-04-12 17:58:46 -07:00
|
|
|
wr: b,
|
2016-03-31 12:59:05 +03:00
|
|
|
vrefIdx: make(map[string]int),
|
|
|
|
|
refIdx: make(map[string]int),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-04-12 17:58:46 -07:00
|
|
|
func WriteObjFile(ctxt *Link, b *bufio.Writer) {
|
2016-03-31 12:59:05 +03:00
|
|
|
w := newObjWriter(ctxt, b)
|
2015-01-19 14:34:58 -05:00
|
|
|
|
2016-03-31 12:59:05 +03:00
|
|
|
// Magic header
|
2017-03-06 10:51:37 -08:00
|
|
|
w.wr.WriteString("\x00\x00go19ld")
|
2015-01-19 14:34:58 -05:00
|
|
|
|
2016-03-31 12:59:05 +03:00
|
|
|
// Version
|
|
|
|
|
w.wr.WriteByte(1)
|
|
|
|
|
|
|
|
|
|
// Autolib
|
2015-03-08 22:41:48 -04:00
|
|
|
for _, pkg := range ctxt.Imports {
|
2016-03-31 12:59:05 +03:00
|
|
|
w.writeString(pkg)
|
2015-01-19 14:34:58 -05:00
|
|
|
}
|
2016-03-31 12:59:05 +03:00
|
|
|
w.writeString("")
|
2016-03-23 00:44:07 +02:00
|
|
|
|
2016-03-31 12:59:05 +03:00
|
|
|
// Symbol references
|
2016-03-14 21:51:09 -07:00
|
|
|
for _, s := range ctxt.Text {
|
2016-03-31 12:59:05 +03:00
|
|
|
w.writeRefs(s)
|
|
|
|
|
w.addLengths(s)
|
2016-03-14 22:57:58 +02:00
|
|
|
}
|
2016-03-14 21:51:09 -07:00
|
|
|
for _, s := range ctxt.Data {
|
2016-03-31 12:59:05 +03:00
|
|
|
w.writeRefs(s)
|
|
|
|
|
w.addLengths(s)
|
2016-03-14 22:57:58 +02:00
|
|
|
}
|
2016-03-31 12:59:05 +03:00
|
|
|
// End symbol references
|
|
|
|
|
w.wr.WriteByte(0xff)
|
2016-03-14 22:57:58 +02:00
|
|
|
|
2016-03-31 12:59:05 +03:00
|
|
|
// Lengths
|
|
|
|
|
w.writeLengths()
|
2016-03-23 00:44:07 +02:00
|
|
|
|
2016-03-31 12:59:05 +03:00
|
|
|
// Data block
|
2016-03-21 10:55:20 +13:00
|
|
|
for _, s := range ctxt.Text {
|
2016-03-31 12:59:05 +03:00
|
|
|
w.wr.Write(s.P)
|
2017-03-03 16:45:21 -08:00
|
|
|
pc := &s.Pcln
|
2016-03-31 12:59:05 +03:00
|
|
|
w.wr.Write(pc.Pcsp.P)
|
|
|
|
|
w.wr.Write(pc.Pcfile.P)
|
|
|
|
|
w.wr.Write(pc.Pcline.P)
|
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
|
|
|
w.wr.Write(pc.Pcinline.P)
|
2016-03-21 10:55:20 +13:00
|
|
|
for i := 0; i < len(pc.Pcdata); i++ {
|
2016-03-31 12:59:05 +03:00
|
|
|
w.wr.Write(pc.Pcdata[i].P)
|
2016-03-21 10:55:20 +13:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
for _, s := range ctxt.Data {
|
2016-03-31 12:59:05 +03:00
|
|
|
w.wr.Write(s.P)
|
2016-03-21 10:55:20 +13:00
|
|
|
}
|
|
|
|
|
|
2016-03-31 12:59:05 +03:00
|
|
|
// Symbols
|
2016-03-14 21:51:09 -07:00
|
|
|
for _, s := range ctxt.Text {
|
2016-03-31 12:59:05 +03:00
|
|
|
w.writeSym(s)
|
2015-01-19 14:34:58 -05:00
|
|
|
}
|
2016-03-14 21:51:09 -07:00
|
|
|
for _, s := range ctxt.Data {
|
2016-03-31 12:59:05 +03:00
|
|
|
w.writeSym(s)
|
2015-01-19 14:34:58 -05:00
|
|
|
}
|
|
|
|
|
|
2016-03-31 12:59:05 +03:00
|
|
|
// Magic footer
|
2017-03-06 10:51:37 -08:00
|
|
|
w.wr.WriteString("\xff\xffgo19ld")
|
2015-01-19 14:34:58 -05:00
|
|
|
}
|
|
|
|
|
|
2016-03-31 12:59:05 +03:00
|
|
|
// Symbols are prefixed so their content doesn't get confused with the magic footer.
|
|
|
|
|
const symPrefix = 0xfe
|
2016-03-17 13:18:34 +02:00
|
|
|
|
2016-03-31 12:59:05 +03:00
|
|
|
func (w *objWriter) writeRef(s *LSym, isPath bool) {
|
2016-03-14 22:57:58 +02:00
|
|
|
if s == nil || s.RefIdx != 0 {
|
|
|
|
|
return
|
|
|
|
|
}
|
2016-03-17 13:18:34 +02:00
|
|
|
var m map[string]int
|
|
|
|
|
switch s.Version {
|
|
|
|
|
case 0:
|
2016-03-31 12:59:05 +03:00
|
|
|
m = w.refIdx
|
2016-03-17 13:18:34 +02:00
|
|
|
case 1:
|
2016-03-31 12:59:05 +03:00
|
|
|
m = w.vrefIdx
|
2016-03-17 13:18:34 +02:00
|
|
|
default:
|
|
|
|
|
log.Fatalf("%s: invalid version number %d", s.Name, s.Version)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
idx := m[s.Name]
|
|
|
|
|
if idx != 0 {
|
|
|
|
|
s.RefIdx = idx
|
|
|
|
|
return
|
|
|
|
|
}
|
2016-03-31 12:59:05 +03:00
|
|
|
w.wr.WriteByte(symPrefix)
|
2016-03-14 22:57:58 +02:00
|
|
|
if isPath {
|
2016-03-31 12:59:05 +03:00
|
|
|
w.writeString(filepath.ToSlash(s.Name))
|
2016-03-14 22:57:58 +02:00
|
|
|
} else {
|
2016-03-31 12:59:05 +03:00
|
|
|
w.writeString(s.Name)
|
2016-03-14 22:57:58 +02:00
|
|
|
}
|
2016-03-31 12:59:05 +03:00
|
|
|
w.writeInt(int64(s.Version))
|
|
|
|
|
w.nRefs++
|
|
|
|
|
s.RefIdx = w.nRefs
|
|
|
|
|
m[s.Name] = w.nRefs
|
2016-03-14 22:57:58 +02:00
|
|
|
}
|
|
|
|
|
|
2016-03-31 12:59:05 +03:00
|
|
|
func (w *objWriter) writeRefs(s *LSym) {
|
|
|
|
|
w.writeRef(s, false)
|
|
|
|
|
w.writeRef(s.Gotype, false)
|
2016-03-14 22:57:58 +02:00
|
|
|
for i := range s.R {
|
2016-03-31 12:59:05 +03:00
|
|
|
w.writeRef(s.R[i].Sym, false)
|
2016-03-14 22:57:58 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if s.Type == STEXT {
|
2017-03-03 16:45:21 -08:00
|
|
|
for _, a := range s.Autom {
|
2016-03-31 12:59:05 +03:00
|
|
|
w.writeRef(a.Asym, false)
|
|
|
|
|
w.writeRef(a.Gotype, false)
|
2016-03-14 22:57:58 +02:00
|
|
|
}
|
2017-03-03 16:45:21 -08:00
|
|
|
pc := &s.Pcln
|
2016-03-14 22:57:58 +02:00
|
|
|
for _, d := range pc.Funcdata {
|
2016-03-31 12:59:05 +03:00
|
|
|
w.writeRef(d, false)
|
2016-03-14 22:57:58 +02:00
|
|
|
}
|
|
|
|
|
for _, f := range pc.File {
|
2016-03-31 12:59:05 +03:00
|
|
|
w.writeRef(f, true)
|
2016-03-14 22:57:58 +02: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
|
|
|
for _, call := range pc.InlTree.nodes {
|
|
|
|
|
w.writeRef(call.Func, false)
|
|
|
|
|
f, _ := linkgetlineFromPos(w.ctxt, call.Pos)
|
|
|
|
|
w.writeRef(f, true)
|
|
|
|
|
}
|
2016-03-14 22:57:58 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-03-31 12:59:05 +03:00
|
|
|
func (w *objWriter) writeSymDebug(s *LSym) {
|
|
|
|
|
ctxt := w.ctxt
|
|
|
|
|
fmt.Fprintf(ctxt.Bso, "%s ", s.Name)
|
|
|
|
|
if s.Version != 0 {
|
|
|
|
|
fmt.Fprintf(ctxt.Bso, "v=%d ", s.Version)
|
|
|
|
|
}
|
|
|
|
|
if s.Type != 0 {
|
|
|
|
|
fmt.Fprintf(ctxt.Bso, "t=%d ", s.Type)
|
|
|
|
|
}
|
2016-10-24 23:15:41 +03:00
|
|
|
if s.DuplicateOK() {
|
2016-03-31 12:59:05 +03:00
|
|
|
fmt.Fprintf(ctxt.Bso, "dupok ")
|
|
|
|
|
}
|
2016-10-24 23:15:41 +03:00
|
|
|
if s.CFunc() {
|
2016-03-31 12:59:05 +03:00
|
|
|
fmt.Fprintf(ctxt.Bso, "cfunc ")
|
|
|
|
|
}
|
2016-10-24 23:15:41 +03:00
|
|
|
if s.NoSplit() {
|
2016-03-31 12:59:05 +03:00
|
|
|
fmt.Fprintf(ctxt.Bso, "nosplit ")
|
|
|
|
|
}
|
|
|
|
|
fmt.Fprintf(ctxt.Bso, "size=%d", s.Size)
|
|
|
|
|
if s.Type == STEXT {
|
|
|
|
|
fmt.Fprintf(ctxt.Bso, " args=%#x locals=%#x", uint64(s.Args), uint64(s.Locals))
|
2016-10-24 23:15:41 +03:00
|
|
|
if s.Leaf() {
|
2016-03-31 12:59:05 +03:00
|
|
|
fmt.Fprintf(ctxt.Bso, " leaf")
|
2015-01-19 14:34:58 -05:00
|
|
|
}
|
2016-03-31 12:59:05 +03:00
|
|
|
}
|
|
|
|
|
fmt.Fprintf(ctxt.Bso, "\n")
|
2017-03-03 16:45:21 -08:00
|
|
|
if s.Type == STEXT {
|
|
|
|
|
for p := s.Text; p != nil; p = p.Link {
|
|
|
|
|
fmt.Fprintf(ctxt.Bso, "\t%#04x %v\n", uint(int(p.Pc)), p)
|
|
|
|
|
}
|
2016-03-31 12:59:05 +03:00
|
|
|
}
|
2017-03-03 16:45:21 -08:00
|
|
|
for i := 0; i < len(s.P); i += 16 {
|
2016-03-31 12:59:05 +03:00
|
|
|
fmt.Fprintf(ctxt.Bso, "\t%#04x", uint(i))
|
2017-03-03 16:45:21 -08:00
|
|
|
j := i
|
2016-03-31 12:59:05 +03:00
|
|
|
for j = i; j < i+16 && j < len(s.P); j++ {
|
|
|
|
|
fmt.Fprintf(ctxt.Bso, " %02x", s.P[j])
|
2015-01-19 14:34:58 -05:00
|
|
|
}
|
2016-03-31 12:59:05 +03:00
|
|
|
for ; j < i+16; j++ {
|
|
|
|
|
fmt.Fprintf(ctxt.Bso, " ")
|
2015-01-19 14:34:58 -05:00
|
|
|
}
|
2016-03-31 12:59:05 +03:00
|
|
|
fmt.Fprintf(ctxt.Bso, " ")
|
|
|
|
|
for j = i; j < i+16 && j < len(s.P); j++ {
|
2017-03-03 16:45:21 -08:00
|
|
|
c := int(s.P[j])
|
2016-03-31 12:59:05 +03:00
|
|
|
if ' ' <= c && c <= 0x7e {
|
|
|
|
|
fmt.Fprintf(ctxt.Bso, "%c", c)
|
|
|
|
|
} else {
|
|
|
|
|
fmt.Fprintf(ctxt.Bso, ".")
|
2015-01-19 14:34:58 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fmt.Fprintf(ctxt.Bso, "\n")
|
2016-03-31 12:59:05 +03:00
|
|
|
}
|
2015-01-19 14:34:58 -05:00
|
|
|
|
2016-03-31 12:59:05 +03:00
|
|
|
sort.Sort(relocByOff(s.R)) // generate stable output
|
|
|
|
|
for _, r := range s.R {
|
|
|
|
|
name := ""
|
|
|
|
|
if r.Sym != nil {
|
|
|
|
|
name = r.Sym.Name
|
2016-04-04 10:49:55 -07:00
|
|
|
} else if r.Type == R_TLS_LE {
|
|
|
|
|
name = "TLS"
|
2015-01-19 14:34:58 -05:00
|
|
|
}
|
2016-04-06 12:01:40 -07:00
|
|
|
if ctxt.Arch.InFamily(sys.ARM, sys.PPC64) {
|
2016-04-14 19:04:45 -07:00
|
|
|
fmt.Fprintf(ctxt.Bso, "\trel %d+%d t=%d %s+%x\n", int(r.Off), r.Siz, r.Type, name, uint64(r.Add))
|
2016-03-31 12:59:05 +03:00
|
|
|
} else {
|
2016-04-14 19:04:45 -07:00
|
|
|
fmt.Fprintf(ctxt.Bso, "\trel %d+%d t=%d %s+%d\n", int(r.Off), r.Siz, r.Type, name, r.Add)
|
2015-01-19 14:34:58 -05:00
|
|
|
}
|
|
|
|
|
}
|
2016-03-31 12:59:05 +03:00
|
|
|
}
|
2015-01-19 14:34:58 -05:00
|
|
|
|
2016-03-31 12:59:05 +03:00
|
|
|
func (w *objWriter) writeSym(s *LSym) {
|
|
|
|
|
ctxt := w.ctxt
|
|
|
|
|
if ctxt.Debugasm != 0 {
|
|
|
|
|
w.writeSymDebug(s)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
w.wr.WriteByte(symPrefix)
|
|
|
|
|
w.writeInt(int64(s.Type))
|
|
|
|
|
w.writeRefIndex(s)
|
2016-03-28 11:34:37 +02:00
|
|
|
flags := int64(0)
|
2016-10-24 23:15:41 +03:00
|
|
|
if s.DuplicateOK() {
|
2016-03-28 11:34:37 +02:00
|
|
|
flags |= 1
|
|
|
|
|
}
|
2016-10-24 23:15:41 +03:00
|
|
|
if s.Local() {
|
2016-03-28 11:34:37 +02:00
|
|
|
flags |= 1 << 1
|
2015-04-18 08:14:08 +12:00
|
|
|
}
|
2016-10-24 23:15:41 +03:00
|
|
|
if s.MakeTypelink() {
|
2016-10-19 07:33:16 +03:00
|
|
|
flags |= 1 << 2
|
|
|
|
|
}
|
2016-03-31 12:59:05 +03:00
|
|
|
w.writeInt(flags)
|
|
|
|
|
w.writeInt(s.Size)
|
|
|
|
|
w.writeRefIndex(s.Gotype)
|
|
|
|
|
w.writeInt(int64(len(s.P)))
|
2015-01-19 14:34:58 -05:00
|
|
|
|
2016-03-31 12:59:05 +03:00
|
|
|
w.writeInt(int64(len(s.R)))
|
2015-02-23 16:07:24 -05:00
|
|
|
var r *Reloc
|
|
|
|
|
for i := 0; i < len(s.R); i++ {
|
2015-01-19 14:34:58 -05:00
|
|
|
r = &s.R[i]
|
2016-03-31 12:59:05 +03:00
|
|
|
w.writeInt(int64(r.Off))
|
|
|
|
|
w.writeInt(int64(r.Siz))
|
|
|
|
|
w.writeInt(int64(r.Type))
|
|
|
|
|
w.writeInt(r.Add)
|
|
|
|
|
w.writeRefIndex(r.Sym)
|
2015-01-19 14:34:58 -05:00
|
|
|
}
|
|
|
|
|
|
2016-03-31 12:59:05 +03:00
|
|
|
if s.Type != STEXT {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
w.writeInt(int64(s.Args))
|
|
|
|
|
w.writeInt(int64(s.Locals))
|
2016-10-24 23:15:41 +03:00
|
|
|
if s.NoSplit() {
|
2016-03-31 12:59:05 +03:00
|
|
|
w.writeInt(1)
|
|
|
|
|
} else {
|
|
|
|
|
w.writeInt(0)
|
|
|
|
|
}
|
|
|
|
|
flags = int64(0)
|
2016-10-24 23:15:41 +03:00
|
|
|
if s.Leaf() {
|
2016-03-31 12:59:05 +03:00
|
|
|
flags |= 1
|
|
|
|
|
}
|
2016-10-24 23:15:41 +03:00
|
|
|
if s.CFunc() {
|
2016-03-31 12:59:05 +03:00
|
|
|
flags |= 1 << 1
|
|
|
|
|
}
|
2016-10-24 23:15:41 +03:00
|
|
|
if s.ReflectMethod() {
|
2016-03-31 12:59:05 +03:00
|
|
|
flags |= 1 << 2
|
|
|
|
|
}
|
|
|
|
|
w.writeInt(flags)
|
2017-03-03 16:45:21 -08:00
|
|
|
w.writeInt(int64(len(s.Autom)))
|
|
|
|
|
for _, a := range s.Autom {
|
2016-03-31 12:59:05 +03:00
|
|
|
w.writeRefIndex(a.Asym)
|
|
|
|
|
w.writeInt(int64(a.Aoffset))
|
|
|
|
|
if a.Name == NAME_AUTO {
|
|
|
|
|
w.writeInt(A_AUTO)
|
|
|
|
|
} else if a.Name == NAME_PARAM {
|
|
|
|
|
w.writeInt(A_PARAM)
|
2016-03-28 11:34:37 +02:00
|
|
|
} else {
|
2016-03-31 12:59:05 +03:00
|
|
|
log.Fatalf("%s: invalid local variable type %d", s.Name, a.Name)
|
2015-01-19 14:34:58 -05:00
|
|
|
}
|
2016-03-31 12:59:05 +03:00
|
|
|
w.writeRefIndex(a.Gotype)
|
|
|
|
|
}
|
2015-01-19 14:34:58 -05:00
|
|
|
|
2017-03-03 16:45:21 -08:00
|
|
|
pc := &s.Pcln
|
2016-03-31 12:59:05 +03:00
|
|
|
w.writeInt(int64(len(pc.Pcsp.P)))
|
|
|
|
|
w.writeInt(int64(len(pc.Pcfile.P)))
|
|
|
|
|
w.writeInt(int64(len(pc.Pcline.P)))
|
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
|
|
|
w.writeInt(int64(len(pc.Pcinline.P)))
|
2016-03-31 12:59:05 +03:00
|
|
|
w.writeInt(int64(len(pc.Pcdata)))
|
|
|
|
|
for i := 0; i < len(pc.Pcdata); i++ {
|
|
|
|
|
w.writeInt(int64(len(pc.Pcdata[i].P)))
|
|
|
|
|
}
|
|
|
|
|
w.writeInt(int64(len(pc.Funcdataoff)))
|
|
|
|
|
for i := 0; i < len(pc.Funcdataoff); i++ {
|
|
|
|
|
w.writeRefIndex(pc.Funcdata[i])
|
|
|
|
|
}
|
|
|
|
|
for i := 0; i < len(pc.Funcdataoff); i++ {
|
|
|
|
|
w.writeInt(pc.Funcdataoff[i])
|
|
|
|
|
}
|
|
|
|
|
w.writeInt(int64(len(pc.File)))
|
|
|
|
|
for _, f := range pc.File {
|
|
|
|
|
w.writeRefIndex(f)
|
2015-01-19 14:34:58 -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
|
|
|
w.writeInt(int64(len(pc.InlTree.nodes)))
|
|
|
|
|
for _, call := range pc.InlTree.nodes {
|
|
|
|
|
w.writeInt(int64(call.Parent))
|
|
|
|
|
f, l := linkgetlineFromPos(w.ctxt, call.Pos)
|
|
|
|
|
w.writeRefIndex(f)
|
|
|
|
|
w.writeInt(int64(l))
|
|
|
|
|
w.writeRefIndex(call.Func)
|
|
|
|
|
}
|
2015-01-19 14:34:58 -05:00
|
|
|
}
|
|
|
|
|
|
2016-03-31 12:59:05 +03:00
|
|
|
func (w *objWriter) writeInt(sval int64) {
|
2015-01-19 14:34:58 -05:00
|
|
|
var v uint64
|
2016-04-14 19:04:45 -07:00
|
|
|
uv := (uint64(sval) << 1) ^ uint64(sval>>63)
|
2016-03-31 12:59:05 +03:00
|
|
|
p := w.varintbuf[:]
|
2015-01-19 14:34:58 -05:00
|
|
|
for v = uv; v >= 0x80; v >>= 7 {
|
|
|
|
|
p[0] = uint8(v | 0x80)
|
|
|
|
|
p = p[1:]
|
|
|
|
|
}
|
|
|
|
|
p[0] = uint8(v)
|
|
|
|
|
p = p[1:]
|
2016-03-31 12:59:05 +03:00
|
|
|
w.wr.Write(w.varintbuf[:len(w.varintbuf)-len(p)])
|
2015-01-19 14:34:58 -05:00
|
|
|
}
|
|
|
|
|
|
2016-03-31 12:59:05 +03:00
|
|
|
func (w *objWriter) writeString(s string) {
|
|
|
|
|
w.writeInt(int64(len(s)))
|
|
|
|
|
w.wr.WriteString(s)
|
2015-01-19 14:34:58 -05:00
|
|
|
}
|
|
|
|
|
|
2016-03-31 12:59:05 +03:00
|
|
|
func (w *objWriter) writeRefIndex(s *LSym) {
|
2015-01-19 14:34:58 -05:00
|
|
|
if s == nil {
|
2016-03-31 12:59:05 +03:00
|
|
|
w.writeInt(0)
|
2015-01-19 14:34:58 -05:00
|
|
|
return
|
|
|
|
|
}
|
2016-03-14 22:57:58 +02:00
|
|
|
if s.RefIdx == 0 {
|
|
|
|
|
log.Fatalln("writing an unreferenced symbol", s.Name)
|
|
|
|
|
}
|
2016-03-31 12:59:05 +03:00
|
|
|
w.writeInt(int64(s.RefIdx))
|
2015-01-19 14:34:58 -05:00
|
|
|
}
|
2016-03-13 10:13:03 -07:00
|
|
|
|
|
|
|
|
// relocByOff sorts relocations by their offsets.
|
|
|
|
|
type relocByOff []Reloc
|
|
|
|
|
|
|
|
|
|
func (x relocByOff) Len() int { return len(x) }
|
|
|
|
|
func (x relocByOff) Less(i, j int) bool { return x[i].Off < x[j].Off }
|
|
|
|
|
func (x relocByOff) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
|
2016-07-28 13:04:41 -04:00
|
|
|
|
|
|
|
|
// implement dwarf.Context
|
|
|
|
|
type dwCtxt struct{ *Link }
|
|
|
|
|
|
|
|
|
|
func (c dwCtxt) PtrSize() int {
|
|
|
|
|
return c.Arch.PtrSize
|
|
|
|
|
}
|
|
|
|
|
func (c dwCtxt) AddInt(s dwarf.Sym, size int, i int64) {
|
|
|
|
|
ls := s.(*LSym)
|
|
|
|
|
ls.WriteInt(c.Link, ls.Size, size, i)
|
|
|
|
|
}
|
|
|
|
|
func (c dwCtxt) AddBytes(s dwarf.Sym, b []byte) {
|
|
|
|
|
ls := s.(*LSym)
|
|
|
|
|
ls.WriteBytes(c.Link, ls.Size, b)
|
|
|
|
|
}
|
|
|
|
|
func (c dwCtxt) AddString(s dwarf.Sym, v string) {
|
|
|
|
|
ls := s.(*LSym)
|
|
|
|
|
ls.WriteString(c.Link, ls.Size, len(v), v)
|
|
|
|
|
ls.WriteInt(c.Link, ls.Size, 1, 0)
|
|
|
|
|
}
|
|
|
|
|
func (c dwCtxt) SymValue(s dwarf.Sym) int64 {
|
|
|
|
|
return 0
|
|
|
|
|
}
|
|
|
|
|
func (c dwCtxt) AddAddress(s dwarf.Sym, data interface{}, value int64) {
|
|
|
|
|
rsym := data.(*LSym)
|
|
|
|
|
ls := s.(*LSym)
|
|
|
|
|
size := c.PtrSize()
|
|
|
|
|
ls.WriteAddr(c.Link, ls.Size, size, rsym, value)
|
|
|
|
|
}
|
|
|
|
|
func (c dwCtxt) AddSectionOffset(s dwarf.Sym, size int, t interface{}, ofs int64) {
|
|
|
|
|
ls := s.(*LSym)
|
|
|
|
|
rsym := t.(*LSym)
|
|
|
|
|
ls.WriteAddr(c.Link, ls.Size, size, rsym, ofs)
|
|
|
|
|
r := &ls.R[len(ls.R)-1]
|
|
|
|
|
r.Type = R_DWARFREF
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func gendwarf(ctxt *Link, text []*LSym) []*LSym {
|
|
|
|
|
dctxt := dwCtxt{ctxt}
|
|
|
|
|
var dw []*LSym
|
|
|
|
|
|
|
|
|
|
for _, s := range text {
|
|
|
|
|
dsym := Linklookup(ctxt, dwarf.InfoPrefix+s.Name, int(s.Version))
|
|
|
|
|
if dsym.Size != 0 {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
dw = append(dw, dsym)
|
|
|
|
|
dsym.Type = SDWARFINFO
|
2016-10-24 23:15:41 +03:00
|
|
|
dsym.Set(AttrDuplicateOK, s.DuplicateOK())
|
2017-02-06 15:52:36 -08:00
|
|
|
var vars []*dwarf.Var
|
2016-07-28 13:04:41 -04:00
|
|
|
var abbrev int
|
|
|
|
|
var offs int32
|
2017-03-03 16:45:21 -08:00
|
|
|
for _, a := range s.Autom {
|
2016-07-28 13:04:41 -04:00
|
|
|
switch a.Name {
|
|
|
|
|
case NAME_AUTO:
|
|
|
|
|
abbrev = dwarf.DW_ABRV_AUTO
|
|
|
|
|
offs = a.Aoffset
|
|
|
|
|
if ctxt.FixedFrameSize() == 0 {
|
|
|
|
|
offs -= int32(ctxt.Arch.PtrSize)
|
|
|
|
|
}
|
2016-09-09 08:13:16 -04:00
|
|
|
if Framepointer_enabled(GOOS, GOARCH) {
|
2016-07-28 13:04:41 -04:00
|
|
|
offs -= int32(ctxt.Arch.PtrSize)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case NAME_PARAM:
|
|
|
|
|
abbrev = dwarf.DW_ABRV_PARAM
|
|
|
|
|
offs = a.Aoffset + int32(ctxt.FixedFrameSize())
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
continue
|
|
|
|
|
}
|
2017-02-06 15:52:36 -08:00
|
|
|
|
2016-07-28 13:04:41 -04:00
|
|
|
typename := dwarf.InfoPrefix + a.Gotype.Name[len("type."):]
|
2017-02-06 15:52:36 -08:00
|
|
|
vars = append(vars, &dwarf.Var{
|
2016-07-28 13:04:41 -04:00
|
|
|
Name: a.Asym.Name,
|
|
|
|
|
Abbrev: abbrev,
|
2017-02-06 15:52:36 -08:00
|
|
|
Offset: offs,
|
2016-07-28 13:04:41 -04:00
|
|
|
Type: Linklookup(ctxt, typename, 0),
|
2017-02-06 15:52:36 -08:00
|
|
|
})
|
2016-07-28 13:04:41 -04:00
|
|
|
}
|
2017-02-06 15:52:36 -08:00
|
|
|
|
|
|
|
|
// We want to sort variables by offset, breaking ties
|
|
|
|
|
// with declaration order. Autom holds variables in
|
|
|
|
|
// reverse declaration order, so we reverse the
|
|
|
|
|
// assembled slice and then apply a stable sort.
|
|
|
|
|
for i, j := 0, len(vars)-1; i < j; i, j = i+1, j-1 {
|
|
|
|
|
vars[i], vars[j] = vars[j], vars[i]
|
|
|
|
|
}
|
|
|
|
|
sort.Stable(dwarf.VarsByOffset(vars))
|
|
|
|
|
|
|
|
|
|
dwarf.PutFunc(dctxt, dsym, s.Name, s.Version == 0, s, s.Size, vars)
|
2016-07-28 13:04:41 -04:00
|
|
|
}
|
|
|
|
|
return dw
|
|
|
|
|
}
|