github.com/4ad/go@v0.0.0-20161219182952-69a12818b605/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/internal/bio" 9 "cmd/internal/obj" 10 "crypto/sha256" 11 "fmt" 12 "io" 13 "strconv" 14 ) 15 16 // architecture-independent object file output 17 const ( 18 ArhdrSize = 60 19 ) 20 21 func formathdr(arhdr []byte, name string, size int64) { 22 copy(arhdr[:], fmt.Sprintf("%-16s%-12d%-6d%-6d%-8o%-10d`\n", name, 0, 0, 0, 0644, size)) 23 } 24 25 // These modes say which kind of object file to generate. 26 // The default use of the toolchain is to set both bits, 27 // generating a combined compiler+linker object, one that 28 // serves to describe the package to both the compiler and the linker. 29 // In fact the compiler and linker read nearly disjoint sections of 30 // that file, though, so in a distributed build setting it can be more 31 // efficient to split the output into two files, supplying the compiler 32 // object only to future compilations and the linker object only to 33 // future links. 34 // 35 // By default a combined object is written, but if -linkobj is specified 36 // on the command line then the default -o output is a compiler object 37 // and the -linkobj output is a linker object. 38 const ( 39 modeCompilerObj = 1 << iota 40 modeLinkerObj 41 ) 42 43 func dumpobj() { 44 if linkobj == "" { 45 dumpobj1(outfile, modeCompilerObj|modeLinkerObj) 46 } else { 47 dumpobj1(outfile, modeCompilerObj) 48 dumpobj1(linkobj, modeLinkerObj) 49 } 50 } 51 52 func dumpobj1(outfile string, mode int) { 53 var err error 54 bout, err = bio.Create(outfile) 55 if err != nil { 56 Flusherrors() 57 fmt.Printf("can't create %s: %v\n", outfile, err) 58 errorexit() 59 } 60 61 startobj := int64(0) 62 var arhdr [ArhdrSize]byte 63 if writearchive { 64 bout.WriteString("!<arch>\n") 65 arhdr = [ArhdrSize]byte{} 66 bout.Write(arhdr[:]) 67 startobj = bout.Offset() 68 } 69 70 printheader := func() { 71 fmt.Fprintf(bout, "go object %s %s %s %s\n", obj.Getgoos(), obj.Getgoarch(), obj.Getgoversion(), obj.Expstring()) 72 if buildid != "" { 73 fmt.Fprintf(bout, "build id %q\n", buildid) 74 } 75 if localpkg.Name == "main" { 76 fmt.Fprintf(bout, "main\n") 77 } 78 if safemode { 79 fmt.Fprintf(bout, "safe\n") 80 } else { 81 fmt.Fprintf(bout, "----\n") // room for some other tool to write "safe" 82 } 83 fmt.Fprintf(bout, "\n") // header ends with blank line 84 } 85 86 printheader() 87 88 if mode&modeCompilerObj != 0 { 89 dumpexport() 90 } 91 92 if writearchive { 93 bout.Flush() 94 size := bout.Offset() - startobj 95 if size&1 != 0 { 96 bout.WriteByte(0) 97 } 98 bout.Seek(startobj-ArhdrSize, 0) 99 formathdr(arhdr[:], "__.PKGDEF", size) 100 bout.Write(arhdr[:]) 101 bout.Flush() 102 bout.Seek(startobj+size+(size&1), 0) 103 } 104 105 if mode&modeLinkerObj == 0 { 106 bout.Close() 107 return 108 } 109 110 if writearchive { 111 // start object file 112 arhdr = [ArhdrSize]byte{} 113 bout.Write(arhdr[:]) 114 startobj = bout.Offset() 115 printheader() 116 } 117 118 if pragcgobuf != "" { 119 if writearchive { 120 // write empty export section; must be before cgo section 121 fmt.Fprintf(bout, "\n$$\n\n$$\n\n") 122 } 123 124 fmt.Fprintf(bout, "\n$$ // cgo\n") 125 fmt.Fprintf(bout, "%s\n$$\n\n", pragcgobuf) 126 } 127 128 fmt.Fprintf(bout, "\n!\n") 129 130 externs := len(externdcl) 131 132 dumpglobls() 133 dumptypestructs() 134 135 // Dump extra globals. 136 tmp := externdcl 137 138 if externdcl != nil { 139 externdcl = externdcl[externs:] 140 } 141 dumpglobls() 142 externdcl = tmp 143 144 if zerosize > 0 { 145 zero := Pkglookup("zero", mappkg) 146 ggloblsym(zero, int32(zerosize), obj.DUPOK|obj.RODATA) 147 } 148 149 dumpdata() 150 obj.Writeobjdirect(Ctxt, bout.Writer) 151 152 if writearchive { 153 bout.Flush() 154 size := bout.Offset() - startobj 155 if size&1 != 0 { 156 bout.WriteByte(0) 157 } 158 bout.Seek(startobj-ArhdrSize, 0) 159 formathdr(arhdr[:], "_go_.o", size) 160 bout.Write(arhdr[:]) 161 } 162 163 bout.Close() 164 } 165 166 func dumpglobls() { 167 // add globals 168 for _, n := range externdcl { 169 if n.Op != ONAME { 170 continue 171 } 172 173 if n.Type == nil { 174 Fatalf("external %v nil type\n", n) 175 } 176 if n.Class == PFUNC { 177 continue 178 } 179 if n.Sym.Pkg != localpkg { 180 continue 181 } 182 dowidth(n.Type) 183 ggloblnod(n) 184 } 185 186 for _, n := range funcsyms { 187 dsymptr(n.Sym, 0, n.Sym.Def.Func.Shortname.Sym, 0) 188 ggloblsym(n.Sym, int32(Widthptr), obj.DUPOK|obj.RODATA) 189 } 190 191 // Do not reprocess funcsyms on next dumpglobls call. 192 funcsyms = nil 193 } 194 195 func Linksym(s *Sym) *obj.LSym { 196 if s == nil { 197 return nil 198 } 199 if s.Lsym != nil { 200 return s.Lsym 201 } 202 var name string 203 if isblanksym(s) { 204 name = "_" 205 } else if s.Linkname != "" { 206 name = s.Linkname 207 } else { 208 name = s.Pkg.Prefix + "." + s.Name 209 } 210 211 ls := obj.Linklookup(Ctxt, name, 0) 212 s.Lsym = ls 213 return ls 214 } 215 216 func duintxx(s *Sym, off int, v uint64, wid int) int { 217 return duintxxLSym(Linksym(s), off, v, wid) 218 } 219 220 func duintxxLSym(s *obj.LSym, off int, v uint64, wid int) int { 221 // Update symbol data directly instead of generating a 222 // DATA instruction that liblink will have to interpret later. 223 // This reduces compilation time and memory usage. 224 off = int(Rnd(int64(off), int64(wid))) 225 226 return int(obj.Setuintxx(Ctxt, s, int64(off), v, int64(wid))) 227 } 228 229 func duint8(s *Sym, off int, v uint8) int { 230 return duintxx(s, off, uint64(v), 1) 231 } 232 233 func duint16(s *Sym, off int, v uint16) int { 234 return duintxx(s, off, uint64(v), 2) 235 } 236 237 func duint32(s *Sym, off int, v uint32) int { 238 return duintxx(s, off, uint64(v), 4) 239 } 240 241 func duintptr(s *Sym, off int, v uint64) int { 242 return duintxx(s, off, v, Widthptr) 243 } 244 245 func dbvec(s *Sym, off int, bv bvec) int { 246 for j := 0; int32(j) < bv.n; j += 32 { 247 word := bv.b[j/32] 248 249 // Runtime reads the bitmaps as byte arrays. Oblige. 250 off = duint8(s, off, uint8(word)) 251 off = duint8(s, off, uint8(word>>8)) 252 off = duint8(s, off, uint8(word>>16)) 253 off = duint8(s, off, uint8(word>>24)) 254 } 255 return off 256 } 257 258 // stringConstantSyms holds the pair of symbols we create for a 259 // constant string. 260 type stringConstantSyms struct { 261 hdr *obj.LSym // string header 262 data *obj.LSym // actual string data 263 } 264 265 // stringConstants maps from the symbol name we use for the string 266 // contents to the pair of linker symbols for that string. 267 var stringConstants = make(map[string]stringConstantSyms, 100) 268 269 func stringsym(s string) (hdr, data *obj.LSym) { 270 var symname string 271 if len(s) > 100 { 272 // Huge strings are hashed to avoid long names in object files. 273 // Indulge in some paranoia by writing the length of s, too, 274 // as protection against length extension attacks. 275 h := sha256.New() 276 io.WriteString(h, s) 277 symname = fmt.Sprintf(".gostring.%d.%x", len(s), h.Sum(nil)) 278 } else { 279 // Small strings get named directly by their contents. 280 symname = strconv.Quote(s) 281 } 282 283 const prefix = "go.string." 284 symdataname := prefix + symname 285 286 // All the strings have the same prefix, so ignore it for map 287 // purposes, but use a slice of the symbol name string to 288 // reduce long-term memory overhead. 289 key := symdataname[len(prefix):] 290 291 if syms, ok := stringConstants[key]; ok { 292 return syms.hdr, syms.data 293 } 294 295 symhdrname := "go.string.hdr." + symname 296 297 symhdr := obj.Linklookup(Ctxt, symhdrname, 0) 298 symdata := obj.Linklookup(Ctxt, symdataname, 0) 299 300 stringConstants[key] = stringConstantSyms{symhdr, symdata} 301 302 // string header 303 off := 0 304 off = dsymptrLSym(symhdr, off, symdata, 0) 305 off = duintxxLSym(symhdr, off, uint64(len(s)), Widthint) 306 ggloblLSym(symhdr, int32(off), obj.DUPOK|obj.RODATA|obj.LOCAL) 307 308 // string data 309 off = dsnameLSym(symdata, 0, s) 310 ggloblLSym(symdata, int32(off), obj.DUPOK|obj.RODATA|obj.LOCAL) 311 312 return symhdr, symdata 313 } 314 315 var slicebytes_gen int 316 317 func slicebytes(nam *Node, s string, len int) { 318 slicebytes_gen++ 319 symname := fmt.Sprintf(".gobytes.%d", slicebytes_gen) 320 sym := Pkglookup(symname, localpkg) 321 sym.Def = newname(sym) 322 323 off := dsname(sym, 0, s) 324 ggloblsym(sym, int32(off), obj.NOPTR|obj.LOCAL) 325 326 if nam.Op != ONAME { 327 Fatalf("slicebytes %v", nam) 328 } 329 off = int(nam.Xoffset) 330 off = dsymptr(nam.Sym, off, sym, 0) 331 off = duintxx(nam.Sym, off, uint64(len), Widthint) 332 duintxx(nam.Sym, off, uint64(len), Widthint) 333 } 334 335 func Datastring(s string, a *obj.Addr) { 336 _, symdata := stringsym(s) 337 a.Type = obj.TYPE_MEM 338 a.Name = obj.NAME_EXTERN 339 a.Sym = symdata 340 a.Offset = 0 341 a.Etype = uint8(Simtype[TINT]) 342 } 343 344 func datagostring(sval string, a *obj.Addr) { 345 symhdr, _ := stringsym(sval) 346 a.Type = obj.TYPE_MEM 347 a.Name = obj.NAME_EXTERN 348 a.Sym = symhdr 349 a.Offset = 0 350 a.Etype = uint8(TSTRING) 351 } 352 353 func dgostringptr(s *Sym, off int, str string) int { 354 if str == "" { 355 return duintptr(s, off, 0) 356 } 357 return dgostrlitptr(s, off, &str) 358 } 359 360 func dgostrlitptr(s *Sym, off int, lit *string) int { 361 if lit == nil { 362 return duintptr(s, off, 0) 363 } 364 off = int(Rnd(int64(off), int64(Widthptr))) 365 symhdr, _ := stringsym(*lit) 366 Linksym(s).WriteAddr(Ctxt, int64(off), Widthptr, symhdr, 0) 367 off += Widthptr 368 return off 369 } 370 371 func dsname(s *Sym, off int, t string) int { 372 return dsnameLSym(Linksym(s), off, t) 373 } 374 375 func dsnameLSym(s *obj.LSym, off int, t string) int { 376 s.WriteString(Ctxt, int64(off), len(t), t) 377 return off + len(t) 378 } 379 380 func dsymptr(s *Sym, off int, x *Sym, xoff int) int { 381 return dsymptrLSym(Linksym(s), off, Linksym(x), xoff) 382 } 383 384 func dsymptrLSym(s *obj.LSym, off int, x *obj.LSym, xoff int) int { 385 off = int(Rnd(int64(off), int64(Widthptr))) 386 s.WriteAddr(Ctxt, int64(off), Widthptr, x, int64(xoff)) 387 off += Widthptr 388 return off 389 } 390 391 func dsymptrOffLSym(s *obj.LSym, off int, x *obj.LSym, xoff int) int { 392 s.WriteOff(Ctxt, int64(off), x, int64(xoff)) 393 off += 4 394 return off 395 } 396 397 func gdata(nam *Node, nr *Node, wid int) { 398 if nam.Op != ONAME { 399 Fatalf("gdata nam op %v", nam.Op) 400 } 401 if nam.Sym == nil { 402 Fatalf("gdata nil nam sym") 403 } 404 405 switch nr.Op { 406 case OLITERAL: 407 switch u := nr.Val().U.(type) { 408 case *Mpcplx: 409 gdatacomplex(nam, u) 410 411 case string: 412 gdatastring(nam, u) 413 414 case bool: 415 i := int64(obj.Bool2int(u)) 416 Linksym(nam.Sym).WriteInt(Ctxt, nam.Xoffset, wid, i) 417 418 case *Mpint: 419 Linksym(nam.Sym).WriteInt(Ctxt, nam.Xoffset, wid, u.Int64()) 420 421 case *Mpflt: 422 s := Linksym(nam.Sym) 423 f := u.Float64() 424 switch nam.Type.Etype { 425 case TFLOAT32: 426 s.WriteFloat32(Ctxt, nam.Xoffset, float32(f)) 427 case TFLOAT64: 428 s.WriteFloat64(Ctxt, nam.Xoffset, f) 429 } 430 431 default: 432 Fatalf("gdata unhandled OLITERAL %v", nr) 433 } 434 435 case OADDR: 436 if nr.Left.Op != ONAME { 437 Fatalf("gdata ADDR left op %s", nr.Left.Op) 438 } 439 to := nr.Left 440 Linksym(nam.Sym).WriteAddr(Ctxt, nam.Xoffset, wid, Linksym(to.Sym), to.Xoffset) 441 442 case ONAME: 443 if nr.Class != PFUNC { 444 Fatalf("gdata NAME not PFUNC %d", nr.Class) 445 } 446 Linksym(nam.Sym).WriteAddr(Ctxt, nam.Xoffset, wid, Linksym(funcsym(nr.Sym)), nr.Xoffset) 447 448 default: 449 Fatalf("gdata unhandled op %v %v\n", nr, nr.Op) 450 } 451 } 452 453 func gdatacomplex(nam *Node, cval *Mpcplx) { 454 t := Types[cplxsubtype(nam.Type.Etype)] 455 r := cval.Real.Float64() 456 i := cval.Imag.Float64() 457 s := Linksym(nam.Sym) 458 459 switch t.Etype { 460 case TFLOAT32: 461 s.WriteFloat32(Ctxt, nam.Xoffset, float32(r)) 462 s.WriteFloat32(Ctxt, nam.Xoffset+4, float32(i)) 463 case TFLOAT64: 464 s.WriteFloat64(Ctxt, nam.Xoffset, r) 465 s.WriteFloat64(Ctxt, nam.Xoffset+8, i) 466 } 467 } 468 469 func gdatastring(nam *Node, sval string) { 470 s := Linksym(nam.Sym) 471 _, symdata := stringsym(sval) 472 s.WriteAddr(Ctxt, nam.Xoffset, Widthptr, symdata, 0) 473 s.WriteInt(Ctxt, nam.Xoffset+int64(Widthptr), Widthint, int64(len(sval))) 474 }