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 | // Except for this comment and the import path, this file is a verbatim copy of the file |
6 | // with the same name in $GOROOT/src/go/internal/gccgoimporter. |
7 | |
8 | // Package gccgoimporter implements Import for gccgo-generated object files. |
9 | package gccgoimporter // import "golang.org/x/tools/go/internal/gccgoimporter" |
10 | |
11 | import ( |
12 | "debug/elf" |
13 | "fmt" |
14 | "go/types" |
15 | "io" |
16 | "os" |
17 | "path/filepath" |
18 | "strings" |
19 | ) |
20 | |
21 | // A PackageInit describes an imported package that needs initialization. |
22 | type PackageInit struct { |
23 | Name string // short package name |
24 | InitFunc string // name of init function |
25 | Priority int // priority of init function, see InitData.Priority |
26 | } |
27 | |
28 | // The gccgo-specific init data for a package. |
29 | type InitData struct { |
30 | // Initialization priority of this package relative to other packages. |
31 | // This is based on the maximum depth of the package's dependency graph; |
32 | // it is guaranteed to be greater than that of its dependencies. |
33 | Priority int |
34 | |
35 | // The list of packages which this package depends on to be initialized, |
36 | // including itself if needed. This is the subset of the transitive closure of |
37 | // the package's dependencies that need initialization. |
38 | Inits []PackageInit |
39 | } |
40 | |
41 | // Locate the file from which to read export data. |
42 | // This is intended to replicate the logic in gofrontend. |
43 | func findExportFile(searchpaths []string, pkgpath string) (string, error) { |
44 | for _, spath := range searchpaths { |
45 | pkgfullpath := filepath.Join(spath, pkgpath) |
46 | pkgdir, name := filepath.Split(pkgfullpath) |
47 | |
48 | for _, filepath := range [...]string{ |
49 | pkgfullpath, |
50 | pkgfullpath + ".gox", |
51 | pkgdir + "lib" + name + ".so", |
52 | pkgdir + "lib" + name + ".a", |
53 | pkgfullpath + ".o", |
54 | } { |
55 | fi, err := os.Stat(filepath) |
56 | if err == nil && !fi.IsDir() { |
57 | return filepath, nil |
58 | } |
59 | } |
60 | } |
61 | |
62 | return "", fmt.Errorf("%s: could not find export data (tried %s)", pkgpath, strings.Join(searchpaths, ":")) |
63 | } |
64 | |
65 | const ( |
66 | gccgov1Magic = "v1;\n" |
67 | gccgov2Magic = "v2;\n" |
68 | gccgov3Magic = "v3;\n" |
69 | goimporterMagic = "\n$$ " |
70 | archiveMagic = "!<ar" |
71 | ) |
72 | |
73 | // Opens the export data file at the given path. If this is an ELF file, |
74 | // searches for and opens the .go_export section. If this is an archive, |
75 | // reads the export data from the first member, which is assumed to be an ELF file. |
76 | // This is intended to replicate the logic in gofrontend. |
77 | func openExportFile(fpath string) (reader io.ReadSeeker, closer io.Closer, err error) { |
78 | f, err := os.Open(fpath) |
79 | if err != nil { |
80 | return |
81 | } |
82 | closer = f |
83 | defer func() { |
84 | if err != nil && closer != nil { |
85 | f.Close() |
86 | } |
87 | }() |
88 | |
89 | var magic [4]byte |
90 | _, err = f.ReadAt(magic[:], 0) |
91 | if err != nil { |
92 | return |
93 | } |
94 | |
95 | var elfreader io.ReaderAt |
96 | switch string(magic[:]) { |
97 | case gccgov1Magic, gccgov2Magic, gccgov3Magic, goimporterMagic: |
98 | // Raw export data. |
99 | reader = f |
100 | return |
101 | |
102 | case archiveMagic: |
103 | reader, err = arExportData(f) |
104 | return |
105 | |
106 | default: |
107 | elfreader = f |
108 | } |
109 | |
110 | ef, err := elf.NewFile(elfreader) |
111 | if err != nil { |
112 | return |
113 | } |
114 | |
115 | sec := ef.Section(".go_export") |
116 | if sec == nil { |
117 | err = fmt.Errorf("%s: .go_export section not found", fpath) |
118 | return |
119 | } |
120 | |
121 | reader = sec.Open() |
122 | return |
123 | } |
124 | |
125 | // An Importer resolves import paths to Packages. The imports map records |
126 | // packages already known, indexed by package path. |
127 | // An importer must determine the canonical package path and check imports |
128 | // to see if it is already present in the map. If so, the Importer can return |
129 | // the map entry. Otherwise, the importer must load the package data for the |
130 | // given path into a new *Package, record it in imports map, and return the |
131 | // package. |
132 | type Importer func(imports map[string]*types.Package, path, srcDir string, lookup func(string) (io.ReadCloser, error)) (*types.Package, error) |
133 | |
134 | func GetImporter(searchpaths []string, initmap map[*types.Package]InitData) Importer { |
135 | return func(imports map[string]*types.Package, pkgpath, srcDir string, lookup func(string) (io.ReadCloser, error)) (pkg *types.Package, err error) { |
136 | // TODO(gri): Use srcDir. |
137 | // Or not. It's possible that srcDir will fade in importance as |
138 | // the go command and other tools provide a translation table |
139 | // for relative imports (like ./foo or vendored imports). |
140 | if pkgpath == "unsafe" { |
141 | return types.Unsafe, nil |
142 | } |
143 | |
144 | var reader io.ReadSeeker |
145 | var fpath string |
146 | var rc io.ReadCloser |
147 | if lookup != nil { |
148 | if p := imports[pkgpath]; p != nil && p.Complete() { |
149 | return p, nil |
150 | } |
151 | rc, err = lookup(pkgpath) |
152 | if err != nil { |
153 | return nil, err |
154 | } |
155 | } |
156 | if rc != nil { |
157 | defer rc.Close() |
158 | rs, ok := rc.(io.ReadSeeker) |
159 | if !ok { |
160 | return nil, fmt.Errorf("gccgo importer requires lookup to return an io.ReadSeeker, have %T", rc) |
161 | } |
162 | reader = rs |
163 | fpath = "<lookup " + pkgpath + ">" |
164 | // Take name from Name method (like on os.File) if present. |
165 | if n, ok := rc.(interface{ Name() string }); ok { |
166 | fpath = n.Name() |
167 | } |
168 | } else { |
169 | fpath, err = findExportFile(searchpaths, pkgpath) |
170 | if err != nil { |
171 | return nil, err |
172 | } |
173 | |
174 | r, closer, err := openExportFile(fpath) |
175 | if err != nil { |
176 | return nil, err |
177 | } |
178 | if closer != nil { |
179 | defer closer.Close() |
180 | } |
181 | reader = r |
182 | } |
183 | |
184 | var magics string |
185 | magics, err = readMagic(reader) |
186 | if err != nil { |
187 | return |
188 | } |
189 | |
190 | if magics == archiveMagic { |
191 | reader, err = arExportData(reader) |
192 | if err != nil { |
193 | return |
194 | } |
195 | magics, err = readMagic(reader) |
196 | if err != nil { |
197 | return |
198 | } |
199 | } |
200 | |
201 | switch magics { |
202 | case gccgov1Magic, gccgov2Magic, gccgov3Magic: |
203 | var p parser |
204 | p.init(fpath, reader, imports) |
205 | pkg = p.parsePackage() |
206 | if initmap != nil { |
207 | initmap[pkg] = p.initdata |
208 | } |
209 | |
210 | // Excluded for now: Standard gccgo doesn't support this import format currently. |
211 | // case goimporterMagic: |
212 | // var data []byte |
213 | // data, err = ioutil.ReadAll(reader) |
214 | // if err != nil { |
215 | // return |
216 | // } |
217 | // var n int |
218 | // n, pkg, err = importer.ImportData(imports, data) |
219 | // if err != nil { |
220 | // return |
221 | // } |
222 | |
223 | // if initmap != nil { |
224 | // suffixreader := bytes.NewReader(data[n:]) |
225 | // var p parser |
226 | // p.init(fpath, suffixreader, nil) |
227 | // p.parseInitData() |
228 | // initmap[pkg] = p.initdata |
229 | // } |
230 | |
231 | default: |
232 | err = fmt.Errorf("unrecognized magic string: %q", magics) |
233 | } |
234 | |
235 | return |
236 | } |
237 | } |
238 | |
239 | // readMagic reads the four bytes at the start of a ReadSeeker and |
240 | // returns them as a string. |
241 | func readMagic(reader io.ReadSeeker) (string, error) { |
242 | var magic [4]byte |
243 | if _, err := reader.Read(magic[:]); err != nil { |
244 | return "", err |
245 | } |
246 | if _, err := reader.Seek(0, io.SeekStart); err != nil { |
247 | return "", err |
248 | } |
249 | return string(magic[:]), nil |
250 | } |
251 |
Members