blob: 73c1cd3a2ce804312253d11df10b9237e3821ee6 [file] [log] [blame]
// Inferno utils/include/ar.h
// https://bitbucket.org/inferno-os/inferno-os/src/master/utils/include/ar.h
//
// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved.
// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net)
// Portions Copyright © 1997-1999 Vita Nuova Limited
// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com)
// Portions Copyright © 2004,2006 Bruce Ellis
// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net)
// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others
// Portions Copyright © 2009 The Go Authors. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
package ld
import (
"cmd/internal/bio"
"cmd/link/internal/loader"
"cmd/link/internal/sym"
"encoding/binary"
"fmt"
"internal/buildcfg"
"io"
"os"
"path/filepath"
"strings"
)
const (
SARMAG = 8
SAR_HDR = 16 + 44
)
const (
ARMAG = "!<arch>\n"
)
type ArHdr struct {
name string
date string
uid string
gid string
mode string
size string
fmag string
}
// pruneUndefsForWindows trims the list "undefs" of currently
// outstanding unresolved symbols to remove references to DLL import
// symbols (e.g. "__imp_XXX"). In older versions of the linker, we
// would just immediately forward references from the import sym
// (__imp_XXX) to the DLL sym (XXX), but with newer compilers this
// strategy falls down in certain cases. We instead now do this
// forwarding later on as a post-processing step, and meaning that
// during the middle part of host object loading we can see a lot of
// unresolved (SXREF) import symbols. We do not, however, want to
// trigger the inclusion of an object from a host archive if the
// reference is going to be eventually forwarded to the corresponding
// SDYNIMPORT symbol, so here we strip out such refs from the undefs
// list.
func pruneUndefsForWindows(ldr *loader.Loader, undefs, froms []loader.Sym) ([]loader.Sym, []loader.Sym) {
var newundefs []loader.Sym
var newfroms []loader.Sym
for _, s := range undefs {
sname := ldr.SymName(s)
if strings.HasPrefix(sname, "__imp_") {
dname := sname[len("__imp_"):]
ds := ldr.Lookup(dname, 0)
if ds != 0 && ldr.SymType(ds) == sym.SDYNIMPORT {
// Don't try to pull things out of a host archive to
// satisfy this symbol.
continue
}
}
newundefs = append(newundefs, s)
newfroms = append(newfroms, s)
}
return newundefs, newfroms
}
// hostArchive reads an archive file holding host objects and links in
// required objects. The general format is the same as a Go archive
// file, but it has an armap listing symbols and the objects that
// define them. This is used for the compiler support library
// libgcc.a.
func hostArchive(ctxt *Link, name string) {
if ctxt.Debugvlog > 1 {
ctxt.Logf("hostArchive(%s)\n", name)
}
f, err := bio.Open(name)
if err != nil {
if os.IsNotExist(err) {
// It's OK if we don't have a libgcc file at all.
if ctxt.Debugvlog != 0 {
ctxt.Logf("skipping libgcc file: %v\n", err)
}
return
}
Exitf("cannot open file %s: %v", name, err)
}
defer f.Close()
var magbuf [len(ARMAG)]byte
if _, err := io.ReadFull(f, magbuf[:]); err != nil {
Exitf("file %s too short", name)
}
if string(magbuf[:]) != ARMAG {
Exitf("%s is not an archive file", name)
}
var arhdr ArHdr
l := nextar(f, f.Offset(), &arhdr)
if l <= 0 {
Exitf("%s missing armap", name)
}
var armap archiveMap
if arhdr.name == "/" || arhdr.name == "/SYM64/" {
armap = readArmap(name, f, arhdr)
} else {
Exitf("%s missing armap", name)
}
loaded := make(map[uint64]bool)
any := true
for any {
var load []uint64
returnAllUndefs := -1
undefs, froms := ctxt.loader.UndefinedRelocTargets(returnAllUndefs)
if buildcfg.GOOS == "windows" {
undefs, froms = pruneUndefsForWindows(ctxt.loader, undefs, froms)
}
for k, symIdx := range undefs {
sname := ctxt.loader.SymName(symIdx)
if off := armap[sname]; off != 0 && !loaded[off] {
load = append(load, off)
loaded[off] = true
if ctxt.Debugvlog > 1 {
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])
}
}
}
for _, off := range load {
l := nextar(f, int64(off), &arhdr)
if l <= 0 {
Exitf("%s missing archive entry at offset %d", name, off)
}
pname := fmt.Sprintf("%s(%s)", name, arhdr.name)
l = atolwhex(arhdr.size)
pkname := filepath.Base(name)
if i := strings.LastIndex(pkname, ".a"); i >= 0 {
pkname = pkname[:i]
}
libar := sym.Library{Pkg: pkname}
h := ldobj(ctxt, f, &libar, l, pname, name)
if h.ld == nil {
Errorf(nil, "%s unrecognized object file at offset %d", name, off)
continue
}
f.MustSeek(h.off, 0)
h.ld(ctxt, f, h.pkg, h.length, h.pn)
if *flagCaptureHostObjs != "" {
captureHostObj(h)
}
}
any = len(load) > 0
}
}
// archiveMap is an archive symbol map: a mapping from symbol name to
// offset within the archive file.
type archiveMap map[string]uint64
// readArmap reads the archive symbol map.
func readArmap(filename string, f *bio.Reader, arhdr ArHdr) archiveMap {
is64 := arhdr.name == "/SYM64/"
wordSize := 4
if is64 {
wordSize = 8
}
contents := make([]byte, atolwhex(arhdr.size))
if _, err := io.ReadFull(f, contents); err != nil {
Exitf("short read from %s", filename)
}
var c uint64
if is64 {
c = binary.BigEndian.Uint64(contents)
} else {
c = uint64(binary.BigEndian.Uint32(contents))
}
contents = contents[wordSize:]
ret := make(archiveMap)
names := contents[c*uint64(wordSize):]
for i := uint64(0); i < c; i++ {
n := 0
for names[n] != 0 {
n++
}
name := string(names[:n])
names = names[n+1:]
// For Mach-O and PE/386 files we strip a leading
// underscore from the symbol name.
if buildcfg.GOOS == "darwin" || buildcfg.GOOS == "ios" || (buildcfg.GOOS == "windows" && buildcfg.GOARCH == "386") {
if name[0] == '_' && len(name) > 1 {
name = name[1:]
}
}
var off uint64
if is64 {
off = binary.BigEndian.Uint64(contents)
} else {
off = uint64(binary.BigEndian.Uint32(contents))
}
contents = contents[wordSize:]
ret[name] = off
}
return ret
}