| // Copyright 2014 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. |
| |
| // Initial scan of packages making up a program. |
| |
| // TODO(rsc): Rename goobj.SymID.Version to StaticID to avoid confusion with the ELF meaning of version. |
| // TODO(rsc): Fix file format so that SBSS/SNOPTRBSS with data is listed as SDATA/SNOPTRDATA. |
| // TODO(rsc): Parallelize scan to overlap file i/o where possible. |
| |
| package main |
| |
| import ( |
| "cmd/internal/goobj" |
| "os" |
| "sort" |
| "strings" |
| ) |
| |
| // scan scans all packages making up the program, starting with package main defined in mainfile. |
| func (p *Prog) scan(mainfile string) { |
| p.initScan() |
| p.scanFile("main", mainfile) |
| if len(p.Missing) > 0 && !p.omitRuntime { |
| p.scanImport("runtime") |
| } |
| |
| var missing []string |
| for sym := range p.Missing { |
| if !p.isAuto(sym) { |
| missing = append(missing, sym.String()) |
| } |
| } |
| |
| if missing != nil { |
| sort.Strings(missing) |
| for _, sym := range missing { |
| p.errorf("undefined: %s", sym) |
| } |
| } |
| |
| // TODO(rsc): Walk import graph to diagnose cycles. |
| } |
| |
| // initScan initializes the Prog fields needed by scan. |
| func (p *Prog) initScan() { |
| p.Packages = make(map[string]*Package) |
| p.Syms = make(map[goobj.SymID]*Sym) |
| p.Missing = make(map[goobj.SymID]bool) |
| p.Missing[p.startSym] = true |
| } |
| |
| // scanFile reads file to learn about the package with the given import path. |
| func (p *Prog) scanFile(pkgpath string, file string) { |
| pkg := &Package{ |
| File: file, |
| } |
| p.Packages[pkgpath] = pkg |
| |
| f, err := os.Open(file) |
| if err != nil { |
| p.errorf("%v", err) |
| return |
| } |
| gp, err := goobj.Parse(f, pkgpath) |
| f.Close() |
| if err != nil { |
| p.errorf("reading %s: %v", file, err) |
| return |
| } |
| |
| // TODO(rsc): Change cmd/internal/goobj to record package name as gp.Name. |
| // TODO(rsc): If pkgpath == "main", check that gp.Name == "main". |
| |
| pkg.Package = gp |
| |
| for _, gs := range gp.Syms { |
| // TODO(rsc): Fix file format instead of this workaround. |
| if gs.Data.Size > 0 { |
| switch gs.Kind { |
| case goobj.SBSS: |
| gs.Kind = goobj.SDATA |
| case goobj.SNOPTRBSS: |
| gs.Kind = goobj.SNOPTRDATA |
| } |
| } |
| |
| if gs.Version != 0 { |
| gs.Version += p.MaxVersion |
| } |
| for i := range gs.Reloc { |
| r := &gs.Reloc[i] |
| if r.Sym.Version != 0 { |
| r.Sym.Version += p.MaxVersion |
| } |
| if p.Syms[r.Sym] == nil { |
| p.Missing[r.Sym] = true |
| } |
| } |
| if gs.Func != nil { |
| for i := range gs.Func.FuncData { |
| fdata := &gs.Func.FuncData[i] |
| if fdata.Sym.Name != "" { |
| if fdata.Sym.Version != 0 { |
| fdata.Sym.Version += p.MaxVersion |
| } |
| if p.Syms[fdata.Sym] == nil { |
| p.Missing[fdata.Sym] = true |
| } |
| } |
| } |
| } |
| if old := p.Syms[gs.SymID]; old != nil { |
| // Duplicate definition of symbol. Is it okay? |
| // TODO(rsc): Write test for this code. |
| switch { |
| // If both symbols are BSS (no data), take max of sizes |
| // but otherwise ignore second symbol. |
| case old.Data.Size == 0 && gs.Data.Size == 0: |
| if old.Size < gs.Size { |
| old.Size = gs.Size |
| } |
| continue |
| |
| // If one is in BSS and one is not, use the one that is not. |
| case old.Data.Size > 0 && gs.Data.Size == 0: |
| continue |
| case gs.Data.Size > 0 && old.Data.Size == 0: |
| break // install gs as new symbol below |
| |
| // If either is marked as DupOK, we can keep either one. |
| // Keep the one that we saw first. |
| case old.DupOK || gs.DupOK: |
| continue |
| |
| // Otherwise, there's an actual conflict: |
| default: |
| p.errorf("symbol %s defined in both %s and %s %v %v", gs.SymID, old.Package.File, file, old.Data, gs.Data) |
| continue |
| } |
| } |
| s := &Sym{ |
| Sym: gs, |
| Package: pkg, |
| } |
| p.addSym(s) |
| delete(p.Missing, gs.SymID) |
| |
| if s.Data.Size > int64(s.Size) { |
| p.errorf("%s: initialized data larger than symbol (%d > %d)", s, s.Data.Size, s.Size) |
| } |
| } |
| p.MaxVersion += pkg.MaxVersion |
| |
| for i, pkgpath := range pkg.Imports { |
| // TODO(rsc): Fix file format to drop .a from recorded import path. |
| pkgpath = strings.TrimSuffix(pkgpath, ".a") |
| pkg.Imports[i] = pkgpath |
| |
| p.scanImport(pkgpath) |
| } |
| } |
| |
| func (p *Prog) addSym(s *Sym) { |
| pkg := s.Package |
| if pkg == nil { |
| pkg = p.Packages[""] |
| if pkg == nil { |
| pkg = &Package{} |
| p.Packages[""] = pkg |
| } |
| s.Package = pkg |
| } |
| pkg.Syms = append(pkg.Syms, s) |
| p.Syms[s.SymID] = s |
| p.SymOrder = append(p.SymOrder, s) |
| } |
| |
| // scanImport finds the object file for the given import path and then scans it. |
| func (p *Prog) scanImport(pkgpath string) { |
| if p.Packages[pkgpath] != nil { |
| return // already loaded |
| } |
| |
| // TODO(rsc): Implement correct search to find file. |
| p.scanFile(pkgpath, p.pkgdir+"/"+pkgpath+".a") |
| } |