github.com/go-asm/go@v1.21.1-0.20240213172139-40c5ead50c48/cmd/link/ld/ar.go (about) 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 (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 "encoding/binary" 35 "fmt" 36 "io" 37 "os" 38 "path/filepath" 39 "strings" 40 41 "github.com/go-asm/go/buildcfg" 42 "github.com/go-asm/go/cmd/bio" 43 "github.com/go-asm/go/cmd/link/loader" 44 "github.com/go-asm/go/cmd/link/sym" 45 ) 46 47 const ( 48 SARMAG = 8 49 SAR_HDR = 16 + 44 50 ) 51 52 const ( 53 ARMAG = "!<arch>\n" 54 ) 55 56 type ArHdr struct { 57 name string 58 date string 59 uid string 60 gid string 61 mode string 62 size string 63 fmag string 64 } 65 66 // pruneUndefsForWindows trims the list "undefs" of currently 67 // outstanding unresolved symbols to remove references to DLL import 68 // symbols (e.g. "__imp_XXX"). In older versions of the linker, we 69 // would just immediately forward references from the import sym 70 // (__imp_XXX) to the DLL sym (XXX), but with newer compilers this 71 // strategy falls down in certain cases. We instead now do this 72 // forwarding later on as a post-processing step, and meaning that 73 // during the middle part of host object loading we can see a lot of 74 // unresolved (SXREF) import symbols. We do not, however, want to 75 // trigger the inclusion of an object from a host archive if the 76 // reference is going to be eventually forwarded to the corresponding 77 // SDYNIMPORT symbol, so here we strip out such refs from the undefs 78 // list. 79 func pruneUndefsForWindows(ldr *loader.Loader, undefs, froms []loader.Sym) ([]loader.Sym, []loader.Sym) { 80 var newundefs []loader.Sym 81 var newfroms []loader.Sym 82 for _, s := range undefs { 83 sname := ldr.SymName(s) 84 if strings.HasPrefix(sname, "__imp_") { 85 dname := sname[len("__imp_"):] 86 ds := ldr.Lookup(dname, 0) 87 if ds != 0 && ldr.SymType(ds) == sym.SDYNIMPORT { 88 // Don't try to pull things out of a host archive to 89 // satisfy this symbol. 90 continue 91 } 92 } 93 newundefs = append(newundefs, s) 94 newfroms = append(newfroms, s) 95 } 96 return newundefs, newfroms 97 } 98 99 // hostArchive reads an archive file holding host objects and links in 100 // required objects. The general format is the same as a Go archive 101 // file, but it has an armap listing symbols and the objects that 102 // define them. This is used for the compiler support library 103 // libgcc.a. 104 func hostArchive(ctxt *Link, name string) { 105 if ctxt.Debugvlog > 1 { 106 ctxt.Logf("hostArchive(%s)\n", name) 107 } 108 f, err := bio.Open(name) 109 if err != nil { 110 if os.IsNotExist(err) { 111 // It's OK if we don't have a libgcc file at all. 112 if ctxt.Debugvlog != 0 { 113 ctxt.Logf("skipping libgcc file: %v\n", err) 114 } 115 return 116 } 117 Exitf("cannot open file %s: %v", name, err) 118 } 119 defer f.Close() 120 121 var magbuf [len(ARMAG)]byte 122 if _, err := io.ReadFull(f, magbuf[:]); err != nil { 123 Exitf("file %s too short", name) 124 } 125 126 if string(magbuf[:]) != ARMAG { 127 Exitf("%s is not an archive file", name) 128 } 129 130 var arhdr ArHdr 131 l := nextar(f, f.Offset(), &arhdr) 132 if l <= 0 { 133 Exitf("%s missing armap", name) 134 } 135 136 var armap archiveMap 137 if arhdr.name == "/" || arhdr.name == "/SYM64/" { 138 armap = readArmap(name, f, arhdr) 139 } else { 140 Exitf("%s missing armap", name) 141 } 142 143 loaded := make(map[uint64]bool) 144 any := true 145 for any { 146 var load []uint64 147 returnAllUndefs := -1 148 undefs, froms := ctxt.loader.UndefinedRelocTargets(returnAllUndefs) 149 if buildcfg.GOOS == "windows" { 150 undefs, froms = pruneUndefsForWindows(ctxt.loader, undefs, froms) 151 } 152 for k, symIdx := range undefs { 153 sname := ctxt.loader.SymName(symIdx) 154 if off := armap[sname]; off != 0 && !loaded[off] { 155 load = append(load, off) 156 loaded[off] = true 157 if ctxt.Debugvlog > 1 { 158 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]) 159 } 160 } 161 } 162 163 for _, off := range load { 164 l := nextar(f, int64(off), &arhdr) 165 if l <= 0 { 166 Exitf("%s missing archive entry at offset %d", name, off) 167 } 168 pname := fmt.Sprintf("%s(%s)", name, arhdr.name) 169 l = atolwhex(arhdr.size) 170 171 pkname := filepath.Base(name) 172 if i := strings.LastIndex(pkname, ".a"); i >= 0 { 173 pkname = pkname[:i] 174 } 175 libar := sym.Library{Pkg: pkname} 176 h := ldobj(ctxt, f, &libar, l, pname, name) 177 if h.ld == nil { 178 Errorf(nil, "%s unrecognized object file at offset %d", name, off) 179 continue 180 } 181 f.MustSeek(h.off, 0) 182 h.ld(ctxt, f, h.pkg, h.length, h.pn) 183 if *flagCaptureHostObjs != "" { 184 captureHostObj(h) 185 } 186 } 187 188 any = len(load) > 0 189 } 190 } 191 192 // archiveMap is an archive symbol map: a mapping from symbol name to 193 // offset within the archive file. 194 type archiveMap map[string]uint64 195 196 // readArmap reads the archive symbol map. 197 func readArmap(filename string, f *bio.Reader, arhdr ArHdr) archiveMap { 198 is64 := arhdr.name == "/SYM64/" 199 wordSize := 4 200 if is64 { 201 wordSize = 8 202 } 203 204 contents := make([]byte, atolwhex(arhdr.size)) 205 if _, err := io.ReadFull(f, contents); err != nil { 206 Exitf("short read from %s", filename) 207 } 208 209 var c uint64 210 if is64 { 211 c = binary.BigEndian.Uint64(contents) 212 } else { 213 c = uint64(binary.BigEndian.Uint32(contents)) 214 } 215 contents = contents[wordSize:] 216 217 ret := make(archiveMap) 218 219 names := contents[c*uint64(wordSize):] 220 for i := uint64(0); i < c; i++ { 221 n := 0 222 for names[n] != 0 { 223 n++ 224 } 225 name := string(names[:n]) 226 names = names[n+1:] 227 228 // For Mach-O and PE/386 files we strip a leading 229 // underscore from the symbol name. 230 if buildcfg.GOOS == "darwin" || buildcfg.GOOS == "ios" || (buildcfg.GOOS == "windows" && buildcfg.GOARCH == "386") { 231 if name[0] == '_' && len(name) > 1 { 232 name = name[1:] 233 } 234 } 235 236 var off uint64 237 if is64 { 238 off = binary.BigEndian.Uint64(contents) 239 } else { 240 off = uint64(binary.BigEndian.Uint32(contents)) 241 } 242 contents = contents[wordSize:] 243 244 ret[name] = off 245 } 246 247 return ret 248 }