| // 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 = 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))) |
| } |