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.
|
|
|
|
|
|
2020-08-11 07:24:52 -04:00
|
|
|
// Writing Go object files.
|
|
|
|
|
|
2015-01-19 14:34:58 -05:00
|
|
|
package obj
|
|
|
|
|
|
|
|
|
|
import (
|
2020-08-11 07:24:52 -04:00
|
|
|
"bytes"
|
|
|
|
|
"cmd/internal/bio"
|
|
|
|
|
"cmd/internal/goobj"
|
2022-04-27 09:03:35 -04:00
|
|
|
"cmd/internal/notsha256"
|
2017-04-18 12:53:25 -07:00
|
|
|
"cmd/internal/objabi"
|
2016-04-06 12:01:40 -07:00
|
|
|
"cmd/internal/sys"
|
2020-08-11 07:24:52 -04:00
|
|
|
"encoding/binary"
|
2015-01-19 14:34:58 -05:00
|
|
|
"fmt"
|
2020-01-26 09:59:25 -08:00
|
|
|
"io"
|
2020-07-19 00:32:02 -04:00
|
|
|
"log"
|
|
|
|
|
"os"
|
2020-08-11 07:24:52 -04:00
|
|
|
"path/filepath"
|
2016-03-13 10:13:03 -07:00
|
|
|
"sort"
|
2020-08-11 07:24:52 -04:00
|
|
|
"strings"
|
2015-01-19 14:34:58 -05:00
|
|
|
)
|
|
|
|
|
|
2022-03-21 13:45:50 -04:00
|
|
|
const UnlinkablePkg = "<unlinkable>" // invalid package path, used when compiled without -p flag
|
|
|
|
|
|
2020-08-11 07:24:52 -04:00
|
|
|
// Entry point of writing new object file.
|
|
|
|
|
func WriteObjFile(ctxt *Link, b *bio.Writer) {
|
2020-03-11 11:18:00 -04:00
|
|
|
|
2020-08-11 07:24:52 -04:00
|
|
|
debugAsmEmit(ctxt)
|
|
|
|
|
|
|
|
|
|
genFuncInfoSyms(ctxt)
|
|
|
|
|
|
|
|
|
|
w := writer{
|
|
|
|
|
Writer: goobj.NewWriter(b),
|
|
|
|
|
ctxt: ctxt,
|
|
|
|
|
pkgpath: objabi.PathToPrefix(ctxt.Pkgpath),
|
2016-03-31 12:59:05 +03:00
|
|
|
}
|
2020-08-11 07:24:52 -04:00
|
|
|
|
|
|
|
|
start := b.Offset()
|
|
|
|
|
w.init()
|
|
|
|
|
|
|
|
|
|
// Header
|
|
|
|
|
// We just reserve the space. We'll fill in the offsets later.
|
|
|
|
|
flags := uint32(0)
|
|
|
|
|
if ctxt.Flag_shared {
|
|
|
|
|
flags |= goobj.ObjFlagShared
|
2016-03-31 12:59:05 +03:00
|
|
|
}
|
2022-03-21 13:45:50 -04:00
|
|
|
if w.pkgpath == UnlinkablePkg {
|
|
|
|
|
flags |= goobj.ObjFlagUnlinkable
|
|
|
|
|
}
|
2020-08-11 07:24:52 -04:00
|
|
|
if w.pkgpath == "" {
|
2022-05-05 17:45:27 -04:00
|
|
|
log.Fatal("empty package path")
|
2016-03-31 12:59:05 +03:00
|
|
|
}
|
2020-08-11 07:24:52 -04:00
|
|
|
if ctxt.IsAsm {
|
|
|
|
|
flags |= goobj.ObjFlagFromAssembly
|
2019-03-28 12:51:30 -04:00
|
|
|
}
|
2020-08-11 07:24:52 -04:00
|
|
|
h := goobj.Header{
|
|
|
|
|
Magic: goobj.Magic,
|
|
|
|
|
Fingerprint: ctxt.Fingerprint,
|
|
|
|
|
Flags: flags,
|
2016-03-31 12:59:05 +03:00
|
|
|
}
|
2020-08-11 07:24:52 -04:00
|
|
|
h.Write(w.Writer)
|
2015-01-19 14:34:58 -05:00
|
|
|
|
2020-08-11 07:24:52 -04:00
|
|
|
// String table
|
|
|
|
|
w.StringTable()
|
2015-01-19 14:34:58 -05:00
|
|
|
|
2020-08-11 07:24:52 -04:00
|
|
|
// Autolib
|
|
|
|
|
h.Offsets[goobj.BlkAutolib] = w.Offset()
|
|
|
|
|
for i := range ctxt.Imports {
|
|
|
|
|
ctxt.Imports[i].Write(w.Writer)
|
2015-01-19 14:34:58 -05:00
|
|
|
}
|
|
|
|
|
|
2020-08-11 07:24:52 -04:00
|
|
|
// Package references
|
|
|
|
|
h.Offsets[goobj.BlkPkgIdx] = w.Offset()
|
|
|
|
|
for _, pkg := range w.pkglist {
|
|
|
|
|
w.StringRef(pkg)
|
|
|
|
|
}
|
2016-07-28 13:04:41 -04:00
|
|
|
|
2020-08-11 07:24:52 -04:00
|
|
|
// File table (for DWARF and pcln generation).
|
|
|
|
|
h.Offsets[goobj.BlkFile] = w.Offset()
|
|
|
|
|
for _, f := range ctxt.PosTable.FileTable() {
|
|
|
|
|
w.StringRef(filepath.ToSlash(f))
|
2017-05-02 16:46:01 +02:00
|
|
|
}
|
2020-08-11 07:24:52 -04:00
|
|
|
|
|
|
|
|
// Symbol definitions
|
|
|
|
|
h.Offsets[goobj.BlkSymdef] = w.Offset()
|
|
|
|
|
for _, s := range ctxt.defs {
|
|
|
|
|
w.Sym(s)
|
2018-09-28 16:44:30 +02:00
|
|
|
}
|
|
|
|
|
|
2020-08-11 07:24:52 -04:00
|
|
|
// Short hashed symbol definitions
|
|
|
|
|
h.Offsets[goobj.BlkHashed64def] = w.Offset()
|
|
|
|
|
for _, s := range ctxt.hashed64defs {
|
|
|
|
|
w.Sym(s)
|
|
|
|
|
}
|
2020-03-12 14:10:09 -04:00
|
|
|
|
2020-08-11 07:24:52 -04:00
|
|
|
// Hashed symbol definitions
|
|
|
|
|
h.Offsets[goobj.BlkHasheddef] = w.Offset()
|
|
|
|
|
for _, s := range ctxt.hasheddefs {
|
|
|
|
|
w.Sym(s)
|
|
|
|
|
}
|
2016-07-28 13:04:41 -04:00
|
|
|
|
2020-08-11 07:24:52 -04:00
|
|
|
// Non-pkg symbol definitions
|
|
|
|
|
h.Offsets[goobj.BlkNonpkgdef] = w.Offset()
|
|
|
|
|
for _, s := range ctxt.nonpkgdefs {
|
|
|
|
|
w.Sym(s)
|
|
|
|
|
}
|
2017-10-06 11:32:28 -04:00
|
|
|
|
2020-08-11 07:24:52 -04:00
|
|
|
// Non-pkg symbol references
|
|
|
|
|
h.Offsets[goobj.BlkNonpkgref] = w.Offset()
|
|
|
|
|
for _, s := range ctxt.nonpkgrefs {
|
|
|
|
|
w.Sym(s)
|
|
|
|
|
}
|
2017-10-06 11:32:28 -04:00
|
|
|
|
2020-08-11 07:24:52 -04:00
|
|
|
// Referenced package symbol flags
|
|
|
|
|
h.Offsets[goobj.BlkRefFlags] = w.Offset()
|
|
|
|
|
w.refFlags()
|
2017-10-06 11:32:28 -04:00
|
|
|
|
2020-08-11 07:24:52 -04:00
|
|
|
// Hashes
|
|
|
|
|
h.Offsets[goobj.BlkHash64] = w.Offset()
|
|
|
|
|
for _, s := range ctxt.hashed64defs {
|
|
|
|
|
w.Hash64(s)
|
|
|
|
|
}
|
|
|
|
|
h.Offsets[goobj.BlkHash] = w.Offset()
|
|
|
|
|
for _, s := range ctxt.hasheddefs {
|
|
|
|
|
w.Hash(s)
|
|
|
|
|
}
|
|
|
|
|
// TODO: hashedrefs unused/unsupported for now
|
2017-10-06 11:32:28 -04:00
|
|
|
|
2020-08-11 07:24:52 -04:00
|
|
|
// Reloc indexes
|
|
|
|
|
h.Offsets[goobj.BlkRelocIdx] = w.Offset()
|
|
|
|
|
nreloc := uint32(0)
|
|
|
|
|
lists := [][]*LSym{ctxt.defs, ctxt.hashed64defs, ctxt.hasheddefs, ctxt.nonpkgdefs}
|
|
|
|
|
for _, list := range lists {
|
|
|
|
|
for _, s := range list {
|
|
|
|
|
w.Uint32(nreloc)
|
|
|
|
|
nreloc += uint32(len(s.R))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
w.Uint32(nreloc)
|
2018-09-28 16:44:30 +02:00
|
|
|
|
2020-08-11 07:24:52 -04:00
|
|
|
// Symbol Info indexes
|
|
|
|
|
h.Offsets[goobj.BlkAuxIdx] = w.Offset()
|
|
|
|
|
naux := uint32(0)
|
|
|
|
|
for _, list := range lists {
|
|
|
|
|
for _, s := range list {
|
|
|
|
|
w.Uint32(naux)
|
|
|
|
|
naux += uint32(nAuxSym(s))
|
|
|
|
|
}
|
2017-04-13 05:57:59 -07:00
|
|
|
}
|
2020-08-11 07:24:52 -04:00
|
|
|
w.Uint32(naux)
|
|
|
|
|
|
|
|
|
|
// Data indexes
|
|
|
|
|
h.Offsets[goobj.BlkDataIdx] = w.Offset()
|
2020-07-19 00:32:02 -04:00
|
|
|
dataOff := int64(0)
|
2020-08-11 07:24:52 -04:00
|
|
|
for _, list := range lists {
|
|
|
|
|
for _, s := range list {
|
2020-07-19 00:32:02 -04:00
|
|
|
w.Uint32(uint32(dataOff))
|
|
|
|
|
dataOff += int64(len(s.P))
|
|
|
|
|
if file := s.File(); file != nil {
|
|
|
|
|
dataOff += int64(file.Size)
|
|
|
|
|
}
|
2020-05-01 19:13:30 -04:00
|
|
|
}
|
2020-08-11 07:24:52 -04:00
|
|
|
}
|
2020-07-19 00:32:02 -04:00
|
|
|
if int64(uint32(dataOff)) != dataOff {
|
|
|
|
|
log.Fatalf("data too large")
|
|
|
|
|
}
|
|
|
|
|
w.Uint32(uint32(dataOff))
|
2020-08-11 07:24:52 -04:00
|
|
|
|
|
|
|
|
// Relocs
|
|
|
|
|
h.Offsets[goobj.BlkReloc] = w.Offset()
|
|
|
|
|
for _, list := range lists {
|
|
|
|
|
for _, s := range list {
|
2022-03-28 12:46:46 -04:00
|
|
|
sort.Sort(relocByOff(s.R)) // some platforms (e.g. PE) requires relocations in address order
|
2020-08-11 07:24:52 -04:00
|
|
|
for i := range s.R {
|
|
|
|
|
w.Reloc(&s.R[i])
|
2020-03-20 16:05:24 -04:00
|
|
|
}
|
2020-05-01 19:13:30 -04:00
|
|
|
}
|
2020-08-11 07:24:52 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Aux symbol info
|
|
|
|
|
h.Offsets[goobj.BlkAux] = w.Offset()
|
|
|
|
|
for _, list := range lists {
|
|
|
|
|
for _, s := range list {
|
|
|
|
|
w.Aux(s)
|
2020-05-01 19:13:30 -04:00
|
|
|
}
|
2020-08-11 07:24:52 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Data
|
|
|
|
|
h.Offsets[goobj.BlkData] = w.Offset()
|
|
|
|
|
for _, list := range lists {
|
|
|
|
|
for _, s := range list {
|
|
|
|
|
w.Bytes(s.P)
|
2020-07-19 00:32:02 -04:00
|
|
|
if file := s.File(); file != nil {
|
|
|
|
|
w.writeFile(ctxt, file)
|
|
|
|
|
}
|
[dev.debug] cmd/compile: better DWARF with optimizations on
Debuggers use DWARF information to find local variables on the
stack and in registers. Prior to this CL, the DWARF information for
functions claimed that all variables were on the stack at all times.
That's incorrect when optimizations are enabled, and results in
debuggers showing data that is out of date or complete gibberish.
After this CL, the compiler is capable of representing variable
locations more accurately, and attempts to do so. Due to limitations of
the SSA backend, it's not possible to be completely correct.
There are a number of problems in the current design. One of the easier
to understand is that variable names currently must be attached to an
SSA value, but not all assignments in the source code actually result
in machine code. For example:
type myint int
var a int
b := myint(int)
and
b := (*uint64)(unsafe.Pointer(a))
don't generate machine code because the underlying representation is the
same, so the correct value of b will not be set when the user would
expect.
Generating the more precise debug information is behind a flag,
dwarflocationlists. Because of the issues described above, setting the
flag may not make the debugging experience much better, and may actually
make it worse in cases where the variable actually is on the stack and
the more complicated analysis doesn't realize it.
A number of changes are included:
- Add a new pseudo-instruction, RegKill, which indicates that the value
in the register has been clobbered.
- Adjust regalloc to emit RegKills in the right places. Significantly,
this means that phis are mixed with StoreReg and RegKills after
regalloc.
- Track variable decomposition in ssa.LocalSlots.
- After the SSA backend is done, analyze the result and build location
lists for each LocalSlot.
- After assembly is done, update the location lists with the assembled
PC offsets, recompose variables, and build DWARF location lists. Emit the
list as a new linker symbol, one per function.
- In the linker, aggregate the location lists into a .debug_loc section.
TODO:
- currently disabled for non-X86/AMD64 because there are no data tables.
go build -toolexec 'toolstash -cmp' -a std succeeds.
With -dwarflocationlists false:
before: f02812195637909ff675782c0b46836a8ff01976
after: 06f61e8112a42ac34fb80e0c818b3cdb84a5e7ec
benchstat -geomean /tmp/220352263 /tmp/621364410
completed 15 of 15, estimated time remaining 0s (eta 3:52PM)
name old time/op new time/op delta
Template 199ms ± 3% 198ms ± 2% ~ (p=0.400 n=15+14)
Unicode 96.6ms ± 5% 96.4ms ± 5% ~ (p=0.838 n=15+15)
GoTypes 653ms ± 2% 647ms ± 2% ~ (p=0.102 n=15+14)
Flate 133ms ± 6% 129ms ± 3% -2.62% (p=0.041 n=15+15)
GoParser 164ms ± 5% 159ms ± 3% -3.05% (p=0.000 n=15+15)
Reflect 428ms ± 4% 422ms ± 3% ~ (p=0.156 n=15+13)
Tar 123ms ±10% 124ms ± 8% ~ (p=0.461 n=15+15)
XML 228ms ± 3% 224ms ± 3% -1.57% (p=0.045 n=15+15)
[Geo mean] 206ms 377ms +82.86%
name old user-time/op new user-time/op delta
Template 292ms ±10% 301ms ±12% ~ (p=0.189 n=15+15)
Unicode 166ms ±37% 158ms ±14% ~ (p=0.418 n=15+14)
GoTypes 962ms ± 6% 963ms ± 7% ~ (p=0.976 n=15+15)
Flate 207ms ±19% 200ms ±14% ~ (p=0.345 n=14+15)
GoParser 246ms ±22% 240ms ±15% ~ (p=0.587 n=15+15)
Reflect 611ms ±13% 587ms ±14% ~ (p=0.085 n=15+13)
Tar 211ms ±12% 217ms ±14% ~ (p=0.355 n=14+15)
XML 335ms ±15% 320ms ±18% ~ (p=0.169 n=15+15)
[Geo mean] 317ms 583ms +83.72%
name old alloc/op new alloc/op delta
Template 40.2MB ± 0% 40.2MB ± 0% -0.15% (p=0.000 n=14+15)
Unicode 29.2MB ± 0% 29.3MB ± 0% ~ (p=0.624 n=15+15)
GoTypes 114MB ± 0% 114MB ± 0% -0.15% (p=0.000 n=15+14)
Flate 25.7MB ± 0% 25.6MB ± 0% -0.18% (p=0.000 n=13+15)
GoParser 32.2MB ± 0% 32.2MB ± 0% -0.14% (p=0.003 n=15+15)
Reflect 77.8MB ± 0% 77.9MB ± 0% ~ (p=0.061 n=15+15)
Tar 27.1MB ± 0% 27.0MB ± 0% -0.11% (p=0.029 n=15+15)
XML 42.7MB ± 0% 42.5MB ± 0% -0.29% (p=0.000 n=15+15)
[Geo mean] 42.1MB 75.0MB +78.05%
name old allocs/op new allocs/op delta
Template 402k ± 1% 398k ± 0% -0.91% (p=0.000 n=15+15)
Unicode 344k ± 1% 344k ± 0% ~ (p=0.715 n=15+14)
GoTypes 1.18M ± 0% 1.17M ± 0% -0.91% (p=0.000 n=15+14)
Flate 243k ± 0% 240k ± 1% -1.05% (p=0.000 n=13+15)
GoParser 327k ± 1% 324k ± 1% -0.96% (p=0.000 n=15+15)
Reflect 984k ± 1% 982k ± 0% ~ (p=0.050 n=15+15)
Tar 261k ± 1% 259k ± 1% -0.77% (p=0.000 n=15+15)
XML 411k ± 0% 404k ± 1% -1.55% (p=0.000 n=15+15)
[Geo mean] 439k 755k +72.01%
name old text-bytes new text-bytes delta
HelloSize 694kB ± 0% 694kB ± 0% -0.00% (p=0.000 n=15+15)
name old data-bytes new data-bytes delta
HelloSize 5.55kB ± 0% 5.55kB ± 0% ~ (all equal)
name old bss-bytes new bss-bytes delta
HelloSize 133kB ± 0% 133kB ± 0% ~ (all equal)
name old exe-bytes new exe-bytes delta
HelloSize 1.04MB ± 0% 1.04MB ± 0% ~ (all equal)
Change-Id: I991fc553ef175db46bb23b2128317bbd48de70d8
Reviewed-on: https://go-review.googlesource.com/41770
Reviewed-by: Josh Bleecher Snyder <josharian@gmail.com>
2017-07-21 18:30:19 -04:00
|
|
|
}
|
2017-04-13 08:00:09 -07:00
|
|
|
}
|
2017-04-13 05:57:59 -07:00
|
|
|
|
2020-08-11 07:24:52 -04:00
|
|
|
// Blocks used only by tools (objdump, nm).
|
|
|
|
|
|
|
|
|
|
// Referenced symbol names from other packages
|
|
|
|
|
h.Offsets[goobj.BlkRefName] = w.Offset()
|
|
|
|
|
w.refNames()
|
|
|
|
|
|
|
|
|
|
h.Offsets[goobj.BlkEnd] = w.Offset()
|
|
|
|
|
|
|
|
|
|
// Fix up block offsets in the header
|
|
|
|
|
end := start + int64(w.Offset())
|
|
|
|
|
b.MustSeek(start, 0)
|
|
|
|
|
h.Write(w.Writer)
|
|
|
|
|
b.MustSeek(end, 0)
|
2017-05-02 16:46:01 +02:00
|
|
|
}
|
|
|
|
|
|
2020-08-11 07:24:52 -04:00
|
|
|
type writer struct {
|
|
|
|
|
*goobj.Writer
|
2020-07-19 00:32:02 -04:00
|
|
|
filebuf []byte
|
2020-08-11 07:24:52 -04:00
|
|
|
ctxt *Link
|
|
|
|
|
pkgpath string // the package import path (escaped), "" if unknown
|
|
|
|
|
pkglist []string // list of packages referenced, indexed by ctxt.pkgIdx
|
2022-11-10 16:10:32 -05:00
|
|
|
|
|
|
|
|
// scratch space for writing (the Write methods escape
|
|
|
|
|
// as they are interface calls)
|
|
|
|
|
tmpSym goobj.Sym
|
|
|
|
|
tmpReloc goobj.Reloc
|
|
|
|
|
tmpAux goobj.Aux
|
|
|
|
|
tmpHash64 goobj.Hash64Type
|
|
|
|
|
tmpHash goobj.HashType
|
|
|
|
|
tmpRefFlags goobj.RefFlags
|
|
|
|
|
tmpRefName goobj.RefName
|
2017-10-24 16:08:46 -04:00
|
|
|
}
|
|
|
|
|
|
2020-08-11 07:24:52 -04:00
|
|
|
// prepare package index list
|
|
|
|
|
func (w *writer) init() {
|
|
|
|
|
w.pkglist = make([]string, len(w.ctxt.pkgIdx)+1)
|
|
|
|
|
w.pkglist[0] = "" // dummy invalid package for index 0
|
|
|
|
|
for pkg, i := range w.ctxt.pkgIdx {
|
|
|
|
|
w.pkglist[i] = pkg
|
|
|
|
|
}
|
2016-07-28 13:04:41 -04:00
|
|
|
}
|
2017-09-03 11:59:18 +02:00
|
|
|
|
2020-07-19 00:32:02 -04:00
|
|
|
func (w *writer) writeFile(ctxt *Link, file *FileInfo) {
|
|
|
|
|
f, err := os.Open(file.Name)
|
|
|
|
|
if err != nil {
|
|
|
|
|
ctxt.Diag("%v", err)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
defer f.Close()
|
|
|
|
|
if w.filebuf == nil {
|
|
|
|
|
w.filebuf = make([]byte, 1024)
|
|
|
|
|
}
|
|
|
|
|
buf := w.filebuf
|
|
|
|
|
written := int64(0)
|
|
|
|
|
for {
|
|
|
|
|
n, err := f.Read(buf)
|
|
|
|
|
w.Bytes(buf[:n])
|
|
|
|
|
written += int64(n)
|
|
|
|
|
if err == io.EOF {
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
if err != nil {
|
|
|
|
|
ctxt.Diag("%v", err)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if written != file.Size {
|
|
|
|
|
ctxt.Diag("copy %s: unexpected length %d != %d", file.Name, written, file.Size)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-08-11 07:24:52 -04:00
|
|
|
func (w *writer) StringTable() {
|
|
|
|
|
w.AddString("")
|
|
|
|
|
for _, p := range w.ctxt.Imports {
|
|
|
|
|
w.AddString(p.Pkg)
|
2017-09-03 11:59:18 +02:00
|
|
|
}
|
2020-08-11 07:24:52 -04:00
|
|
|
for _, pkg := range w.pkglist {
|
|
|
|
|
w.AddString(pkg)
|
|
|
|
|
}
|
|
|
|
|
w.ctxt.traverseSyms(traverseAll, func(s *LSym) {
|
2022-05-05 17:22:17 -04:00
|
|
|
// Don't put names of builtins into the string table (to save
|
|
|
|
|
// space).
|
|
|
|
|
if s.PkgIdx == goobj.PkgIdxBuiltin {
|
|
|
|
|
return
|
|
|
|
|
}
|
2020-08-11 07:24:52 -04:00
|
|
|
// TODO: this includes references of indexed symbols from other packages,
|
|
|
|
|
// for which the linker doesn't need the name. Consider moving them to
|
|
|
|
|
// a separate block (for tools only).
|
2022-05-05 17:22:17 -04:00
|
|
|
if w.ctxt.Flag_noRefName && s.PkgIdx < goobj.PkgIdxSpecial {
|
|
|
|
|
// Don't include them if Flag_noRefName
|
|
|
|
|
return
|
|
|
|
|
}
|
2020-08-11 07:24:52 -04:00
|
|
|
if w.pkgpath != "" {
|
|
|
|
|
s.Name = strings.Replace(s.Name, "\"\".", w.pkgpath+".", -1)
|
|
|
|
|
}
|
|
|
|
|
w.AddString(s.Name)
|
2017-09-03 11:59:18 +02:00
|
|
|
})
|
2017-10-06 11:32:28 -04:00
|
|
|
|
2020-08-11 07:24:52 -04:00
|
|
|
// All filenames are in the postable.
|
|
|
|
|
for _, f := range w.ctxt.PosTable.FileTable() {
|
|
|
|
|
w.AddString(filepath.ToSlash(f))
|
2017-10-06 11:32:28 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-19 10:40:24 -04:00
|
|
|
// cutoff is the maximum data section size permitted by the linker
|
|
|
|
|
// (see issue #9862).
|
|
|
|
|
const cutoff = int64(2e9) // 2 GB (or so; looks better in errors than 2^31)
|
|
|
|
|
|
2020-08-11 07:24:52 -04:00
|
|
|
func (w *writer) Sym(s *LSym) {
|
|
|
|
|
abi := uint16(s.ABI())
|
|
|
|
|
if s.Static() {
|
|
|
|
|
abi = goobj.SymABIstatic
|
|
|
|
|
}
|
|
|
|
|
flag := uint8(0)
|
|
|
|
|
if s.DuplicateOK() {
|
|
|
|
|
flag |= goobj.SymFlagDupok
|
|
|
|
|
}
|
|
|
|
|
if s.Local() {
|
|
|
|
|
flag |= goobj.SymFlagLocal
|
|
|
|
|
}
|
|
|
|
|
if s.MakeTypelink() {
|
|
|
|
|
flag |= goobj.SymFlagTypelink
|
|
|
|
|
}
|
|
|
|
|
if s.Leaf() {
|
|
|
|
|
flag |= goobj.SymFlagLeaf
|
|
|
|
|
}
|
|
|
|
|
if s.NoSplit() {
|
|
|
|
|
flag |= goobj.SymFlagNoSplit
|
|
|
|
|
}
|
|
|
|
|
if s.ReflectMethod() {
|
|
|
|
|
flag |= goobj.SymFlagReflectMethod
|
|
|
|
|
}
|
2021-05-08 00:45:06 +07:00
|
|
|
if strings.HasPrefix(s.Name, "type:") && s.Name[5] != '.' && s.Type == objabi.SRODATA {
|
2020-08-11 07:24:52 -04:00
|
|
|
flag |= goobj.SymFlagGoType
|
|
|
|
|
}
|
|
|
|
|
flag2 := uint8(0)
|
|
|
|
|
if s.UsedInIface() {
|
|
|
|
|
flag2 |= goobj.SymFlagUsedInIface
|
|
|
|
|
}
|
2021-05-08 00:45:06 +07:00
|
|
|
if strings.HasPrefix(s.Name, "go:itab.") && s.Type == objabi.SRODATA {
|
2020-08-11 07:24:52 -04:00
|
|
|
flag2 |= goobj.SymFlagItab
|
|
|
|
|
}
|
2021-09-22 08:52:41 -07:00
|
|
|
if strings.HasPrefix(s.Name, w.ctxt.Pkgpath) && strings.HasPrefix(s.Name[len(w.ctxt.Pkgpath):], ".") && strings.HasPrefix(s.Name[len(w.ctxt.Pkgpath)+1:], objabi.GlobalDictPrefix) {
|
2021-09-03 17:00:41 +02:00
|
|
|
flag2 |= goobj.SymFlagDict
|
|
|
|
|
}
|
2023-01-26 15:12:00 -05:00
|
|
|
if s.IsPkgInit() {
|
|
|
|
|
flag2 |= goobj.SymFlagPkgInit
|
|
|
|
|
}
|
2020-08-11 07:24:52 -04:00
|
|
|
name := s.Name
|
|
|
|
|
if strings.HasPrefix(name, "gofile..") {
|
|
|
|
|
name = filepath.ToSlash(name)
|
|
|
|
|
}
|
|
|
|
|
var align uint32
|
2020-07-19 00:30:12 -04:00
|
|
|
if fn := s.Func(); fn != nil {
|
|
|
|
|
align = uint32(fn.Align)
|
2020-08-11 07:24:52 -04:00
|
|
|
}
|
2021-10-01 12:21:36 -04:00
|
|
|
if s.ContentAddressable() && s.Size != 0 {
|
2023-01-19 22:26:15 +00:00
|
|
|
// We generally assume data symbols are naturally aligned
|
2021-10-01 12:21:36 -04:00
|
|
|
// (e.g. integer constants), except for strings and a few
|
|
|
|
|
// compiler-emitted funcdata. If we dedup a string symbol and
|
|
|
|
|
// a non-string symbol with the same content, we should keep
|
2020-08-11 07:24:52 -04:00
|
|
|
// the largest alignment.
|
|
|
|
|
// TODO: maybe the compiler could set the alignment for all
|
|
|
|
|
// data symbols more carefully.
|
2021-10-01 12:21:36 -04:00
|
|
|
switch {
|
2021-05-08 00:45:06 +07:00
|
|
|
case strings.HasPrefix(s.Name, "go:string."),
|
|
|
|
|
strings.HasPrefix(name, "type:.namedata."),
|
|
|
|
|
strings.HasPrefix(name, "type:.importpath."),
|
2021-10-01 12:21:36 -04:00
|
|
|
strings.HasSuffix(name, ".opendefer"),
|
|
|
|
|
strings.HasSuffix(name, ".arginfo0"),
|
cmd/compile, runtime: track argument stack slot liveness
Currently, for stack traces (e.g. at panic or when runtime.Stack
is called), we print argument values from the stack. With register
ABI, we may never store the argument to stack therefore the
argument value on stack may be meaningless. This causes confusion.
This CL makes the compiler keep trace of which argument stack
slots are meaningful. If it is meaningful, it will be printed in
stack traces as before. If it may not be meaningful, it will be
printed as the stack value with a question mark ("?"). In general,
the value could be meaningful on some code paths but not others
depending on the execution, and the compiler couldn't know
statically, so we still print the stack value, instead of not
printing it at all. Also note that if the argument variable is
updated in the function body the printed value may be stale (like
before register ABI) but still considered meaningful.
Arguments passed on stack are always meaningful therefore always
printed without a question mark. Results are never printed, as
before.
(Due to a bug in the compiler we sometimes don't spill args into
their dedicated spill slots (as we should), causing it having
fewer meaningful values than it should be.)
This increases binary sizes a bit:
old new
hello 1129760 1142080 +1.09%
cmd/go 13932320 14088016 +1.12%
cmd/link 6267696 6329168 +0.98%
Fixes #45728.
Change-Id: I308a0402e5c5ab94ca0953f8bd85a56acd28f58c
Reviewed-on: https://go-review.googlesource.com/c/go/+/352057
Trust: Cherry Mui <cherryyz@google.com>
Reviewed-by: Michael Knyszek <mknyszek@google.com>
2021-09-24 16:46:05 -04:00
|
|
|
strings.HasSuffix(name, ".arginfo1"),
|
|
|
|
|
strings.HasSuffix(name, ".argliveinfo"):
|
2021-10-01 12:21:36 -04:00
|
|
|
// These are just bytes, or varints.
|
|
|
|
|
align = 1
|
|
|
|
|
case strings.HasPrefix(name, "gclocals·"):
|
|
|
|
|
// It has 32-bit fields.
|
|
|
|
|
align = 4
|
|
|
|
|
default:
|
2020-08-11 07:24:52 -04:00
|
|
|
switch {
|
|
|
|
|
case w.ctxt.Arch.PtrSize == 8 && s.Size%8 == 0:
|
|
|
|
|
align = 8
|
|
|
|
|
case s.Size%4 == 0:
|
|
|
|
|
align = 4
|
|
|
|
|
case s.Size%2 == 0:
|
|
|
|
|
align = 2
|
2021-10-01 12:21:36 -04:00
|
|
|
default:
|
|
|
|
|
align = 1
|
2020-08-11 07:24:52 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-10-19 10:40:24 -04:00
|
|
|
if s.Size > cutoff {
|
|
|
|
|
w.ctxt.Diag("%s: symbol too large (%d bytes > %d bytes)", s.Name, s.Size, cutoff)
|
|
|
|
|
}
|
2022-11-10 16:10:32 -05:00
|
|
|
o := &w.tmpSym
|
2020-08-11 07:24:52 -04:00
|
|
|
o.SetName(name, w.Writer)
|
|
|
|
|
o.SetABI(abi)
|
|
|
|
|
o.SetType(uint8(s.Type))
|
|
|
|
|
o.SetFlag(flag)
|
|
|
|
|
o.SetFlag2(flag2)
|
|
|
|
|
o.SetSiz(uint32(s.Size))
|
|
|
|
|
o.SetAlign(align)
|
|
|
|
|
o.Write(w.Writer)
|
2017-10-06 11:32:28 -04:00
|
|
|
}
|
|
|
|
|
|
2020-08-11 07:24:52 -04:00
|
|
|
func (w *writer) Hash64(s *LSym) {
|
|
|
|
|
if !s.ContentAddressable() || len(s.R) != 0 {
|
2021-02-17 01:48:21 +00:00
|
|
|
panic("Hash of non-content-addressable symbol")
|
2020-08-11 07:24:52 -04:00
|
|
|
}
|
2022-11-10 16:10:32 -05:00
|
|
|
w.tmpHash64 = contentHash64(s)
|
|
|
|
|
w.Bytes(w.tmpHash64[:])
|
2017-10-06 11:32:28 -04:00
|
|
|
}
|
|
|
|
|
|
2020-08-11 07:24:52 -04:00
|
|
|
func (w *writer) Hash(s *LSym) {
|
|
|
|
|
if !s.ContentAddressable() {
|
2021-02-17 01:48:21 +00:00
|
|
|
panic("Hash of non-content-addressable symbol")
|
2020-08-11 07:24:52 -04:00
|
|
|
}
|
2022-11-10 16:10:32 -05:00
|
|
|
w.tmpHash = w.contentHash(s)
|
|
|
|
|
w.Bytes(w.tmpHash[:])
|
2017-10-06 11:32:28 -04:00
|
|
|
}
|
|
|
|
|
|
2021-09-27 13:39:43 -07:00
|
|
|
// contentHashSection returns a mnemonic for s's section.
|
|
|
|
|
// The goal is to prevent content-addressability from moving symbols between sections.
|
|
|
|
|
// contentHashSection only distinguishes between sets of sections for which this matters.
|
|
|
|
|
// Allowing flexibility increases the effectiveness of content-addressibility.
|
|
|
|
|
// But in some cases, such as doing addressing based on a base symbol,
|
2023-01-19 22:26:15 +00:00
|
|
|
// we need to ensure that a symbol is always in a particular section.
|
2021-09-27 13:39:43 -07:00
|
|
|
// Some of these conditions are duplicated in cmd/link/internal/ld.(*Link).symtab.
|
|
|
|
|
// TODO: instead of duplicating them, have the compiler decide where symbols go.
|
|
|
|
|
func contentHashSection(s *LSym) byte {
|
|
|
|
|
name := s.Name
|
2021-09-27 15:35:46 -04:00
|
|
|
if s.IsPcdata() {
|
|
|
|
|
return 'P'
|
2021-09-24 13:50:22 -07:00
|
|
|
}
|
|
|
|
|
if strings.HasPrefix(name, "gcargs.") ||
|
|
|
|
|
strings.HasPrefix(name, "gclocals.") ||
|
|
|
|
|
strings.HasPrefix(name, "gclocals·") ||
|
|
|
|
|
strings.HasSuffix(name, ".opendefer") ||
|
|
|
|
|
strings.HasSuffix(name, ".arginfo0") ||
|
|
|
|
|
strings.HasSuffix(name, ".arginfo1") ||
|
cmd/compile, runtime: track argument stack slot liveness
Currently, for stack traces (e.g. at panic or when runtime.Stack
is called), we print argument values from the stack. With register
ABI, we may never store the argument to stack therefore the
argument value on stack may be meaningless. This causes confusion.
This CL makes the compiler keep trace of which argument stack
slots are meaningful. If it is meaningful, it will be printed in
stack traces as before. If it may not be meaningful, it will be
printed as the stack value with a question mark ("?"). In general,
the value could be meaningful on some code paths but not others
depending on the execution, and the compiler couldn't know
statically, so we still print the stack value, instead of not
printing it at all. Also note that if the argument variable is
updated in the function body the printed value may be stale (like
before register ABI) but still considered meaningful.
Arguments passed on stack are always meaningful therefore always
printed without a question mark. Results are never printed, as
before.
(Due to a bug in the compiler we sometimes don't spill args into
their dedicated spill slots (as we should), causing it having
fewer meaningful values than it should be.)
This increases binary sizes a bit:
old new
hello 1129760 1142080 +1.09%
cmd/go 13932320 14088016 +1.12%
cmd/link 6267696 6329168 +0.98%
Fixes #45728.
Change-Id: I308a0402e5c5ab94ca0953f8bd85a56acd28f58c
Reviewed-on: https://go-review.googlesource.com/c/go/+/352057
Trust: Cherry Mui <cherryyz@google.com>
Reviewed-by: Michael Knyszek <mknyszek@google.com>
2021-09-24 16:46:05 -04:00
|
|
|
strings.HasSuffix(name, ".argliveinfo") ||
|
2022-02-07 12:00:44 -05:00
|
|
|
strings.HasSuffix(name, ".wrapinfo") ||
|
2021-09-24 13:50:22 -07:00
|
|
|
strings.HasSuffix(name, ".args_stackmap") ||
|
|
|
|
|
strings.HasSuffix(name, ".stkobj") {
|
2021-05-08 00:45:06 +07:00
|
|
|
return 'F' // go:func.* or go:funcrel.*
|
2021-09-24 13:50:22 -07:00
|
|
|
}
|
2021-05-08 00:45:06 +07:00
|
|
|
if strings.HasPrefix(name, "type:") {
|
2021-09-27 13:39:43 -07:00
|
|
|
return 'T'
|
|
|
|
|
}
|
|
|
|
|
return 0
|
|
|
|
|
}
|
|
|
|
|
|
2020-08-11 07:24:52 -04:00
|
|
|
func contentHash64(s *LSym) goobj.Hash64Type {
|
2021-09-27 13:39:43 -07:00
|
|
|
if contentHashSection(s) != 0 {
|
|
|
|
|
panic("short hash of non-default-section sym " + s.Name)
|
|
|
|
|
}
|
2020-08-11 07:24:52 -04:00
|
|
|
var b goobj.Hash64Type
|
|
|
|
|
copy(b[:], s.P)
|
|
|
|
|
return b
|
2017-10-06 11:32:28 -04:00
|
|
|
}
|
|
|
|
|
|
2020-08-11 07:24:52 -04:00
|
|
|
// Compute the content hash for a content-addressable symbol.
|
|
|
|
|
// We build a content hash based on its content and relocations.
|
|
|
|
|
// Depending on the category of the referenced symbol, we choose
|
|
|
|
|
// different hash algorithms such that the hash is globally
|
|
|
|
|
// consistent.
|
2022-02-03 14:12:08 -05:00
|
|
|
// - For referenced content-addressable symbol, its content hash
|
|
|
|
|
// is globally consistent.
|
|
|
|
|
// - For package symbol and builtin symbol, its local index is
|
|
|
|
|
// globally consistent.
|
|
|
|
|
// - For non-package symbol, its fully-expanded name is globally
|
|
|
|
|
// consistent. For now, we require we know the current package
|
|
|
|
|
// path so we can always expand symbol names. (Otherwise,
|
|
|
|
|
// symbols with relocations are not considered hashable.)
|
2020-08-11 07:24:52 -04:00
|
|
|
//
|
|
|
|
|
// For now, we assume there is no circular dependencies among
|
|
|
|
|
// hashed symbols.
|
|
|
|
|
func (w *writer) contentHash(s *LSym) goobj.HashType {
|
2022-04-27 09:03:35 -04:00
|
|
|
h := notsha256.New()
|
2020-08-12 12:54:03 -04:00
|
|
|
var tmp [14]byte
|
|
|
|
|
|
|
|
|
|
// Include the size of the symbol in the hash.
|
|
|
|
|
// This preserves the length of symbols, preventing the following two symbols
|
|
|
|
|
// from hashing the same:
|
|
|
|
|
//
|
|
|
|
|
// [2]int{1,2} ≠ [10]int{1,2,0,0,0...}
|
|
|
|
|
//
|
|
|
|
|
// In this case, if the smaller symbol is alive, the larger is not kept unless
|
|
|
|
|
// needed.
|
|
|
|
|
binary.LittleEndian.PutUint64(tmp[:8], uint64(s.Size))
|
2021-09-27 13:39:43 -07:00
|
|
|
// Some symbols require being in separate sections.
|
|
|
|
|
tmp[8] = contentHashSection(s)
|
|
|
|
|
h.Write(tmp[:9])
|
2020-08-12 12:54:03 -04:00
|
|
|
|
2020-08-11 07:24:52 -04:00
|
|
|
// The compiler trims trailing zeros _sometimes_. We just do
|
|
|
|
|
// it always.
|
|
|
|
|
h.Write(bytes.TrimRight(s.P, "\x00"))
|
|
|
|
|
for i := range s.R {
|
|
|
|
|
r := &s.R[i]
|
|
|
|
|
binary.LittleEndian.PutUint32(tmp[:4], uint32(r.Off))
|
|
|
|
|
tmp[4] = r.Siz
|
|
|
|
|
tmp[5] = uint8(r.Type)
|
|
|
|
|
binary.LittleEndian.PutUint64(tmp[6:14], uint64(r.Add))
|
|
|
|
|
h.Write(tmp[:])
|
|
|
|
|
rs := r.Sym
|
2021-06-09 19:30:16 -07:00
|
|
|
if rs == nil {
|
|
|
|
|
fmt.Printf("symbol: %s\n", s)
|
|
|
|
|
fmt.Printf("relocation: %#v\n", r)
|
|
|
|
|
panic("nil symbol target in relocation")
|
|
|
|
|
}
|
2020-08-11 07:24:52 -04:00
|
|
|
switch rs.PkgIdx {
|
|
|
|
|
case goobj.PkgIdxHashed64:
|
|
|
|
|
h.Write([]byte{0})
|
|
|
|
|
t := contentHash64(rs)
|
|
|
|
|
h.Write(t[:])
|
|
|
|
|
case goobj.PkgIdxHashed:
|
|
|
|
|
h.Write([]byte{1})
|
|
|
|
|
t := w.contentHash(rs)
|
|
|
|
|
h.Write(t[:])
|
|
|
|
|
case goobj.PkgIdxNone:
|
|
|
|
|
h.Write([]byte{2})
|
|
|
|
|
io.WriteString(h, rs.Name) // name is already expanded at this point
|
|
|
|
|
case goobj.PkgIdxBuiltin:
|
|
|
|
|
h.Write([]byte{3})
|
|
|
|
|
binary.LittleEndian.PutUint32(tmp[:4], uint32(rs.SymIdx))
|
|
|
|
|
h.Write(tmp[:4])
|
|
|
|
|
case goobj.PkgIdxSelf:
|
|
|
|
|
io.WriteString(h, w.pkgpath)
|
|
|
|
|
binary.LittleEndian.PutUint32(tmp[:4], uint32(rs.SymIdx))
|
|
|
|
|
h.Write(tmp[:4])
|
|
|
|
|
default:
|
|
|
|
|
io.WriteString(h, rs.Pkg)
|
|
|
|
|
binary.LittleEndian.PutUint32(tmp[:4], uint32(rs.SymIdx))
|
|
|
|
|
h.Write(tmp[:4])
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
var b goobj.HashType
|
|
|
|
|
copy(b[:], h.Sum(nil))
|
|
|
|
|
return b
|
2017-10-06 11:32:28 -04:00
|
|
|
}
|
|
|
|
|
|
2020-08-11 07:24:52 -04:00
|
|
|
func makeSymRef(s *LSym) goobj.SymRef {
|
|
|
|
|
if s == nil {
|
|
|
|
|
return goobj.SymRef{}
|
|
|
|
|
}
|
|
|
|
|
if s.PkgIdx == 0 || !s.Indexed() {
|
|
|
|
|
fmt.Printf("unindexed symbol reference: %v\n", s)
|
|
|
|
|
panic("unindexed symbol reference")
|
2017-10-06 11:32:28 -04:00
|
|
|
}
|
2020-08-11 07:24:52 -04:00
|
|
|
return goobj.SymRef{PkgIdx: uint32(s.PkgIdx), SymIdx: uint32(s.SymIdx)}
|
2017-10-06 11:32:28 -04:00
|
|
|
}
|
|
|
|
|
|
2020-08-11 07:24:52 -04:00
|
|
|
func (w *writer) Reloc(r *Reloc) {
|
2022-11-10 16:10:32 -05:00
|
|
|
o := &w.tmpReloc
|
2020-08-11 07:24:52 -04:00
|
|
|
o.SetOff(r.Off)
|
|
|
|
|
o.SetSiz(r.Siz)
|
2020-11-08 11:27:53 -05:00
|
|
|
o.SetType(uint16(r.Type))
|
2020-08-11 07:24:52 -04:00
|
|
|
o.SetAdd(r.Add)
|
|
|
|
|
o.SetSym(makeSymRef(r.Sym))
|
|
|
|
|
o.Write(w.Writer)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (w *writer) aux1(typ uint8, rs *LSym) {
|
2022-11-10 16:10:32 -05:00
|
|
|
o := &w.tmpAux
|
2020-08-11 07:24:52 -04:00
|
|
|
o.SetType(typ)
|
|
|
|
|
o.SetSym(makeSymRef(rs))
|
|
|
|
|
o.Write(w.Writer)
|
2017-10-06 11:32:28 -04:00
|
|
|
}
|
|
|
|
|
|
2020-08-11 07:24:52 -04:00
|
|
|
func (w *writer) Aux(s *LSym) {
|
|
|
|
|
if s.Gotype != nil {
|
|
|
|
|
w.aux1(goobj.AuxGotype, s.Gotype)
|
2017-10-06 11:32:28 -04:00
|
|
|
}
|
2020-07-19 00:30:12 -04:00
|
|
|
if fn := s.Func(); fn != nil {
|
|
|
|
|
w.aux1(goobj.AuxFuncInfo, fn.FuncInfoSym)
|
2017-10-06 11:32:28 -04:00
|
|
|
|
2020-07-19 00:30:12 -04:00
|
|
|
for _, d := range fn.Pcln.Funcdata {
|
2020-08-11 07:24:52 -04:00
|
|
|
w.aux1(goobj.AuxFuncdata, d)
|
|
|
|
|
}
|
2017-10-06 11:32:28 -04:00
|
|
|
|
2020-07-19 00:30:12 -04:00
|
|
|
if fn.dwarfInfoSym != nil && fn.dwarfInfoSym.Size != 0 {
|
|
|
|
|
w.aux1(goobj.AuxDwarfInfo, fn.dwarfInfoSym)
|
2020-08-11 07:24:52 -04:00
|
|
|
}
|
2020-07-19 00:30:12 -04:00
|
|
|
if fn.dwarfLocSym != nil && fn.dwarfLocSym.Size != 0 {
|
|
|
|
|
w.aux1(goobj.AuxDwarfLoc, fn.dwarfLocSym)
|
2020-08-11 07:24:52 -04:00
|
|
|
}
|
2020-07-19 00:30:12 -04:00
|
|
|
if fn.dwarfRangesSym != nil && fn.dwarfRangesSym.Size != 0 {
|
|
|
|
|
w.aux1(goobj.AuxDwarfRanges, fn.dwarfRangesSym)
|
2020-08-11 07:24:52 -04:00
|
|
|
}
|
2020-07-19 00:30:12 -04:00
|
|
|
if fn.dwarfDebugLinesSym != nil && fn.dwarfDebugLinesSym.Size != 0 {
|
|
|
|
|
w.aux1(goobj.AuxDwarfLines, fn.dwarfDebugLinesSym)
|
2020-08-11 07:24:52 -04:00
|
|
|
}
|
2020-07-19 00:30:12 -04:00
|
|
|
if fn.Pcln.Pcsp != nil && fn.Pcln.Pcsp.Size != 0 {
|
|
|
|
|
w.aux1(goobj.AuxPcsp, fn.Pcln.Pcsp)
|
2020-08-07 11:31:20 -04:00
|
|
|
}
|
2020-07-19 00:30:12 -04:00
|
|
|
if fn.Pcln.Pcfile != nil && fn.Pcln.Pcfile.Size != 0 {
|
|
|
|
|
w.aux1(goobj.AuxPcfile, fn.Pcln.Pcfile)
|
2020-08-07 11:31:20 -04:00
|
|
|
}
|
2020-07-19 00:30:12 -04:00
|
|
|
if fn.Pcln.Pcline != nil && fn.Pcln.Pcline.Size != 0 {
|
|
|
|
|
w.aux1(goobj.AuxPcline, fn.Pcln.Pcline)
|
2020-08-07 11:31:20 -04:00
|
|
|
}
|
2020-07-19 00:30:12 -04:00
|
|
|
if fn.Pcln.Pcinline != nil && fn.Pcln.Pcinline.Size != 0 {
|
|
|
|
|
w.aux1(goobj.AuxPcinline, fn.Pcln.Pcinline)
|
2020-08-07 11:31:20 -04:00
|
|
|
}
|
2023-01-16 16:21:48 +01:00
|
|
|
if fn.sehUnwindInfoSym != nil && fn.sehUnwindInfoSym.Size != 0 {
|
|
|
|
|
w.aux1(goobj.AuxSehUnwindInfo, fn.sehUnwindInfoSym)
|
|
|
|
|
}
|
2020-07-19 00:30:12 -04:00
|
|
|
for _, pcSym := range fn.Pcln.Pcdata {
|
2020-08-07 11:31:20 -04:00
|
|
|
w.aux1(goobj.AuxPcdata, pcSym)
|
|
|
|
|
}
|
2023-01-22 15:30:59 -08:00
|
|
|
if fn.WasmImportSym != nil {
|
|
|
|
|
if fn.WasmImportSym.Size == 0 {
|
|
|
|
|
panic("wasmimport aux sym must have non-zero size")
|
|
|
|
|
}
|
|
|
|
|
w.aux1(goobj.AuxWasmImport, fn.WasmImportSym)
|
|
|
|
|
}
|
cmd/compile: delay inlinable method compilation for -c=1
When the concurrent back end is not enabled, it is possible to have a
scenario where: we compile a specific inlinable non-pointer-receiver
method T.M, then at some point later on in the compilation we visit a
type that triggers generation of a pointer-receiver wrapper (*T).M,
which then results in an inline of T.M into (*T).M. This introduces
subtle differences in the DWARF as compared with when the concurrent
back end is enabled (in the concurrent case, by the time we run the
SSA back end on T.M is is marked as being inlined, whereas in the
non-current case it is not marked inlined).
As a fix, at the point where we would normally compile a given
function in the xtop list right away, if the function is a method AND
is inlinable AND hasn't been inlined, then delay its compilation until
compileFunctions (so as to make sure that when we do compile it, all
possible inlining has been complete). In addition, make sure that
the abstract function symbol for the inlined function gets recorded
correctly.
Fixes #38068.
Change-Id: I57410ab5658bd4ee5b4b80750518e9b20fd6ba52
Reviewed-on: https://go-review.googlesource.com/c/go/+/234178
Run-TryBot: Than McIntosh <thanm@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Cherry Zhang <cherryyz@google.com>
Reviewed-by: Keith Randall <khr@golang.org>
2020-05-15 12:19:07 -04:00
|
|
|
}
|
2020-08-11 07:24:52 -04:00
|
|
|
}
|
cmd/compile: delay inlinable method compilation for -c=1
When the concurrent back end is not enabled, it is possible to have a
scenario where: we compile a specific inlinable non-pointer-receiver
method T.M, then at some point later on in the compilation we visit a
type that triggers generation of a pointer-receiver wrapper (*T).M,
which then results in an inline of T.M into (*T).M. This introduces
subtle differences in the DWARF as compared with when the concurrent
back end is enabled (in the concurrent case, by the time we run the
SSA back end on T.M is is marked as being inlined, whereas in the
non-current case it is not marked inlined).
As a fix, at the point where we would normally compile a given
function in the xtop list right away, if the function is a method AND
is inlinable AND hasn't been inlined, then delay its compilation until
compileFunctions (so as to make sure that when we do compile it, all
possible inlining has been complete). In addition, make sure that
the abstract function symbol for the inlined function gets recorded
correctly.
Fixes #38068.
Change-Id: I57410ab5658bd4ee5b4b80750518e9b20fd6ba52
Reviewed-on: https://go-review.googlesource.com/c/go/+/234178
Run-TryBot: Than McIntosh <thanm@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Cherry Zhang <cherryyz@google.com>
Reviewed-by: Keith Randall <khr@golang.org>
2020-05-15 12:19:07 -04:00
|
|
|
|
2020-08-11 07:24:52 -04:00
|
|
|
// Emits flags of referenced indexed symbols.
|
|
|
|
|
func (w *writer) refFlags() {
|
|
|
|
|
seen := make(map[*LSym]bool)
|
|
|
|
|
w.ctxt.traverseSyms(traverseRefs, func(rs *LSym) { // only traverse refs, not auxs, as tools don't need auxs
|
|
|
|
|
switch rs.PkgIdx {
|
|
|
|
|
case goobj.PkgIdxNone, goobj.PkgIdxHashed64, goobj.PkgIdxHashed, goobj.PkgIdxBuiltin, goobj.PkgIdxSelf: // not an external indexed reference
|
|
|
|
|
return
|
|
|
|
|
case goobj.PkgIdxInvalid:
|
|
|
|
|
panic("unindexed symbol reference")
|
|
|
|
|
}
|
|
|
|
|
if seen[rs] {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
seen[rs] = true
|
|
|
|
|
symref := makeSymRef(rs)
|
|
|
|
|
flag2 := uint8(0)
|
|
|
|
|
if rs.UsedInIface() {
|
|
|
|
|
flag2 |= goobj.SymFlagUsedInIface
|
|
|
|
|
}
|
|
|
|
|
if flag2 == 0 {
|
|
|
|
|
return // no need to write zero flags
|
|
|
|
|
}
|
2022-11-10 16:10:32 -05:00
|
|
|
o := &w.tmpRefFlags
|
2020-08-11 07:24:52 -04:00
|
|
|
o.SetSym(symref)
|
|
|
|
|
o.SetFlag2(flag2)
|
|
|
|
|
o.Write(w.Writer)
|
|
|
|
|
})
|
2017-10-06 11:32:28 -04:00
|
|
|
}
|
|
|
|
|
|
2020-08-11 07:24:52 -04:00
|
|
|
// Emits names of referenced indexed symbols, used by tools (objdump, nm)
|
|
|
|
|
// only.
|
|
|
|
|
func (w *writer) refNames() {
|
2022-05-05 17:22:17 -04:00
|
|
|
if w.ctxt.Flag_noRefName {
|
|
|
|
|
return
|
|
|
|
|
}
|
2020-08-11 07:24:52 -04:00
|
|
|
seen := make(map[*LSym]bool)
|
|
|
|
|
w.ctxt.traverseSyms(traverseRefs, func(rs *LSym) { // only traverse refs, not auxs, as tools don't need auxs
|
|
|
|
|
switch rs.PkgIdx {
|
|
|
|
|
case goobj.PkgIdxNone, goobj.PkgIdxHashed64, goobj.PkgIdxHashed, goobj.PkgIdxBuiltin, goobj.PkgIdxSelf: // not an external indexed reference
|
|
|
|
|
return
|
|
|
|
|
case goobj.PkgIdxInvalid:
|
|
|
|
|
panic("unindexed symbol reference")
|
2017-10-06 11:32:28 -04:00
|
|
|
}
|
2020-08-11 07:24:52 -04:00
|
|
|
if seen[rs] {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
seen[rs] = true
|
|
|
|
|
symref := makeSymRef(rs)
|
2022-11-10 16:10:32 -05:00
|
|
|
o := &w.tmpRefName
|
2020-08-11 07:24:52 -04:00
|
|
|
o.SetSym(symref)
|
|
|
|
|
o.SetName(rs.Name, w.Writer)
|
|
|
|
|
o.Write(w.Writer)
|
|
|
|
|
})
|
|
|
|
|
// TODO: output in sorted order?
|
|
|
|
|
// Currently tools (cmd/internal/goobj package) doesn't use mmap,
|
|
|
|
|
// and it just read it into a map in memory upfront. If it uses
|
|
|
|
|
// mmap, if the output is sorted, it probably could avoid reading
|
|
|
|
|
// into memory and just do lookups in the mmap'd object file.
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// return the number of aux symbols s have.
|
|
|
|
|
func nAuxSym(s *LSym) int {
|
|
|
|
|
n := 0
|
|
|
|
|
if s.Gotype != nil {
|
|
|
|
|
n++
|
|
|
|
|
}
|
2020-07-19 00:30:12 -04:00
|
|
|
if fn := s.Func(); fn != nil {
|
2020-08-11 07:24:52 -04:00
|
|
|
// FuncInfo is an aux symbol, each Funcdata is an aux symbol
|
2020-07-19 00:30:12 -04:00
|
|
|
n += 1 + len(fn.Pcln.Funcdata)
|
|
|
|
|
if fn.dwarfInfoSym != nil && fn.dwarfInfoSym.Size != 0 {
|
2020-08-11 07:24:52 -04:00
|
|
|
n++
|
|
|
|
|
}
|
2020-07-19 00:30:12 -04:00
|
|
|
if fn.dwarfLocSym != nil && fn.dwarfLocSym.Size != 0 {
|
2020-08-11 07:24:52 -04:00
|
|
|
n++
|
|
|
|
|
}
|
2020-07-19 00:30:12 -04:00
|
|
|
if fn.dwarfRangesSym != nil && fn.dwarfRangesSym.Size != 0 {
|
2020-08-11 07:24:52 -04:00
|
|
|
n++
|
|
|
|
|
}
|
2020-07-19 00:30:12 -04:00
|
|
|
if fn.dwarfDebugLinesSym != nil && fn.dwarfDebugLinesSym.Size != 0 {
|
2020-08-11 07:24:52 -04:00
|
|
|
n++
|
2017-10-06 11:32:28 -04:00
|
|
|
}
|
2020-07-19 00:30:12 -04:00
|
|
|
if fn.Pcln.Pcsp != nil && fn.Pcln.Pcsp.Size != 0 {
|
2020-08-07 11:31:20 -04:00
|
|
|
n++
|
|
|
|
|
}
|
2020-07-19 00:30:12 -04:00
|
|
|
if fn.Pcln.Pcfile != nil && fn.Pcln.Pcfile.Size != 0 {
|
2020-08-07 11:31:20 -04:00
|
|
|
n++
|
|
|
|
|
}
|
2020-07-19 00:30:12 -04:00
|
|
|
if fn.Pcln.Pcline != nil && fn.Pcln.Pcline.Size != 0 {
|
2020-08-07 11:31:20 -04:00
|
|
|
n++
|
|
|
|
|
}
|
2020-07-19 00:30:12 -04:00
|
|
|
if fn.Pcln.Pcinline != nil && fn.Pcln.Pcinline.Size != 0 {
|
2020-08-07 11:31:20 -04:00
|
|
|
n++
|
|
|
|
|
}
|
2023-01-16 16:21:48 +01:00
|
|
|
if fn.sehUnwindInfoSym != nil && fn.sehUnwindInfoSym.Size != 0 {
|
|
|
|
|
n++
|
|
|
|
|
}
|
2020-07-19 00:30:12 -04:00
|
|
|
n += len(fn.Pcln.Pcdata)
|
2023-01-22 15:30:59 -08:00
|
|
|
if fn.WasmImport != nil {
|
|
|
|
|
if fn.WasmImportSym == nil || fn.WasmImportSym.Size == 0 {
|
|
|
|
|
panic("wasmimport aux sym must exist and have non-zero size")
|
|
|
|
|
}
|
|
|
|
|
n++
|
|
|
|
|
}
|
2017-10-06 11:32:28 -04:00
|
|
|
}
|
2020-08-11 07:24:52 -04:00
|
|
|
return n
|
2017-10-06 11:32:28 -04:00
|
|
|
}
|
|
|
|
|
|
2020-08-11 07:24:52 -04:00
|
|
|
// generate symbols for FuncInfo.
|
|
|
|
|
func genFuncInfoSyms(ctxt *Link) {
|
|
|
|
|
infosyms := make([]*LSym, 0, len(ctxt.Text))
|
|
|
|
|
var b bytes.Buffer
|
|
|
|
|
symidx := int32(len(ctxt.defs))
|
|
|
|
|
for _, s := range ctxt.Text {
|
2020-07-19 00:30:12 -04:00
|
|
|
fn := s.Func()
|
|
|
|
|
if fn == nil {
|
2020-08-11 07:24:52 -04:00
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
o := goobj.FuncInfo{
|
2022-09-07 13:23:19 -04:00
|
|
|
Args: uint32(fn.Args),
|
|
|
|
|
Locals: uint32(fn.Locals),
|
|
|
|
|
FuncID: fn.FuncID,
|
|
|
|
|
FuncFlag: fn.FuncFlag,
|
|
|
|
|
StartLine: fn.StartLine,
|
2020-08-11 07:24:52 -04:00
|
|
|
}
|
2020-07-19 00:30:12 -04:00
|
|
|
pc := &fn.Pcln
|
2020-08-11 07:24:52 -04:00
|
|
|
i := 0
|
|
|
|
|
o.File = make([]goobj.CUFileIndex, len(pc.UsedFiles))
|
|
|
|
|
for f := range pc.UsedFiles {
|
|
|
|
|
o.File[i] = f
|
|
|
|
|
i++
|
|
|
|
|
}
|
|
|
|
|
sort.Slice(o.File, func(i, j int) bool { return o.File[i] < o.File[j] })
|
|
|
|
|
o.InlTree = make([]goobj.InlTreeNode, len(pc.InlTree.nodes))
|
|
|
|
|
for i, inl := range pc.InlTree.nodes {
|
2022-10-28 15:15:25 -04:00
|
|
|
f, l := ctxt.getFileIndexAndLine(inl.Pos)
|
2020-08-11 07:24:52 -04:00
|
|
|
o.InlTree[i] = goobj.InlTreeNode{
|
|
|
|
|
Parent: int32(inl.Parent),
|
|
|
|
|
File: goobj.CUFileIndex(f),
|
|
|
|
|
Line: l,
|
|
|
|
|
Func: makeSymRef(inl.Func),
|
|
|
|
|
ParentPC: inl.ParentPC,
|
|
|
|
|
}
|
|
|
|
|
}
|
2017-10-06 11:32:28 -04:00
|
|
|
|
2020-08-11 07:24:52 -04:00
|
|
|
o.Write(&b)
|
2022-04-25 16:36:01 +08:00
|
|
|
p := b.Bytes()
|
2020-08-11 07:24:52 -04:00
|
|
|
isym := &LSym{
|
|
|
|
|
Type: objabi.SDATA, // for now, I don't think it matters
|
|
|
|
|
PkgIdx: goobj.PkgIdxSelf,
|
|
|
|
|
SymIdx: symidx,
|
2022-04-25 16:36:01 +08:00
|
|
|
P: append([]byte(nil), p...),
|
|
|
|
|
Size: int64(len(p)),
|
2020-08-11 07:24:52 -04:00
|
|
|
}
|
|
|
|
|
isym.Set(AttrIndexed, true)
|
|
|
|
|
symidx++
|
|
|
|
|
infosyms = append(infosyms, isym)
|
2020-07-19 00:30:12 -04:00
|
|
|
fn.FuncInfoSym = isym
|
2020-08-11 07:24:52 -04:00
|
|
|
b.Reset()
|
|
|
|
|
|
2023-01-16 16:21:48 +01:00
|
|
|
auxsyms := []*LSym{fn.dwarfRangesSym, fn.dwarfLocSym, fn.dwarfDebugLinesSym, fn.dwarfInfoSym, fn.WasmImportSym, fn.sehUnwindInfoSym}
|
2023-01-22 15:30:59 -08:00
|
|
|
for _, s := range auxsyms {
|
2020-08-11 07:24:52 -04:00
|
|
|
if s == nil || s.Size == 0 {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
s.PkgIdx = goobj.PkgIdxSelf
|
|
|
|
|
s.SymIdx = symidx
|
|
|
|
|
s.Set(AttrIndexed, true)
|
|
|
|
|
symidx++
|
|
|
|
|
infosyms = append(infosyms, s)
|
|
|
|
|
}
|
2017-10-06 11:32:28 -04:00
|
|
|
}
|
2020-08-11 07:24:52 -04:00
|
|
|
ctxt.defs = append(ctxt.defs, infosyms...)
|
|
|
|
|
}
|
2017-10-06 11:32:28 -04:00
|
|
|
|
2020-08-11 07:24:52 -04:00
|
|
|
func writeAuxSymDebug(ctxt *Link, par *LSym, aux *LSym) {
|
|
|
|
|
// Most aux symbols (ex: funcdata) are not interesting--
|
|
|
|
|
// pick out just the DWARF ones for now.
|
|
|
|
|
if aux.Type != objabi.SDWARFLOC &&
|
|
|
|
|
aux.Type != objabi.SDWARFFCN &&
|
|
|
|
|
aux.Type != objabi.SDWARFABSFCN &&
|
|
|
|
|
aux.Type != objabi.SDWARFLINES &&
|
|
|
|
|
aux.Type != objabi.SDWARFRANGE {
|
|
|
|
|
return
|
2017-10-06 11:32:28 -04:00
|
|
|
}
|
2020-08-11 07:24:52 -04:00
|
|
|
ctxt.writeSymDebugNamed(aux, "aux for "+par.Name)
|
2017-10-06 11:32:28 -04:00
|
|
|
}
|
|
|
|
|
|
2020-08-11 07:24:52 -04:00
|
|
|
func debugAsmEmit(ctxt *Link) {
|
|
|
|
|
if ctxt.Debugasm > 0 {
|
|
|
|
|
ctxt.traverseSyms(traverseDefs, ctxt.writeSymDebug)
|
|
|
|
|
if ctxt.Debugasm > 1 {
|
|
|
|
|
fn := func(par *LSym, aux *LSym) {
|
|
|
|
|
writeAuxSymDebug(ctxt, par, aux)
|
2017-10-06 11:32:28 -04:00
|
|
|
}
|
2020-08-11 07:24:52 -04:00
|
|
|
ctxt.traverseAuxSyms(traverseAux, fn)
|
2017-10-06 11:32:28 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-08-11 07:24:52 -04:00
|
|
|
func (ctxt *Link) writeSymDebug(s *LSym) {
|
|
|
|
|
ctxt.writeSymDebugNamed(s, s.Name)
|
2017-10-06 11:32:28 -04:00
|
|
|
}
|
|
|
|
|
|
2020-08-11 07:24:52 -04:00
|
|
|
func (ctxt *Link) writeSymDebugNamed(s *LSym, name string) {
|
2020-09-17 15:35:31 -04:00
|
|
|
ver := ""
|
|
|
|
|
if ctxt.Debugasm > 1 {
|
|
|
|
|
ver = fmt.Sprintf("<%d>", s.ABI())
|
|
|
|
|
}
|
|
|
|
|
fmt.Fprintf(ctxt.Bso, "%s%s ", name, ver)
|
2020-08-11 07:24:52 -04:00
|
|
|
if s.Type != 0 {
|
|
|
|
|
fmt.Fprintf(ctxt.Bso, "%v ", s.Type)
|
2017-10-06 11:32:28 -04:00
|
|
|
}
|
2020-08-11 07:24:52 -04:00
|
|
|
if s.Static() {
|
|
|
|
|
fmt.Fprint(ctxt.Bso, "static ")
|
2017-10-06 11:32:28 -04:00
|
|
|
}
|
2020-08-11 07:24:52 -04:00
|
|
|
if s.DuplicateOK() {
|
|
|
|
|
fmt.Fprintf(ctxt.Bso, "dupok ")
|
2017-10-06 11:32:28 -04:00
|
|
|
}
|
2020-08-11 07:24:52 -04:00
|
|
|
if s.CFunc() {
|
|
|
|
|
fmt.Fprintf(ctxt.Bso, "cfunc ")
|
|
|
|
|
}
|
|
|
|
|
if s.NoSplit() {
|
|
|
|
|
fmt.Fprintf(ctxt.Bso, "nosplit ")
|
|
|
|
|
}
|
cmd/asm, cmd/link, runtime: introduce FuncInfo flag bits
The runtime traceback code has its own definition of which functions
mark the top frame of a stack, separate from the TOPFRAME bits that
exist in the assembly and are passed along in DWARF information.
It's error-prone and redundant to have two different sources of truth.
This CL provides the actual TOPFRAME bits to the runtime, so that
the runtime can use those bits instead of reinventing its own category.
This CL also adds a new bit, SPWRITE, which marks functions that
write directly to SP (anything but adding and subtracting constants).
Such functions must stop a traceback, because the traceback has no
way to rederive the SP on entry. Again, the runtime has its own definition
which is mostly correct, but also missing some functions. During ordinary
goroutine context switches, such functions do not appear on the stack,
so the incompleteness in the runtime usually doesn't matter.
But profiling signals can arrive at any moment, and the runtime may
crash during traceback if it attempts to unwind an SP-writing frame
and gets out-of-sync with the actual stack. The runtime contains code
to try to detect likely candidates but again it is incomplete.
Deriving the SPWRITE bit automatically from the actual assembly code
provides the complete truth, and passing it to the runtime lets the
runtime use it.
This CL is part of a stack adding windows/arm64
support (#36439), intended to land in the Go 1.17 cycle.
This CL is, however, not windows/arm64-specific.
It is cleanup meant to make the port (and future ports) easier.
Change-Id: I227f53b23ac5b3dabfcc5e8ee3f00df4e113cf58
Reviewed-on: https://go-review.googlesource.com/c/go/+/288800
Trust: Russ Cox <rsc@golang.org>
Trust: Jason A. Donenfeld <Jason@zx2c4.com>
Reviewed-by: Cherry Zhang <cherryyz@google.com>
Reviewed-by: Jason A. Donenfeld <Jason@zx2c4.com>
2021-01-28 15:21:33 -05:00
|
|
|
if s.Func() != nil && s.Func().FuncFlag&objabi.FuncFlag_TOPFRAME != 0 {
|
2020-08-11 07:24:52 -04:00
|
|
|
fmt.Fprintf(ctxt.Bso, "topframe ")
|
|
|
|
|
}
|
2021-10-01 16:19:27 -07:00
|
|
|
if s.Func() != nil && s.Func().FuncFlag&objabi.FuncFlag_ASM != 0 {
|
|
|
|
|
fmt.Fprintf(ctxt.Bso, "asm ")
|
|
|
|
|
}
|
2020-08-11 07:24:52 -04:00
|
|
|
fmt.Fprintf(ctxt.Bso, "size=%d", s.Size)
|
|
|
|
|
if s.Type == objabi.STEXT {
|
2020-07-19 00:30:12 -04:00
|
|
|
fn := s.Func()
|
2021-03-09 16:55:20 -06:00
|
|
|
fmt.Fprintf(ctxt.Bso, " args=%#x locals=%#x funcid=%#x align=%#x", uint64(fn.Args), uint64(fn.Locals), uint64(fn.FuncID), uint64(fn.Align))
|
2020-08-11 07:24:52 -04:00
|
|
|
if s.Leaf() {
|
|
|
|
|
fmt.Fprintf(ctxt.Bso, " leaf")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
fmt.Fprintf(ctxt.Bso, "\n")
|
|
|
|
|
if s.Type == objabi.STEXT {
|
2020-07-19 00:30:12 -04:00
|
|
|
for p := s.Func().Text; p != nil; p = p.Link {
|
2020-08-11 07:24:52 -04:00
|
|
|
fmt.Fprintf(ctxt.Bso, "\t%#04x ", uint(int(p.Pc)))
|
|
|
|
|
if ctxt.Debugasm > 1 {
|
|
|
|
|
io.WriteString(ctxt.Bso, p.String())
|
|
|
|
|
} else {
|
|
|
|
|
p.InnermostString(ctxt.Bso)
|
|
|
|
|
}
|
|
|
|
|
fmt.Fprintln(ctxt.Bso)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
for i := 0; i < len(s.P); i += 16 {
|
|
|
|
|
fmt.Fprintf(ctxt.Bso, "\t%#04x", uint(i))
|
|
|
|
|
j := i
|
|
|
|
|
for ; j < i+16 && j < len(s.P); j++ {
|
|
|
|
|
fmt.Fprintf(ctxt.Bso, " %02x", s.P[j])
|
2017-10-06 11:32:28 -04:00
|
|
|
}
|
2020-08-11 07:24:52 -04:00
|
|
|
for ; j < i+16; j++ {
|
|
|
|
|
fmt.Fprintf(ctxt.Bso, " ")
|
|
|
|
|
}
|
|
|
|
|
fmt.Fprintf(ctxt.Bso, " ")
|
|
|
|
|
for j = i; j < i+16 && j < len(s.P); j++ {
|
|
|
|
|
c := int(s.P[j])
|
|
|
|
|
b := byte('.')
|
|
|
|
|
if ' ' <= c && c <= 0x7e {
|
|
|
|
|
b = byte(c)
|
|
|
|
|
}
|
|
|
|
|
ctxt.Bso.WriteByte(b)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fmt.Fprintf(ctxt.Bso, "\n")
|
2017-10-06 11:32:28 -04:00
|
|
|
}
|
|
|
|
|
|
2020-08-11 07:24:52 -04:00
|
|
|
sort.Sort(relocByOff(s.R)) // generate stable output
|
|
|
|
|
for _, r := range s.R {
|
|
|
|
|
name := ""
|
2020-09-17 15:35:31 -04:00
|
|
|
ver := ""
|
2020-08-11 07:24:52 -04:00
|
|
|
if r.Sym != nil {
|
|
|
|
|
name = r.Sym.Name
|
2020-09-17 15:35:31 -04:00
|
|
|
if ctxt.Debugasm > 1 {
|
2020-09-24 16:11:43 -04:00
|
|
|
ver = fmt.Sprintf("<%d>", r.Sym.ABI())
|
2020-09-17 15:35:31 -04:00
|
|
|
}
|
2020-08-11 07:24:52 -04:00
|
|
|
} else if r.Type == objabi.R_TLS_LE {
|
|
|
|
|
name = "TLS"
|
|
|
|
|
}
|
|
|
|
|
if ctxt.Arch.InFamily(sys.ARM, sys.PPC64) {
|
2020-09-17 15:35:31 -04:00
|
|
|
fmt.Fprintf(ctxt.Bso, "\trel %d+%d t=%d %s%s+%x\n", int(r.Off), r.Siz, r.Type, name, ver, uint64(r.Add))
|
2017-10-06 11:32:28 -04:00
|
|
|
} else {
|
2020-09-17 15:35:31 -04:00
|
|
|
fmt.Fprintf(ctxt.Bso, "\trel %d+%d t=%d %s%s+%d\n", int(r.Off), r.Siz, r.Type, name, ver, r.Add)
|
2017-10-06 11:32:28 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-08-11 07:24:52 -04:00
|
|
|
// relocByOff sorts relocations by their offsets.
|
|
|
|
|
type relocByOff []Reloc
|
2017-10-06 11:32:28 -04:00
|
|
|
|
2020-08-11 07:24:52 -04:00
|
|
|
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] }
|