blob: b0d10b5fd5c029269cdc6f21d1646696d35c78a0 [file] [log] [blame]
// Copyright 2015 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 obj
import (
"bufio"
"bytes"
"fmt"
"io"
"log"
"os"
"strconv"
"strings"
"time"
)
const REG_NONE = 0
var start time.Time
func Cputime() float64 {
if start.IsZero() {
start = time.Now()
}
return time.Since(start).Seconds()
}
type Biobuf struct {
unget [2]int
numUnget int
f *os.File
r *bufio.Reader
w *bufio.Writer
linelen int
}
func Bopenw(name string) (*Biobuf, error) {
f, err := os.Create(name)
if err != nil {
return nil, err
}
return &Biobuf{f: f, w: bufio.NewWriter(f)}, nil
}
func Bopenr(name string) (*Biobuf, error) {
f, err := os.Open(name)
if err != nil {
return nil, err
}
return &Biobuf{f: f, r: bufio.NewReader(f)}, nil
}
func Binitw(w io.Writer) *Biobuf {
return &Biobuf{w: bufio.NewWriter(w)}
}
func (b *Biobuf) Write(p []byte) (int, error) {
return b.w.Write(p)
}
func Bwritestring(b *Biobuf, p string) (int, error) {
return b.w.WriteString(p)
}
func Bseek(b *Biobuf, offset int64, whence int) int64 {
if b.w != nil {
if err := b.w.Flush(); err != nil {
log.Fatalf("writing output: %v", err)
}
} else if b.r != nil {
if whence == 1 {
offset -= int64(b.r.Buffered())
}
}
off, err := b.f.Seek(offset, whence)
if err != nil {
log.Fatalf("seeking in output: %v", err)
}
if b.r != nil {
b.r.Reset(b.f)
}
return off
}
func Boffset(b *Biobuf) int64 {
if err := b.w.Flush(); err != nil {
log.Fatalf("writing output: %v", err)
}
off, err := b.f.Seek(0, 1)
if err != nil {
log.Fatalf("seeking in output: %v", err)
}
return off
}
func (b *Biobuf) Flush() error {
return b.w.Flush()
}
func Bwrite(b *Biobuf, p []byte) (int, error) {
return b.w.Write(p)
}
func Bputc(b *Biobuf, c byte) {
b.w.WriteByte(c)
}
const Beof = -1
func Bread(b *Biobuf, p []byte) int {
n, err := io.ReadFull(b.r, p)
if n == 0 {
if err != nil && err != io.EOF {
n = -1
}
}
return n
}
func Bgetc(b *Biobuf) int {
if b.numUnget > 0 {
b.numUnget--
return int(b.unget[b.numUnget])
}
c, err := b.r.ReadByte()
r := int(c)
if err != nil {
r = -1
}
b.unget[1] = b.unget[0]
b.unget[0] = r
return r
}
func Bgetrune(b *Biobuf) int {
r, _, err := b.r.ReadRune()
if err != nil {
return -1
}
return int(r)
}
func Bungetrune(b *Biobuf) {
b.r.UnreadRune()
}
func (b *Biobuf) Read(p []byte) (int, error) {
return b.r.Read(p)
}
func Brdline(b *Biobuf, delim int) string {
s, err := b.r.ReadBytes(byte(delim))
if err != nil {
log.Fatalf("reading input: %v", err)
}
b.linelen = len(s)
return string(s)
}
func Brdstr(b *Biobuf, delim int, cut int) string {
s, err := b.r.ReadString(byte(delim))
if err != nil {
log.Fatalf("reading input: %v", err)
}
if len(s) > 0 && cut > 0 {
s = s[:len(s)-1]
}
return s
}
func Access(name string, mode int) int {
if mode != 0 {
panic("bad access")
}
_, err := os.Stat(name)
if err != nil {
return -1
}
return 0
}
func Blinelen(b *Biobuf) int {
return b.linelen
}
func Bungetc(b *Biobuf) {
b.numUnget++
}
func Bflush(b *Biobuf) error {
return b.w.Flush()
}
func Bterm(b *Biobuf) error {
var err error
if b.w != nil {
err = b.w.Flush()
}
err1 := b.f.Close()
if err == nil {
err = err1
}
return err
}
func envOr(key, value string) string {
if x := os.Getenv(key); x != "" {
return x
}
return value
}
func Getgoroot() string {
return envOr("GOROOT", defaultGOROOT)
}
func Getgoarch() string {
return envOr("GOARCH", defaultGOARCH)
}
func Getgoos() string {
return envOr("GOOS", defaultGOOS)
}
func Getgoarm() string {
return envOr("GOARM", defaultGOARM)
}
func Getgo386() string {
return envOr("GO386", defaultGO386)
}
func Getgoextlinkenabled() string {
return envOr("GO_EXTLINK_ENABLED", defaultGO_EXTLINK_ENABLED)
}
func Getgoversion() string {
return version
}
func Atoi(s string) int {
i, _ := strconv.Atoi(s)
return i
}
func (p *Prog) Line() string {
return Linklinefmt(p.Ctxt, int(p.Lineno), false, false)
}
var armCondCode = []string{
".EQ",
".NE",
".CS",
".CC",
".MI",
".PL",
".VS",
".VC",
".HI",
".LS",
".GE",
".LT",
".GT",
".LE",
"",
".NV",
}
/* ARM scond byte */
const (
C_SCOND = (1 << 4) - 1
C_SBIT = 1 << 4
C_PBIT = 1 << 5
C_WBIT = 1 << 6
C_FBIT = 1 << 7
C_UBIT = 1 << 7
C_SCOND_XOR = 14
)
// CConv formats ARM condition codes.
func CConv(s uint8) string {
if s == 0 {
return ""
}
sc := armCondCode[(s&C_SCOND)^C_SCOND_XOR]
if s&C_SBIT != 0 {
sc += ".S"
}
if s&C_PBIT != 0 {
sc += ".P"
}
if s&C_WBIT != 0 {
sc += ".W"
}
if s&C_UBIT != 0 { /* ambiguous with FBIT */
sc += ".U"
}
return sc
}
func (p *Prog) String() string {
if p.Ctxt == nil {
return "<Prog without ctxt>"
}
sc := CConv(p.Scond)
var buf bytes.Buffer
fmt.Fprintf(&buf, "%.5d (%v)\t%v%s", p.Pc, p.Line(), Aconv(int(p.As)), sc)
sep := "\t"
if p.From.Type != TYPE_NONE {
fmt.Fprintf(&buf, "%s%v", sep, Dconv(p, &p.From))
sep = ", "
}
if p.Reg != REG_NONE {
// Should not happen but might as well show it if it does.
fmt.Fprintf(&buf, "%s%v", sep, Rconv(int(p.Reg)))
sep = ", "
}
if p.From3.Type != TYPE_NONE {
if p.From3.Type == TYPE_CONST && (p.As == ADATA || p.As == ATEXT || p.As == AGLOBL) {
// Special case - omit $.
fmt.Fprintf(&buf, "%s%d", sep, p.From3.Offset)
} else {
fmt.Fprintf(&buf, "%s%v", sep, Dconv(p, &p.From3))
}
sep = ", "
}
if p.To.Type != TYPE_NONE {
fmt.Fprintf(&buf, "%s%v", sep, Dconv(p, &p.To))
}
if p.To2.Type != TYPE_NONE {
fmt.Fprintf(&buf, "%s%v", sep, Dconv(p, &p.To2))
}
return buf.String()
}
func (ctxt *Link) NewProg() *Prog {
p := new(Prog) // should be the only call to this; all others should use ctxt.NewProg
p.Ctxt = ctxt
return p
}
func (ctxt *Link) Line(n int) string {
return Linklinefmt(ctxt, n, false, false)
}
func Getcallerpc(interface{}) uintptr {
return 1
}
func (ctxt *Link) Dconv(a *Addr) string {
return Dconv(nil, a)
}
func Dconv(p *Prog, a *Addr) string {
var str string
switch a.Type {
default:
str = fmt.Sprintf("type=%d", a.Type)
case TYPE_NONE:
str = ""
if a.Name != NAME_NONE || a.Reg != 0 || a.Sym != nil {
str = fmt.Sprintf("%v(%v)(NONE)", Mconv(a), Rconv(int(a.Reg)))
}
case TYPE_REG:
// TODO(rsc): This special case is for x86 instructions like
// PINSRQ CX,$1,X6
// where the $1 is included in the p->to Addr.
// Move into a new field.
if a.Offset != 0 {
str = fmt.Sprintf("$%d,%v", a.Offset, Rconv(int(a.Reg)))
break
}
str = fmt.Sprintf("%v", Rconv(int(a.Reg)))
if a.Name != TYPE_NONE || a.Sym != nil {
str = fmt.Sprintf("%v(%v)(REG)", Mconv(a), Rconv(int(a.Reg)))
}
case TYPE_BRANCH:
if a.Sym != nil {
str = fmt.Sprintf("%s(SB)", a.Sym.Name)
} else if p != nil && p.Pcond != nil {
str = fmt.Sprintf("%d", p.Pcond.Pc)
} else if a.Val != nil {
str = fmt.Sprintf("%d", a.Val.(*Prog).Pc)
} else {
str = fmt.Sprintf("%d(PC)", a.Offset)
}
case TYPE_INDIR:
str = fmt.Sprintf("*%s", Mconv(a))
case TYPE_MEM:
str = Mconv(a)
if a.Index != REG_NONE {
str += fmt.Sprintf("(%v*%d)", Rconv(int(a.Index)), int(a.Scale))
}
case TYPE_CONST:
if a.Reg != 0 {
str = fmt.Sprintf("$%v(%v)", Mconv(a), Rconv(int(a.Reg)))
} else {
str = fmt.Sprintf("$%v", Mconv(a))
}
case TYPE_TEXTSIZE:
if a.Val.(int32) == ArgsSizeUnknown {
str = fmt.Sprintf("$%d", a.Offset)
} else {
str = fmt.Sprintf("$%d-%d", a.Offset, a.Val.(int32))
}
case TYPE_FCONST:
str = fmt.Sprintf("%.17g", a.Val.(float64))
// Make sure 1 prints as 1.0
if !strings.ContainsAny(str, ".e") {
str += ".0"
}
str = fmt.Sprintf("$(%s)", str)
case TYPE_SCONST:
str = fmt.Sprintf("$%q", a.Val.(string))
case TYPE_ADDR:
str = fmt.Sprintf("$%s", Mconv(a))
case TYPE_SHIFT:
v := int(a.Offset)
op := string("<<>>->@>"[((v>>5)&3)<<1:])
if v&(1<<4) != 0 {
str = fmt.Sprintf("R%d%c%cR%d", v&15, op[0], op[1], (v>>8)&15)
} else {
str = fmt.Sprintf("R%d%c%c%d", v&15, op[0], op[1], (v>>7)&31)
}
if a.Reg != 0 {
str += fmt.Sprintf("(%v)", Rconv(int(a.Reg)))
}
case TYPE_REGREG:
str = fmt.Sprintf("(%v, %v)", Rconv(int(a.Reg)), Rconv(int(a.Offset)))
case TYPE_REGREG2:
str = fmt.Sprintf("%v, %v", Rconv(int(a.Reg)), Rconv(int(a.Offset)))
case TYPE_REGLIST:
str = regListConv(int(a.Offset))
}
return str
}
func Mconv(a *Addr) string {
var str string
switch a.Name {
default:
str = fmt.Sprintf("name=%d", a.Name)
case NAME_NONE:
switch {
case a.Reg == REG_NONE:
str = fmt.Sprintf("%d", a.Offset)
case a.Offset == 0:
str = fmt.Sprintf("(%v)", Rconv(int(a.Reg)))
case a.Offset != 0:
str = fmt.Sprintf("%d(%v)", a.Offset, Rconv(int(a.Reg)))
}
case NAME_EXTERN:
str = fmt.Sprintf("%s%s(SB)", a.Sym.Name, offConv(a.Offset))
case NAME_STATIC:
str = fmt.Sprintf("%s<>%s(SB)", a.Sym.Name, offConv(a.Offset))
case NAME_AUTO:
if a.Sym != nil {
str = fmt.Sprintf("%s%s(SP)", a.Sym.Name, offConv(a.Offset))
} else {
str = fmt.Sprintf("%s(SP)", offConv(a.Offset))
}
case NAME_PARAM:
if a.Sym != nil {
str = fmt.Sprintf("%s%s(FP)", a.Sym.Name, offConv(a.Offset))
} else {
str = fmt.Sprintf("%s(FP)", offConv(a.Offset))
}
}
return str
}
func offConv(off int64) string {
if off == 0 {
return ""
}
return fmt.Sprintf("%+d", off)
}
type regSet struct {
lo int
hi int
Rconv func(int) string
}
// Few enough architectures that a linear scan is fastest.
// Not even worth sorting.
var regSpace []regSet
/*
Each architecture defines a register space as a unique
integer range.
Here is the list of architectures and the base of their register spaces.
*/
const (
// Because of masking operations in the encodings, each register
// space should start at 0 modulo some power of 2.
RBase386 = 1 * 1024
RBaseAMD64 = 2 * 1024
RBaseARM = 3 * 1024
RBasePPC64 = 4 * 1024 // range [4k, 8k)
RBaseARM64 = 8 * 1024 // range [8k, 12k)
)
// RegisterRegister binds a pretty-printer (Rconv) for register
// numbers to a given register number range. Lo is inclusive,
// hi exclusive (valid registers are lo through hi-1).
func RegisterRegister(lo, hi int, Rconv func(int) string) {
regSpace = append(regSpace, regSet{lo, hi, Rconv})
}
func Rconv(reg int) string {
if reg == REG_NONE {
return "NONE"
}
for i := range regSpace {
rs := &regSpace[i]
if rs.lo <= reg && reg < rs.hi {
return rs.Rconv(reg)
}
}
return fmt.Sprintf("R???%d", reg)
}
func regListConv(list int) string {
str := ""
for i := 0; i < 16; i++ { // TODO: 16 is ARM-specific.
if list&(1<<uint(i)) != 0 {
if str == "" {
str += "["
} else {
str += ","
}
// This is ARM-specific; R10 is g.
if i == 10 {
str += "g"
} else {
str += fmt.Sprintf("R%d", i)
}
}
}
str += "]"
return str
}
/*
Each architecture defines an instruction (A*) space as a unique
integer range.
Global opcodes like CALL start at 0; the architecture-specific ones
start at a distinct, big-maskable offsets.
Here is the list of architectures and the base of their opcode spaces.
*/
const (
ABase386 = (1 + iota) << 12
ABaseARM
ABaseAMD64
ABasePPC64
ABaseARM64
AMask = 1<<12 - 1 // AND with this to use the opcode as an array index.
)
type opSet struct {
lo int
names []string
}
// Not even worth sorting
var aSpace []opSet
// RegisterOpcode binds a list of instruction names
// to a given instruction number range.
func RegisterOpcode(lo int, Anames []string) {
aSpace = append(aSpace, opSet{lo, Anames})
}
func Aconv(a int) string {
if a < A_ARCHSPECIFIC {
return Anames[a]
}
for i := range aSpace {
as := &aSpace[i]
if as.lo <= a && a < as.lo+len(as.names) {
return as.names[a-as.lo]
}
}
return fmt.Sprintf("A???%d", a)
}
var Anames = []string{
"XXX",
"CALL",
"CHECKNIL",
"DATA",
"DUFFCOPY",
"DUFFZERO",
"END",
"FUNCDATA",
"GLOBL",
"JMP",
"NOP",
"PCDATA",
"RET",
"TEXT",
"TYPE",
"UNDEF",
"USEFIELD",
"VARDEF",
"VARKILL",
}