// used otherwise.
package main
-import "cmd/internal/obj"
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"
)
-// TODO(rsc): Implement.
-// For now we just check that the objwriter binary is available to be run.
+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() {
- _ = obj.Exported
- _ = x86.Exported
+ 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 = uint8(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 = uint8(rdint(b))
+ a.Scale = int8(rdint(b))
+ a.Reg = int8(rdint(b))
+ a.Name = int8(rdint(b))
+ a.Class = int8(rdint(b))
+ a.Etype = uint8(rdint(b))
+ a.Offset2 = 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)))
}