Source file src/cmd/internal/obj/objfile.go

     1  // Copyright 2013 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  // Writing Go object files.
     6  
     7  package obj
     8  
     9  import (
    10  	"bytes"
    11  	"cmd/internal/bio"
    12  	"cmd/internal/goobj"
    13  	"cmd/internal/hash"
    14  	"cmd/internal/objabi"
    15  	"cmd/internal/sys"
    16  	"cmp"
    17  	"encoding/base64"
    18  	"encoding/binary"
    19  	"fmt"
    20  	"internal/abi"
    21  	"io"
    22  	"log"
    23  	"os"
    24  	"path/filepath"
    25  	"slices"
    26  	"sort"
    27  	"strings"
    28  )
    29  
    30  const UnlinkablePkg = "<unlinkable>" // invalid package path, used when compiled without -p flag
    31  
    32  // Entry point of writing new object file.
    33  func WriteObjFile(ctxt *Link, b *bio.Writer) {
    34  
    35  	debugAsmEmit(ctxt)
    36  
    37  	genFuncInfoSyms(ctxt)
    38  
    39  	w := writer{
    40  		Writer:  goobj.NewWriter(b),
    41  		ctxt:    ctxt,
    42  		pkgpath: objabi.PathToPrefix(ctxt.Pkgpath),
    43  	}
    44  
    45  	start := b.Offset()
    46  	w.init()
    47  
    48  	// Header
    49  	// We just reserve the space. We'll fill in the offsets later.
    50  	flags := uint32(0)
    51  	if ctxt.Flag_shared {
    52  		flags |= goobj.ObjFlagShared
    53  	}
    54  	if w.pkgpath == UnlinkablePkg {
    55  		flags |= goobj.ObjFlagUnlinkable
    56  	}
    57  	if w.pkgpath == "" {
    58  		log.Fatal("empty package path")
    59  	}
    60  	if ctxt.IsAsm {
    61  		flags |= goobj.ObjFlagFromAssembly
    62  	}
    63  	if ctxt.Std {
    64  		flags |= goobj.ObjFlagStd
    65  	}
    66  	h := goobj.Header{
    67  		Magic:       goobj.Magic,
    68  		Fingerprint: ctxt.Fingerprint,
    69  		Flags:       flags,
    70  	}
    71  	h.Write(w.Writer)
    72  
    73  	// String table
    74  	w.StringTable()
    75  
    76  	// Autolib
    77  	h.Offsets[goobj.BlkAutolib] = w.Offset()
    78  	for i := range ctxt.Imports {
    79  		ctxt.Imports[i].Write(w.Writer)
    80  	}
    81  
    82  	// Package references
    83  	h.Offsets[goobj.BlkPkgIdx] = w.Offset()
    84  	for _, pkg := range w.pkglist {
    85  		w.StringRef(pkg)
    86  	}
    87  
    88  	// File table (for DWARF and pcln generation).
    89  	h.Offsets[goobj.BlkFile] = w.Offset()
    90  	for _, f := range ctxt.PosTable.FileTable() {
    91  		w.StringRef(filepath.ToSlash(f))
    92  	}
    93  
    94  	// Symbol definitions
    95  	h.Offsets[goobj.BlkSymdef] = w.Offset()
    96  	for _, s := range ctxt.defs {
    97  		w.Sym(s)
    98  	}
    99  
   100  	// Short hashed symbol definitions
   101  	h.Offsets[goobj.BlkHashed64def] = w.Offset()
   102  	for _, s := range ctxt.hashed64defs {
   103  		w.Sym(s)
   104  	}
   105  
   106  	// Hashed symbol definitions
   107  	h.Offsets[goobj.BlkHasheddef] = w.Offset()
   108  	for _, s := range ctxt.hasheddefs {
   109  		w.Sym(s)
   110  	}
   111  
   112  	// Non-pkg symbol definitions
   113  	h.Offsets[goobj.BlkNonpkgdef] = w.Offset()
   114  	for _, s := range ctxt.nonpkgdefs {
   115  		w.Sym(s)
   116  	}
   117  
   118  	// Non-pkg symbol references
   119  	h.Offsets[goobj.BlkNonpkgref] = w.Offset()
   120  	for _, s := range ctxt.nonpkgrefs {
   121  		w.Sym(s)
   122  	}
   123  
   124  	// Referenced package symbol flags
   125  	h.Offsets[goobj.BlkRefFlags] = w.Offset()
   126  	w.refFlags()
   127  
   128  	// Hashes
   129  	h.Offsets[goobj.BlkHash64] = w.Offset()
   130  	for _, s := range ctxt.hashed64defs {
   131  		w.Hash64(s)
   132  	}
   133  	h.Offsets[goobj.BlkHash] = w.Offset()
   134  	for _, s := range ctxt.hasheddefs {
   135  		w.Hash(s)
   136  	}
   137  	// TODO: hashedrefs unused/unsupported for now
   138  
   139  	// Reloc indexes
   140  	h.Offsets[goobj.BlkRelocIdx] = w.Offset()
   141  	nreloc := uint32(0)
   142  	lists := [][]*LSym{ctxt.defs, ctxt.hashed64defs, ctxt.hasheddefs, ctxt.nonpkgdefs}
   143  	for _, list := range lists {
   144  		for _, s := range list {
   145  			w.Uint32(nreloc)
   146  			nreloc += uint32(len(s.R))
   147  		}
   148  	}
   149  	w.Uint32(nreloc)
   150  
   151  	// Symbol Info indexes
   152  	h.Offsets[goobj.BlkAuxIdx] = w.Offset()
   153  	naux := uint32(0)
   154  	for _, list := range lists {
   155  		for _, s := range list {
   156  			w.Uint32(naux)
   157  			naux += uint32(nAuxSym(s))
   158  		}
   159  	}
   160  	w.Uint32(naux)
   161  
   162  	// Data indexes
   163  	h.Offsets[goobj.BlkDataIdx] = w.Offset()
   164  	dataOff := int64(0)
   165  	for _, list := range lists {
   166  		for _, s := range list {
   167  			w.Uint32(uint32(dataOff))
   168  			dataOff += int64(len(s.P))
   169  			if file := s.File(); file != nil {
   170  				dataOff += file.Size
   171  			}
   172  		}
   173  	}
   174  	if int64(uint32(dataOff)) != dataOff {
   175  		log.Fatalf("data too large")
   176  	}
   177  	w.Uint32(uint32(dataOff))
   178  
   179  	// Relocs
   180  	h.Offsets[goobj.BlkReloc] = w.Offset()
   181  	for _, list := range lists {
   182  		for _, s := range list {
   183  			slices.SortFunc(s.R, relocByOffCmp) // some platforms (e.g. PE) requires relocations in address order
   184  			for i := range s.R {
   185  				w.Reloc(&s.R[i])
   186  			}
   187  		}
   188  	}
   189  
   190  	// Aux symbol info
   191  	h.Offsets[goobj.BlkAux] = w.Offset()
   192  	for _, list := range lists {
   193  		for _, s := range list {
   194  			w.Aux(s)
   195  		}
   196  	}
   197  
   198  	// Data
   199  	h.Offsets[goobj.BlkData] = w.Offset()
   200  	for _, list := range lists {
   201  		for _, s := range list {
   202  			w.Bytes(s.P)
   203  			if file := s.File(); file != nil {
   204  				w.writeFile(ctxt, file)
   205  			}
   206  		}
   207  	}
   208  
   209  	// Blocks used only by tools (objdump, nm).
   210  
   211  	// Referenced symbol names from other packages
   212  	h.Offsets[goobj.BlkRefName] = w.Offset()
   213  	w.refNames()
   214  
   215  	h.Offsets[goobj.BlkEnd] = w.Offset()
   216  
   217  	// Fix up block offsets in the header
   218  	end := start + int64(w.Offset())
   219  	b.MustSeek(start, 0)
   220  	h.Write(w.Writer)
   221  	b.MustSeek(end, 0)
   222  }
   223  
   224  type writer struct {
   225  	*goobj.Writer
   226  	filebuf []byte
   227  	ctxt    *Link
   228  	pkgpath string   // the package import path (escaped), "" if unknown
   229  	pkglist []string // list of packages referenced, indexed by ctxt.pkgIdx
   230  
   231  	// scratch space for writing (the Write methods escape
   232  	// as they are interface calls)
   233  	tmpSym      goobj.Sym
   234  	tmpReloc    goobj.Reloc
   235  	tmpAux      goobj.Aux
   236  	tmpHash64   goobj.Hash64Type
   237  	tmpHash     goobj.HashType
   238  	tmpRefFlags goobj.RefFlags
   239  	tmpRefName  goobj.RefName
   240  }
   241  
   242  // prepare package index list
   243  func (w *writer) init() {
   244  	w.pkglist = make([]string, len(w.ctxt.pkgIdx)+1)
   245  	w.pkglist[0] = "" // dummy invalid package for index 0
   246  	for pkg, i := range w.ctxt.pkgIdx {
   247  		w.pkglist[i] = pkg
   248  	}
   249  }
   250  
   251  func (w *writer) writeFile(ctxt *Link, file *FileInfo) {
   252  	f, err := os.Open(file.Name)
   253  	if err != nil {
   254  		ctxt.Diag("%v", err)
   255  		return
   256  	}
   257  	defer f.Close()
   258  	if w.filebuf == nil {
   259  		w.filebuf = make([]byte, 1024)
   260  	}
   261  	buf := w.filebuf
   262  	written := int64(0)
   263  	for {
   264  		n, err := f.Read(buf)
   265  		w.Bytes(buf[:n])
   266  		written += int64(n)
   267  		if err == io.EOF {
   268  			break
   269  		}
   270  		if err != nil {
   271  			ctxt.Diag("%v", err)
   272  			return
   273  		}
   274  	}
   275  	if written != file.Size {
   276  		ctxt.Diag("copy %s: unexpected length %d != %d", file.Name, written, file.Size)
   277  	}
   278  }
   279  
   280  var inlHashLen = base64.StdEncoding.EncodedLen(8)
   281  
   282  // TrimInlineHash strips the content hash of inlined call stacks from a symbol name.
   283  func TrimInlineHash(name string) string {
   284  	// The inline hash is in the form of #NNNN#, where NNNN is
   285  	// the base64 encoding of a 8-byte hash value.
   286  	a, b, ok := strings.Cut(name, "#")
   287  	if !ok {
   288  		return name
   289  	}
   290  	if len(b) < inlHashLen+1 || b[inlHashLen] != '#' {
   291  		return name
   292  	}
   293  	return a + b[inlHashLen+1:]
   294  }
   295  
   296  func (w *writer) StringTable() {
   297  	w.AddString("")
   298  	for _, p := range w.ctxt.Imports {
   299  		w.AddString(p.Pkg)
   300  	}
   301  	for _, pkg := range w.pkglist {
   302  		w.AddString(pkg)
   303  	}
   304  	w.ctxt.traverseSyms(traverseAll, func(s *LSym) {
   305  		// Don't put names of builtins into the string table (to save
   306  		// space).
   307  		if s.PkgIdx == goobj.PkgIdxBuiltin {
   308  			return
   309  		}
   310  		// TODO: this includes references of indexed symbols from other packages,
   311  		// for which the linker doesn't need the name. Consider moving them to
   312  		// a separate block (for tools only).
   313  		if w.ctxt.Flag_noRefName && s.PkgIdx < goobj.PkgIdxSpecial {
   314  			// Don't include them if Flag_noRefName
   315  			return
   316  		}
   317  		if strings.HasPrefix(s.Name, `"".`) {
   318  			w.ctxt.Diag("unqualified symbol name: %v", s.Name)
   319  		}
   320  		w.AddString(TrimInlineHash(s.Name))
   321  	})
   322  
   323  	// All filenames are in the postable.
   324  	for _, f := range w.ctxt.PosTable.FileTable() {
   325  		w.AddString(filepath.ToSlash(f))
   326  	}
   327  }
   328  
   329  // MaxSymSize is the maximum data section size permitted by the linker
   330  // (see issue #9862).
   331  const MaxSymSize = int64(2e9) // 2 GB (or so; looks better in errors than 2^31)
   332  
   333  func (w *writer) Sym(s *LSym) {
   334  	name := s.Name
   335  	abi := uint16(s.ABI())
   336  	if s.Static() {
   337  		abi = goobj.SymABIstatic
   338  	}
   339  	flag := uint8(0)
   340  	if s.DuplicateOK() {
   341  		flag |= goobj.SymFlagDupok
   342  	}
   343  	if s.Local() {
   344  		flag |= goobj.SymFlagLocal
   345  	}
   346  	if s.MakeTypelink() {
   347  		flag |= goobj.SymFlagTypelink
   348  	}
   349  	if s.Leaf() {
   350  		flag |= goobj.SymFlagLeaf
   351  	}
   352  	if s.NoSplit() {
   353  		flag |= goobj.SymFlagNoSplit
   354  	}
   355  	if s.ReflectMethod() {
   356  		flag |= goobj.SymFlagReflectMethod
   357  	}
   358  	if strings.HasPrefix(s.Name, "type:") && s.Name[5] != '.' && s.Type == objabi.SRODATA {
   359  		flag |= goobj.SymFlagGoType
   360  	}
   361  	flag2 := uint8(0)
   362  	if s.UsedInIface() {
   363  		flag2 |= goobj.SymFlagUsedInIface
   364  	}
   365  	if strings.HasPrefix(s.Name, "go:itab.") && s.Type == objabi.SRODATA {
   366  		flag2 |= goobj.SymFlagItab
   367  	}
   368  	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) {
   369  		flag2 |= goobj.SymFlagDict
   370  	}
   371  	if s.IsPkgInit() {
   372  		flag2 |= goobj.SymFlagPkgInit
   373  	}
   374  	if s.IsLinkname() || name == "main.main" {
   375  		// The runtime linknames main.main.
   376  		flag2 |= goobj.SymFlagLinkname
   377  	}
   378  	if s.IsLinknameStd() {
   379  		flag2 |= goobj.SymFlagLinknameStd
   380  	}
   381  	if s.ABIWrapper() {
   382  		flag2 |= goobj.SymFlagABIWrapper
   383  	}
   384  	if s.Func() != nil && s.Func().WasmExport != nil {
   385  		flag2 |= goobj.SymFlagWasmExport
   386  	}
   387  	if strings.HasPrefix(name, "gofile..") {
   388  		name = filepath.ToSlash(name)
   389  	}
   390  	align := uint32(s.Align)
   391  	if s.ContentAddressable() && s.Size != 0 && align == 0 && s.Type != objabi.STEXT && s.Type != objabi.STEXTFIPS {
   392  		// The linker applies a default alignment for text symbols, so they are okay.
   393  		// TODO: Check that alignment is set for all symbols.
   394  		w.ctxt.Diag("%s: is content-addressable but alignment is not set (size is %d)", s.Name, s.Size)
   395  	}
   396  	if s.Size > MaxSymSize {
   397  		w.ctxt.Diag("%s: symbol too large (%d bytes > %d bytes)", s.Name, s.Size, MaxSymSize)
   398  	}
   399  	o := &w.tmpSym
   400  	o.SetName(TrimInlineHash(name), w.Writer)
   401  	o.SetABI(abi)
   402  	o.SetType(uint8(s.Type))
   403  	o.SetFlag(flag)
   404  	o.SetFlag2(flag2)
   405  	o.SetSiz(uint32(s.Size))
   406  	o.SetAlign(align)
   407  	o.Write(w.Writer)
   408  }
   409  
   410  func (w *writer) Hash64(s *LSym) {
   411  	if !s.ContentAddressable() || len(s.R) != 0 {
   412  		panic("Hash of non-content-addressable symbol")
   413  	}
   414  	w.tmpHash64 = contentHash64(s)
   415  	w.Bytes(w.tmpHash64[:])
   416  }
   417  
   418  func (w *writer) Hash(s *LSym) {
   419  	if !s.ContentAddressable() {
   420  		panic("Hash of non-content-addressable symbol")
   421  	}
   422  	w.tmpHash = w.contentHash(s)
   423  	w.Bytes(w.tmpHash[:])
   424  }
   425  
   426  // contentHashSection returns a mnemonic for s's section.
   427  // The goal is to prevent content-addressability from moving symbols between sections.
   428  // contentHashSection only distinguishes between sets of sections for which this matters.
   429  // Allowing flexibility increases the effectiveness of content-addressability.
   430  // But in some cases, such as doing addressing based on a base symbol,
   431  // we need to ensure that a symbol is always in a particular section.
   432  // Some of these conditions are duplicated in cmd/link/internal/ld.(*Link).symtab.
   433  // TODO: instead of duplicating them, have the compiler decide where symbols go.
   434  func contentHashSection(s *LSym) byte {
   435  	if s.Type == objabi.STEXT {
   436  		return 't'
   437  	}
   438  	if s.Type == objabi.STEXTFIPS {
   439  		return 'f'
   440  	}
   441  	if s.IsPcdata() {
   442  		return 'P'
   443  	}
   444  	name := s.Name
   445  	if strings.HasPrefix(name, "gcargs.") ||
   446  		strings.HasPrefix(name, "gclocals.") ||
   447  		strings.HasPrefix(name, "gclocals·") ||
   448  		strings.HasSuffix(name, ".opendefer") ||
   449  		strings.HasSuffix(name, ".arginfo0") ||
   450  		strings.HasSuffix(name, ".arginfo1") ||
   451  		strings.HasSuffix(name, ".argliveinfo") ||
   452  		strings.HasSuffix(name, ".wrapinfo") ||
   453  		strings.HasSuffix(name, ".args_stackmap") ||
   454  		strings.HasSuffix(name, ".stkobj") {
   455  		return 'F' // go:func.* or go:funcrel.*
   456  	}
   457  	if strings.HasPrefix(name, "type:") {
   458  		return 'T'
   459  	}
   460  	return 0
   461  }
   462  
   463  func contentHash64(s *LSym) goobj.Hash64Type {
   464  	if contentHashSection(s) != 0 {
   465  		panic("short hash of non-default-section sym " + s.Name)
   466  	}
   467  	var b goobj.Hash64Type
   468  	copy(b[:], s.P)
   469  	return b
   470  }
   471  
   472  // Compute the content hash for a content-addressable symbol.
   473  // We build a content hash based on its content and relocations.
   474  // Depending on the category of the referenced symbol, we choose
   475  // different hash algorithms such that the hash is globally
   476  // consistent.
   477  //   - For referenced content-addressable symbol, its content hash
   478  //     is globally consistent.
   479  //   - For package symbol and builtin symbol, its local index is
   480  //     globally consistent.
   481  //   - For non-package symbol, its fully-expanded name is globally
   482  //     consistent. For now, we require we know the current package
   483  //     path so we can always expand symbol names. (Otherwise,
   484  //     symbols with relocations are not considered hashable.)
   485  //
   486  // For now, we assume there is no circular dependencies among
   487  // hashed symbols.
   488  func (w *writer) contentHash(s *LSym) goobj.HashType {
   489  	h := hash.New32()
   490  	var tmp [14]byte
   491  
   492  	// Include the size of the symbol in the hash.
   493  	// This preserves the length of symbols, preventing the following two symbols
   494  	// from hashing the same:
   495  	//
   496  	//    [2]int{1,2} ≠ [10]int{1,2,0,0,0...}
   497  	//
   498  	// In this case, if the smaller symbol is alive, the larger is not kept unless
   499  	// needed.
   500  	binary.LittleEndian.PutUint64(tmp[:8], uint64(s.Size))
   501  	// Some symbols require being in separate sections.
   502  	tmp[8] = contentHashSection(s)
   503  	h.Write(tmp[:9])
   504  
   505  	if s.Type == objabi.STEXT || s.Type == objabi.STEXTFIPS {
   506  		// We don't want to combine irrelevant functions.
   507  		// Include the name to distinguish, without the temporary
   508  		// inline hash, so different inlined copies do get a chance
   509  		// to be combined.
   510  		io.WriteString(h, TrimInlineHash(s.Name))
   511  	}
   512  
   513  	// The compiler trims trailing zeros _sometimes_. We just do
   514  	// it always.
   515  	h.Write(bytes.TrimRight(s.P, "\x00"))
   516  	for i := range s.R {
   517  		r := &s.R[i]
   518  		binary.LittleEndian.PutUint32(tmp[:4], uint32(r.Off))
   519  		tmp[4] = r.Siz
   520  		tmp[5] = uint8(r.Type)
   521  		binary.LittleEndian.PutUint64(tmp[6:14], uint64(r.Add))
   522  		h.Write(tmp[:])
   523  		rs := r.Sym
   524  		if rs == nil {
   525  			// marker relocation
   526  			io.WriteString(h, "nil symbol")
   527  			continue
   528  		}
   529  		if rs == s {
   530  			io.WriteString(h, "self symbol")
   531  			continue
   532  			// TODO: mutual recursion?
   533  		}
   534  		switch rs.PkgIdx {
   535  		case goobj.PkgIdxHashed64:
   536  			h.Write([]byte{0})
   537  			t := contentHash64(rs)
   538  			h.Write(t[:])
   539  		case goobj.PkgIdxHashed:
   540  			h.Write([]byte{1})
   541  			t := w.contentHash(rs)
   542  			h.Write(t[:])
   543  		case goobj.PkgIdxNone:
   544  			h.Write([]byte{2})
   545  			io.WriteString(h, rs.Name) // name is already expanded at this point
   546  		case goobj.PkgIdxBuiltin:
   547  			h.Write([]byte{3})
   548  			binary.LittleEndian.PutUint32(tmp[:4], uint32(rs.SymIdx))
   549  			h.Write(tmp[:4])
   550  		case goobj.PkgIdxSelf:
   551  			io.WriteString(h, w.pkgpath)
   552  			binary.LittleEndian.PutUint32(tmp[:4], uint32(rs.SymIdx))
   553  			h.Write(tmp[:4])
   554  		default:
   555  			io.WriteString(h, rs.Pkg)
   556  			binary.LittleEndian.PutUint32(tmp[:4], uint32(rs.SymIdx))
   557  			h.Write(tmp[:4])
   558  		}
   559  	}
   560  	var b goobj.HashType
   561  	copy(b[:], h.Sum(nil))
   562  	return b
   563  }
   564  
   565  func makeSymRef(s *LSym) goobj.SymRef {
   566  	if s == nil {
   567  		return goobj.SymRef{}
   568  	}
   569  	if s.PkgIdx == 0 || !s.Indexed() {
   570  		fmt.Printf("unindexed symbol reference: %v\n", s)
   571  		panic("unindexed symbol reference")
   572  	}
   573  	return goobj.SymRef{PkgIdx: uint32(s.PkgIdx), SymIdx: uint32(s.SymIdx)}
   574  }
   575  
   576  func (w *writer) Reloc(r *Reloc) {
   577  	o := &w.tmpReloc
   578  	o.SetOff(r.Off)
   579  	o.SetSiz(r.Siz)
   580  	o.SetType(uint16(r.Type))
   581  	o.SetAdd(r.Add)
   582  	o.SetSym(makeSymRef(r.Sym))
   583  	o.Write(w.Writer)
   584  }
   585  
   586  func (w *writer) aux1(typ uint8, rs *LSym) {
   587  	o := &w.tmpAux
   588  	o.SetType(typ)
   589  	o.SetSym(makeSymRef(rs))
   590  	o.Write(w.Writer)
   591  }
   592  
   593  func (w *writer) Aux(s *LSym) {
   594  	if s.Gotype != nil {
   595  		w.aux1(goobj.AuxGotype, s.Gotype)
   596  	}
   597  	if fn := s.Func(); fn != nil {
   598  		w.aux1(goobj.AuxFuncInfo, fn.FuncInfoSym)
   599  
   600  		for _, d := range fn.Pcln.Funcdata {
   601  			w.aux1(goobj.AuxFuncdata, d)
   602  		}
   603  
   604  		if fn.dwarfInfoSym != nil && fn.dwarfInfoSym.Size != 0 {
   605  			w.aux1(goobj.AuxDwarfInfo, fn.dwarfInfoSym)
   606  		}
   607  		if fn.dwarfLocSym != nil && fn.dwarfLocSym.Size != 0 {
   608  			w.aux1(goobj.AuxDwarfLoc, fn.dwarfLocSym)
   609  		}
   610  		if fn.dwarfRangesSym != nil && fn.dwarfRangesSym.Size != 0 {
   611  			w.aux1(goobj.AuxDwarfRanges, fn.dwarfRangesSym)
   612  		}
   613  		if fn.dwarfDebugLinesSym != nil && fn.dwarfDebugLinesSym.Size != 0 {
   614  			w.aux1(goobj.AuxDwarfLines, fn.dwarfDebugLinesSym)
   615  		}
   616  		if fn.Pcln.Pcsp != nil && fn.Pcln.Pcsp.Size != 0 {
   617  			w.aux1(goobj.AuxPcsp, fn.Pcln.Pcsp)
   618  		}
   619  		if fn.Pcln.Pcfile != nil && fn.Pcln.Pcfile.Size != 0 {
   620  			w.aux1(goobj.AuxPcfile, fn.Pcln.Pcfile)
   621  		}
   622  		if fn.Pcln.Pcline != nil && fn.Pcln.Pcline.Size != 0 {
   623  			w.aux1(goobj.AuxPcline, fn.Pcln.Pcline)
   624  		}
   625  		if fn.Pcln.Pcinline != nil && fn.Pcln.Pcinline.Size != 0 {
   626  			w.aux1(goobj.AuxPcinline, fn.Pcln.Pcinline)
   627  		}
   628  		if fn.sehUnwindInfoSym != nil && fn.sehUnwindInfoSym.Size != 0 {
   629  			w.aux1(goobj.AuxSehUnwindInfo, fn.sehUnwindInfoSym)
   630  		}
   631  		for _, pcSym := range fn.Pcln.Pcdata {
   632  			w.aux1(goobj.AuxPcdata, pcSym)
   633  		}
   634  		if fn.WasmImport != nil {
   635  			if fn.WasmImport.AuxSym.Size == 0 {
   636  				panic("wasmimport aux sym must have non-zero size")
   637  			}
   638  			w.aux1(goobj.AuxWasmImport, fn.WasmImport.AuxSym)
   639  		}
   640  		if fn.WasmExport != nil {
   641  			w.aux1(goobj.AuxWasmType, fn.WasmExport.AuxSym)
   642  		}
   643  	} else if v := s.VarInfo(); v != nil {
   644  		if v.dwarfInfoSym != nil && v.dwarfInfoSym.Size != 0 {
   645  			w.aux1(goobj.AuxDwarfInfo, v.dwarfInfoSym)
   646  		}
   647  	}
   648  }
   649  
   650  // Emits flags of referenced indexed symbols.
   651  func (w *writer) refFlags() {
   652  	seen := make(map[*LSym]bool)
   653  	w.ctxt.traverseSyms(traverseRefs, func(rs *LSym) { // only traverse refs, not auxs, as tools don't need auxs
   654  		switch rs.PkgIdx {
   655  		case goobj.PkgIdxNone, goobj.PkgIdxHashed64, goobj.PkgIdxHashed, goobj.PkgIdxBuiltin, goobj.PkgIdxSelf: // not an external indexed reference
   656  			return
   657  		case goobj.PkgIdxInvalid:
   658  			panic("unindexed symbol reference")
   659  		}
   660  		if seen[rs] {
   661  			return
   662  		}
   663  		seen[rs] = true
   664  		symref := makeSymRef(rs)
   665  		flag2 := uint8(0)
   666  		if rs.UsedInIface() {
   667  			flag2 |= goobj.SymFlagUsedInIface
   668  		}
   669  		if flag2 == 0 {
   670  			return // no need to write zero flags
   671  		}
   672  		o := &w.tmpRefFlags
   673  		o.SetSym(symref)
   674  		o.SetFlag2(flag2)
   675  		o.Write(w.Writer)
   676  	})
   677  }
   678  
   679  // Emits names of referenced indexed symbols, used by tools (objdump, nm)
   680  // only.
   681  func (w *writer) refNames() {
   682  	if w.ctxt.Flag_noRefName {
   683  		return
   684  	}
   685  	seen := make(map[*LSym]bool)
   686  	w.ctxt.traverseSyms(traverseRefs, func(rs *LSym) { // only traverse refs, not auxs, as tools don't need auxs
   687  		switch rs.PkgIdx {
   688  		case goobj.PkgIdxNone, goobj.PkgIdxHashed64, goobj.PkgIdxHashed, goobj.PkgIdxBuiltin, goobj.PkgIdxSelf: // not an external indexed reference
   689  			return
   690  		case goobj.PkgIdxInvalid:
   691  			panic("unindexed symbol reference")
   692  		}
   693  		if seen[rs] {
   694  			return
   695  		}
   696  		seen[rs] = true
   697  		symref := makeSymRef(rs)
   698  		o := &w.tmpRefName
   699  		o.SetSym(symref)
   700  		o.SetName(rs.Name, w.Writer)
   701  		o.Write(w.Writer)
   702  	})
   703  	// TODO: output in sorted order?
   704  	// Currently tools (cmd/internal/goobj package) doesn't use mmap,
   705  	// and it just read it into a map in memory upfront. If it uses
   706  	// mmap, if the output is sorted, it probably could avoid reading
   707  	// into memory and just do lookups in the mmap'd object file.
   708  }
   709  
   710  // return the number of aux symbols s have.
   711  func nAuxSym(s *LSym) int {
   712  	n := 0
   713  	if s.Gotype != nil {
   714  		n++
   715  	}
   716  	if fn := s.Func(); fn != nil {
   717  		// FuncInfo is an aux symbol, each Funcdata is an aux symbol
   718  		n += 1 + len(fn.Pcln.Funcdata)
   719  		if fn.dwarfInfoSym != nil && fn.dwarfInfoSym.Size != 0 {
   720  			n++
   721  		}
   722  		if fn.dwarfLocSym != nil && fn.dwarfLocSym.Size != 0 {
   723  			n++
   724  		}
   725  		if fn.dwarfRangesSym != nil && fn.dwarfRangesSym.Size != 0 {
   726  			n++
   727  		}
   728  		if fn.dwarfDebugLinesSym != nil && fn.dwarfDebugLinesSym.Size != 0 {
   729  			n++
   730  		}
   731  		if fn.Pcln.Pcsp != nil && fn.Pcln.Pcsp.Size != 0 {
   732  			n++
   733  		}
   734  		if fn.Pcln.Pcfile != nil && fn.Pcln.Pcfile.Size != 0 {
   735  			n++
   736  		}
   737  		if fn.Pcln.Pcline != nil && fn.Pcln.Pcline.Size != 0 {
   738  			n++
   739  		}
   740  		if fn.Pcln.Pcinline != nil && fn.Pcln.Pcinline.Size != 0 {
   741  			n++
   742  		}
   743  		if fn.sehUnwindInfoSym != nil && fn.sehUnwindInfoSym.Size != 0 {
   744  			n++
   745  		}
   746  		n += len(fn.Pcln.Pcdata)
   747  		if fn.WasmImport != nil {
   748  			if fn.WasmImport.AuxSym == nil || fn.WasmImport.AuxSym.Size == 0 {
   749  				panic("wasmimport aux sym must exist and have non-zero size")
   750  			}
   751  			n++
   752  		}
   753  		if fn.WasmExport != nil {
   754  			n++
   755  		}
   756  	} else if v := s.VarInfo(); v != nil {
   757  		if v.dwarfInfoSym != nil && v.dwarfInfoSym.Size != 0 {
   758  			n++
   759  		}
   760  	}
   761  	return n
   762  }
   763  
   764  // generate symbols for FuncInfo.
   765  func genFuncInfoSyms(ctxt *Link) {
   766  	infosyms := make([]*LSym, 0, len(ctxt.Text))
   767  	var b bytes.Buffer
   768  	symidx := int32(len(ctxt.defs))
   769  	for _, s := range ctxt.Text {
   770  		fn := s.Func()
   771  		if fn == nil {
   772  			continue
   773  		}
   774  		o := goobj.FuncInfo{
   775  			Args:      uint32(fn.Args),
   776  			Locals:    uint32(fn.Locals),
   777  			FuncID:    fn.FuncID,
   778  			FuncFlag:  fn.FuncFlag,
   779  			StartLine: fn.StartLine,
   780  		}
   781  		pc := &fn.Pcln
   782  		i := 0
   783  		o.File = make([]goobj.CUFileIndex, len(pc.UsedFiles))
   784  		for f := range pc.UsedFiles {
   785  			o.File[i] = f
   786  			i++
   787  		}
   788  		sort.Slice(o.File, func(i, j int) bool { return o.File[i] < o.File[j] })
   789  		o.InlTree = make([]goobj.InlTreeNode, len(pc.InlTree.nodes))
   790  		for i, inl := range pc.InlTree.nodes {
   791  			f, l := ctxt.getFileIndexAndLine(inl.Pos)
   792  			o.InlTree[i] = goobj.InlTreeNode{
   793  				Parent:   int32(inl.Parent),
   794  				File:     goobj.CUFileIndex(f),
   795  				Line:     l,
   796  				Func:     makeSymRef(inl.Func),
   797  				ParentPC: inl.ParentPC,
   798  			}
   799  		}
   800  
   801  		o.Write(&b)
   802  		p := b.Bytes()
   803  		isym := &LSym{
   804  			Type:   objabi.SDATA, // for now, I don't think it matters
   805  			PkgIdx: goobj.PkgIdxSelf,
   806  			SymIdx: symidx,
   807  			P:      append([]byte(nil), p...),
   808  			Size:   int64(len(p)),
   809  		}
   810  		isym.Set(AttrIndexed, true)
   811  		symidx++
   812  		infosyms = append(infosyms, isym)
   813  		fn.FuncInfoSym = isym
   814  		b.Reset()
   815  
   816  		auxsyms := []*LSym{fn.dwarfRangesSym, fn.dwarfLocSym, fn.dwarfDebugLinesSym, fn.dwarfInfoSym}
   817  		if wi := fn.WasmImport; wi != nil {
   818  			auxsyms = append(auxsyms, wi.AuxSym)
   819  		}
   820  		if we := fn.WasmExport; we != nil {
   821  			auxsyms = append(auxsyms, we.AuxSym)
   822  		}
   823  		for _, s := range auxsyms {
   824  			if s == nil || s.Size == 0 {
   825  				continue
   826  			}
   827  			if s.OnList() {
   828  				panic("a symbol is added to defs multiple times")
   829  			}
   830  			s.PkgIdx = goobj.PkgIdxSelf
   831  			s.SymIdx = symidx
   832  			s.Set(AttrIndexed, true)
   833  			s.Set(AttrOnList, true)
   834  			symidx++
   835  			infosyms = append(infosyms, s)
   836  		}
   837  	}
   838  	ctxt.defs = append(ctxt.defs, infosyms...)
   839  }
   840  
   841  func writeAuxSymDebug(ctxt *Link, par *LSym, aux *LSym) {
   842  	// Most aux symbols (ex: funcdata) are not interesting--
   843  	// pick out just the DWARF ones for now.
   844  	switch aux.Type {
   845  	case objabi.SDWARFLOC,
   846  		objabi.SDWARFFCN,
   847  		objabi.SDWARFABSFCN,
   848  		objabi.SDWARFLINES,
   849  		objabi.SDWARFRANGE,
   850  		objabi.SDWARFVAR:
   851  	default:
   852  		return
   853  	}
   854  	ctxt.writeSymDebugNamed(aux, "aux for "+par.Name)
   855  }
   856  
   857  func debugAsmEmit(ctxt *Link) {
   858  	if ctxt.Debugasm > 0 {
   859  		ctxt.traverseSyms(traverseDefs, ctxt.writeSymDebug)
   860  		if ctxt.Debugasm > 1 {
   861  			fn := func(par *LSym, aux *LSym) {
   862  				writeAuxSymDebug(ctxt, par, aux)
   863  			}
   864  			ctxt.traverseAuxSyms(traverseAux, fn)
   865  		}
   866  	}
   867  }
   868  
   869  func (ctxt *Link) writeSymDebug(s *LSym) {
   870  	ctxt.writeSymDebugNamed(s, s.Name)
   871  }
   872  
   873  func (ctxt *Link) writeSymDebugNamed(s *LSym, name string) {
   874  	ver := ""
   875  	if ctxt.Debugasm > 1 {
   876  		ver = fmt.Sprintf("<%d>", s.ABI())
   877  		if ctxt.Debugasm > 2 {
   878  			ver += fmt.Sprintf("<idx %d %d>", s.PkgIdx, s.SymIdx)
   879  		}
   880  	}
   881  	fmt.Fprintf(ctxt.Bso, "%s%s ", name, ver)
   882  	if s.Type != 0 {
   883  		fmt.Fprintf(ctxt.Bso, "%v ", s.Type)
   884  	}
   885  	if s.Static() {
   886  		fmt.Fprint(ctxt.Bso, "static ")
   887  	}
   888  	if s.DuplicateOK() {
   889  		fmt.Fprintf(ctxt.Bso, "dupok ")
   890  	}
   891  	if s.CFunc() {
   892  		fmt.Fprintf(ctxt.Bso, "cfunc ")
   893  	}
   894  	if s.NoSplit() {
   895  		fmt.Fprintf(ctxt.Bso, "nosplit ")
   896  	}
   897  	if s.Func() != nil && s.Func().FuncFlag&abi.FuncFlagTopFrame != 0 {
   898  		fmt.Fprintf(ctxt.Bso, "topframe ")
   899  	}
   900  	if s.Func() != nil && s.Func().FuncFlag&abi.FuncFlagAsm != 0 {
   901  		fmt.Fprintf(ctxt.Bso, "asm ")
   902  	}
   903  	fmt.Fprintf(ctxt.Bso, "size=%d align=%#x", s.Size, s.Align)
   904  	if s.Type.IsText() {
   905  		fn := s.Func()
   906  		fmt.Fprintf(ctxt.Bso, " args=%#x locals=%#x funcid=%#x", uint64(fn.Args), uint64(fn.Locals), uint64(fn.FuncID))
   907  		if s.Leaf() {
   908  			fmt.Fprintf(ctxt.Bso, " leaf")
   909  		}
   910  	}
   911  	fmt.Fprintf(ctxt.Bso, "\n")
   912  	if s.Type.IsText() {
   913  		for p := s.Func().Text; p != nil; p = p.Link {
   914  			fmt.Fprintf(ctxt.Bso, "\t%#04x ", uint(int(p.Pc)))
   915  			if ctxt.Debugasm > 1 {
   916  				io.WriteString(ctxt.Bso, p.String())
   917  			} else {
   918  				p.InnermostString(ctxt.Bso)
   919  			}
   920  			fmt.Fprintln(ctxt.Bso)
   921  		}
   922  	}
   923  	for i := 0; i < len(s.P); i += 16 {
   924  		fmt.Fprintf(ctxt.Bso, "\t%#04x", uint(i))
   925  		j := i
   926  		for ; j < i+16 && j < len(s.P); j++ {
   927  			fmt.Fprintf(ctxt.Bso, " %02x", s.P[j])
   928  		}
   929  		for ; j < i+16; j++ {
   930  			fmt.Fprintf(ctxt.Bso, "   ")
   931  		}
   932  		fmt.Fprintf(ctxt.Bso, "  ")
   933  		for j = i; j < i+16 && j < len(s.P); j++ {
   934  			c := int(s.P[j])
   935  			b := byte('.')
   936  			if ' ' <= c && c <= 0x7e {
   937  				b = byte(c)
   938  			}
   939  			ctxt.Bso.WriteByte(b)
   940  		}
   941  
   942  		fmt.Fprintf(ctxt.Bso, "\n")
   943  	}
   944  
   945  	slices.SortFunc(s.R, relocByOffCmp) // generate stable output
   946  	for _, r := range s.R {
   947  		name := ""
   948  		ver := ""
   949  		if r.Sym != nil {
   950  			name = r.Sym.Name
   951  			if ctxt.Debugasm > 1 {
   952  				ver = fmt.Sprintf("<%d>", r.Sym.ABI())
   953  			}
   954  		} else if r.Type == objabi.R_TLS_LE {
   955  			name = "TLS"
   956  		}
   957  		if ctxt.Arch.InFamily(sys.ARM, sys.PPC64) {
   958  			fmt.Fprintf(ctxt.Bso, "\trel %d+%d t=%v %s%s+%x\n", int(r.Off), r.Siz, r.Type, name, ver, uint64(r.Add))
   959  		} else {
   960  			fmt.Fprintf(ctxt.Bso, "\trel %d+%d t=%v %s%s+%d\n", int(r.Off), r.Siz, r.Type, name, ver, r.Add)
   961  		}
   962  	}
   963  }
   964  
   965  // relocByOffCmp compare relocations by their offsets.
   966  func relocByOffCmp(x, y Reloc) int {
   967  	return cmp.Compare(x.Off, y.Off)
   968  }
   969  

View as plain text