1// Inferno utils/include/ar.h
2// https://bitbucket.org/inferno-os/inferno-os/src/master/utils/include/ar.h
3//
4//	Copyright © 1994-1999 Lucent Technologies Inc.  All rights reserved.
5//	Portions Copyright © 1995-1997 C H Forsyth ([email protected])
6//	Portions Copyright © 1997-1999 Vita Nuova Limited
7//	Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com)
8//	Portions Copyright © 2004,2006 Bruce Ellis
9//	Portions Copyright © 2005-2007 C H Forsyth ([email protected])
10//	Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others
11//	Portions Copyright © 2009 The Go Authors. All rights reserved.
12//
13// Permission is hereby granted, free of charge, to any person obtaining a copy
14// of this software and associated documentation files (the "Software"), to deal
15// in the Software without restriction, including without limitation the rights
16// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
17// copies of the Software, and to permit persons to whom the Software is
18// furnished to do so, subject to the following conditions:
19//
20// The above copyright notice and this permission notice shall be included in
21// all copies or substantial portions of the Software.
22//
23// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
24// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
26// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
28// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
29// THE SOFTWARE.
30
31package ld
32
33import (
34	"cmd/internal/bio"
35	"cmd/link/internal/loader"
36	"cmd/link/internal/sym"
37	"encoding/binary"
38	"fmt"
39	"internal/buildcfg"
40	"io"
41	"os"
42	"path/filepath"
43	"strings"
44)
45
46const (
47	SARMAG  = 8
48	SAR_HDR = 16 + 44
49)
50
51const (
52	ARMAG = "!<arch>\n"
53)
54
55type ArHdr struct {
56	name string
57	date string
58	uid  string
59	gid  string
60	mode string
61	size string
62	fmag string
63}
64
65// pruneUndefsForWindows trims the list "undefs" of currently
66// outstanding unresolved symbols to remove references to DLL import
67// symbols (e.g. "__imp_XXX"). In older versions of the linker, we
68// would just immediately forward references from the import sym
69// (__imp_XXX) to the DLL sym (XXX), but with newer compilers this
70// strategy falls down in certain cases. We instead now do this
71// forwarding later on as a post-processing step, and meaning that
72// during the middle part of host object loading we can see a lot of
73// unresolved (SXREF) import symbols. We do not, however, want to
74// trigger the inclusion of an object from a host archive if the
75// reference is going to be eventually forwarded to the corresponding
76// SDYNIMPORT symbol, so here we strip out such refs from the undefs
77// list.
78func pruneUndefsForWindows(ldr *loader.Loader, undefs, froms []loader.Sym) ([]loader.Sym, []loader.Sym) {
79	var newundefs []loader.Sym
80	var newfroms []loader.Sym
81	for _, s := range undefs {
82		sname := ldr.SymName(s)
83		if strings.HasPrefix(sname, "__imp_") {
84			dname := sname[len("__imp_"):]
85			ds := ldr.Lookup(dname, 0)
86			if ds != 0 && ldr.SymType(ds) == sym.SDYNIMPORT {
87				// Don't try to pull things out of a host archive to
88				// satisfy this symbol.
89				continue
90			}
91		}
92		newundefs = append(newundefs, s)
93		newfroms = append(newfroms, s)
94	}
95	return newundefs, newfroms
96}
97
98// hostArchive reads an archive file holding host objects and links in
99// required objects. The general format is the same as a Go archive
100// file, but it has an armap listing symbols and the objects that
101// define them. This is used for the compiler support library
102// libgcc.a.
103func hostArchive(ctxt *Link, name string) {
104	if ctxt.Debugvlog > 1 {
105		ctxt.Logf("hostArchive(%s)\n", name)
106	}
107	f, err := bio.Open(name)
108	if err != nil {
109		if os.IsNotExist(err) {
110			// It's OK if we don't have a libgcc file at all.
111			if ctxt.Debugvlog != 0 {
112				ctxt.Logf("skipping libgcc file: %v\n", err)
113			}
114			return
115		}
116		Exitf("cannot open file %s: %v", name, err)
117	}
118	defer f.Close()
119
120	var magbuf [len(ARMAG)]byte
121	if _, err := io.ReadFull(f, magbuf[:]); err != nil {
122		Exitf("file %s too short", name)
123	}
124
125	if string(magbuf[:]) != ARMAG {
126		Exitf("%s is not an archive file", name)
127	}
128
129	var arhdr ArHdr
130	l := nextar(f, f.Offset(), &arhdr)
131	if l <= 0 {
132		Exitf("%s missing armap", name)
133	}
134
135	var armap archiveMap
136	if arhdr.name == "/" || arhdr.name == "/SYM64/" {
137		armap = readArmap(name, f, arhdr)
138	} else {
139		Exitf("%s missing armap", name)
140	}
141
142	loaded := make(map[uint64]bool)
143	any := true
144	for any {
145		var load []uint64
146		returnAllUndefs := -1
147		undefs, froms := ctxt.loader.UndefinedRelocTargets(returnAllUndefs)
148		if buildcfg.GOOS == "windows" {
149			undefs, froms = pruneUndefsForWindows(ctxt.loader, undefs, froms)
150		}
151		for k, symIdx := range undefs {
152			sname := ctxt.loader.SymName(symIdx)
153			if off := armap[sname]; off != 0 && !loaded[off] {
154				load = append(load, off)
155				loaded[off] = true
156				if ctxt.Debugvlog > 1 {
157					ctxt.Logf("hostArchive(%s): selecting object at offset %x to resolve %s [%d] reference from %s [%d]\n", name, off, sname, symIdx, ctxt.loader.SymName(froms[k]), froms[k])
158				}
159			}
160		}
161
162		for _, off := range load {
163			l := nextar(f, int64(off), &arhdr)
164			if l <= 0 {
165				Exitf("%s missing archive entry at offset %d", name, off)
166			}
167			pname := fmt.Sprintf("%s(%s)", name, arhdr.name)
168			l = atolwhex(arhdr.size)
169
170			pkname := filepath.Base(name)
171			if i := strings.LastIndex(pkname, ".a"); i >= 0 {
172				pkname = pkname[:i]
173			}
174			libar := sym.Library{Pkg: pkname}
175			h := ldobj(ctxt, f, &libar, l, pname, name)
176			if h.ld == nil {
177				Errorf(nil, "%s unrecognized object file at offset %d", name, off)
178				continue
179			}
180			f.MustSeek(h.off, 0)
181			h.ld(ctxt, f, h.pkg, h.length, h.pn)
182			if *flagCaptureHostObjs != "" {
183				captureHostObj(h)
184			}
185		}
186
187		any = len(load) > 0
188	}
189}
190
191// archiveMap is an archive symbol map: a mapping from symbol name to
192// offset within the archive file.
193type archiveMap map[string]uint64
194
195// readArmap reads the archive symbol map.
196func readArmap(filename string, f *bio.Reader, arhdr ArHdr) archiveMap {
197	is64 := arhdr.name == "/SYM64/"
198	wordSize := 4
199	if is64 {
200		wordSize = 8
201	}
202
203	contents := make([]byte, atolwhex(arhdr.size))
204	if _, err := io.ReadFull(f, contents); err != nil {
205		Exitf("short read from %s", filename)
206	}
207
208	var c uint64
209	if is64 {
210		c = binary.BigEndian.Uint64(contents)
211	} else {
212		c = uint64(binary.BigEndian.Uint32(contents))
213	}
214	contents = contents[wordSize:]
215
216	ret := make(archiveMap)
217
218	names := contents[c*uint64(wordSize):]
219	for i := uint64(0); i < c; i++ {
220		n := 0
221		for names[n] != 0 {
222			n++
223		}
224		name := string(names[:n])
225		names = names[n+1:]
226
227		// For Mach-O and PE/386 files we strip a leading
228		// underscore from the symbol name.
229		if buildcfg.GOOS == "darwin" || buildcfg.GOOS == "ios" || (buildcfg.GOOS == "windows" && buildcfg.GOARCH == "386") {
230			if name[0] == '_' && len(name) > 1 {
231				name = name[1:]
232			}
233		}
234
235		var off uint64
236		if is64 {
237			off = binary.BigEndian.Uint64(contents)
238		} else {
239			off = uint64(binary.BigEndian.Uint32(contents))
240		}
241		contents = contents[wordSize:]
242
243		ret[name] = off
244	}
245
246	return ret
247}
248