...

Source file src/github.com/twitchyliquid64/golang-asm/obj/sym.go

Documentation: github.com/twitchyliquid64/golang-asm/obj

     1  // Derived from Inferno utils/6l/obj.c and utils/6l/span.c
     2  // https://bitbucket.org/inferno-os/inferno-os/src/master/utils/6l/obj.c
     3  // https://bitbucket.org/inferno-os/inferno-os/src/master/utils/6l/span.c
     4  //
     5  //	Copyright © 1994-1999 Lucent Technologies Inc.  All rights reserved.
     6  //	Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net)
     7  //	Portions Copyright © 1997-1999 Vita Nuova Limited
     8  //	Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com)
     9  //	Portions Copyright © 2004,2006 Bruce Ellis
    10  //	Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net)
    11  //	Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others
    12  //	Portions Copyright © 2009 The Go Authors. All rights reserved.
    13  //
    14  // Permission is hereby granted, free of charge, to any person obtaining a copy
    15  // of this software and associated documentation files (the "Software"), to deal
    16  // in the Software without restriction, including without limitation the rights
    17  // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    18  // copies of the Software, and to permit persons to whom the Software is
    19  // furnished to do so, subject to the following conditions:
    20  //
    21  // The above copyright notice and this permission notice shall be included in
    22  // all copies or substantial portions of the Software.
    23  //
    24  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    25  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    26  // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
    27  // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    28  // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    29  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    30  // THE SOFTWARE.
    31  
    32  package obj
    33  
    34  import (
    35  	"github.com/twitchyliquid64/golang-asm/goobj"
    36  	"github.com/twitchyliquid64/golang-asm/objabi"
    37  	"fmt"
    38  	"log"
    39  	"math"
    40  	"sort"
    41  )
    42  
    43  func Linknew(arch *LinkArch) *Link {
    44  	ctxt := new(Link)
    45  	ctxt.hash = make(map[string]*LSym)
    46  	ctxt.funchash = make(map[string]*LSym)
    47  	ctxt.statichash = make(map[string]*LSym)
    48  	ctxt.Arch = arch
    49  	ctxt.Pathname = objabi.WorkingDir()
    50  
    51  	if err := ctxt.Headtype.Set(objabi.GOOS); err != nil {
    52  		log.Fatalf("unknown goos %s", objabi.GOOS)
    53  	}
    54  
    55  	ctxt.Flag_optimize = true
    56  	return ctxt
    57  }
    58  
    59  // LookupDerived looks up or creates the symbol with name name derived from symbol s.
    60  // The resulting symbol will be static iff s is.
    61  func (ctxt *Link) LookupDerived(s *LSym, name string) *LSym {
    62  	if s.Static() {
    63  		return ctxt.LookupStatic(name)
    64  	}
    65  	return ctxt.Lookup(name)
    66  }
    67  
    68  // LookupStatic looks up the static symbol with name name.
    69  // If it does not exist, it creates it.
    70  func (ctxt *Link) LookupStatic(name string) *LSym {
    71  	s := ctxt.statichash[name]
    72  	if s == nil {
    73  		s = &LSym{Name: name, Attribute: AttrStatic}
    74  		ctxt.statichash[name] = s
    75  	}
    76  	return s
    77  }
    78  
    79  // LookupABI looks up a symbol with the given ABI.
    80  // If it does not exist, it creates it.
    81  func (ctxt *Link) LookupABI(name string, abi ABI) *LSym {
    82  	return ctxt.LookupABIInit(name, abi, nil)
    83  }
    84  
    85  // LookupABI looks up a symbol with the given ABI.
    86  // If it does not exist, it creates it and
    87  // passes it to init for one-time initialization.
    88  func (ctxt *Link) LookupABIInit(name string, abi ABI, init func(s *LSym)) *LSym {
    89  	var hash map[string]*LSym
    90  	switch abi {
    91  	case ABI0:
    92  		hash = ctxt.hash
    93  	case ABIInternal:
    94  		hash = ctxt.funchash
    95  	default:
    96  		panic("unknown ABI")
    97  	}
    98  
    99  	ctxt.hashmu.Lock()
   100  	s := hash[name]
   101  	if s == nil {
   102  		s = &LSym{Name: name}
   103  		s.SetABI(abi)
   104  		hash[name] = s
   105  		if init != nil {
   106  			init(s)
   107  		}
   108  	}
   109  	ctxt.hashmu.Unlock()
   110  	return s
   111  }
   112  
   113  // Lookup looks up the symbol with name name.
   114  // If it does not exist, it creates it.
   115  func (ctxt *Link) Lookup(name string) *LSym {
   116  	return ctxt.LookupInit(name, nil)
   117  }
   118  
   119  // LookupInit looks up the symbol with name name.
   120  // If it does not exist, it creates it and
   121  // passes it to init for one-time initialization.
   122  func (ctxt *Link) LookupInit(name string, init func(s *LSym)) *LSym {
   123  	ctxt.hashmu.Lock()
   124  	s := ctxt.hash[name]
   125  	if s == nil {
   126  		s = &LSym{Name: name}
   127  		ctxt.hash[name] = s
   128  		if init != nil {
   129  			init(s)
   130  		}
   131  	}
   132  	ctxt.hashmu.Unlock()
   133  	return s
   134  }
   135  
   136  func (ctxt *Link) Float32Sym(f float32) *LSym {
   137  	i := math.Float32bits(f)
   138  	name := fmt.Sprintf("$f32.%08x", i)
   139  	return ctxt.LookupInit(name, func(s *LSym) {
   140  		s.Size = 4
   141  		s.WriteFloat32(ctxt, 0, f)
   142  		s.Type = objabi.SRODATA
   143  		s.Set(AttrLocal, true)
   144  		s.Set(AttrContentAddressable, true)
   145  		ctxt.constSyms = append(ctxt.constSyms, s)
   146  	})
   147  }
   148  
   149  func (ctxt *Link) Float64Sym(f float64) *LSym {
   150  	i := math.Float64bits(f)
   151  	name := fmt.Sprintf("$f64.%016x", i)
   152  	return ctxt.LookupInit(name, func(s *LSym) {
   153  		s.Size = 8
   154  		s.WriteFloat64(ctxt, 0, f)
   155  		s.Type = objabi.SRODATA
   156  		s.Set(AttrLocal, true)
   157  		s.Set(AttrContentAddressable, true)
   158  		ctxt.constSyms = append(ctxt.constSyms, s)
   159  	})
   160  }
   161  
   162  func (ctxt *Link) Int64Sym(i int64) *LSym {
   163  	name := fmt.Sprintf("$i64.%016x", uint64(i))
   164  	return ctxt.LookupInit(name, func(s *LSym) {
   165  		s.Size = 8
   166  		s.WriteInt(ctxt, 0, 8, i)
   167  		s.Type = objabi.SRODATA
   168  		s.Set(AttrLocal, true)
   169  		s.Set(AttrContentAddressable, true)
   170  		ctxt.constSyms = append(ctxt.constSyms, s)
   171  	})
   172  }
   173  
   174  // Assign index to symbols.
   175  // asm is set to true if this is called by the assembler (i.e. not the compiler),
   176  // in which case all the symbols are non-package (for now).
   177  func (ctxt *Link) NumberSyms() {
   178  	if ctxt.Headtype == objabi.Haix {
   179  		// Data must be sorted to keep a constant order in TOC symbols.
   180  		// As they are created during Progedit, two symbols can be switched between
   181  		// two different compilations. Therefore, BuildID will be different.
   182  		// TODO: find a better place and optimize to only sort TOC symbols
   183  		sort.Slice(ctxt.Data, func(i, j int) bool {
   184  			return ctxt.Data[i].Name < ctxt.Data[j].Name
   185  		})
   186  	}
   187  
   188  	// Constant symbols are created late in the concurrent phase. Sort them
   189  	// to ensure a deterministic order.
   190  	sort.Slice(ctxt.constSyms, func(i, j int) bool {
   191  		return ctxt.constSyms[i].Name < ctxt.constSyms[j].Name
   192  	})
   193  	ctxt.Data = append(ctxt.Data, ctxt.constSyms...)
   194  	ctxt.constSyms = nil
   195  
   196  	ctxt.pkgIdx = make(map[string]int32)
   197  	ctxt.defs = []*LSym{}
   198  	ctxt.hashed64defs = []*LSym{}
   199  	ctxt.hasheddefs = []*LSym{}
   200  	ctxt.nonpkgdefs = []*LSym{}
   201  
   202  	var idx, hashedidx, hashed64idx, nonpkgidx int32
   203  	ctxt.traverseSyms(traverseDefs, func(s *LSym) {
   204  		// if Pkgpath is unknown, cannot hash symbols with relocations, as it
   205  		// may reference named symbols whose names are not fully expanded.
   206  		if s.ContentAddressable() && (ctxt.Pkgpath != "" || len(s.R) == 0) {
   207  			if len(s.P) <= 8 && len(s.R) == 0 { // we can use short hash only for symbols without relocations
   208  				s.PkgIdx = goobj.PkgIdxHashed64
   209  				s.SymIdx = hashed64idx
   210  				if hashed64idx != int32(len(ctxt.hashed64defs)) {
   211  					panic("bad index")
   212  				}
   213  				ctxt.hashed64defs = append(ctxt.hashed64defs, s)
   214  				hashed64idx++
   215  			} else {
   216  				s.PkgIdx = goobj.PkgIdxHashed
   217  				s.SymIdx = hashedidx
   218  				if hashedidx != int32(len(ctxt.hasheddefs)) {
   219  					panic("bad index")
   220  				}
   221  				ctxt.hasheddefs = append(ctxt.hasheddefs, s)
   222  				hashedidx++
   223  			}
   224  		} else if isNonPkgSym(ctxt, s) {
   225  			s.PkgIdx = goobj.PkgIdxNone
   226  			s.SymIdx = nonpkgidx
   227  			if nonpkgidx != int32(len(ctxt.nonpkgdefs)) {
   228  				panic("bad index")
   229  			}
   230  			ctxt.nonpkgdefs = append(ctxt.nonpkgdefs, s)
   231  			nonpkgidx++
   232  		} else {
   233  			s.PkgIdx = goobj.PkgIdxSelf
   234  			s.SymIdx = idx
   235  			if idx != int32(len(ctxt.defs)) {
   236  				panic("bad index")
   237  			}
   238  			ctxt.defs = append(ctxt.defs, s)
   239  			idx++
   240  		}
   241  		s.Set(AttrIndexed, true)
   242  	})
   243  
   244  	ipkg := int32(1) // 0 is invalid index
   245  	nonpkgdef := nonpkgidx
   246  	ctxt.traverseSyms(traverseRefs|traverseAux, func(rs *LSym) {
   247  		if rs.PkgIdx != goobj.PkgIdxInvalid {
   248  			return
   249  		}
   250  		if !ctxt.Flag_linkshared {
   251  			// Assign special index for builtin symbols.
   252  			// Don't do it when linking against shared libraries, as the runtime
   253  			// may be in a different library.
   254  			if i := goobj.BuiltinIdx(rs.Name, int(rs.ABI())); i != -1 {
   255  				rs.PkgIdx = goobj.PkgIdxBuiltin
   256  				rs.SymIdx = int32(i)
   257  				rs.Set(AttrIndexed, true)
   258  				return
   259  			}
   260  		}
   261  		pkg := rs.Pkg
   262  		if rs.ContentAddressable() {
   263  			// for now, only support content-addressable symbols that are always locally defined.
   264  			panic("hashed refs unsupported for now")
   265  		}
   266  		if pkg == "" || pkg == "\"\"" || pkg == "_" || !rs.Indexed() {
   267  			rs.PkgIdx = goobj.PkgIdxNone
   268  			rs.SymIdx = nonpkgidx
   269  			rs.Set(AttrIndexed, true)
   270  			if nonpkgidx != nonpkgdef+int32(len(ctxt.nonpkgrefs)) {
   271  				panic("bad index")
   272  			}
   273  			ctxt.nonpkgrefs = append(ctxt.nonpkgrefs, rs)
   274  			nonpkgidx++
   275  			return
   276  		}
   277  		if k, ok := ctxt.pkgIdx[pkg]; ok {
   278  			rs.PkgIdx = k
   279  			return
   280  		}
   281  		rs.PkgIdx = ipkg
   282  		ctxt.pkgIdx[pkg] = ipkg
   283  		ipkg++
   284  	})
   285  }
   286  
   287  // Returns whether s is a non-package symbol, which needs to be referenced
   288  // by name instead of by index.
   289  func isNonPkgSym(ctxt *Link, s *LSym) bool {
   290  	if ctxt.IsAsm && !s.Static() {
   291  		// asm symbols are referenced by name only, except static symbols
   292  		// which are file-local and can be referenced by index.
   293  		return true
   294  	}
   295  	if ctxt.Flag_linkshared {
   296  		// The referenced symbol may be in a different shared library so
   297  		// the linker cannot see its index.
   298  		return true
   299  	}
   300  	if s.Pkg == "_" {
   301  		// The frontend uses package "_" to mark symbols that should not
   302  		// be referenced by index, e.g. linkname'd symbols.
   303  		return true
   304  	}
   305  	if s.DuplicateOK() {
   306  		// Dupok symbol needs to be dedup'd by name.
   307  		return true
   308  	}
   309  	return false
   310  }
   311  
   312  // StaticNamePref is the prefix the front end applies to static temporary
   313  // variables. When turned into LSyms, these can be tagged as static so
   314  // as to avoid inserting them into the linker's name lookup tables.
   315  const StaticNamePref = ".stmp_"
   316  
   317  type traverseFlag uint32
   318  
   319  const (
   320  	traverseDefs traverseFlag = 1 << iota
   321  	traverseRefs
   322  	traverseAux
   323  
   324  	traverseAll = traverseDefs | traverseRefs | traverseAux
   325  )
   326  
   327  // Traverse symbols based on flag, call fn for each symbol.
   328  func (ctxt *Link) traverseSyms(flag traverseFlag, fn func(*LSym)) {
   329  	lists := [][]*LSym{ctxt.Text, ctxt.Data, ctxt.ABIAliases}
   330  	for _, list := range lists {
   331  		for _, s := range list {
   332  			if flag&traverseDefs != 0 {
   333  				fn(s)
   334  			}
   335  			if flag&traverseRefs != 0 {
   336  				for _, r := range s.R {
   337  					if r.Sym != nil {
   338  						fn(r.Sym)
   339  					}
   340  				}
   341  			}
   342  			if flag&traverseAux != 0 {
   343  				if s.Gotype != nil {
   344  					fn(s.Gotype)
   345  				}
   346  				if s.Type == objabi.STEXT {
   347  					f := func(parent *LSym, aux *LSym) {
   348  						fn(aux)
   349  					}
   350  					ctxt.traverseFuncAux(flag, s, f)
   351  				}
   352  			}
   353  		}
   354  	}
   355  }
   356  
   357  func (ctxt *Link) traverseFuncAux(flag traverseFlag, fsym *LSym, fn func(parent *LSym, aux *LSym)) {
   358  	pc := &fsym.Func.Pcln
   359  	if flag&traverseAux == 0 {
   360  		// NB: should it become necessary to walk aux sym reloc references
   361  		// without walking the aux syms themselves, this can be changed.
   362  		panic("should not be here")
   363  	}
   364  	for _, d := range pc.Funcdata {
   365  		if d != nil {
   366  			fn(fsym, d)
   367  		}
   368  	}
   369  	files := ctxt.PosTable.FileTable()
   370  	usedFiles := make([]goobj.CUFileIndex, 0, len(pc.UsedFiles))
   371  	for f := range pc.UsedFiles {
   372  		usedFiles = append(usedFiles, f)
   373  	}
   374  	sort.Slice(usedFiles, func(i, j int) bool { return usedFiles[i] < usedFiles[j] })
   375  	for _, f := range usedFiles {
   376  		if filesym := ctxt.Lookup(files[f]); filesym != nil {
   377  			fn(fsym, filesym)
   378  		}
   379  	}
   380  	for _, call := range pc.InlTree.nodes {
   381  		if call.Func != nil {
   382  			fn(fsym, call.Func)
   383  		}
   384  		f, _ := linkgetlineFromPos(ctxt, call.Pos)
   385  		if filesym := ctxt.Lookup(f); filesym != nil {
   386  			fn(fsym, filesym)
   387  		}
   388  	}
   389  	dwsyms := []*LSym{fsym.Func.dwarfRangesSym, fsym.Func.dwarfLocSym, fsym.Func.dwarfDebugLinesSym, fsym.Func.dwarfInfoSym}
   390  	for _, dws := range dwsyms {
   391  		if dws == nil || dws.Size == 0 {
   392  			continue
   393  		}
   394  		fn(fsym, dws)
   395  		if flag&traverseRefs != 0 {
   396  			for _, r := range dws.R {
   397  				if r.Sym != nil {
   398  					fn(dws, r.Sym)
   399  				}
   400  			}
   401  		}
   402  	}
   403  }
   404  
   405  // Traverse aux symbols, calling fn for each sym/aux pair.
   406  func (ctxt *Link) traverseAuxSyms(flag traverseFlag, fn func(parent *LSym, aux *LSym)) {
   407  	lists := [][]*LSym{ctxt.Text, ctxt.Data, ctxt.ABIAliases}
   408  	for _, list := range lists {
   409  		for _, s := range list {
   410  			if s.Gotype != nil {
   411  				if flag&traverseDefs != 0 {
   412  					fn(s, s.Gotype)
   413  				}
   414  			}
   415  			if s.Type != objabi.STEXT {
   416  				continue
   417  			}
   418  			ctxt.traverseFuncAux(flag, s, fn)
   419  		}
   420  	}
   421  }
   422  

View as plain text