github.com/bir3/gocompiler@v0.9.2202/src/cmd/compile/internal/noder/import.go (about) 1 // Copyright 2009 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 noder 6 7 import ( 8 "errors" 9 "fmt" 10 "github.com/bir3/gocompiler/src/internal/buildcfg" 11 "github.com/bir3/gocompiler/src/internal/pkgbits" 12 "os" 13 pathpkg "path" 14 "runtime" 15 "strings" 16 "unicode" 17 "unicode/utf8" 18 19 "github.com/bir3/gocompiler/src/cmd/compile/internal/base" 20 "github.com/bir3/gocompiler/src/cmd/compile/internal/importer" 21 "github.com/bir3/gocompiler/src/cmd/compile/internal/ir" 22 "github.com/bir3/gocompiler/src/cmd/compile/internal/typecheck" 23 "github.com/bir3/gocompiler/src/cmd/compile/internal/types" 24 "github.com/bir3/gocompiler/src/cmd/compile/internal/types2" 25 "github.com/bir3/gocompiler/src/cmd/internal/archive" 26 "github.com/bir3/gocompiler/src/cmd/internal/bio" 27 "github.com/bir3/gocompiler/src/cmd/internal/goobj" 28 "github.com/bir3/gocompiler/src/cmd/internal/objabi" 29 ) 30 31 type gcimports struct { 32 ctxt *types2.Context 33 packages map[string]*types2.Package 34 } 35 36 func (m *gcimports) Import(path string) (*types2.Package, error) { 37 return m.ImportFrom(path, "" /* no vendoring */, 0) 38 } 39 40 func (m *gcimports) ImportFrom(path, srcDir string, mode types2.ImportMode) (*types2.Package, error) { 41 if mode != 0 { 42 panic("mode must be 0") 43 } 44 45 _, pkg, err := readImportFile(path, typecheck.Target, m.ctxt, m.packages) 46 return pkg, err 47 } 48 49 func isDriveLetter(b byte) bool { 50 return 'a' <= b && b <= 'z' || 'A' <= b && b <= 'Z' 51 } 52 53 // is this path a local name? begins with ./ or ../ or / 54 func islocalname(name string) bool { 55 return strings.HasPrefix(name, "/") || 56 runtime.GOOS == "windows" && len(name) >= 3 && isDriveLetter(name[0]) && name[1] == ':' && name[2] == '/' || 57 strings.HasPrefix(name, "./") || name == "." || 58 strings.HasPrefix(name, "../") || name == ".." 59 } 60 61 func openPackage(path string) (*os.File, error) { 62 if islocalname(path) { 63 if base.Flag.NoLocalImports { 64 return nil, errors.New("local imports disallowed") 65 } 66 67 if base.Flag.Cfg.PackageFile != nil { 68 return os.Open(base.Flag.Cfg.PackageFile[path]) 69 } 70 71 // try .a before .o. important for building libraries: 72 // if there is an array.o in the array.a library, 73 // want to find all of array.a, not just array.o. 74 if file, err := os.Open(fmt.Sprintf("%s.a", path)); err == nil { 75 return file, nil 76 } 77 if file, err := os.Open(fmt.Sprintf("%s.o", path)); err == nil { 78 return file, nil 79 } 80 return nil, errors.New("file not found") 81 } 82 83 // local imports should be canonicalized already. 84 // don't want to see "encoding/../encoding/base64" 85 // as different from "encoding/base64". 86 if q := pathpkg.Clean(path); q != path { 87 return nil, fmt.Errorf("non-canonical import path %q (should be %q)", path, q) 88 } 89 90 if base.Flag.Cfg.PackageFile != nil { 91 return os.Open(base.Flag.Cfg.PackageFile[path]) 92 } 93 94 for _, dir := range base.Flag.Cfg.ImportDirs { 95 if file, err := os.Open(fmt.Sprintf("%s/%s.a", dir, path)); err == nil { 96 return file, nil 97 } 98 if file, err := os.Open(fmt.Sprintf("%s/%s.o", dir, path)); err == nil { 99 return file, nil 100 } 101 } 102 103 if buildcfg.GOROOT != "" { 104 suffix := "" 105 if base.Flag.InstallSuffix != "" { 106 suffix = "_" + base.Flag.InstallSuffix 107 } else if base.Flag.Race { 108 suffix = "_race" 109 } else if base.Flag.MSan { 110 suffix = "_msan" 111 } else if base.Flag.ASan { 112 suffix = "_asan" 113 } 114 115 if file, err := os.Open(fmt.Sprintf("%s/pkg/%s_%s%s/%s.a", buildcfg.GOROOT, buildcfg.GOOS, buildcfg.GOARCH, suffix, path)); err == nil { 116 return file, nil 117 } 118 if file, err := os.Open(fmt.Sprintf("%s/pkg/%s_%s%s/%s.o", buildcfg.GOROOT, buildcfg.GOOS, buildcfg.GOARCH, suffix, path)); err == nil { 119 return file, nil 120 } 121 } 122 return nil, errors.New("file not found") 123 } 124 125 // resolveImportPath resolves an import path as it appears in a Go 126 // source file to the package's full path. 127 func resolveImportPath(path string) (string, error) { 128 // The package name main is no longer reserved, 129 // but we reserve the import path "main" to identify 130 // the main package, just as we reserve the import 131 // path "math" to identify the standard math package. 132 if path == "main" { 133 return "", errors.New("cannot import \"main\"") 134 } 135 136 if base.Ctxt.Pkgpath == "" { 137 panic("missing pkgpath") 138 } 139 if path == base.Ctxt.Pkgpath { 140 return "", fmt.Errorf("import %q while compiling that package (import cycle)", path) 141 } 142 143 if mapped, ok := base.Flag.Cfg.ImportMap[path]; ok { 144 path = mapped 145 } 146 147 if islocalname(path) { 148 if path[0] == '/' { 149 return "", errors.New("import path cannot be absolute path") 150 } 151 152 prefix := base.Flag.D 153 if prefix == "" { 154 // Questionable, but when -D isn't specified, historically we 155 // resolve local import paths relative to the directory the 156 // compiler's current directory, not the respective source 157 // file's directory. 158 prefix = base.Ctxt.Pathname 159 } 160 path = pathpkg.Join(prefix, path) 161 162 if err := checkImportPath(path, true); err != nil { 163 return "", err 164 } 165 } 166 167 return path, nil 168 } 169 170 // readImportFile reads the import file for the given package path and 171 // returns its types.Pkg representation. If packages is non-nil, the 172 // types2.Package representation is also returned. 173 func readImportFile(path string, target *ir.Package, env *types2.Context, packages map[string]*types2.Package) (pkg1 *types.Pkg, pkg2 *types2.Package, err error) { 174 path, err = resolveImportPath(path) 175 if err != nil { 176 return 177 } 178 179 if path == "unsafe" { 180 pkg1, pkg2 = types.UnsafePkg, types2.Unsafe 181 182 // TODO(mdempsky): Investigate if this actually matters. Why would 183 // the linker or runtime care whether a package imported unsafe? 184 if !pkg1.Direct { 185 pkg1.Direct = true 186 target.Imports = append(target.Imports, pkg1) 187 } 188 189 return 190 } 191 192 pkg1 = types.NewPkg(path, "") 193 if packages != nil { 194 pkg2 = packages[path] 195 assert(pkg1.Direct == (pkg2 != nil && pkg2.Complete())) 196 } 197 198 if pkg1.Direct { 199 return 200 } 201 pkg1.Direct = true 202 target.Imports = append(target.Imports, pkg1) 203 204 f, err := openPackage(path) 205 if err != nil { 206 return 207 } 208 defer f.Close() 209 210 r, end, err := findExportData(f) 211 if err != nil { 212 return 213 } 214 215 if base.Debug.Export != 0 { 216 fmt.Printf("importing %s (%s)\n", path, f.Name()) 217 } 218 219 c, err := r.ReadByte() 220 if err != nil { 221 return 222 } 223 224 pos := r.Offset() 225 226 // Map export data section into memory as a single large 227 // string. This reduces heap fragmentation and allows returning 228 // individual substrings very efficiently. 229 var data string 230 data, err = base.MapFile(r.File(), pos, end-pos) 231 if err != nil { 232 return 233 } 234 235 switch c { 236 case 'u': 237 // TODO(mdempsky): This seems a bit clunky. 238 data = strings.TrimSuffix(data, "\n$$\n") 239 240 pr := pkgbits.NewPkgDecoder(pkg1.Path, data) 241 242 // Read package descriptors for both types2 and compiler backend. 243 readPackage(newPkgReader(pr), pkg1, false) 244 pkg2 = importer.ReadPackage(env, packages, pr) 245 246 default: 247 // Indexed format is distinguished by an 'i' byte, 248 // whereas previous export formats started with 'c', 'd', or 'v'. 249 err = fmt.Errorf("unexpected package format byte: %v", c) 250 return 251 } 252 253 err = addFingerprint(path, f, end) 254 return 255 } 256 257 // findExportData returns a *bio.Reader positioned at the start of the 258 // binary export data section, and a file offset for where to stop 259 // reading. 260 func findExportData(f *os.File) (r *bio.Reader, end int64, err error) { 261 r = bio.NewReader(f) 262 263 // check object header 264 line, err := r.ReadString('\n') 265 if err != nil { 266 return 267 } 268 269 if line == "!<arch>\n" { // package archive 270 // package export block should be first 271 sz := int64(archive.ReadHeader(r.Reader, "__.PKGDEF")) 272 if sz <= 0 { 273 err = errors.New("not a package file") 274 return 275 } 276 end = r.Offset() + sz 277 line, err = r.ReadString('\n') 278 if err != nil { 279 return 280 } 281 } else { 282 // Not an archive; provide end of file instead. 283 // TODO(mdempsky): I don't think this happens anymore. 284 var fi os.FileInfo 285 fi, err = f.Stat() 286 if err != nil { 287 return 288 } 289 end = fi.Size() 290 } 291 292 if !strings.HasPrefix(line, "go object ") { 293 err = fmt.Errorf("not a go object file: %s", line) 294 return 295 } 296 if expect := objabi.HeaderString(); line != expect { 297 err = fmt.Errorf("object is [%s] expected [%s]", line, expect) 298 return 299 } 300 301 // process header lines 302 for !strings.HasPrefix(line, "$$") { 303 line, err = r.ReadString('\n') 304 if err != nil { 305 return 306 } 307 } 308 309 // Expect $$B\n to signal binary import format. 310 if line != "$$B\n" { 311 err = errors.New("old export format no longer supported (recompile library)") 312 return 313 } 314 315 return 316 } 317 318 // addFingerprint reads the linker fingerprint included at the end of 319 // the exportdata. 320 func addFingerprint(path string, f *os.File, end int64) error { 321 const eom = "\n$$\n" 322 var fingerprint goobj.FingerprintType 323 324 var buf [len(fingerprint) + len(eom)]byte 325 if _, err := f.ReadAt(buf[:], end-int64(len(buf))); err != nil { 326 return err 327 } 328 329 // Caller should have given us the end position of the export data, 330 // which should end with the "\n$$\n" marker. As a consistency check 331 // to make sure we're reading at the right offset, make sure we 332 // found the marker. 333 if s := string(buf[len(fingerprint):]); s != eom { 334 return fmt.Errorf("expected $$ marker, but found %q", s) 335 } 336 337 copy(fingerprint[:], buf[:]) 338 base.Ctxt.AddImport(path, fingerprint) 339 340 return nil 341 } 342 343 func checkImportPath(path string, allowSpace bool) error { 344 if path == "" { 345 return errors.New("import path is empty") 346 } 347 348 if strings.Contains(path, "\x00") { 349 return errors.New("import path contains NUL") 350 } 351 352 for ri := range base.ReservedImports { 353 if path == ri { 354 return fmt.Errorf("import path %q is reserved and cannot be used", path) 355 } 356 } 357 358 for _, r := range path { 359 switch { 360 case r == utf8.RuneError: 361 return fmt.Errorf("import path contains invalid UTF-8 sequence: %q", path) 362 case r < 0x20 || r == 0x7f: 363 return fmt.Errorf("import path contains control character: %q", path) 364 case r == '\\': 365 return fmt.Errorf("import path contains backslash; use slash: %q", path) 366 case !allowSpace && unicode.IsSpace(r): 367 return fmt.Errorf("import path contains space character: %q", path) 368 case strings.ContainsRune("!\"#$%&'()*,:;<=>?[]^`{|}", r): 369 return fmt.Errorf("import path contains invalid character '%c': %q", r, path) 370 } 371 } 372 373 return nil 374 }