blob: df83298ecca0b352f0a98ef42c8a2f55a2c345f9 [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.
// Objwriter reads an object file description in an unspecified format
// and writes a Go object file. It is invoked by parts of the toolchain
// that have not yet been converted from C to Go and should not be
// used otherwise.
package main
import (
"bufio"
"bytes"
"flag"
"fmt"
"io"
"io/ioutil"
"log"
"math"
"os"
"runtime/pprof"
"strconv"
"strings"
"cmd/internal/obj"
"cmd/internal/obj/arm"
"cmd/internal/obj/i386"
"cmd/internal/obj/ppc64"
"cmd/internal/obj/x86"
)
var arch *obj.LinkArch
var cpuprofile = flag.String("cpuprofile", "", "write cpu profile to this file")
var memprofile = flag.String("memprofile", "", "write memory profile to this file")
func main() {
log.SetPrefix("goobj: ")
log.SetFlags(0)
flag.Parse()
if flag.NArg() == 1 && flag.Arg(0) == "ping" {
// old invocation from liblink, just testing that objwriter exists
return
}
if flag.NArg() != 4 {
fmt.Fprintf(os.Stderr, "usage: goobj infile objfile offset goarch\n")
os.Exit(2)
}
if *cpuprofile != "" {
f, err := os.Create(*cpuprofile)
if err != nil {
log.Fatal(err)
}
pprof.StartCPUProfile(f)
defer pprof.StopCPUProfile()
}
if *memprofile != "" {
f, err := os.Create(*memprofile)
if err != nil {
log.Fatal(err)
}
defer pprof.WriteHeapProfile(f)
}
switch flag.Arg(3) {
case "amd64":
arch = &x86.Linkamd64
case "amd64p32":
arch = &x86.Linkamd64p32
case "386":
// TODO(rsc): Move Link386 to package x86.
arch = &i386.Link386
case "arm":
arch = &arm.Linkarm
case "ppc64":
arch = &ppc64.Linkppc64
case "ppc64le":
arch = &ppc64.Linkppc64le
}
input()
}
const (
// must match liblink/objfilego.c
TypeEnd = iota
TypeCtxt
TypePlist
TypeSym
TypeProg
TypeAddr
TypeHist
)
var (
ctxt *obj.Link
plists = map[int64]*obj.Plist{}
syms = map[int64]*obj.LSym{}
progs = map[int64]*obj.Prog{}
hists = map[int64]*obj.Hist{}
undef = map[interface{}]bool{}
)
func input() {
args := flag.Args()
ctxt = obj.Linknew(arch)
ctxt.Debugasm = 1
ctxt.Bso = obj.Binitw(os.Stdout)
defer obj.Bflush(ctxt.Bso)
ctxt.Diag = log.Fatalf
f, err := os.Open(args[0])
if err != nil {
log.Fatal(err)
}
b := bufio.NewReaderSize(f, 1<<20)
if v := rdint(b); v != TypeCtxt {
log.Fatalf("invalid input - missing ctxt - got %d", v)
}
name := rdstring(b)
if name != ctxt.Arch.Name {
log.Fatalf("bad arch %s - want %s", name, ctxt.Arch.Name)
}
ctxt.Goarm = int32(rdint(b))
ctxt.Debugasm = int32(rdint(b))
ctxt.Trimpath = rdstring(b)
ctxt.Plist = rdplist(b)
ctxt.Plast = rdplist(b)
ctxt.Hist = rdhist(b)
ctxt.Ehist = rdhist(b)
for {
i := rdint(b)
if i < 0 {
break
}
ctxt.Hash[i] = rdsym(b)
}
last := int64(TypeCtxt)
Loop:
for {
t := rdint(b)
switch t {
default:
log.Fatalf("unexpected input after type %d: %v", last, t)
case TypeEnd:
break Loop
case TypePlist:
readplist(b, rdplist(b))
case TypeSym:
readsym(b, rdsym(b))
case TypeProg:
readprog(b, rdprog(b))
case TypeHist:
readhist(b, rdhist(b))
}
last = t
}
if len(undef) > 0 {
panic("missing definitions")
}
var buf bytes.Buffer
obuf := obj.Binitw(&buf)
obj.Writeobjdirect(ctxt, obuf)
obj.Bflush(obuf)
data, err := ioutil.ReadFile(args[1])
if err != nil {
log.Fatal(err)
}
offset, err := strconv.Atoi(args[2])
if err != nil {
log.Fatalf("bad offset: %v", err)
}
if offset > len(data) {
log.Fatalf("offset too large: %v > %v", offset, len(data))
}
old := data[offset:]
if len(old) > 0 && !bytes.Equal(old, buf.Bytes()) {
out := strings.TrimSuffix(args[0], ".in") + ".out"
if err := ioutil.WriteFile(out, append(data[:offset:offset], buf.Bytes()...), 0666); err != nil {
log.Fatal(err)
}
log.Fatalf("goobj produced different output:\n\toriginal: %s\n\tgoobj: %s", args[1], out)
}
if len(old) == 0 {
data = append(data, buf.Bytes()...)
if err := ioutil.WriteFile(args[1], data, 0666); err != nil {
log.Fatal(err)
}
}
}
func rdstring(b *bufio.Reader) string {
v := rdint(b)
buf := make([]byte, v)
io.ReadFull(b, buf)
return string(buf)
}
func rdint(b *bufio.Reader) int64 {
var v uint64
shift := uint(0)
for {
b, err := b.ReadByte()
if err != nil {
log.Fatal(err)
}
v |= uint64(b&0x7F) << shift
shift += 7
if b&0x80 == 0 {
break
}
}
return int64(v>>1) ^ int64(v<<63)>>63
}
func rdplist(b *bufio.Reader) *obj.Plist {
id := rdint(b)
if id == 0 {
return nil
}
pl := plists[id]
if pl == nil {
pl = new(obj.Plist)
plists[id] = pl
undef[pl] = true
}
return pl
}
func rdsym(b *bufio.Reader) *obj.LSym {
id := rdint(b)
if id == 0 {
return nil
}
sym := syms[id]
if sym == nil {
sym = new(obj.LSym)
syms[id] = sym
undef[sym] = true
}
return sym
}
func rdprog(b *bufio.Reader) *obj.Prog {
id := rdint(b)
if id == 0 {
return nil
}
prog := progs[id]
if prog == nil {
prog = new(obj.Prog)
prog.Ctxt = ctxt
progs[id] = prog
undef[prog] = true
}
return prog
}
func rdhist(b *bufio.Reader) *obj.Hist {
id := rdint(b)
if id == 0 {
return nil
}
h := hists[id]
if h == nil {
h = new(obj.Hist)
hists[id] = h
undef[h] = true
}
return h
}
func readplist(b *bufio.Reader, pl *obj.Plist) {
if !undef[pl] {
panic("double-def")
}
delete(undef, pl)
pl.Recur = int(rdint(b))
pl.Name = rdsym(b)
pl.Firstpc = rdprog(b)
pl.Link = rdplist(b)
}
func readsym(b *bufio.Reader, s *obj.LSym) {
if !undef[s] {
panic("double-def")
}
delete(undef, s)
s.Name = rdstring(b)
s.Extname = rdstring(b)
s.Type = int16(rdint(b))
s.Version = int16(rdint(b))
s.Dupok = uint8(rdint(b))
s.External = uint8(rdint(b))
s.Nosplit = uint8(rdint(b))
s.Reachable = uint8(rdint(b))
s.Cgoexport = uint8(rdint(b))
s.Special = uint8(rdint(b))
s.Stkcheck = uint8(rdint(b))
s.Hide = uint8(rdint(b))
s.Leaf = uint8(rdint(b))
s.Fnptr = uint8(rdint(b))
s.Seenglobl = uint8(rdint(b))
s.Onlist = uint8(rdint(b))
s.Symid = int16(rdint(b))
s.Dynid = int32(rdint(b))
s.Sig = int32(rdint(b))
s.Plt = int32(rdint(b))
s.Got = int32(rdint(b))
s.Align = int32(rdint(b))
s.Elfsym = int32(rdint(b))
s.Args = int32(rdint(b))
s.Locals = int32(rdint(b))
s.Value = rdint(b)
s.Size = rdint(b)
s.Hash = rdsym(b)
s.Allsym = rdsym(b)
s.Next = rdsym(b)
s.Sub = rdsym(b)
s.Outer = rdsym(b)
s.Gotype = rdsym(b)
s.Reachparent = rdsym(b)
s.Queue = rdsym(b)
s.File = rdstring(b)
s.Dynimplib = rdstring(b)
s.Dynimpvers = rdstring(b)
s.Text = rdprog(b)
s.Etext = rdprog(b)
n := int(rdint(b))
if n > 0 {
s.P = make([]byte, n)
io.ReadFull(b, s.P)
}
s.R = make([]obj.Reloc, int(rdint(b)))
for i := range s.R {
r := &s.R[i]
r.Off = int32(rdint(b))
r.Siz = uint8(rdint(b))
r.Done = uint8(rdint(b))
r.Type = int32(rdint(b))
r.Add = rdint(b)
r.Xadd = rdint(b)
r.Sym = rdsym(b)
r.Xsym = rdsym(b)
}
}
func readprog(b *bufio.Reader, p *obj.Prog) {
if !undef[p] {
panic("double-def")
}
delete(undef, p)
p.Pc = rdint(b)
p.Lineno = int32(rdint(b))
p.Link = rdprog(b)
p.As = int16(rdint(b))
p.Reg = int16(rdint(b))
p.Scond = uint8(rdint(b))
p.Width = int8(rdint(b))
readaddr(b, &p.From)
readaddr(b, &p.From3)
readaddr(b, &p.To)
}
func readaddr(b *bufio.Reader, a *obj.Addr) {
if rdint(b) != TypeAddr {
log.Fatal("out of sync")
}
a.Offset = rdint(b)
a.U.Dval = rdfloat(b)
buf := make([]byte, 8)
io.ReadFull(b, buf)
a.U.Sval = string(buf)
a.U.Branch = rdprog(b)
a.Sym = rdsym(b)
a.Gotype = rdsym(b)
a.Type = int16(rdint(b))
a.Index = int16(rdint(b))
a.Scale = int8(rdint(b))
a.Reg = int16(rdint(b))
a.Name = int8(rdint(b))
a.Class = int8(rdint(b))
a.Etype = uint8(rdint(b))
a.U.Argsize = int32(rdint(b))
a.Width = rdint(b)
}
func readhist(b *bufio.Reader, h *obj.Hist) {
if !undef[h] {
panic("double-def")
}
delete(undef, h)
h.Link = rdhist(b)
h.Name = rdstring(b)
h.Line = int32(rdint(b))
h.Offset = int32(rdint(b))
}
func rdfloat(b *bufio.Reader) float64 {
return math.Float64frombits(uint64(rdint(b)))
}