cmd/go, cmd/vet: make vet work with gccgo
[official-gcc.git] / libgo / go / go / internal / gccgoimporter / importer.go
blobddaed405c18ca02eca79a302982e186233363d95
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.
5 // Package gccgoimporter implements Import for gccgo-generated object files.
6 package gccgoimporter // import "go/internal/gccgoimporter"
8 import (
9 "bytes"
10 "debug/elf"
11 "debug/xcoff"
12 "fmt"
13 "go/types"
14 "io"
15 "os"
16 "os/exec"
17 "path/filepath"
18 "runtime"
19 "strings"
22 // A PackageInit describes an imported package that needs initialization.
23 type PackageInit struct {
24 Name string // short package name
25 InitFunc string // name of init function
26 Priority int // priority of init function, see InitData.Priority
29 // The gccgo-specific init data for a package.
30 type InitData struct {
31 // Initialization priority of this package relative to other packages.
32 // This is based on the maximum depth of the package's dependency graph;
33 // it is guaranteed to be greater than that of its dependencies.
34 Priority int
36 // The list of packages which this package depends on to be initialized,
37 // including itself if needed. This is the subset of the transitive closure of
38 // the package's dependencies that need initialization.
39 Inits []PackageInit
42 // Locate the file from which to read export data.
43 // This is intended to replicate the logic in gofrontend.
44 func findExportFile(searchpaths []string, pkgpath string) (string, error) {
45 for _, spath := range searchpaths {
46 pkgfullpath := filepath.Join(spath, pkgpath)
47 pkgdir, name := filepath.Split(pkgfullpath)
49 for _, filepath := range [...]string{
50 pkgfullpath,
51 pkgfullpath + ".gox",
52 pkgdir + "lib" + name + ".so",
53 pkgdir + "lib" + name + ".a",
54 pkgfullpath + ".o",
55 } {
56 fi, err := os.Stat(filepath)
57 if err == nil && !fi.IsDir() {
58 return filepath, nil
63 return "", fmt.Errorf("%s: could not find export data (tried %s)", pkgpath, strings.Join(searchpaths, ":"))
66 const (
67 gccgov1Magic = "v1;\n"
68 gccgov2Magic = "v2;\n"
69 goimporterMagic = "\n$$ "
70 archiveMagic = "!<ar"
71 aixbigafMagic = "<big"
74 // Opens the export data file at the given path. If this is an object file,
75 // searches for and opens the .go_export section. If this is an archive,
76 // reads the export data from the first member, which is assumed to be an object file.
77 // This is intended to replicate the logic in gofrontend.
78 func openExportFile(fpath string) (reader io.ReadSeeker, closer io.Closer, err error) {
79 f, err := os.Open(fpath)
80 if err != nil {
81 return
83 closer = f
84 defer func() {
85 if err != nil && closer != nil {
86 f.Close()
88 }()
90 var magic [4]byte
91 _, err = f.ReadAt(magic[:], 0)
92 if err != nil {
93 return
96 var objreader io.ReaderAt
97 switch string(magic[:]) {
98 case gccgov1Magic, gccgov2Magic, goimporterMagic:
99 // Raw export data.
100 reader = f
101 return
103 case archiveMagic, aixbigafMagic:
104 // TODO(pcc): Read the archive directly instead of using "ar".
105 f.Close()
106 closer = nil
107 var cmd *exec.Cmd
109 if runtime.GOOS == "aix" && runtime.GOARCH == "ppc64" {
110 // AIX puts both 32-bit and 64-bit objects in the same archive.
111 // Tell the AIX "ar" command to only care about 64-bit objects.
112 cmd = exec.Command("ar", "-X64", "p", fpath)
113 } else {
114 cmd = exec.Command("ar", "p", fpath)
116 var out []byte
117 out, err = cmd.Output()
118 if err != nil {
119 return
122 objreader = bytes.NewReader(out)
124 default:
125 objreader = f
128 ef, err := elf.NewFile(objreader)
129 if err == nil {
130 sec := ef.Section(".go_export")
131 if sec == nil {
132 err = fmt.Errorf("%s: .go_export section not found", fpath)
133 return
135 reader = sec.Open()
136 return
139 xf, err := xcoff.NewFile(objreader)
140 if err == nil {
141 sdat := xf.CSect(".go_export")
142 if sdat == nil {
143 err = fmt.Errorf("%s: .go_export section not found", fpath)
144 return
146 reader = bytes.NewReader(sdat)
147 return
150 return
153 // An Importer resolves import paths to Packages. The imports map records
154 // packages already known, indexed by package path.
155 // An importer must determine the canonical package path and check imports
156 // to see if it is already present in the map. If so, the Importer can return
157 // the map entry. Otherwise, the importer must load the package data for the
158 // given path into a new *Package, record it in imports map, and return the
159 // package.
160 type Importer func(imports map[string]*types.Package, path, srcDir string, lookup func(string) (io.ReadCloser, error)) (*types.Package, error)
162 func GetImporter(searchpaths []string, initmap map[*types.Package]InitData) Importer {
163 return func(imports map[string]*types.Package, pkgpath, srcDir string, lookup func(string) (io.ReadCloser, error)) (pkg *types.Package, err error) {
164 // TODO(gri): Use srcDir.
165 // Or not. It's possible that srcDir will fade in importance as
166 // the go command and other tools provide a translation table
167 // for relative imports (like ./foo or vendored imports).
168 if pkgpath == "unsafe" {
169 return types.Unsafe, nil
172 var reader io.ReadSeeker
173 var fpath string
174 if lookup != nil {
175 if p := imports[pkgpath]; p != nil && p.Complete() {
176 return p, nil
178 rc, err := lookup(pkgpath)
179 if err == nil && rc != nil {
180 defer rc.Close()
181 rs, ok := rc.(io.ReadSeeker)
182 if !ok {
183 return nil, fmt.Errorf("gccgo importer requires lookup to return an io.ReadSeeker, have %T", rc)
185 reader = rs
186 fpath = "<lookup " + pkgpath + ">"
187 // Take name from Name method (like on os.File) if present.
188 if n, ok := rc.(interface{ Name() string }); ok {
189 fpath = n.Name()
193 if reader == nil {
194 fpath, err = findExportFile(searchpaths, pkgpath)
195 if err != nil {
196 return nil, err
199 r, closer, err := openExportFile(fpath)
200 if err != nil {
201 return nil, err
203 if closer != nil {
204 defer closer.Close()
206 reader = r
209 var magic [4]byte
210 _, err = reader.Read(magic[:])
211 if err != nil {
212 return
214 _, err = reader.Seek(0, io.SeekStart)
215 if err != nil {
216 return
219 switch string(magic[:]) {
220 case gccgov1Magic, gccgov2Magic:
221 var p parser
222 p.init(fpath, reader, imports)
223 pkg = p.parsePackage()
224 if initmap != nil {
225 initmap[pkg] = p.initdata
228 // Excluded for now: Standard gccgo doesn't support this import format currently.
229 // case goimporterMagic:
230 // var data []byte
231 // data, err = ioutil.ReadAll(reader)
232 // if err != nil {
233 // return
234 // }
235 // var n int
236 // n, pkg, err = importer.ImportData(imports, data)
237 // if err != nil {
238 // return
239 // }
241 // if initmap != nil {
242 // suffixreader := bytes.NewReader(data[n:])
243 // var p parser
244 // p.init(fpath, suffixreader, nil)
245 // p.parseInitData()
246 // initmap[pkg] = p.initdata
247 // }
249 default:
250 err = fmt.Errorf("unrecognized magic string: %q", string(magic[:]))
253 return