1// Copyright 2018 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//go:generate go test . -run=^TestGenerated$ -fix
6
7package platform
8
9// An OSArch is a pair of GOOS and GOARCH values indicating a platform.
10type OSArch struct {
11	GOOS, GOARCH string
12}
13
14func (p OSArch) String() string {
15	return p.GOOS + "/" + p.GOARCH
16}
17
18// RaceDetectorSupported reports whether goos/goarch supports the race
19// detector. There is a copy of this function in cmd/dist/test.go.
20// Race detector only supports 48-bit VMA on arm64. But it will always
21// return true for arm64, because we don't have VMA size information during
22// the compile time.
23func RaceDetectorSupported(goos, goarch string) bool {
24	switch goos {
25	case "linux":
26		return goarch == "amd64" || goarch == "ppc64le" || goarch == "arm64" || goarch == "s390x"
27	case "darwin":
28		return goarch == "amd64" || goarch == "arm64"
29	case "freebsd", "netbsd", "windows":
30		return goarch == "amd64"
31	default:
32		return false
33	}
34}
35
36// MSanSupported reports whether goos/goarch supports the memory
37// sanitizer option.
38func MSanSupported(goos, goarch string) bool {
39	switch goos {
40	case "linux":
41		return goarch == "amd64" || goarch == "arm64" || goarch == "loong64"
42	case "freebsd":
43		return goarch == "amd64"
44	default:
45		return false
46	}
47}
48
49// ASanSupported reports whether goos/goarch supports the address
50// sanitizer option.
51func ASanSupported(goos, goarch string) bool {
52	switch goos {
53	case "linux":
54		return goarch == "arm64" || goarch == "amd64" || goarch == "loong64" || goarch == "riscv64" || goarch == "ppc64le"
55	default:
56		return false
57	}
58}
59
60// FuzzSupported reports whether goos/goarch supports fuzzing
61// ('go test -fuzz=.').
62func FuzzSupported(goos, goarch string) bool {
63	switch goos {
64	case "darwin", "freebsd", "linux", "windows":
65		return true
66	default:
67		return false
68	}
69}
70
71// FuzzInstrumented reports whether fuzzing on goos/goarch uses coverage
72// instrumentation. (FuzzInstrumented implies FuzzSupported.)
73func FuzzInstrumented(goos, goarch string) bool {
74	switch goarch {
75	case "amd64", "arm64":
76		// TODO(#14565): support more architectures.
77		return FuzzSupported(goos, goarch)
78	default:
79		return false
80	}
81}
82
83// MustLinkExternal reports whether goos/goarch requires external linking
84// with or without cgo dependencies.
85func MustLinkExternal(goos, goarch string, withCgo bool) bool {
86	if withCgo {
87		switch goarch {
88		case "loong64", "mips", "mipsle", "mips64", "mips64le":
89			// Internally linking cgo is incomplete on some architectures.
90			// https://go.dev/issue/14449
91			return true
92		case "arm64":
93			if goos == "windows" {
94				// windows/arm64 internal linking is not implemented.
95				return true
96			}
97		case "ppc64":
98			// Big Endian PPC64 cgo internal linking is not implemented for aix or linux.
99			// https://go.dev/issue/8912
100			if goos == "aix" || goos == "linux" {
101				return true
102			}
103		}
104
105		switch goos {
106		case "android":
107			return true
108		case "dragonfly":
109			// It seems that on Dragonfly thread local storage is
110			// set up by the dynamic linker, so internal cgo linking
111			// doesn't work. Test case is "go test runtime/cgo".
112			return true
113		}
114	}
115
116	switch goos {
117	case "android":
118		if goarch != "arm64" {
119			return true
120		}
121	case "ios":
122		if goarch == "arm64" {
123			return true
124		}
125	}
126	return false
127}
128
129// BuildModeSupported reports whether goos/goarch supports the given build mode
130// using the given compiler.
131// There is a copy of this function in cmd/dist/test.go.
132func BuildModeSupported(compiler, buildmode, goos, goarch string) bool {
133	if compiler == "gccgo" {
134		return true
135	}
136
137	if _, ok := distInfo[OSArch{goos, goarch}]; !ok {
138		return false // platform unrecognized
139	}
140
141	platform := goos + "/" + goarch
142	switch buildmode {
143	case "archive":
144		return true
145
146	case "c-archive":
147		switch goos {
148		case "aix", "darwin", "ios", "windows":
149			return true
150		case "linux":
151			switch goarch {
152			case "386", "amd64", "arm", "armbe", "arm64", "arm64be", "loong64", "ppc64le", "riscv64", "s390x":
153				// linux/ppc64 not supported because it does
154				// not support external linking mode yet.
155				return true
156			default:
157				// Other targets do not support -shared,
158				// per ParseFlags in
159				// cmd/compile/internal/base/flag.go.
160				// For c-archive the Go tool passes -shared,
161				// so that the result is suitable for inclusion
162				// in a PIE or shared library.
163				return false
164			}
165		case "freebsd":
166			return goarch == "amd64"
167		}
168		return false
169
170	case "c-shared":
171		switch platform {
172		case "linux/amd64", "linux/arm", "linux/arm64", "linux/loong64", "linux/386", "linux/ppc64le", "linux/riscv64", "linux/s390x",
173			"android/amd64", "android/arm", "android/arm64", "android/386",
174			"freebsd/amd64",
175			"darwin/amd64", "darwin/arm64",
176			"windows/amd64", "windows/386", "windows/arm64":
177			return true
178		}
179		return false
180
181	case "default":
182		return true
183
184	case "exe":
185		return true
186
187	case "pie":
188		switch platform {
189		case "linux/386", "linux/amd64", "linux/arm", "linux/arm64", "linux/loong64", "linux/ppc64le", "linux/riscv64", "linux/s390x",
190			"android/amd64", "android/arm", "android/arm64", "android/386",
191			"freebsd/amd64",
192			"darwin/amd64", "darwin/arm64",
193			"ios/amd64", "ios/arm64",
194			"aix/ppc64",
195			"openbsd/arm64",
196			"windows/386", "windows/amd64", "windows/arm", "windows/arm64":
197			return true
198		}
199		return false
200
201	case "shared":
202		switch platform {
203		case "linux/386", "linux/amd64", "linux/arm", "linux/arm64", "linux/ppc64le", "linux/s390x":
204			return true
205		}
206		return false
207
208	case "plugin":
209		switch platform {
210		case "linux/amd64", "linux/arm", "linux/arm64", "linux/386", "linux/loong64", "linux/s390x", "linux/ppc64le",
211			"android/amd64", "android/386",
212			"darwin/amd64", "darwin/arm64",
213			"freebsd/amd64":
214			return true
215		}
216		return false
217
218	default:
219		return false
220	}
221}
222
223func InternalLinkPIESupported(goos, goarch string) bool {
224	switch goos + "/" + goarch {
225	case "android/arm64",
226		"darwin/amd64", "darwin/arm64",
227		"linux/amd64", "linux/arm64", "linux/ppc64le",
228		"windows/386", "windows/amd64", "windows/arm", "windows/arm64":
229		return true
230	}
231	return false
232}
233
234// DefaultPIE reports whether goos/goarch produces a PIE binary when using the
235// "default" buildmode. On Windows this is affected by -race,
236// so force the caller to pass that in to centralize that choice.
237func DefaultPIE(goos, goarch string, isRace bool) bool {
238	switch goos {
239	case "android", "ios":
240		return true
241	case "windows":
242		if isRace {
243			// PIE is not supported with -race on windows;
244			// see https://go.dev/cl/416174.
245			return false
246		}
247		return true
248	case "darwin":
249		return true
250	}
251	return false
252}
253
254// ExecutableHasDWARF reports whether the linked executable includes DWARF
255// symbols on goos/goarch.
256func ExecutableHasDWARF(goos, goarch string) bool {
257	switch goos {
258	case "plan9", "ios":
259		return false
260	}
261	return true
262}
263
264// osArchInfo describes information about an OSArch extracted from cmd/dist and
265// stored in the generated distInfo map.
266type osArchInfo struct {
267	CgoSupported bool
268	FirstClass   bool
269	Broken       bool
270}
271
272// CgoSupported reports whether goos/goarch supports cgo.
273func CgoSupported(goos, goarch string) bool {
274	return distInfo[OSArch{goos, goarch}].CgoSupported
275}
276
277// FirstClass reports whether goos/goarch is considered a “first class” port.
278// (See https://go.dev/wiki/PortingPolicy#first-class-ports.)
279func FirstClass(goos, goarch string) bool {
280	return distInfo[OSArch{goos, goarch}].FirstClass
281}
282
283// Broken reportsr whether goos/goarch is considered a broken port.
284// (See https://go.dev/wiki/PortingPolicy#broken-ports.)
285func Broken(goos, goarch string) bool {
286	return distInfo[OSArch{goos, goarch}].Broken
287}
288