1// Copyright 2017 The Bazel Authors. All rights reserved. 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15package main 16 17import ( 18 "go/build" 19 "io/ioutil" 20 "os" 21 "path/filepath" 22 "regexp" 23 "runtime" 24 "strconv" 25 "strings" 26) 27 28var ASM_DEFINES = []string{ 29 "-D", "GOOS_" + build.Default.GOOS, 30 "-D", "GOARCH_" + build.Default.GOARCH, 31 "-D", "GOOS_GOARCH_" + build.Default.GOOS + "_" + build.Default.GOARCH, 32} 33 34// buildSymabisFile generates a file from assembly files that is consumed 35// by the compiler. This is only needed in go1.12+ when there is at least one 36// .s file. If the symabis file is not needed, no file will be generated, 37// and "", nil will be returned. 38func buildSymabisFile(goenv *env, sFiles, hFiles []fileInfo, asmhdr string) (string, error) { 39 if len(sFiles) == 0 { 40 return "", nil 41 } 42 43 // Check version. The symabis file is only required and can only be built 44 // starting at go1.12. 45 version := runtime.Version() 46 if strings.HasPrefix(version, "go1.") { 47 minor := version[len("go1."):] 48 if i := strings.IndexByte(minor, '.'); i >= 0 { 49 minor = minor[:i] 50 } 51 n, err := strconv.Atoi(minor) 52 if err == nil && n <= 11 { 53 return "", nil 54 } 55 // Fall through if the version can't be parsed. It's probably a newer 56 // development version. 57 } 58 59 // Create an empty go_asm.h file. The compiler will write this later, but 60 // we need one to exist now. 61 asmhdrFile, err := os.Create(asmhdr) 62 if err != nil { 63 return "", err 64 } 65 if err := asmhdrFile.Close(); err != nil { 66 return "", err 67 } 68 asmhdrDir := filepath.Dir(asmhdr) 69 70 // Create a temporary output file. The caller is responsible for deleting it. 71 var symabisName string 72 symabisFile, err := ioutil.TempFile("", "symabis") 73 if err != nil { 74 return "", err 75 } 76 symabisName = symabisFile.Name() 77 symabisFile.Close() 78 79 // Run the assembler. 80 wd, err := os.Getwd() 81 if err != nil { 82 return symabisName, err 83 } 84 asmargs := goenv.goTool("asm") 85 asmargs = append(asmargs, "-trimpath", wd) 86 asmargs = append(asmargs, "-I", wd) 87 asmargs = append(asmargs, "-I", filepath.Join(os.Getenv("GOROOT"), "pkg", "include")) 88 asmargs = append(asmargs, "-I", asmhdrDir) 89 seenHdrDirs := map[string]bool{wd: true, asmhdrDir: true} 90 for _, hFile := range hFiles { 91 hdrDir := filepath.Dir(abs(hFile.filename)) 92 if !seenHdrDirs[hdrDir] { 93 asmargs = append(asmargs, "-I", hdrDir) 94 seenHdrDirs[hdrDir] = true 95 } 96 } 97 asmargs = append(asmargs, ASM_DEFINES...) 98 asmargs = append(asmargs, "-gensymabis", "-o", symabisName, "--") 99 for _, sFile := range sFiles { 100 asmargs = append(asmargs, sFile.filename) 101 } 102 103 err = goenv.runCommand(asmargs) 104 return symabisName, err 105} 106 107func asmFile(goenv *env, srcPath, packagePath string, asmFlags []string, outPath string) error { 108 args := goenv.goTool("asm") 109 args = append(args, asmFlags...) 110 // The package path has to be specified as of Go 1.19 or the resulting 111 // object will be unlinkable, but the -p flag is also only available 112 // since Go 1.19. 113 if packagePath != "" && isGo119OrHigher() { 114 args = append(args, "-p", packagePath) 115 } 116 args = append(args, ASM_DEFINES...) 117 args = append(args, "-trimpath", ".") 118 args = append(args, "-o", outPath) 119 args = append(args, "--", srcPath) 120 absArgs(args, []string{"-I", "-o", "-trimpath"}) 121 return goenv.runCommand(args) 122} 123 124var goMinorVersionRegexp = regexp.MustCompile(`^go1\.(\d+)`) 125 126func isGo119OrHigher() bool { 127 match := goMinorVersionRegexp.FindStringSubmatch(runtime.Version()) 128 if match == nil { 129 // Developer version or something with an unparseable version string, 130 // assume Go 1.19 or higher. 131 return true 132 } 133 minorVersion, err := strconv.Atoi(match[1]) 134 if err != nil { 135 return true 136 } 137 return minorVersion >= 19 138} 139