github.com/bgentry/go@v0.0.0-20150121062915-6cf5a733d54d/src/cmd/link/scan.go (about)

     1  // Copyright 2014 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  // Initial scan of packages making up a program.
     6  
     7  // TODO(rsc): Rename goobj.SymID.Version to StaticID to avoid confusion with the ELF meaning of version.
     8  // TODO(rsc): Fix file format so that SBSS/SNOPTRBSS with data is listed as SDATA/SNOPTRDATA.
     9  // TODO(rsc): Parallelize scan to overlap file i/o where possible.
    10  
    11  package main
    12  
    13  import (
    14  	"cmd/internal/goobj"
    15  	"os"
    16  	"sort"
    17  	"strings"
    18  )
    19  
    20  // scan scans all packages making up the program, starting with package main defined in mainfile.
    21  func (p *Prog) scan(mainfile string) {
    22  	p.initScan()
    23  	p.scanFile("main", mainfile)
    24  	if len(p.Missing) > 0 && !p.omitRuntime {
    25  		p.scanImport("runtime")
    26  	}
    27  
    28  	var missing []string
    29  	for sym := range p.Missing {
    30  		if !p.isAuto(sym) {
    31  			missing = append(missing, sym.String())
    32  		}
    33  	}
    34  
    35  	if missing != nil {
    36  		sort.Strings(missing)
    37  		for _, sym := range missing {
    38  			p.errorf("undefined: %s", sym)
    39  		}
    40  	}
    41  
    42  	// TODO(rsc): Walk import graph to diagnose cycles.
    43  }
    44  
    45  // initScan initializes the Prog fields needed by scan.
    46  func (p *Prog) initScan() {
    47  	p.Packages = make(map[string]*Package)
    48  	p.Syms = make(map[goobj.SymID]*Sym)
    49  	p.Missing = make(map[goobj.SymID]bool)
    50  	p.Missing[p.startSym] = true
    51  }
    52  
    53  // scanFile reads file to learn about the package with the given import path.
    54  func (p *Prog) scanFile(pkgpath string, file string) {
    55  	pkg := &Package{
    56  		File: file,
    57  	}
    58  	p.Packages[pkgpath] = pkg
    59  
    60  	f, err := os.Open(file)
    61  	if err != nil {
    62  		p.errorf("%v", err)
    63  		return
    64  	}
    65  	gp, err := goobj.Parse(f, pkgpath)
    66  	f.Close()
    67  	if err != nil {
    68  		p.errorf("reading %s: %v", file, err)
    69  		return
    70  	}
    71  
    72  	// TODO(rsc): Change cmd/internal/goobj to record package name as gp.Name.
    73  	// TODO(rsc): If pkgpath == "main", check that gp.Name == "main".
    74  
    75  	pkg.Package = gp
    76  
    77  	for _, gs := range gp.Syms {
    78  		// TODO(rsc): Fix file format instead of this workaround.
    79  		if gs.Data.Size > 0 {
    80  			switch gs.Kind {
    81  			case goobj.SBSS:
    82  				gs.Kind = goobj.SDATA
    83  			case goobj.SNOPTRBSS:
    84  				gs.Kind = goobj.SNOPTRDATA
    85  			}
    86  		}
    87  
    88  		if gs.Version != 0 {
    89  			gs.Version += p.MaxVersion
    90  		}
    91  		for i := range gs.Reloc {
    92  			r := &gs.Reloc[i]
    93  			if r.Sym.Version != 0 {
    94  				r.Sym.Version += p.MaxVersion
    95  			}
    96  			if p.Syms[r.Sym] == nil {
    97  				p.Missing[r.Sym] = true
    98  			}
    99  		}
   100  		if gs.Func != nil {
   101  			for i := range gs.Func.FuncData {
   102  				fdata := &gs.Func.FuncData[i]
   103  				if fdata.Sym.Name != "" {
   104  					if fdata.Sym.Version != 0 {
   105  						fdata.Sym.Version += p.MaxVersion
   106  					}
   107  					if p.Syms[fdata.Sym] == nil {
   108  						p.Missing[fdata.Sym] = true
   109  					}
   110  				}
   111  			}
   112  		}
   113  		if old := p.Syms[gs.SymID]; old != nil {
   114  			// Duplicate definition of symbol. Is it okay?
   115  			// TODO(rsc): Write test for this code.
   116  			switch {
   117  			// If both symbols are BSS (no data), take max of sizes
   118  			// but otherwise ignore second symbol.
   119  			case old.Data.Size == 0 && gs.Data.Size == 0:
   120  				if old.Size < gs.Size {
   121  					old.Size = gs.Size
   122  				}
   123  				continue
   124  
   125  			// If one is in BSS and one is not, use the one that is not.
   126  			case old.Data.Size > 0 && gs.Data.Size == 0:
   127  				continue
   128  			case gs.Data.Size > 0 && old.Data.Size == 0:
   129  				break // install gs as new symbol below
   130  
   131  			// If either is marked as DupOK, we can keep either one.
   132  			// Keep the one that we saw first.
   133  			case old.DupOK || gs.DupOK:
   134  				continue
   135  
   136  			// Otherwise, there's an actual conflict:
   137  			default:
   138  				p.errorf("symbol %s defined in both %s and %s %v %v", gs.SymID, old.Package.File, file, old.Data, gs.Data)
   139  				continue
   140  			}
   141  		}
   142  		s := &Sym{
   143  			Sym:     gs,
   144  			Package: pkg,
   145  		}
   146  		p.addSym(s)
   147  		delete(p.Missing, gs.SymID)
   148  
   149  		if s.Data.Size > int64(s.Size) {
   150  			p.errorf("%s: initialized data larger than symbol (%d > %d)", s, s.Data.Size, s.Size)
   151  		}
   152  	}
   153  	p.MaxVersion += pkg.MaxVersion
   154  
   155  	for i, pkgpath := range pkg.Imports {
   156  		// TODO(rsc): Fix file format to drop .a from recorded import path.
   157  		pkgpath = strings.TrimSuffix(pkgpath, ".a")
   158  		pkg.Imports[i] = pkgpath
   159  
   160  		p.scanImport(pkgpath)
   161  	}
   162  }
   163  
   164  func (p *Prog) addSym(s *Sym) {
   165  	pkg := s.Package
   166  	if pkg == nil {
   167  		pkg = p.Packages[""]
   168  		if pkg == nil {
   169  			pkg = &Package{}
   170  			p.Packages[""] = pkg
   171  		}
   172  		s.Package = pkg
   173  	}
   174  	pkg.Syms = append(pkg.Syms, s)
   175  	p.Syms[s.SymID] = s
   176  	p.SymOrder = append(p.SymOrder, s)
   177  }
   178  
   179  // scanImport finds the object file for the given import path and then scans it.
   180  func (p *Prog) scanImport(pkgpath string) {
   181  	if p.Packages[pkgpath] != nil {
   182  		return // already loaded
   183  	}
   184  
   185  	// TODO(rsc): Implement correct search to find file.
   186  	p.scanFile(pkgpath, p.pkgdir+"/"+pkgpath+".a")
   187  }