1// Derived from Inferno utils/6l/obj.c and utils/6l/span.c
2// https://bitbucket.org/inferno-os/inferno-os/src/master/utils/6l/obj.c
3// https://bitbucket.org/inferno-os/inferno-os/src/master/utils/6l/span.c
4//
5//	Copyright © 1994-1999 Lucent Technologies Inc.  All rights reserved.
6//	Portions Copyright © 1995-1997 C H Forsyth ([email protected])
7//	Portions Copyright © 1997-1999 Vita Nuova Limited
8//	Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com)
9//	Portions Copyright © 2004,2006 Bruce Ellis
10//	Portions Copyright © 2005-2007 C H Forsyth ([email protected])
11//	Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others
12//	Portions Copyright © 2009 The Go Authors. All rights reserved.
13//
14// Permission is hereby granted, free of charge, to any person obtaining a copy
15// of this software and associated documentation files (the "Software"), to deal
16// in the Software without restriction, including without limitation the rights
17// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
18// copies of the Software, and to permit persons to whom the Software is
19// furnished to do so, subject to the following conditions:
20//
21// The above copyright notice and this permission notice shall be included in
22// all copies or substantial portions of the Software.
23//
24// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
25// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
26// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
27// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
28// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
29// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
30// THE SOFTWARE.
31
32package ld
33
34import (
35	"log"
36	"os"
37	"path"
38	"path/filepath"
39	"strconv"
40	"strings"
41
42	"cmd/internal/goobj"
43	"cmd/link/internal/loader"
44	"cmd/link/internal/sym"
45)
46
47func (ctxt *Link) readImportCfg(file string) {
48	ctxt.PackageFile = make(map[string]string)
49	ctxt.PackageShlib = make(map[string]string)
50	data, err := os.ReadFile(file)
51	if err != nil {
52		log.Fatalf("-importcfg: %v", err)
53	}
54
55	for lineNum, line := range strings.Split(string(data), "\n") {
56		lineNum++ // 1-based
57		line = strings.TrimSpace(line)
58		if line == "" {
59			continue
60		}
61		if line == "" || strings.HasPrefix(line, "#") {
62			continue
63		}
64
65		verb, args, found := strings.Cut(line, " ")
66		if found {
67			args = strings.TrimSpace(args)
68		}
69		before, after, exist := strings.Cut(args, "=")
70		if !exist {
71			before = ""
72		}
73		switch verb {
74		default:
75			log.Fatalf("%s:%d: unknown directive %q", file, lineNum, verb)
76		case "packagefile":
77			if before == "" || after == "" {
78				log.Fatalf(`%s:%d: invalid packagefile: syntax is "packagefile path=filename"`, file, lineNum)
79			}
80			ctxt.PackageFile[before] = after
81		case "packageshlib":
82			if before == "" || after == "" {
83				log.Fatalf(`%s:%d: invalid packageshlib: syntax is "packageshlib path=filename"`, file, lineNum)
84			}
85			ctxt.PackageShlib[before] = after
86		case "modinfo":
87			s, err := strconv.Unquote(args)
88			if err != nil {
89				log.Fatalf("%s:%d: invalid modinfo: %v", file, lineNum, err)
90			}
91			addstrdata1(ctxt, "runtime.modinfo="+s)
92		}
93	}
94}
95
96func pkgname(ctxt *Link, lib string) string {
97	return path.Clean(lib)
98}
99
100func findlib(ctxt *Link, lib string) (string, bool) {
101	name := path.Clean(lib)
102
103	var pname string
104	isshlib := false
105
106	if ctxt.linkShared && ctxt.PackageShlib[name] != "" {
107		pname = ctxt.PackageShlib[name]
108		isshlib = true
109	} else if ctxt.PackageFile != nil {
110		pname = ctxt.PackageFile[name]
111		if pname == "" {
112			ctxt.Logf("cannot find package %s (using -importcfg)\n", name)
113			return "", false
114		}
115	} else {
116		pkg := pkgname(ctxt, lib)
117
118		// search -L "libdir" directories
119		for _, dir := range ctxt.Libdir {
120			if ctxt.linkShared {
121				pname = filepath.Join(dir, pkg+".shlibname")
122				if _, err := os.Stat(pname); err == nil {
123					isshlib = true
124					break
125				}
126			}
127			pname = filepath.Join(dir, name+".a")
128			if _, err := os.Stat(pname); err == nil {
129				break
130			}
131			pname = filepath.Join(dir, name+".o")
132			if _, err := os.Stat(pname); err == nil {
133				break
134			}
135		}
136		pname = filepath.Clean(pname)
137	}
138
139	return pname, isshlib
140}
141
142func addlib(ctxt *Link, src, obj, lib string, fingerprint goobj.FingerprintType) *sym.Library {
143	pkg := pkgname(ctxt, lib)
144
145	// already loaded?
146	if l := ctxt.LibraryByPkg[pkg]; l != nil && !l.Fingerprint.IsZero() {
147		// Normally, packages are loaded in dependency order, and if l != nil
148		// l is already loaded with the actual fingerprint. In shared build mode,
149		// however, packages may be added not in dependency order, and it is
150		// possible that l's fingerprint is not yet loaded -- exclude it in
151		// checking.
152		checkFingerprint(l, l.Fingerprint, src, fingerprint)
153		return l
154	}
155
156	pname, isshlib := findlib(ctxt, lib)
157
158	if ctxt.Debugvlog > 1 {
159		ctxt.Logf("addlib: %s %s pulls in %s isshlib %v\n", obj, src, pname, isshlib)
160	}
161
162	if isshlib {
163		return addlibpath(ctxt, src, obj, "", pkg, pname, fingerprint)
164	}
165	return addlibpath(ctxt, src, obj, pname, pkg, "", fingerprint)
166}
167
168/*
169 * add library to library list, return added library.
170 *	srcref: src file referring to package
171 *	objref: object file referring to package
172 *	file: object file, e.g., /home/rsc/go/pkg/container/vector.a
173 *	pkg: package import path, e.g. container/vector
174 *	shlib: path to shared library, or .shlibname file holding path
175 *	fingerprint: if not 0, expected fingerprint for import from srcref
176 *	             fingerprint is 0 if the library is not imported (e.g. main)
177 */
178func addlibpath(ctxt *Link, srcref, objref, file, pkg, shlib string, fingerprint goobj.FingerprintType) *sym.Library {
179	if l := ctxt.LibraryByPkg[pkg]; l != nil {
180		return l
181	}
182
183	if ctxt.Debugvlog > 1 {
184		ctxt.Logf("addlibpath: srcref: %s objref: %s file: %s pkg: %s shlib: %s fingerprint: %x\n", srcref, objref, file, pkg, shlib, fingerprint)
185	}
186
187	l := &sym.Library{}
188	ctxt.LibraryByPkg[pkg] = l
189	ctxt.Library = append(ctxt.Library, l)
190	l.Objref = objref
191	l.Srcref = srcref
192	l.File = file
193	l.Pkg = pkg
194	l.Fingerprint = fingerprint
195	if shlib != "" {
196		if strings.HasSuffix(shlib, ".shlibname") {
197			data, err := os.ReadFile(shlib)
198			if err != nil {
199				Errorf(nil, "cannot read %s: %v", shlib, err)
200			}
201			shlib = strings.TrimSpace(string(data))
202		}
203		l.Shlib = shlib
204	}
205	return l
206}
207
208func atolwhex(s string) int64 {
209	n, _ := strconv.ParseInt(s, 0, 64)
210	return n
211}
212
213// PrepareAddmoduledata returns a symbol builder that target-specific
214// code can use to build up the linker-generated go.link.addmoduledata
215// function, along with the sym for runtime.addmoduledata itself. If
216// this function is not needed (for example in cases where we're
217// linking a module that contains the runtime) the returned builder
218// will be nil.
219func PrepareAddmoduledata(ctxt *Link) (*loader.SymbolBuilder, loader.Sym) {
220	if !ctxt.DynlinkingGo() {
221		return nil, 0
222	}
223	amd := ctxt.loader.LookupOrCreateSym("runtime.addmoduledata", 0)
224	if ctxt.loader.SymType(amd) == sym.STEXT && ctxt.BuildMode != BuildModePlugin {
225		// we're linking a module containing the runtime -> no need for
226		// an init function
227		return nil, 0
228	}
229	ctxt.loader.SetAttrReachable(amd, true)
230
231	// Create a new init func text symbol. Caller will populate this
232	// sym with arch-specific content.
233	ifs := ctxt.loader.LookupOrCreateSym("go:link.addmoduledata", 0)
234	initfunc := ctxt.loader.MakeSymbolUpdater(ifs)
235	ctxt.loader.SetAttrReachable(ifs, true)
236	ctxt.loader.SetAttrLocal(ifs, true)
237	initfunc.SetType(sym.STEXT)
238
239	// Add the init func and/or addmoduledata to Textp.
240	if ctxt.BuildMode == BuildModePlugin {
241		ctxt.Textp = append(ctxt.Textp, amd)
242	}
243	ctxt.Textp = append(ctxt.Textp, initfunc.Sym())
244
245	// Create an init array entry
246	amdi := ctxt.loader.LookupOrCreateSym("go:link.addmoduledatainit", 0)
247	initarray_entry := ctxt.loader.MakeSymbolUpdater(amdi)
248	ctxt.loader.SetAttrReachable(amdi, true)
249	ctxt.loader.SetAttrLocal(amdi, true)
250	initarray_entry.SetType(sym.SINITARR)
251	initarray_entry.AddAddr(ctxt.Arch, ifs)
252
253	return initfunc, amd
254}
255