Home | History | Annotate | Download | only in gc
      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.GOOS, obj.GOARCH, obj.Version, 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 	dumpptabs()
    134 	dumptypestructs()
    135 
    136 	// Dump extra globals.
    137 	tmp := externdcl
    138 
    139 	if externdcl != nil {
    140 		externdcl = externdcl[externs:]
    141 	}
    142 	dumpglobls()
    143 	externdcl = tmp
    144 
    145 	if zerosize > 0 {
    146 		zero := Pkglookup("zero", mappkg)
    147 		ggloblsym(zero, int32(zerosize), obj.DUPOK|obj.RODATA)
    148 	}
    149 
    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 dumpptabs() {
    167 	if !Ctxt.Flag_dynlink || localpkg.Name != "main" {
    168 		return
    169 	}
    170 	for _, exportn := range exportlist {
    171 		s := exportn.Sym
    172 		n := s.Def
    173 		if n == nil {
    174 			continue
    175 		}
    176 		if n.Op != ONAME {
    177 			continue
    178 		}
    179 		if !exportname(s.Name) {
    180 			continue
    181 		}
    182 		if s.Pkg.Name != "main" {
    183 			continue
    184 		}
    185 		if n.Type.Etype == TFUNC && n.Class == PFUNC {
    186 			// function
    187 			ptabs = append(ptabs, ptabEntry{s: s, t: s.Def.Type})
    188 		} else {
    189 			// variable
    190 			ptabs = append(ptabs, ptabEntry{s: s, t: typPtr(s.Def.Type)})
    191 		}
    192 	}
    193 }
    194 
    195 func dumpglobls() {
    196 	// add globals
    197 	for _, n := range externdcl {
    198 		if n.Op != ONAME {
    199 			continue
    200 		}
    201 
    202 		if n.Type == nil {
    203 			Fatalf("external %v nil type\n", n)
    204 		}
    205 		if n.Class == PFUNC {
    206 			continue
    207 		}
    208 		if n.Sym.Pkg != localpkg {
    209 			continue
    210 		}
    211 		dowidth(n.Type)
    212 		ggloblnod(n)
    213 	}
    214 
    215 	for _, n := range funcsyms {
    216 		dsymptr(n.Sym, 0, n.Sym.Def.Func.Shortname.Sym, 0)
    217 		ggloblsym(n.Sym, int32(Widthptr), obj.DUPOK|obj.RODATA)
    218 	}
    219 
    220 	// Do not reprocess funcsyms on next dumpglobls call.
    221 	funcsyms = nil
    222 }
    223 
    224 func Linksym(s *Sym) *obj.LSym {
    225 	if s == nil {
    226 		return nil
    227 	}
    228 	if s.Lsym != nil {
    229 		return s.Lsym
    230 	}
    231 	var name string
    232 	if isblanksym(s) {
    233 		name = "_"
    234 	} else if s.Linkname != "" {
    235 		name = s.Linkname
    236 	} else {
    237 		name = s.Pkg.Prefix + "." + s.Name
    238 	}
    239 
    240 	ls := obj.Linklookup(Ctxt, name, 0)
    241 	s.Lsym = ls
    242 	return ls
    243 }
    244 
    245 func duintxx(s *Sym, off int, v uint64, wid int) int {
    246 	return duintxxLSym(Linksym(s), off, v, wid)
    247 }
    248 
    249 func duintxxLSym(s *obj.LSym, off int, v uint64, wid int) int {
    250 	// Update symbol data directly instead of generating a
    251 	// DATA instruction that liblink will have to interpret later.
    252 	// This reduces compilation time and memory usage.
    253 	off = int(Rnd(int64(off), int64(wid)))
    254 
    255 	return int(obj.Setuintxx(Ctxt, s, int64(off), v, int64(wid)))
    256 }
    257 
    258 func duint8(s *Sym, off int, v uint8) int {
    259 	return duintxx(s, off, uint64(v), 1)
    260 }
    261 
    262 func duint16(s *Sym, off int, v uint16) int {
    263 	return duintxx(s, off, uint64(v), 2)
    264 }
    265 
    266 func duint32(s *Sym, off int, v uint32) int {
    267 	return duintxx(s, off, uint64(v), 4)
    268 }
    269 
    270 func duintptr(s *Sym, off int, v uint64) int {
    271 	return duintxx(s, off, v, Widthptr)
    272 }
    273 
    274 func dbvec(s *Sym, off int, bv bvec) int {
    275 	// Runtime reads the bitmaps as byte arrays. Oblige.
    276 	for j := 0; int32(j) < bv.n; j += 8 {
    277 		word := bv.b[j/32]
    278 		off = duint8(s, off, uint8(word>>(uint(j)%32)))
    279 	}
    280 	return off
    281 }
    282 
    283 func stringsym(s string) (data *obj.LSym) {
    284 	var symname string
    285 	if len(s) > 100 {
    286 		// Huge strings are hashed to avoid long names in object files.
    287 		// Indulge in some paranoia by writing the length of s, too,
    288 		// as protection against length extension attacks.
    289 		h := sha256.New()
    290 		io.WriteString(h, s)
    291 		symname = fmt.Sprintf(".gostring.%d.%x", len(s), h.Sum(nil))
    292 	} else {
    293 		// Small strings get named directly by their contents.
    294 		symname = strconv.Quote(s)
    295 	}
    296 
    297 	const prefix = "go.string."
    298 	symdataname := prefix + symname
    299 
    300 	symdata := obj.Linklookup(Ctxt, symdataname, 0)
    301 
    302 	if !symdata.SeenGlobl() {
    303 		// string data
    304 		off := dsnameLSym(symdata, 0, s)
    305 		ggloblLSym(symdata, int32(off), obj.DUPOK|obj.RODATA|obj.LOCAL)
    306 	}
    307 
    308 	return symdata
    309 }
    310 
    311 var slicebytes_gen int
    312 
    313 func slicebytes(nam *Node, s string, len int) {
    314 	slicebytes_gen++
    315 	symname := fmt.Sprintf(".gobytes.%d", slicebytes_gen)
    316 	sym := Pkglookup(symname, localpkg)
    317 	sym.Def = newname(sym)
    318 
    319 	off := dsname(sym, 0, s)
    320 	ggloblsym(sym, int32(off), obj.NOPTR|obj.LOCAL)
    321 
    322 	if nam.Op != ONAME {
    323 		Fatalf("slicebytes %v", nam)
    324 	}
    325 	off = int(nam.Xoffset)
    326 	off = dsymptr(nam.Sym, off, sym, 0)
    327 	off = duintxx(nam.Sym, off, uint64(len), Widthint)
    328 	duintxx(nam.Sym, off, uint64(len), Widthint)
    329 }
    330 
    331 func dsname(s *Sym, off int, t string) int {
    332 	return dsnameLSym(Linksym(s), off, t)
    333 }
    334 
    335 func dsnameLSym(s *obj.LSym, off int, t string) int {
    336 	s.WriteString(Ctxt, int64(off), len(t), t)
    337 	return off + len(t)
    338 }
    339 
    340 func dsymptr(s *Sym, off int, x *Sym, xoff int) int {
    341 	return dsymptrLSym(Linksym(s), off, Linksym(x), xoff)
    342 }
    343 
    344 func dsymptrLSym(s *obj.LSym, off int, x *obj.LSym, xoff int) int {
    345 	off = int(Rnd(int64(off), int64(Widthptr)))
    346 	s.WriteAddr(Ctxt, int64(off), Widthptr, x, int64(xoff))
    347 	off += Widthptr
    348 	return off
    349 }
    350 
    351 func dsymptrOffLSym(s *obj.LSym, off int, x *obj.LSym, xoff int) int {
    352 	s.WriteOff(Ctxt, int64(off), x, int64(xoff))
    353 	off += 4
    354 	return off
    355 }
    356 
    357 func dsymptrWeakOffLSym(s *obj.LSym, off int, x *obj.LSym) int {
    358 	s.WriteWeakOff(Ctxt, int64(off), x, 0)
    359 	off += 4
    360 	return off
    361 }
    362 
    363 func gdata(nam *Node, nr *Node, wid int) {
    364 	if nam.Op != ONAME {
    365 		Fatalf("gdata nam op %v", nam.Op)
    366 	}
    367 	if nam.Sym == nil {
    368 		Fatalf("gdata nil nam sym")
    369 	}
    370 	s := Linksym(nam.Sym)
    371 
    372 	switch nr.Op {
    373 	case OLITERAL:
    374 		switch u := nr.Val().U.(type) {
    375 		case bool:
    376 			i := int64(obj.Bool2int(u))
    377 			s.WriteInt(Ctxt, nam.Xoffset, wid, i)
    378 
    379 		case *Mpint:
    380 			s.WriteInt(Ctxt, nam.Xoffset, wid, u.Int64())
    381 
    382 		case *Mpflt:
    383 			f := u.Float64()
    384 			switch nam.Type.Etype {
    385 			case TFLOAT32:
    386 				s.WriteFloat32(Ctxt, nam.Xoffset, float32(f))
    387 			case TFLOAT64:
    388 				s.WriteFloat64(Ctxt, nam.Xoffset, f)
    389 			}
    390 
    391 		case *Mpcplx:
    392 			r := u.Real.Float64()
    393 			i := u.Imag.Float64()
    394 			switch nam.Type.Etype {
    395 			case TCOMPLEX64:
    396 				s.WriteFloat32(Ctxt, nam.Xoffset, float32(r))
    397 				s.WriteFloat32(Ctxt, nam.Xoffset+4, float32(i))
    398 			case TCOMPLEX128:
    399 				s.WriteFloat64(Ctxt, nam.Xoffset, r)
    400 				s.WriteFloat64(Ctxt, nam.Xoffset+8, i)
    401 			}
    402 
    403 		case string:
    404 			symdata := stringsym(u)
    405 			s.WriteAddr(Ctxt, nam.Xoffset, Widthptr, symdata, 0)
    406 			s.WriteInt(Ctxt, nam.Xoffset+int64(Widthptr), Widthint, int64(len(u)))
    407 
    408 		default:
    409 			Fatalf("gdata unhandled OLITERAL %v", nr)
    410 		}
    411 
    412 	case OADDR:
    413 		if nr.Left.Op != ONAME {
    414 			Fatalf("gdata ADDR left op %v", nr.Left.Op)
    415 		}
    416 		to := nr.Left
    417 		s.WriteAddr(Ctxt, nam.Xoffset, wid, Linksym(to.Sym), to.Xoffset)
    418 
    419 	case ONAME:
    420 		if nr.Class != PFUNC {
    421 			Fatalf("gdata NAME not PFUNC %d", nr.Class)
    422 		}
    423 		s.WriteAddr(Ctxt, nam.Xoffset, wid, Linksym(funcsym(nr.Sym)), nr.Xoffset)
    424 
    425 	default:
    426 		Fatalf("gdata unhandled op %v %v\n", nr, nr.Op)
    427 	}
    428 }
    429