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// go-specific code shared across loaders (5l, 6l, 8l).
6
7package ld
8
9import (
10	"cmd/internal/bio"
11	"cmd/internal/obj"
12	"cmd/internal/objabi"
13	"cmd/internal/sys"
14	"cmd/link/internal/loader"
15	"cmd/link/internal/sym"
16	"debug/elf"
17	"encoding/json"
18	"fmt"
19	"io"
20	"os"
21	"sort"
22	"strconv"
23	"strings"
24)
25
26// go-specific code shared across loaders (5l, 6l, 8l).
27
28// TODO:
29//	generate debugging section in binary.
30//	once the dust settles, try to move some code to
31//		libmach, so that other linkers and ar can share.
32
33func ldpkg(ctxt *Link, f *bio.Reader, lib *sym.Library, length int64, filename string) {
34	if *flagG {
35		return
36	}
37
38	if int64(int(length)) != length {
39		fmt.Fprintf(os.Stderr, "%s: too much pkg data in %s\n", os.Args[0], filename)
40		return
41	}
42
43	bdata := make([]byte, length)
44	if _, err := io.ReadFull(f, bdata); err != nil {
45		fmt.Fprintf(os.Stderr, "%s: short pkg read %s\n", os.Args[0], filename)
46		return
47	}
48	data := string(bdata)
49
50	// process header lines
51	for data != "" {
52		var line string
53		line, data, _ = strings.Cut(data, "\n")
54		if line == "main" {
55			lib.Main = true
56		}
57		if line == "" {
58			break
59		}
60	}
61
62	// look for cgo section
63	p0 := strings.Index(data, "\n$$  // cgo")
64	var p1 int
65	if p0 >= 0 {
66		p0 += p1
67		i := strings.IndexByte(data[p0+1:], '\n')
68		if i < 0 {
69			fmt.Fprintf(os.Stderr, "%s: found $$ // cgo but no newline in %s\n", os.Args[0], filename)
70			return
71		}
72		p0 += 1 + i
73
74		p1 = strings.Index(data[p0:], "\n$$")
75		if p1 < 0 {
76			p1 = strings.Index(data[p0:], "\n!\n")
77		}
78		if p1 < 0 {
79			fmt.Fprintf(os.Stderr, "%s: cannot find end of // cgo section in %s\n", os.Args[0], filename)
80			return
81		}
82		p1 += p0
83		loadcgo(ctxt, filename, objabi.PathToPrefix(lib.Pkg), data[p0:p1])
84	}
85}
86
87func loadcgo(ctxt *Link, file string, pkg string, p string) {
88	var directives [][]string
89	if err := json.NewDecoder(strings.NewReader(p)).Decode(&directives); err != nil {
90		fmt.Fprintf(os.Stderr, "%s: %s: failed decoding cgo directives: %v\n", os.Args[0], file, err)
91		nerrors++
92		return
93	}
94
95	// Record the directives. We'll process them later after Symbols are created.
96	ctxt.cgodata = append(ctxt.cgodata, cgodata{file, pkg, directives})
97}
98
99// Set symbol attributes or flags based on cgo directives.
100// Any newly discovered HOSTOBJ syms are added to 'hostObjSyms'.
101func setCgoAttr(ctxt *Link, file string, pkg string, directives [][]string, hostObjSyms map[loader.Sym]struct{}) {
102	l := ctxt.loader
103	for _, f := range directives {
104		switch f[0] {
105		case "cgo_import_dynamic":
106			if len(f) < 2 || len(f) > 4 {
107				break
108			}
109
110			local := f[1]
111			remote := local
112			if len(f) > 2 {
113				remote = f[2]
114			}
115			lib := ""
116			if len(f) > 3 {
117				lib = f[3]
118			}
119
120			if *FlagD {
121				fmt.Fprintf(os.Stderr, "%s: %s: cannot use dynamic imports with -d flag\n", os.Args[0], file)
122				nerrors++
123				return
124			}
125
126			if local == "_" && remote == "_" {
127				// allow #pragma dynimport _ _ "foo.so"
128				// to force a link of foo.so.
129				havedynamic = 1
130
131				if ctxt.HeadType == objabi.Hdarwin {
132					machoadddynlib(lib, ctxt.LinkMode)
133				} else {
134					dynlib = append(dynlib, lib)
135				}
136				continue
137			}
138
139			q := ""
140			if before, after, found := strings.Cut(remote, "#"); found {
141				remote, q = before, after
142			}
143			s := l.LookupOrCreateSym(local, 0)
144			st := l.SymType(s)
145			if st == 0 || st == sym.SXREF || st == sym.SBSS || st == sym.SNOPTRBSS || st == sym.SHOSTOBJ {
146				l.SetSymDynimplib(s, lib)
147				l.SetSymExtname(s, remote)
148				l.SetSymDynimpvers(s, q)
149				if st != sym.SHOSTOBJ {
150					su := l.MakeSymbolUpdater(s)
151					su.SetType(sym.SDYNIMPORT)
152				} else {
153					hostObjSyms[s] = struct{}{}
154				}
155				havedynamic = 1
156				if lib != "" && ctxt.IsDarwin() {
157					machoadddynlib(lib, ctxt.LinkMode)
158				}
159			}
160
161			continue
162
163		case "cgo_import_static":
164			if len(f) != 2 {
165				break
166			}
167			local := f[1]
168
169			s := l.LookupOrCreateSym(local, 0)
170			su := l.MakeSymbolUpdater(s)
171			su.SetType(sym.SHOSTOBJ)
172			su.SetSize(0)
173			hostObjSyms[s] = struct{}{}
174			continue
175
176		case "cgo_export_static", "cgo_export_dynamic":
177			if len(f) < 2 || len(f) > 4 {
178				break
179			}
180			local := f[1]
181			remote := local
182			if len(f) > 2 {
183				remote = f[2]
184			}
185			// The compiler adds a fourth argument giving
186			// the definition ABI of function symbols.
187			abi := obj.ABI0
188			if len(f) > 3 {
189				var ok bool
190				abi, ok = obj.ParseABI(f[3])
191				if !ok {
192					fmt.Fprintf(os.Stderr, "%s: bad ABI in cgo_export directive %s\n", os.Args[0], f)
193					nerrors++
194					return
195				}
196			}
197
198			s := l.LookupOrCreateSym(local, sym.ABIToVersion(abi))
199
200			if l.SymType(s) == sym.SHOSTOBJ {
201				hostObjSyms[s] = struct{}{}
202			}
203
204			switch ctxt.BuildMode {
205			case BuildModeCShared, BuildModeCArchive, BuildModePlugin:
206				if s == l.Lookup("main", 0) {
207					continue
208				}
209			}
210
211			// export overrides import, for openbsd/cgo.
212			// see issue 4878.
213			if l.SymDynimplib(s) != "" {
214				l.SetSymDynimplib(s, "")
215				l.SetSymDynimpvers(s, "")
216				l.SetSymExtname(s, "")
217				var su *loader.SymbolBuilder
218				su = l.MakeSymbolUpdater(s)
219				su.SetType(0)
220			}
221
222			if !(l.AttrCgoExportStatic(s) || l.AttrCgoExportDynamic(s)) {
223				l.SetSymExtname(s, remote)
224			} else if l.SymExtname(s) != remote {
225				fmt.Fprintf(os.Stderr, "%s: conflicting cgo_export directives: %s as %s and %s\n", os.Args[0], l.SymName(s), l.SymExtname(s), remote)
226				nerrors++
227				return
228			}
229
230			// Mark exported symbols and also add them to
231			// the lists used for roots in the deadcode pass.
232			if f[0] == "cgo_export_static" {
233				if ctxt.LinkMode == LinkExternal && !l.AttrCgoExportStatic(s) {
234					// Static cgo exports appear
235					// in the exported symbol table.
236					ctxt.dynexp = append(ctxt.dynexp, s)
237				}
238				if ctxt.LinkMode == LinkInternal {
239					// For internal linking, we're
240					// responsible for resolving
241					// relocations from host objects.
242					// Record the right Go symbol
243					// version to use.
244					l.AddCgoExport(s)
245				}
246				l.SetAttrCgoExportStatic(s, true)
247			} else {
248				if ctxt.LinkMode == LinkInternal && !l.AttrCgoExportDynamic(s) {
249					// Dynamic cgo exports appear
250					// in the exported symbol table.
251					ctxt.dynexp = append(ctxt.dynexp, s)
252				}
253				l.SetAttrCgoExportDynamic(s, true)
254			}
255
256			continue
257
258		case "cgo_dynamic_linker":
259			if len(f) != 2 {
260				break
261			}
262
263			if *flagInterpreter == "" {
264				if interpreter != "" && interpreter != f[1] {
265					fmt.Fprintf(os.Stderr, "%s: conflict dynlinker: %s and %s\n", os.Args[0], interpreter, f[1])
266					nerrors++
267					return
268				}
269
270				interpreter = f[1]
271			}
272			continue
273
274		case "cgo_ldflag":
275			if len(f) != 2 {
276				break
277			}
278			ldflag = append(ldflag, f[1])
279			continue
280		}
281
282		fmt.Fprintf(os.Stderr, "%s: %s: invalid cgo directive: %q\n", os.Args[0], file, f)
283		nerrors++
284	}
285	return
286}
287
288// openbsdTrimLibVersion indicates whether a shared library is
289// versioned and if it is, returns the unversioned name. The
290// OpenBSD library naming scheme is lib<name>.so.<major>.<minor>
291func openbsdTrimLibVersion(lib string) (string, bool) {
292	parts := strings.Split(lib, ".")
293	if len(parts) != 4 {
294		return "", false
295	}
296	if parts[1] != "so" {
297		return "", false
298	}
299	if _, err := strconv.Atoi(parts[2]); err != nil {
300		return "", false
301	}
302	if _, err := strconv.Atoi(parts[3]); err != nil {
303		return "", false
304	}
305	return fmt.Sprintf("%s.%s", parts[0], parts[1]), true
306}
307
308// dedupLibrariesOpenBSD dedups a list of shared libraries, treating versioned
309// and unversioned libraries as equivalents. Versioned libraries are preferred
310// and retained over unversioned libraries. This avoids the situation where
311// the use of cgo results in a DT_NEEDED for a versioned library (for example,
312// libc.so.96.1), while a dynamic import specifies an unversioned library (for
313// example, libc.so) - this would otherwise result in two DT_NEEDED entries
314// for the same library, resulting in a failure when ld.so attempts to load
315// the Go binary.
316func dedupLibrariesOpenBSD(ctxt *Link, libs []string) []string {
317	libraries := make(map[string]string)
318	for _, lib := range libs {
319		if name, ok := openbsdTrimLibVersion(lib); ok {
320			// Record unversioned name as seen.
321			seenlib[name] = true
322			libraries[name] = lib
323		} else if _, ok := libraries[lib]; !ok {
324			libraries[lib] = lib
325		}
326	}
327
328	libs = nil
329	for _, lib := range libraries {
330		libs = append(libs, lib)
331	}
332	sort.Strings(libs)
333
334	return libs
335}
336
337func dedupLibraries(ctxt *Link, libs []string) []string {
338	if ctxt.Target.IsOpenbsd() {
339		return dedupLibrariesOpenBSD(ctxt, libs)
340	}
341	return libs
342}
343
344var seenlib = make(map[string]bool)
345
346func adddynlib(ctxt *Link, lib string) {
347	if seenlib[lib] || ctxt.LinkMode == LinkExternal {
348		return
349	}
350	seenlib[lib] = true
351
352	if ctxt.IsELF {
353		dsu := ctxt.loader.MakeSymbolUpdater(ctxt.DynStr)
354		if dsu.Size() == 0 {
355			dsu.Addstring("")
356		}
357		du := ctxt.loader.MakeSymbolUpdater(ctxt.Dynamic)
358		Elfwritedynent(ctxt.Arch, du, elf.DT_NEEDED, uint64(dsu.Addstring(lib)))
359	} else {
360		Errorf(nil, "adddynlib: unsupported binary format")
361	}
362}
363
364func Adddynsym(ldr *loader.Loader, target *Target, syms *ArchSyms, s loader.Sym) {
365	if ldr.SymDynid(s) >= 0 || target.LinkMode == LinkExternal {
366		return
367	}
368
369	if target.IsELF {
370		elfadddynsym(ldr, target, syms, s)
371	} else if target.HeadType == objabi.Hdarwin {
372		ldr.Errorf(s, "adddynsym: missed symbol (Extname=%s)", ldr.SymExtname(s))
373	} else if target.HeadType == objabi.Hwindows {
374		// already taken care of
375	} else {
376		ldr.Errorf(s, "adddynsym: unsupported binary format")
377	}
378}
379
380func fieldtrack(arch *sys.Arch, l *loader.Loader) {
381	var buf strings.Builder
382	for i := loader.Sym(1); i < loader.Sym(l.NSym()); i++ {
383		if name := l.SymName(i); strings.HasPrefix(name, "go:track.") {
384			if l.AttrReachable(i) {
385				l.SetAttrSpecial(i, true)
386				l.SetAttrNotInSymbolTable(i, true)
387				buf.WriteString(name[9:])
388				for p := l.Reachparent[i]; p != 0; p = l.Reachparent[p] {
389					buf.WriteString("\t")
390					buf.WriteString(l.SymName(p))
391				}
392				buf.WriteString("\n")
393			}
394		}
395	}
396	l.Reachparent = nil // we are done with it
397	if *flagFieldTrack == "" {
398		return
399	}
400	s := l.Lookup(*flagFieldTrack, 0)
401	if s == 0 || !l.AttrReachable(s) {
402		return
403	}
404	bld := l.MakeSymbolUpdater(s)
405	bld.SetType(sym.SDATA)
406	addstrdata(arch, l, *flagFieldTrack, buf.String())
407}
408
409func (ctxt *Link) addexport() {
410	// Track undefined external symbols during external link.
411	if ctxt.LinkMode == LinkExternal {
412		for _, s := range ctxt.Textp {
413			if ctxt.loader.AttrSpecial(s) || ctxt.loader.AttrSubSymbol(s) {
414				continue
415			}
416			relocs := ctxt.loader.Relocs(s)
417			for i := 0; i < relocs.Count(); i++ {
418				if rs := relocs.At(i).Sym(); rs != 0 {
419					if ctxt.loader.SymType(rs) == sym.Sxxx && !ctxt.loader.AttrLocal(rs) {
420						// sanity check
421						if len(ctxt.loader.Data(rs)) != 0 {
422							panic("expected no data on undef symbol")
423						}
424						su := ctxt.loader.MakeSymbolUpdater(rs)
425						su.SetType(sym.SUNDEFEXT)
426					}
427				}
428			}
429		}
430	}
431
432	// TODO(aix)
433	if ctxt.HeadType == objabi.Hdarwin || ctxt.HeadType == objabi.Haix {
434		return
435	}
436
437	// Add dynamic symbols.
438	for _, s := range ctxt.dynexp {
439		// Consistency check.
440		if !ctxt.loader.AttrReachable(s) {
441			panic("dynexp entry not reachable")
442		}
443
444		Adddynsym(ctxt.loader, &ctxt.Target, &ctxt.ArchSyms, s)
445	}
446
447	for _, lib := range dedupLibraries(ctxt, dynlib) {
448		adddynlib(ctxt, lib)
449	}
450}
451