github.com/mdempsky/go@v0.0.0-20151201204031-5dd372bd1e70/src/cmd/link/internal/ld/ar.go (about)

     1  // Inferno utils/include/ar.h
     2  // http://code.google.com/p/inferno-os/source/browse/utils/include/ar.h
     3  //
     4  //	Copyright © 1994-1999 Lucent Technologies Inc.  All rights reserved.
     5  //	Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net)
     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 (forsyth@terzarima.net)
    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  
    31  package ld
    32  
    33  import (
    34  	"cmd/internal/obj"
    35  	"encoding/binary"
    36  	"fmt"
    37  	"os"
    38  )
    39  
    40  const (
    41  	SARMAG  = 8
    42  	SAR_HDR = 16 + 44
    43  )
    44  
    45  const (
    46  	ARMAG = "!<arch>\n"
    47  )
    48  
    49  type ArHdr struct {
    50  	name string
    51  	date string
    52  	uid  string
    53  	gid  string
    54  	mode string
    55  	size string
    56  	fmag string
    57  }
    58  
    59  // hostArchive reads an archive file holding host objects and links in
    60  // required objects.  The general format is the same as a Go archive
    61  // file, but it has an armap listing symbols and the objects that
    62  // define them.  This is used for the compiler support library
    63  // libgcc.a.
    64  func hostArchive(name string) {
    65  	f, err := obj.Bopenr(name)
    66  	if err != nil {
    67  		if os.IsNotExist(err) {
    68  			// It's OK if we don't have a libgcc file at all.
    69  			if Debug['v'] != 0 {
    70  				fmt.Fprintf(&Bso, "skipping libgcc file: %v\n", err)
    71  			}
    72  			return
    73  		}
    74  		Exitf("cannot open file %s: %v", name, err)
    75  	}
    76  	defer obj.Bterm(f)
    77  
    78  	magbuf := make([]byte, len(ARMAG))
    79  	if obj.Bread(f, magbuf) != len(magbuf) {
    80  		Exitf("file %s too short", name)
    81  	}
    82  
    83  	var arhdr ArHdr
    84  	l := nextar(f, obj.Boffset(f), &arhdr)
    85  	if l <= 0 {
    86  		Exitf("%s missing armap", name)
    87  	}
    88  
    89  	var armap archiveMap
    90  	if arhdr.name == "/" || arhdr.name == "/SYM64/" {
    91  		armap = readArmap(name, f, arhdr)
    92  	} else {
    93  		Exitf("%s missing armap", name)
    94  	}
    95  
    96  	loaded := make(map[uint64]bool)
    97  	any := true
    98  	for any {
    99  		var load []uint64
   100  		for s := Ctxt.Allsym; s != nil; s = s.Allsym {
   101  			for _, r := range s.R {
   102  				if r.Sym != nil && r.Sym.Type&obj.SMASK == obj.SXREF {
   103  					if off := armap[r.Sym.Name]; off != 0 && !loaded[off] {
   104  						load = append(load, off)
   105  						loaded[off] = true
   106  					}
   107  				}
   108  			}
   109  		}
   110  
   111  		for _, off := range load {
   112  			l := nextar(f, int64(off), &arhdr)
   113  			if l <= 0 {
   114  				Exitf("%s missing archive entry at offset %d", name, off)
   115  			}
   116  			pname := fmt.Sprintf("%s(%s)", name, arhdr.name)
   117  			l = atolwhex(arhdr.size)
   118  
   119  			h := ldobj(f, "libgcc", l, pname, name, ArchiveObj)
   120  			obj.Bseek(f, h.off, 0)
   121  			h.ld(f, h.pkg, h.length, h.pn)
   122  		}
   123  
   124  		any = len(load) > 0
   125  	}
   126  }
   127  
   128  // archiveMap is an archive symbol map: a mapping from symbol name to
   129  // offset within the archive file.
   130  type archiveMap map[string]uint64
   131  
   132  // readArmap reads the archive symbol map.
   133  func readArmap(filename string, f *obj.Biobuf, arhdr ArHdr) archiveMap {
   134  	is64 := arhdr.name == "/SYM64/"
   135  	wordSize := 4
   136  	if is64 {
   137  		wordSize = 8
   138  	}
   139  
   140  	l := atolwhex(arhdr.size)
   141  	contents := make([]byte, l)
   142  	if obj.Bread(f, contents) != int(l) {
   143  		Exitf("short read from %s", filename)
   144  	}
   145  
   146  	var c uint64
   147  	if is64 {
   148  		c = binary.BigEndian.Uint64(contents)
   149  	} else {
   150  		c = uint64(binary.BigEndian.Uint32(contents))
   151  	}
   152  	contents = contents[wordSize:]
   153  
   154  	ret := make(archiveMap)
   155  
   156  	names := contents[c*uint64(wordSize):]
   157  	for i := uint64(0); i < c; i++ {
   158  		n := 0
   159  		for names[n] != 0 {
   160  			n++
   161  		}
   162  		name := string(names[:n])
   163  		names = names[n+1:]
   164  
   165  		// For Mach-O and PE/386 files we strip a leading
   166  		// underscore from the symbol name.
   167  		if goos == "darwin" || (goos == "windows" && goarch == "386") {
   168  			if name[0] == '_' && len(name) > 1 {
   169  				name = name[1:]
   170  			}
   171  		}
   172  
   173  		var off uint64
   174  		if is64 {
   175  			off = binary.BigEndian.Uint64(contents)
   176  		} else {
   177  			off = uint64(binary.BigEndian.Uint32(contents))
   178  		}
   179  		contents = contents[wordSize:]
   180  
   181  		ret[name] = off
   182  	}
   183  
   184  	return ret
   185  }