// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package gc
import (
"cmd/internal/obj"
"fmt"
"strconv"
)
/*
* architecture-independent object file output
*/
const (
ArhdrSize = 60
)
func formathdr(arhdr []byte, name string, size int64) {
copy(arhdr[:], fmt.Sprintf("%-16s%-12d%-6d%-6d%-8o%-10d`\n", name, 0, 0, 0, 0644, size))
}
func dumpobj() {
var err error
bout, err = obj.Bopenw(outfile)
if err != nil {
Flusherrors()
fmt.Printf("can't create %s: %v\n", outfile, err)
errorexit()
}
startobj := int64(0)
var arhdr [ArhdrSize]byte
if writearchive != 0 {
obj.Bwritestring(bout, "!<arch>\n")
arhdr = [ArhdrSize]byte{}
bout.Write(arhdr[:])
startobj = obj.Boffset(bout)
}
fmt.Fprintf(bout, "go object %s %s %s %s\n", obj.Getgoos(), obj.Getgoarch(), obj.Getgoversion(), obj.Expstring())
dumpexport()
if writearchive != 0 {
bout.Flush()
size := obj.Boffset(bout) - startobj
if size&1 != 0 {
obj.Bputc(bout, 0)
}
obj.Bseek(bout, startobj-ArhdrSize, 0)
formathdr(arhdr[:], "__.PKGDEF", size)
bout.Write(arhdr[:])
bout.Flush()
obj.Bseek(bout, startobj+size+(size&1), 0)
arhdr = [ArhdrSize]byte{}
bout.Write(arhdr[:])
startobj = obj.Boffset(bout)
fmt.Fprintf(bout, "go object %s %s %s %s\n", obj.Getgoos(), obj.Getgoarch(), obj.Getgoversion(), obj.Expstring())
}
if pragcgobuf != "" {
if writearchive != 0 {
// write empty export section; must be before cgo section
fmt.Fprintf(bout, "\n$$\n\n$$\n\n")
}
fmt.Fprintf(bout, "\n$$ // cgo\n")
fmt.Fprintf(bout, "%s\n$$\n\n", pragcgobuf)
}
fmt.Fprintf(bout, "\n!\n")
var externs *NodeList
if externdcl != nil {
externs = externdcl.End
}
dumpglobls()
dumptypestructs()
// Dump extra globals.
tmp := externdcl
if externs != nil {
externdcl = externs.Next
}
dumpglobls()
externdcl = tmp
zero := Pkglookup("zerovalue", Runtimepkg)
ggloblsym(zero, int32(zerosize), obj.DUPOK|obj.RODATA)
dumpdata()
obj.Writeobjdirect(Ctxt, bout)
if writearchive != 0 {
bout.Flush()
size := obj.Boffset(bout) - startobj
if size&1 != 0 {
obj.Bputc(bout, 0)
}
obj.Bseek(bout, startobj-ArhdrSize, 0)
formathdr(arhdr[:], "_go_.o", size)
bout.Write(arhdr[:])
}
obj.Bterm(bout)
}
func dumpglobls() {
var n *Node
// add globals
for l := externdcl; l != nil; l = l.Next {
n = l.N
if n.Op != ONAME {
continue
}
if n.Type == nil {
Fatal("external %v nil type\n", n)
}
if n.Class == PFUNC {
continue
}
if n.Sym.Pkg != localpkg {
continue
}
dowidth(n.Type)
ggloblnod(n)
}
for l := funcsyms; l != nil; l = l.Next {
n = l.N
dsymptr(n.Sym, 0, n.Sym.Def.Func.Shortname.Sym, 0)
ggloblsym(n.Sym, int32(Widthptr), obj.DUPOK|obj.RODATA)
}
// Do not reprocess funcsyms on next dumpglobls call.
funcsyms = nil
}
func Bputname(b *obj.Biobuf, s *obj.LSym) {
obj.Bwritestring(b, s.Name)
obj.Bputc(b, 0)
}
func Linksym(s *Sym) *obj.LSym {
if s == nil {
return nil
}
if s.Lsym != nil {
return s.Lsym
}
var name string
if isblanksym(s) {
name = "_"
} else if s.Linkname != "" {
name = s.Linkname
} else {
name = s.Pkg.Prefix + "." + s.Name
}
ls := obj.Linklookup(Ctxt, name, 0)
s.Lsym = ls
return ls
}
func duintxx(s *Sym, off int, v uint64, wid int) int {
// Update symbol data directly instead of generating a
// DATA instruction that liblink will have to interpret later.
// This reduces compilation time and memory usage.
off = int(Rnd(int64(off), int64(wid)))
return int(obj.Setuintxx(Ctxt, Linksym(s), int64(off), v, int64(wid)))
}
func duint8(s *Sym, off int, v uint8) int {
return duintxx(s, off, uint64(v), 1)
}
func duint16(s *Sym, off int, v uint16) int {
return duintxx(s, off, uint64(v), 2)
}
func duint32(s *Sym, off int, v uint32) int {
return duintxx(s, off, uint64(v), 4)
}
func duint64(s *Sym, off int, v uint64) int {
return duintxx(s, off, v, 8)
}
func duintptr(s *Sym, off int, v uint64) int {
return duintxx(s, off, v, Widthptr)
}
var stringsym_gen int
func stringsym(s string) (hdr, data *Sym) {
var symname string
var pkg *Pkg
if len(s) > 100 {
// huge strings are made static to avoid long names
stringsym_gen++
symname = fmt.Sprintf(".gostring.%d", stringsym_gen)
pkg = localpkg
} else {
// small strings get named by their contents,
// so that multiple modules using the same string
// can share it.
symname = strconv.Quote(s)
pkg = gostringpkg
}
symhdr := Pkglookup("hdr."+symname, pkg)
symdata := Pkglookup(symname, pkg)
// SymUniq flag indicates that data is generated already
if symhdr.Flags&SymUniq != 0 {
return symhdr, symdata
}
symhdr.Flags |= SymUniq
symhdr.Def = newname(symhdr)
// string header
off := 0
off = dsymptr(symhdr, off, symdata, 0)
off = duintxx(symhdr, off, uint64(len(s)), Widthint)
ggloblsym(symhdr, int32(off), obj.DUPOK|obj.RODATA|obj.LOCAL)
// string data
if symdata.Flags&SymUniq != 0 {
return symhdr, symdata
}
symdata.Flags |= SymUniq
symdata.Def = newname(symdata)
off = 0
var m int
for n := 0; n < len(s); n += m {
m = 8
if m > len(s)-n {
m = len(s) - n
}
off = dsname(symdata, off, s[n:n+m])
}
off = duint8(symdata, off, 0) // terminating NUL for runtime
off = (off + Widthptr - 1) &^ (Widthptr - 1) // round to pointer alignment
ggloblsym(symdata, int32(off), obj.DUPOK|obj.RODATA|obj.LOCAL)
return symhdr, symdata
}
var slicebytes_gen int
func slicebytes(nam *Node, s string, len int) {
var m int
slicebytes_gen++
symname := fmt.Sprintf(".gobytes.%d", slicebytes_gen)
sym := Pkglookup(symname, localpkg)
sym.Def = newname(sym)
off := 0
for n := 0; n < len; n += m {
m = 8
if m > len-n {
m = len - n
}
off = dsname(sym, off, s[n:n+m])
}
ggloblsym(sym, int32(off), obj.NOPTR|obj.LOCAL)
if nam.Op != ONAME {
Fatal("slicebytes %v", nam)
}
off = int(nam.Xoffset)
off = dsymptr(nam.Sym, off, sym, 0)
off = duintxx(nam.Sym, off, uint64(len), Widthint)
duintxx(nam.Sym, off, uint64(len), Widthint)
}
func dstringptr(s *Sym, off int, str string) int {
off = int(Rnd(int64(off), int64(Widthptr)))
p := Thearch.Gins(obj.ADATA, nil, nil)
p.From.Type = obj.TYPE_MEM
p.From.Name = obj.NAME_EXTERN
p.From.Sym = Linksym(s)
p.From.Offset = int64(off)
p.From3 = new(obj.Addr)
p.From3.Type = obj.TYPE_CONST
p.From3.Offset = int64(Widthptr)
Datastring(str+"\x00", &p.To) // TODO(rsc): Remove NUL
p.To.Type = obj.TYPE_ADDR
p.To.Etype = Simtype[TINT]
off += Widthptr
return off
}
func Datastring(s string, a *obj.Addr) {
_, symdata := stringsym(s)
a.Type = obj.TYPE_MEM
a.Name = obj.NAME_EXTERN
a.Sym = Linksym(symdata)
a.Node = symdata.Def
a.Offset = 0
a.Etype = Simtype[TINT]
}
func datagostring(sval string, a *obj.Addr) {
symhdr, _ := stringsym(sval)
a.Type = obj.TYPE_MEM
a.Name = obj.NAME_EXTERN
a.Sym = Linksym(symhdr)
a.Node = symhdr.Def
a.Offset = 0
a.Etype = TSTRING
}
func dgostringptr(s *Sym, off int, str string) int {
if str == "" {
return duintptr(s, off, 0)
}
return dgostrlitptr(s, off, &str)
}
func dgostrlitptr(s *Sym, off int, lit *string) int {
if lit == nil {
return duintptr(s, off, 0)
}
off = int(Rnd(int64(off), int64(Widthptr)))
p := Thearch.Gins(obj.ADATA, nil, nil)
p.From.Type = obj.TYPE_MEM
p.From.Name = obj.NAME_EXTERN
p.From.Sym = Linksym(s)
p.From.Offset = int64(off)
p.From3 = new(obj.Addr)
p.From3.Type = obj.TYPE_CONST
p.From3.Offset = int64(Widthptr)
datagostring(*lit, &p.To)
p.To.Type = obj.TYPE_ADDR
p.To.Etype = Simtype[TINT]
off += Widthptr
return off
}
func dsname(s *Sym, off int, t string) int {
p := Thearch.Gins(obj.ADATA, nil, nil)
p.From.Type = obj.TYPE_MEM
p.From.Name = obj.NAME_EXTERN
p.From.Offset = int64(off)
p.From.Sym = Linksym(s)
p.From3 = new(obj.Addr)
p.From3.Type = obj.TYPE_CONST
p.From3.Offset = int64(len(t))
p.To.Type = obj.TYPE_SCONST
p.To.Val = t
return off + len(t)
}
func dsymptr(s *Sym, off int, x *Sym, xoff int) int {
off = int(Rnd(int64(off), int64(Widthptr)))
p := Thearch.Gins(obj.ADATA, nil, nil)
p.From.Type = obj.TYPE_MEM
p.From.Name = obj.NAME_EXTERN
p.From.Sym = Linksym(s)
p.From.Offset = int64(off)
p.From3 = new(obj.Addr)
p.From3.Type = obj.TYPE_CONST
p.From3.Offset = int64(Widthptr)
p.To.Type = obj.TYPE_ADDR
p.To.Name = obj.NAME_EXTERN
p.To.Sym = Linksym(x)
p.To.Offset = int64(xoff)
off += Widthptr
return off
}
func gdata(nam *Node, nr *Node, wid int) {
if nr.Op == OLITERAL {
switch nr.Val().Ctype() {
case CTCPLX:
gdatacomplex(nam, nr.Val().U.(*Mpcplx))
return
case CTSTR:
gdatastring(nam, nr.Val().U.(string))
return
}
}
p := Thearch.Gins(obj.ADATA, nam, nr)
p.From3 = new(obj.Addr)
p.From3.Type = obj.TYPE_CONST
p.From3.Offset = int64(wid)
}
func gdatacomplex(nam *Node, cval *Mpcplx) {
w := cplxsubtype(int(nam.Type.Etype))
w = int(Types[w].Width)
p := Thearch.Gins(obj.ADATA, nam, nil)
p.From3 = new(obj.Addr)
p.From3.Type = obj.TYPE_CONST
p.From3.Offset = int64(w)
p.To.Type = obj.TYPE_FCONST
p.To.Val = mpgetflt(&cval.Real)
p = Thearch.Gins(obj.ADATA, nam, nil)
p.From3 = new(obj.Addr)
p.From3.Type = obj.TYPE_CONST
p.From3.Offset = int64(w)
p.From.Offset += int64(w)
p.To.Type = obj.TYPE_FCONST
p.To.Val = mpgetflt(&cval.Imag)
}
func gdatastring(nam *Node, sval string) {
var nod1 Node
p := Thearch.Gins(obj.ADATA, nam, nil)
Datastring(sval, &p.To)
p.From3 = new(obj.Addr)
p.From3.Type = obj.TYPE_CONST
p.From3.Offset = Types[Tptr].Width
p.To.Type = obj.TYPE_ADDR
//print("%v\n", p);
Nodconst(&nod1, Types[TINT], int64(len(sval)))
p = Thearch.Gins(obj.ADATA, nam, &nod1)
p.From3 = new(obj.Addr)
p.From3.Type = obj.TYPE_CONST
p.From3.Offset = int64(Widthint)
p.From.Offset += int64(Widthptr)
}