1// Copyright 2016 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 5package ld 6 7import ( 8 "fmt" 9 "internal/buildcfg" 10 "internal/platform" 11) 12 13// A BuildMode indicates the sort of object we are building. 14// 15// Possible build modes are the same as those for the -buildmode flag 16// in cmd/go, and are documented in 'go help buildmode'. 17type BuildMode uint8 18 19const ( 20 BuildModeUnset BuildMode = iota 21 BuildModeExe 22 BuildModePIE 23 BuildModeCArchive 24 BuildModeCShared 25 BuildModeShared 26 BuildModePlugin 27) 28 29// Set implements flag.Value to set the build mode based on the argument 30// to the -buildmode flag. 31func (mode *BuildMode) Set(s string) error { 32 switch s { 33 default: 34 return fmt.Errorf("invalid buildmode: %q", s) 35 case "exe": 36 switch buildcfg.GOOS + "/" + buildcfg.GOARCH { 37 case "darwin/arm64", "windows/arm", "windows/arm64": // On these platforms, everything is PIE 38 *mode = BuildModePIE 39 default: 40 *mode = BuildModeExe 41 } 42 case "pie": 43 *mode = BuildModePIE 44 case "c-archive": 45 *mode = BuildModeCArchive 46 case "c-shared": 47 *mode = BuildModeCShared 48 case "shared": 49 *mode = BuildModeShared 50 case "plugin": 51 *mode = BuildModePlugin 52 } 53 54 if !platform.BuildModeSupported("gc", s, buildcfg.GOOS, buildcfg.GOARCH) { 55 return fmt.Errorf("buildmode %s not supported on %s/%s", s, buildcfg.GOOS, buildcfg.GOARCH) 56 } 57 58 return nil 59} 60 61func (mode BuildMode) String() string { 62 switch mode { 63 case BuildModeUnset: 64 return "" // avoid showing a default in usage message 65 case BuildModeExe: 66 return "exe" 67 case BuildModePIE: 68 return "pie" 69 case BuildModeCArchive: 70 return "c-archive" 71 case BuildModeCShared: 72 return "c-shared" 73 case BuildModeShared: 74 return "shared" 75 case BuildModePlugin: 76 return "plugin" 77 } 78 return fmt.Sprintf("BuildMode(%d)", uint8(mode)) 79} 80 81// LinkMode indicates whether an external linker is used for the final link. 82type LinkMode uint8 83 84const ( 85 LinkAuto LinkMode = iota 86 LinkInternal 87 LinkExternal 88) 89 90func (mode *LinkMode) Set(s string) error { 91 switch s { 92 default: 93 return fmt.Errorf("invalid linkmode: %q", s) 94 case "auto": 95 *mode = LinkAuto 96 case "internal": 97 *mode = LinkInternal 98 case "external": 99 *mode = LinkExternal 100 } 101 return nil 102} 103 104func (mode *LinkMode) String() string { 105 switch *mode { 106 case LinkAuto: 107 return "auto" 108 case LinkInternal: 109 return "internal" 110 case LinkExternal: 111 return "external" 112 } 113 return fmt.Sprintf("LinkMode(%d)", uint8(*mode)) 114} 115 116// mustLinkExternal reports whether the program being linked requires 117// the external linker be used to complete the link. 118func mustLinkExternal(ctxt *Link) (res bool, reason string) { 119 if ctxt.Debugvlog > 1 { 120 defer func() { 121 if res { 122 ctxt.Logf("external linking is forced by: %s\n", reason) 123 } 124 }() 125 } 126 127 if platform.MustLinkExternal(buildcfg.GOOS, buildcfg.GOARCH, false) { 128 return true, fmt.Sprintf("%s/%s requires external linking", buildcfg.GOOS, buildcfg.GOARCH) 129 } 130 131 if *flagMsan { 132 return true, "msan" 133 } 134 135 if *flagAsan { 136 return true, "asan" 137 } 138 139 if iscgo && platform.MustLinkExternal(buildcfg.GOOS, buildcfg.GOARCH, true) { 140 return true, buildcfg.GOARCH + " does not support internal cgo" 141 } 142 143 // Some build modes require work the internal linker cannot do (yet). 144 switch ctxt.BuildMode { 145 case BuildModeCArchive: 146 return true, "buildmode=c-archive" 147 case BuildModeCShared: 148 return true, "buildmode=c-shared" 149 case BuildModePIE: 150 if !platform.InternalLinkPIESupported(buildcfg.GOOS, buildcfg.GOARCH) { 151 // Internal linking does not support TLS_IE. 152 return true, "buildmode=pie" 153 } 154 case BuildModePlugin: 155 return true, "buildmode=plugin" 156 case BuildModeShared: 157 return true, "buildmode=shared" 158 } 159 if ctxt.linkShared { 160 return true, "dynamically linking with a shared library" 161 } 162 163 if unknownObjFormat { 164 return true, "some input objects have an unrecognized file format" 165 } 166 167 if len(dynimportfail) > 0 { 168 // This error means that we were unable to generate 169 // the _cgo_import.go file for some packages. 170 // This typically means that there are some dependencies 171 // that the cgo tool could not figure out. 172 // See issue #52863. 173 return true, fmt.Sprintf("some packages could not be built to support internal linking (%v)", dynimportfail) 174 } 175 176 return false, "" 177} 178 179// determineLinkMode sets ctxt.LinkMode. 180// 181// It is called after flags are processed and inputs are processed, 182// so the ctxt.LinkMode variable has an initial value from the -linkmode 183// flag and the iscgo, externalobj, and unknownObjFormat variables are set. 184func determineLinkMode(ctxt *Link) { 185 extNeeded, extReason := mustLinkExternal(ctxt) 186 via := "" 187 188 if ctxt.LinkMode == LinkAuto { 189 // The environment variable GO_EXTLINK_ENABLED controls the 190 // default value of -linkmode. If it is not set when the 191 // linker is called we take the value it was set to when 192 // cmd/link was compiled. (See make.bash.) 193 switch buildcfg.Getgoextlinkenabled() { 194 case "0": 195 ctxt.LinkMode = LinkInternal 196 via = "via GO_EXTLINK_ENABLED " 197 case "1": 198 ctxt.LinkMode = LinkExternal 199 via = "via GO_EXTLINK_ENABLED " 200 default: 201 preferExternal := len(preferlinkext) != 0 202 if preferExternal && ctxt.Debugvlog > 0 { 203 ctxt.Logf("external linking prefer list is %v\n", preferlinkext) 204 } 205 if extNeeded || (iscgo && (externalobj || preferExternal)) { 206 ctxt.LinkMode = LinkExternal 207 } else { 208 ctxt.LinkMode = LinkInternal 209 } 210 } 211 } 212 213 switch ctxt.LinkMode { 214 case LinkInternal: 215 if extNeeded { 216 Exitf("internal linking requested %sbut external linking required: %s", via, extReason) 217 } 218 case LinkExternal: 219 switch { 220 case buildcfg.GOARCH == "ppc64" && buildcfg.GOOS == "linux": 221 Exitf("external linking not supported for %s/ppc64", buildcfg.GOOS) 222 } 223 } 224} 225