github.com/mattn/go@v0.0.0-20171011075504-07f7db3ea99f/src/cmd/compile/internal/gc/obj.go (about) 1 // Copyright 2009 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package gc 6 7 import ( 8 "cmd/compile/internal/types" 9 "cmd/internal/bio" 10 "cmd/internal/obj" 11 "cmd/internal/objabi" 12 "crypto/sha256" 13 "fmt" 14 "io" 15 "strconv" 16 ) 17 18 // architecture-independent object file output 19 const ( 20 ArhdrSize = 60 21 ) 22 23 func formathdr(arhdr []byte, name string, size int64) { 24 copy(arhdr[:], fmt.Sprintf("%-16s%-12d%-6d%-6d%-8o%-10d`\n", name, 0, 0, 0, 0644, size)) 25 } 26 27 // These modes say which kind of object file to generate. 28 // The default use of the toolchain is to set both bits, 29 // generating a combined compiler+linker object, one that 30 // serves to describe the package to both the compiler and the linker. 31 // In fact the compiler and linker read nearly disjoint sections of 32 // that file, though, so in a distributed build setting it can be more 33 // efficient to split the output into two files, supplying the compiler 34 // object only to future compilations and the linker object only to 35 // future links. 36 // 37 // By default a combined object is written, but if -linkobj is specified 38 // on the command line then the default -o output is a compiler object 39 // and the -linkobj output is a linker object. 40 const ( 41 modeCompilerObj = 1 << iota 42 modeLinkerObj 43 ) 44 45 func dumpobj() { 46 if !dolinkobj { 47 dumpobj1(outfile, modeCompilerObj) 48 return 49 } 50 if linkobj == "" { 51 dumpobj1(outfile, modeCompilerObj|modeLinkerObj) 52 return 53 } 54 dumpobj1(outfile, modeCompilerObj) 55 dumpobj1(linkobj, modeLinkerObj) 56 } 57 58 func dumpobj1(outfile string, mode int) { 59 bout, err := bio.Create(outfile) 60 if err != nil { 61 flusherrors() 62 fmt.Printf("can't create %s: %v\n", outfile, err) 63 errorexit() 64 } 65 66 startobj := int64(0) 67 var arhdr [ArhdrSize]byte 68 if writearchive { 69 bout.WriteString("!<arch>\n") 70 arhdr = [ArhdrSize]byte{} 71 bout.Write(arhdr[:]) 72 startobj = bout.Offset() 73 } 74 75 printheader := func() { 76 fmt.Fprintf(bout, "go object %s %s %s %s\n", objabi.GOOS, objabi.GOARCH, objabi.Version, objabi.Expstring()) 77 if buildid != "" { 78 fmt.Fprintf(bout, "build id %q\n", buildid) 79 } 80 if localpkg.Name == "main" { 81 fmt.Fprintf(bout, "main\n") 82 } 83 if safemode { 84 fmt.Fprintf(bout, "safe\n") 85 } else { 86 fmt.Fprintf(bout, "----\n") // room for some other tool to write "safe" 87 } 88 fmt.Fprintf(bout, "\n") // header ends with blank line 89 } 90 91 printheader() 92 93 if mode&modeCompilerObj != 0 { 94 dumpexport(bout) 95 } 96 97 if writearchive { 98 bout.Flush() 99 size := bout.Offset() - startobj 100 if size&1 != 0 { 101 bout.WriteByte(0) 102 } 103 bout.Seek(startobj-ArhdrSize, 0) 104 formathdr(arhdr[:], "__.PKGDEF", size) 105 bout.Write(arhdr[:]) 106 bout.Flush() 107 bout.Seek(startobj+size+(size&1), 0) 108 } 109 110 if mode&modeLinkerObj == 0 { 111 bout.Close() 112 return 113 } 114 115 if writearchive { 116 // start object file 117 arhdr = [ArhdrSize]byte{} 118 bout.Write(arhdr[:]) 119 startobj = bout.Offset() 120 printheader() 121 } 122 123 if pragcgobuf != "" { 124 if writearchive { 125 // write empty export section; must be before cgo section 126 fmt.Fprintf(bout, "\n$$\n\n$$\n\n") 127 } 128 129 fmt.Fprintf(bout, "\n$$ // cgo\n") 130 fmt.Fprintf(bout, "%s\n$$\n\n", pragcgobuf) 131 } 132 133 fmt.Fprintf(bout, "\n!\n") 134 135 externs := len(externdcl) 136 137 dumpglobls() 138 addptabs() 139 addsignats(externdcl) 140 dumpsignats() 141 dumptabs() 142 dumpimportstrings() 143 dumpbasictypes() 144 145 // Dump extra globals. 146 tmp := externdcl 147 148 if externdcl != nil { 149 externdcl = externdcl[externs:] 150 } 151 dumpglobls() 152 externdcl = tmp 153 154 if zerosize > 0 { 155 zero := mappkg.Lookup("zero") 156 ggloblsym(zero.Linksym(), int32(zerosize), obj.DUPOK|obj.RODATA) 157 } 158 159 addGCLocals() 160 161 obj.WriteObjFile(Ctxt, bout.Writer) 162 163 if writearchive { 164 bout.Flush() 165 size := bout.Offset() - startobj 166 if size&1 != 0 { 167 bout.WriteByte(0) 168 } 169 bout.Seek(startobj-ArhdrSize, 0) 170 formathdr(arhdr[:], "_go_.o", size) 171 bout.Write(arhdr[:]) 172 } 173 174 bout.Close() 175 } 176 177 func addptabs() { 178 if !Ctxt.Flag_dynlink || localpkg.Name != "main" { 179 return 180 } 181 for _, exportn := range exportlist { 182 s := exportn.Sym 183 n := asNode(s.Def) 184 if n == nil { 185 continue 186 } 187 if n.Op != ONAME { 188 continue 189 } 190 if !exportname(s.Name) { 191 continue 192 } 193 if s.Pkg.Name != "main" { 194 continue 195 } 196 if n.Type.Etype == TFUNC && n.Class() == PFUNC { 197 // function 198 ptabs = append(ptabs, ptabEntry{s: s, t: asNode(s.Def).Type}) 199 } else { 200 // variable 201 ptabs = append(ptabs, ptabEntry{s: s, t: types.NewPtr(asNode(s.Def).Type)}) 202 } 203 } 204 } 205 206 func dumpGlobal(n *Node) { 207 if n.Type == nil { 208 Fatalf("external %v nil type\n", n) 209 } 210 if n.Class() == PFUNC { 211 return 212 } 213 if n.Sym.Pkg != localpkg { 214 return 215 } 216 dowidth(n.Type) 217 ggloblnod(n) 218 } 219 220 func dumpGlobalConst(n *Node) { 221 // only export typed constants 222 t := n.Type 223 if t == nil { 224 return 225 } 226 if n.Sym.Pkg != localpkg { 227 return 228 } 229 // only export integer constants for now 230 switch t.Etype { 231 case TINT8: 232 case TINT16: 233 case TINT32: 234 case TINT64: 235 case TINT: 236 case TUINT8: 237 case TUINT16: 238 case TUINT32: 239 case TUINT64: 240 case TUINT: 241 case TUINTPTR: 242 // ok 243 case TIDEAL: 244 if !Isconst(n, CTINT) { 245 return 246 } 247 x := n.Val().U.(*Mpint) 248 if x.Cmp(minintval[TINT]) < 0 || x.Cmp(maxintval[TINT]) > 0 { 249 return 250 } 251 // Ideal integers we export as int (if they fit). 252 t = types.Types[TINT] 253 default: 254 return 255 } 256 Ctxt.DwarfIntConst(myimportpath, n.Sym.Name, typesymname(t), n.Int64()) 257 } 258 259 func dumpglobls() { 260 // add globals 261 for _, n := range externdcl { 262 switch n.Op { 263 case ONAME: 264 dumpGlobal(n) 265 case OLITERAL: 266 dumpGlobalConst(n) 267 } 268 } 269 270 obj.SortSlice(funcsyms, func(i, j int) bool { 271 return funcsyms[i].LinksymName() < funcsyms[j].LinksymName() 272 }) 273 for _, s := range funcsyms { 274 sf := s.Pkg.Lookup(funcsymname(s)).Linksym() 275 dsymptr(sf, 0, s.Linksym(), 0) 276 ggloblsym(sf, int32(Widthptr), obj.DUPOK|obj.RODATA) 277 } 278 279 // Do not reprocess funcsyms on next dumpglobls call. 280 funcsyms = nil 281 } 282 283 // addGCLocals adds gcargs and gclocals symbols to Ctxt.Data. 284 // It takes care not to add any duplicates. 285 // Though the object file format handles duplicates efficiently, 286 // storing only a single copy of the data, 287 // failure to remove these duplicates adds a few percent to object file size. 288 func addGCLocals() { 289 seen := make(map[string]bool) 290 for _, s := range Ctxt.Text { 291 if s.Func == nil { 292 continue 293 } 294 for _, gcsym := range []*obj.LSym{&s.Func.GCArgs, &s.Func.GCLocals} { 295 if seen[gcsym.Name] { 296 continue 297 } 298 Ctxt.Data = append(Ctxt.Data, gcsym) 299 seen[gcsym.Name] = true 300 } 301 } 302 } 303 304 func duintxx(s *obj.LSym, off int, v uint64, wid int) int { 305 if off&(wid-1) != 0 { 306 Fatalf("duintxxLSym: misaligned: v=%d wid=%d off=%d", v, wid, off) 307 } 308 s.WriteInt(Ctxt, int64(off), wid, int64(v)) 309 return off + wid 310 } 311 312 func duint8(s *obj.LSym, off int, v uint8) int { 313 return duintxx(s, off, uint64(v), 1) 314 } 315 316 func duint16(s *obj.LSym, off int, v uint16) int { 317 return duintxx(s, off, uint64(v), 2) 318 } 319 320 func duint32(s *obj.LSym, off int, v uint32) int { 321 return duintxx(s, off, uint64(v), 4) 322 } 323 324 func duintptr(s *obj.LSym, off int, v uint64) int { 325 return duintxx(s, off, v, Widthptr) 326 } 327 328 func dbvec(s *obj.LSym, off int, bv bvec) int { 329 // Runtime reads the bitmaps as byte arrays. Oblige. 330 for j := 0; int32(j) < bv.n; j += 8 { 331 word := bv.b[j/32] 332 off = duint8(s, off, uint8(word>>(uint(j)%32))) 333 } 334 return off 335 } 336 337 func stringsym(s string) (data *obj.LSym) { 338 var symname string 339 if len(s) > 100 { 340 // Huge strings are hashed to avoid long names in object files. 341 // Indulge in some paranoia by writing the length of s, too, 342 // as protection against length extension attacks. 343 h := sha256.New() 344 io.WriteString(h, s) 345 symname = fmt.Sprintf(".gostring.%d.%x", len(s), h.Sum(nil)) 346 } else { 347 // Small strings get named directly by their contents. 348 symname = strconv.Quote(s) 349 } 350 351 const prefix = "go.string." 352 symdataname := prefix + symname 353 354 symdata := Ctxt.Lookup(symdataname) 355 356 if !symdata.SeenGlobl() { 357 // string data 358 off := dsname(symdata, 0, s) 359 ggloblsym(symdata, int32(off), obj.DUPOK|obj.RODATA|obj.LOCAL) 360 } 361 362 return symdata 363 } 364 365 var slicebytes_gen int 366 367 func slicebytes(nam *Node, s string, len int) { 368 slicebytes_gen++ 369 symname := fmt.Sprintf(".gobytes.%d", slicebytes_gen) 370 sym := localpkg.Lookup(symname) 371 sym.Def = asTypesNode(newname(sym)) 372 373 lsym := sym.Linksym() 374 off := dsname(lsym, 0, s) 375 ggloblsym(lsym, int32(off), obj.NOPTR|obj.LOCAL) 376 377 if nam.Op != ONAME { 378 Fatalf("slicebytes %v", nam) 379 } 380 nsym := nam.Sym.Linksym() 381 off = int(nam.Xoffset) 382 off = dsymptr(nsym, off, lsym, 0) 383 off = duintptr(nsym, off, uint64(len)) 384 duintptr(nsym, off, uint64(len)) 385 } 386 387 func dsname(s *obj.LSym, off int, t string) int { 388 s.WriteString(Ctxt, int64(off), len(t), t) 389 return off + len(t) 390 } 391 392 func dsymptr(s *obj.LSym, off int, x *obj.LSym, xoff int) int { 393 off = int(Rnd(int64(off), int64(Widthptr))) 394 s.WriteAddr(Ctxt, int64(off), Widthptr, x, int64(xoff)) 395 off += Widthptr 396 return off 397 } 398 399 func dsymptrOff(s *obj.LSym, off int, x *obj.LSym, xoff int) int { 400 s.WriteOff(Ctxt, int64(off), x, int64(xoff)) 401 off += 4 402 return off 403 } 404 405 func dsymptrWeakOff(s *obj.LSym, off int, x *obj.LSym) int { 406 s.WriteWeakOff(Ctxt, int64(off), x, 0) 407 off += 4 408 return off 409 } 410 411 func gdata(nam *Node, nr *Node, wid int) { 412 if nam.Op != ONAME { 413 Fatalf("gdata nam op %v", nam.Op) 414 } 415 if nam.Sym == nil { 416 Fatalf("gdata nil nam sym") 417 } 418 s := nam.Sym.Linksym() 419 420 switch nr.Op { 421 case OLITERAL: 422 switch u := nr.Val().U.(type) { 423 case bool: 424 i := int64(obj.Bool2int(u)) 425 s.WriteInt(Ctxt, nam.Xoffset, wid, i) 426 427 case *Mpint: 428 s.WriteInt(Ctxt, nam.Xoffset, wid, u.Int64()) 429 430 case *Mpflt: 431 f := u.Float64() 432 switch nam.Type.Etype { 433 case TFLOAT32: 434 s.WriteFloat32(Ctxt, nam.Xoffset, float32(f)) 435 case TFLOAT64: 436 s.WriteFloat64(Ctxt, nam.Xoffset, f) 437 } 438 439 case *Mpcplx: 440 r := u.Real.Float64() 441 i := u.Imag.Float64() 442 switch nam.Type.Etype { 443 case TCOMPLEX64: 444 s.WriteFloat32(Ctxt, nam.Xoffset, float32(r)) 445 s.WriteFloat32(Ctxt, nam.Xoffset+4, float32(i)) 446 case TCOMPLEX128: 447 s.WriteFloat64(Ctxt, nam.Xoffset, r) 448 s.WriteFloat64(Ctxt, nam.Xoffset+8, i) 449 } 450 451 case string: 452 symdata := stringsym(u) 453 s.WriteAddr(Ctxt, nam.Xoffset, Widthptr, symdata, 0) 454 s.WriteInt(Ctxt, nam.Xoffset+int64(Widthptr), Widthptr, int64(len(u))) 455 456 default: 457 Fatalf("gdata unhandled OLITERAL %v", nr) 458 } 459 460 case OADDR: 461 if nr.Left.Op != ONAME { 462 Fatalf("gdata ADDR left op %v", nr.Left.Op) 463 } 464 to := nr.Left 465 s.WriteAddr(Ctxt, nam.Xoffset, wid, to.Sym.Linksym(), to.Xoffset) 466 467 case ONAME: 468 if nr.Class() != PFUNC { 469 Fatalf("gdata NAME not PFUNC %d", nr.Class()) 470 } 471 s.WriteAddr(Ctxt, nam.Xoffset, wid, funcsym(nr.Sym).Linksym(), nr.Xoffset) 472 473 default: 474 Fatalf("gdata unhandled op %v %v\n", nr, nr.Op) 475 } 476 }