// 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 }