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