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