summaryrefslogtreecommitdiffstats
path: root/vendor/github.com/google/gops/internal/obj/objfile.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/google/gops/internal/obj/objfile.go')
-rw-r--r--vendor/github.com/google/gops/internal/obj/objfile.go606
1 files changed, 606 insertions, 0 deletions
diff --git a/vendor/github.com/google/gops/internal/obj/objfile.go b/vendor/github.com/google/gops/internal/obj/objfile.go
new file mode 100644
index 00000000..8a897f09
--- /dev/null
+++ b/vendor/github.com/google/gops/internal/obj/objfile.go
@@ -0,0 +1,606 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// 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:
+//
+// - magic header: "\x00\x00go17ld"
+// - byte 1 - version number
+// - sequence of strings giving dependencies (imported packages)
+// - empty string (marks end of sequence)
+// - sequence of symbol references used by the defined symbols
+// - byte 0xff (marks end of sequence)
+// - 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
+// - data, the content of the defined symbols
+// - sequence of defined symbols
+// - byte 0xff (marks end of sequence)
+// - magic footer: "\xff\xffgo17ld"
+//
+// 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.
+//
+// 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.
+//
+// Each symbol is laid out as the following fields (taken from LSym*):
+//
+// - byte 0xfe (sanity check for synchronization)
+// - type [int]
+// - name & version [symref index]
+// - flags [int]
+// 1<<0 dupok
+// 1<<1 local
+// 1<<2 add to typelink table
+// - size [int]
+// - gotype [symref index]
+// - 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]
+// 1<<0 leaf
+// 1<<1 C function
+// 1<<2 function may call reflect.Type.Method
+// - nlocal [int]
+// - local [nlocal automatics]
+// - pcln [pcln table]
+//
+// Each relocation has the encoding:
+//
+// - off [int]
+// - siz [int]
+// - type [int]
+// - add [int]
+// - sym [symref index]
+//
+// Each local has the encoding:
+//
+// - asym [symref index]
+// - offset [int]
+// - type [int]
+// - gotype [symref index]
+//
+// The pcln table has the encoding:
+//
+// - pcsp [data block]
+// - pcfile [data block]
+// - pcline [data block]
+// - npcdata [int]
+// - pcdata [npcdata data blocks]
+// - nfuncdata [int]
+// - funcdata [nfuncdata symref index]
+// - funcdatasym [nfuncdata ints]
+// - nfile [int]
+// - file [nfile symref index]
+//
+// 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.
+
+package obj
+
+import (
+ "bufio"
+ "fmt"
+ "log"
+ "path/filepath"
+ "sort"
+
+ "github.com/google/gops/internal/dwarf"
+ "github.com/google/gops/internal/sys"
+)
+
+// The Go and C compilers, and the assembler, call writeobj to write
+// out a Go object file. The linker does not call this; the linker
+// does not write out object files.
+func Writeobjdirect(ctxt *Link, b *bufio.Writer) {
+ Flushplist(ctxt)
+ WriteObjFile(ctxt, b)
+}
+
+// objWriter writes Go object files.
+type objWriter struct {
+ wr *bufio.Writer
+ ctxt *Link
+ // Temporary buffer for zigzag int writing.
+ varintbuf [10]uint8
+
+ // Provide the the index of a symbol reference by symbol name.
+ // 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
+}
+
+func (w *objWriter) addLengths(s *LSym) {
+ w.nData += len(s.P)
+ w.nReloc += len(s.R)
+
+ if s.Type != STEXT {
+ return
+ }
+
+ pc := s.Pcln
+
+ data := 0
+ data += len(pc.Pcsp.P)
+ data += len(pc.Pcfile.P)
+ data += len(pc.Pcline.P)
+ for i := 0; i < len(pc.Pcdata); i++ {
+ data += len(pc.Pcdata[i].P)
+ }
+
+ w.nData += data
+ w.nPcdata += len(pc.Pcdata)
+
+ autom := 0
+ for a := s.Autom; a != nil; a = a.Link {
+ autom++
+ }
+ w.nAutom += autom
+ w.nFuncdata += len(pc.Funcdataoff)
+ w.nFile += len(pc.File)
+}
+
+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))
+}
+
+func newObjWriter(ctxt *Link, b *bufio.Writer) *objWriter {
+ return &objWriter{
+ ctxt: ctxt,
+ wr: b,
+ vrefIdx: make(map[string]int),
+ refIdx: make(map[string]int),
+ }
+}
+
+func WriteObjFile(ctxt *Link, b *bufio.Writer) {
+ w := newObjWriter(ctxt, b)
+
+ // Magic header
+ w.wr.WriteString("\x00\x00go17ld")
+
+ // Version
+ w.wr.WriteByte(1)
+
+ // Autolib
+ for _, pkg := range ctxt.Imports {
+ w.writeString(pkg)
+ }
+ w.writeString("")
+
+ // Symbol references
+ for _, s := range ctxt.Text {
+ w.writeRefs(s)
+ w.addLengths(s)
+ }
+ for _, s := range ctxt.Data {
+ w.writeRefs(s)
+ w.addLengths(s)
+ }
+ // End symbol references
+ w.wr.WriteByte(0xff)
+
+ // Lengths
+ w.writeLengths()
+
+ // Data block
+ for _, s := range ctxt.Text {
+ w.wr.Write(s.P)
+ pc := s.Pcln
+ w.wr.Write(pc.Pcsp.P)
+ w.wr.Write(pc.Pcfile.P)
+ w.wr.Write(pc.Pcline.P)
+ for i := 0; i < len(pc.Pcdata); i++ {
+ w.wr.Write(pc.Pcdata[i].P)
+ }
+ }
+ for _, s := range ctxt.Data {
+ w.wr.Write(s.P)
+ }
+
+ // Symbols
+ for _, s := range ctxt.Text {
+ w.writeSym(s)
+ }
+ for _, s := range ctxt.Data {
+ w.writeSym(s)
+ }
+
+ // Magic footer
+ w.wr.WriteString("\xff\xffgo17ld")
+}
+
+// Symbols are prefixed so their content doesn't get confused with the magic footer.
+const symPrefix = 0xfe
+
+func (w *objWriter) writeRef(s *LSym, isPath bool) {
+ if s == nil || s.RefIdx != 0 {
+ return
+ }
+ var m map[string]int
+ switch s.Version {
+ case 0:
+ m = w.refIdx
+ case 1:
+ m = w.vrefIdx
+ default:
+ log.Fatalf("%s: invalid version number %d", s.Name, s.Version)
+ }
+
+ idx := m[s.Name]
+ if idx != 0 {
+ s.RefIdx = idx
+ return
+ }
+ w.wr.WriteByte(symPrefix)
+ if isPath {
+ w.writeString(filepath.ToSlash(s.Name))
+ } else {
+ w.writeString(s.Name)
+ }
+ w.writeInt(int64(s.Version))
+ w.nRefs++
+ s.RefIdx = w.nRefs
+ m[s.Name] = w.nRefs
+}
+
+func (w *objWriter) writeRefs(s *LSym) {
+ w.writeRef(s, false)
+ w.writeRef(s.Gotype, false)
+ for i := range s.R {
+ w.writeRef(s.R[i].Sym, false)
+ }
+
+ if s.Type == STEXT {
+ for a := s.Autom; a != nil; a = a.Link {
+ w.writeRef(a.Asym, false)
+ w.writeRef(a.Gotype, false)
+ }
+ pc := s.Pcln
+ for _, d := range pc.Funcdata {
+ w.writeRef(d, false)
+ }
+ for _, f := range pc.File {
+ w.writeRef(f, true)
+ }
+ }
+}
+
+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)
+ }
+ if s.DuplicateOK() {
+ fmt.Fprintf(ctxt.Bso, "dupok ")
+ }
+ if s.CFunc() {
+ fmt.Fprintf(ctxt.Bso, "cfunc ")
+ }
+ if s.NoSplit() {
+ 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))
+ if s.Leaf() {
+ fmt.Fprintf(ctxt.Bso, " leaf")
+ }
+ }
+
+ fmt.Fprintf(ctxt.Bso, "\n")
+ for p := s.Text; p != nil; p = p.Link {
+ fmt.Fprintf(ctxt.Bso, "\t%#04x %v\n", uint(int(p.Pc)), p)
+ }
+ var c int
+ var j int
+ for i := 0; i < len(s.P); {
+ fmt.Fprintf(ctxt.Bso, "\t%#04x", uint(i))
+ for j = i; j < i+16 && j < len(s.P); j++ {
+ fmt.Fprintf(ctxt.Bso, " %02x", s.P[j])
+ }
+ 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])
+ if ' ' <= c && c <= 0x7e {
+ fmt.Fprintf(ctxt.Bso, "%c", c)
+ } else {
+ fmt.Fprintf(ctxt.Bso, ".")
+ }
+ }
+
+ fmt.Fprintf(ctxt.Bso, "\n")
+ i += 16
+ }
+
+ sort.Sort(relocByOff(s.R)) // generate stable output
+ for _, r := range s.R {
+ name := ""
+ if r.Sym != nil {
+ name = r.Sym.Name
+ } else if r.Type == R_TLS_LE {
+ name = "TLS"
+ }
+ if ctxt.Arch.InFamily(sys.ARM, sys.PPC64) {
+ fmt.Fprintf(ctxt.Bso, "\trel %d+%d t=%d %s+%x\n", int(r.Off), r.Siz, r.Type, name, uint64(r.Add))
+ } else {
+ fmt.Fprintf(ctxt.Bso, "\trel %d+%d t=%d %s+%d\n", int(r.Off), r.Siz, r.Type, name, r.Add)
+ }
+ }
+}
+
+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)
+ flags := int64(0)
+ if s.DuplicateOK() {
+ flags |= 1
+ }
+ if s.Local() {
+ flags |= 1 << 1
+ }
+ if s.MakeTypelink() {
+ flags |= 1 << 2
+ }
+ w.writeInt(flags)
+ w.writeInt(s.Size)
+ w.writeRefIndex(s.Gotype)
+ w.writeInt(int64(len(s.P)))
+
+ w.writeInt(int64(len(s.R)))
+ var r *Reloc
+ for i := 0; i < len(s.R); i++ {
+ r = &s.R[i]
+ w.writeInt(int64(r.Off))
+ w.writeInt(int64(r.Siz))
+ w.writeInt(int64(r.Type))
+ w.writeInt(r.Add)
+ w.writeRefIndex(r.Sym)
+ }
+
+ if s.Type != STEXT {
+ return
+ }
+
+ w.writeInt(int64(s.Args))
+ w.writeInt(int64(s.Locals))
+ if s.NoSplit() {
+ w.writeInt(1)
+ } else {
+ w.writeInt(0)
+ }
+ flags = int64(0)
+ if s.Leaf() {
+ flags |= 1
+ }
+ if s.CFunc() {
+ flags |= 1 << 1
+ }
+ if s.ReflectMethod() {
+ flags |= 1 << 2
+ }
+ w.writeInt(flags)
+ n := 0
+ for a := s.Autom; a != nil; a = a.Link {
+ n++
+ }
+ w.writeInt(int64(n))
+ for a := s.Autom; a != nil; a = a.Link {
+ 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)
+ } else {
+ log.Fatalf("%s: invalid local variable type %d", s.Name, a.Name)
+ }
+ w.writeRefIndex(a.Gotype)
+ }
+
+ pc := s.Pcln
+ w.writeInt(int64(len(pc.Pcsp.P)))
+ w.writeInt(int64(len(pc.Pcfile.P)))
+ w.writeInt(int64(len(pc.Pcline.P)))
+ 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)
+ }
+}
+
+func (w *objWriter) writeInt(sval int64) {
+ var v uint64
+ uv := (uint64(sval) << 1) ^ uint64(sval>>63)
+ p := w.varintbuf[:]
+ for v = uv; v >= 0x80; v >>= 7 {
+ p[0] = uint8(v | 0x80)
+ p = p[1:]
+ }
+ p[0] = uint8(v)
+ p = p[1:]
+ w.wr.Write(w.varintbuf[:len(w.varintbuf)-len(p)])
+}
+
+func (w *objWriter) writeString(s string) {
+ w.writeInt(int64(len(s)))
+ w.wr.WriteString(s)
+}
+
+func (w *objWriter) writeRefIndex(s *LSym) {
+ if s == nil {
+ w.writeInt(0)
+ return
+ }
+ if s.RefIdx == 0 {
+ log.Fatalln("writing an unreferenced symbol", s.Name)
+ }
+ w.writeInt(int64(s.RefIdx))
+}
+
+// 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] }
+
+// 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
+ dsym.Set(AttrDuplicateOK, s.DuplicateOK())
+ var vars dwarf.Var
+ var abbrev int
+ var offs int32
+ for a := s.Autom; a != nil; a = a.Link {
+ switch a.Name {
+ case NAME_AUTO:
+ abbrev = dwarf.DW_ABRV_AUTO
+ offs = a.Aoffset
+ if ctxt.FixedFrameSize() == 0 {
+ offs -= int32(ctxt.Arch.PtrSize)
+ }
+ if Framepointer_enabled(GOOS, GOARCH) {
+ offs -= int32(ctxt.Arch.PtrSize)
+ }
+
+ case NAME_PARAM:
+ abbrev = dwarf.DW_ABRV_PARAM
+ offs = a.Aoffset + int32(ctxt.FixedFrameSize())
+
+ default:
+ continue
+ }
+ typename := dwarf.InfoPrefix + a.Gotype.Name[len("type."):]
+ dwvar := &dwarf.Var{
+ Name: a.Asym.Name,
+ Abbrev: abbrev,
+ Offset: int32(offs),
+ Type: Linklookup(ctxt, typename, 0),
+ }
+ dws := &vars.Link
+ for ; *dws != nil; dws = &(*dws).Link {
+ if offs <= (*dws).Offset {
+ break
+ }
+ }
+ dwvar.Link = *dws
+ *dws = dwvar
+ }
+ dwarf.PutFunc(dctxt, dsym, s.Name, s.Version == 0, s, s.Size, vars.Link)
+ }
+ return dw
+}