From: Russ Cox Date: Sat, 28 Feb 2015 03:57:28 +0000 (-0500) Subject: cmd/new5l etc: convert from C to Go X-Git-Tag: go1.5beta1~1805 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=1f9dbb60ef5cf8b0027c1149fdb1d6f08c41cfed;p=gostls13.git cmd/new5l etc: convert from C to Go Using rsc.io/c2go rev fc8cbfa's run.ld script. Change-Id: I4d4d14fce96f8ce7a934bf8b9701b84fa9cf772d Reviewed-on: https://go-review.googlesource.com/6335 Reviewed-by: Rob Pike --- diff --git a/src/cmd/internal/ld/ar.go b/src/cmd/internal/ld/ar.go new file mode 100644 index 0000000000..c464a623f6 --- /dev/null +++ b/src/cmd/internal/ld/ar.go @@ -0,0 +1,51 @@ +// Inferno utils/include/ar.h +// http://code.google.com/p/inferno-os/source/browse/utils/include/ar.h +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package ld + +const ( + SARMAG = 8 + SARNAME = 16 + SAR_HDR = 16 + 44 +) + +var ARMAG string = "!\n" + +var ARFMAG string = "`\n" + +type ArHdr struct { + name string + date string + uid string + gid string + mode string + size string + fmag string +} diff --git a/src/cmd/internal/ld/arch.go b/src/cmd/internal/ld/arch.go new file mode 100644 index 0000000000..3dd6a5f206 --- /dev/null +++ b/src/cmd/internal/ld/arch.go @@ -0,0 +1,67 @@ +// 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 ld + +import "encoding/binary" + +var Linkarm = LinkArch{ + ByteOrder: binary.LittleEndian, + Name: "arm", + Thechar: '5', + Endian: LittleEndian, + Minlc: 4, + Ptrsize: 4, + Regsize: 4, +} + +var Linkamd64 = LinkArch{ + ByteOrder: binary.LittleEndian, + Name: "amd64", + Thechar: '6', + Endian: LittleEndian, + Minlc: 1, + Ptrsize: 8, + Regsize: 8, +} + +var Linkamd64p32 = LinkArch{ + ByteOrder: binary.LittleEndian, + Name: "amd64p32", + Thechar: '6', + Endian: LittleEndian, + Minlc: 1, + Ptrsize: 4, + Regsize: 8, +} + +var Link386 = LinkArch{ + ByteOrder: binary.LittleEndian, + Name: "386", + Thechar: '8', + Endian: LittleEndian, + Minlc: 1, + Ptrsize: 4, + Regsize: 4, +} + +var Linkppc64 = LinkArch{ + ByteOrder: binary.BigEndian, + Name: "ppc64", + Thechar: '9', + Endian: BigEndian, + Minlc: 4, + Ptrsize: 8, + Regsize: 8, +} + +var Linkppc64le = LinkArch{ + ByteOrder: binary.LittleEndian, + Name: "ppc64le", + Thechar: '9', + Endian: LittleEndian, + Minlc: 4, + Ptrsize: 8, + Regsize: 8, +} diff --git a/src/cmd/internal/ld/data.go b/src/cmd/internal/ld/data.go new file mode 100644 index 0000000000..ff2d09f4ae --- /dev/null +++ b/src/cmd/internal/ld/data.go @@ -0,0 +1,1798 @@ +// Derived from Inferno utils/6l/obj.c and utils/6l/span.c +// http://code.google.com/p/inferno-os/source/browse/utils/6l/obj.c +// http://code.google.com/p/inferno-os/source/browse/utils/6l/span.c +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package ld + +import ( + "cmd/internal/obj" + "fmt" + "log" + "strings" +) + +func Symgrow(ctxt *Link, s *LSym, siz int64) { + if int64(int(siz)) != siz { + log.Fatalf("symgrow size %d too long", siz) + } + if int64(len(s.P)) >= siz { + return + } + for cap(s.P) < int(siz) { + s.P = append(s.P[:len(s.P)], 0) + } + s.P = s.P[:siz] +} + +func Addrel(s *LSym) *Reloc { + s.R = append(s.R, Reloc{}) + return &s.R[len(s.R)-1] +} + +func setuintxx(ctxt *Link, s *LSym, off int64, v uint64, wid int64) int64 { + if s.Type == 0 { + s.Type = SDATA + } + s.Reachable = true + if s.Size < off+wid { + s.Size = off + wid + Symgrow(ctxt, s, s.Size) + } + + switch wid { + case 1: + s.P[off] = uint8(v) + case 2: + ctxt.Arch.ByteOrder.PutUint16(s.P[off:], uint16(v)) + case 4: + ctxt.Arch.ByteOrder.PutUint32(s.P[off:], uint32(v)) + case 8: + ctxt.Arch.ByteOrder.PutUint64(s.P[off:], uint64(v)) + } + + return off + wid +} + +func adduintxx(ctxt *Link, s *LSym, v uint64, wid int) int64 { + var off int64 + + off = s.Size + setuintxx(ctxt, s, off, v, int64(wid)) + return off +} + +func Adduint8(ctxt *Link, s *LSym, v uint8) int64 { + return adduintxx(ctxt, s, uint64(v), 1) +} + +func Adduint16(ctxt *Link, s *LSym, v uint16) int64 { + return adduintxx(ctxt, s, uint64(v), 2) +} + +func Adduint32(ctxt *Link, s *LSym, v uint32) int64 { + return adduintxx(ctxt, s, uint64(v), 4) +} + +func Adduint64(ctxt *Link, s *LSym, v uint64) int64 { + return adduintxx(ctxt, s, v, 8) +} + +func setuint8(ctxt *Link, s *LSym, r int64, v uint8) int64 { + return setuintxx(ctxt, s, r, uint64(v), 1) +} + +func setuint32(ctxt *Link, s *LSym, r int64, v uint32) int64 { + return setuintxx(ctxt, s, r, uint64(v), 4) +} + +func Addaddrplus(ctxt *Link, s *LSym, t *LSym, add int64) int64 { + var i int64 + var r *Reloc + + if s.Type == 0 { + s.Type = SDATA + } + s.Reachable = true + i = s.Size + s.Size += int64(ctxt.Arch.Ptrsize) + Symgrow(ctxt, s, s.Size) + r = Addrel(s) + r.Sym = t + r.Off = int32(i) + r.Siz = uint8(ctxt.Arch.Ptrsize) + r.Type = R_ADDR + r.Add = add + return i + int64(r.Siz) +} + +func Addpcrelplus(ctxt *Link, s *LSym, t *LSym, add int64) int64 { + var i int64 + var r *Reloc + + if s.Type == 0 { + s.Type = SDATA + } + s.Reachable = true + i = s.Size + s.Size += 4 + Symgrow(ctxt, s, s.Size) + r = Addrel(s) + r.Sym = t + r.Off = int32(i) + r.Add = add + r.Type = R_PCREL + r.Siz = 4 + return i + int64(r.Siz) +} + +func Addaddr(ctxt *Link, s *LSym, t *LSym) int64 { + return Addaddrplus(ctxt, s, t, 0) +} + +func setaddrplus(ctxt *Link, s *LSym, off int64, t *LSym, add int64) int64 { + var r *Reloc + + if s.Type == 0 { + s.Type = SDATA + } + s.Reachable = true + if off+int64(ctxt.Arch.Ptrsize) > s.Size { + s.Size = off + int64(ctxt.Arch.Ptrsize) + Symgrow(ctxt, s, s.Size) + } + + r = Addrel(s) + r.Sym = t + r.Off = int32(off) + r.Siz = uint8(ctxt.Arch.Ptrsize) + r.Type = R_ADDR + r.Add = add + return off + int64(r.Siz) +} + +func setaddr(ctxt *Link, s *LSym, off int64, t *LSym) int64 { + return setaddrplus(ctxt, s, off, t, 0) +} + +func addsize(ctxt *Link, s *LSym, t *LSym) int64 { + var i int64 + var r *Reloc + + if s.Type == 0 { + s.Type = SDATA + } + s.Reachable = true + i = s.Size + s.Size += int64(ctxt.Arch.Ptrsize) + Symgrow(ctxt, s, s.Size) + r = Addrel(s) + r.Sym = t + r.Off = int32(i) + r.Siz = uint8(ctxt.Arch.Ptrsize) + r.Type = R_SIZE + return i + int64(r.Siz) +} + +func addaddrplus4(ctxt *Link, s *LSym, t *LSym, add int64) int64 { + var i int64 + var r *Reloc + + if s.Type == 0 { + s.Type = SDATA + } + s.Reachable = true + i = s.Size + s.Size += 4 + Symgrow(ctxt, s, s.Size) + r = Addrel(s) + r.Sym = t + r.Off = int32(i) + r.Siz = 4 + r.Type = R_ADDR + r.Add = add + return i + int64(r.Siz) +} + +/* + * divide-and-conquer list-link + * sort of LSym* structures. + * Used for the data block. + */ +func datcmp(s1 *LSym, s2 *LSym) int { + if s1.Type != s2.Type { + return int(s1.Type) - int(s2.Type) + } + + // For ppc64, we want to interleave the .got and .toc sections + // from input files. Both are type SELFGOT, so in that case + // fall through to the name comparison (conveniently, .got + // sorts before .toc). + if s1.Type != SELFGOT && s1.Size != s2.Size { + if s1.Size < s2.Size { + return -1 + } + return +1 + } + + return stringsCompare(s1.Name, s2.Name) +} + +func listnextp(s *LSym) **LSym { + return &s.Next +} + +func listsubp(s *LSym) **LSym { + return &s.Sub +} + +func listsort(l *LSym, cmp func(*LSym, *LSym) int, nextp func(*LSym) **LSym) *LSym { + var l1 *LSym + var l2 *LSym + var le *LSym + + if l == nil || *nextp(l) == nil { + return l + } + + l1 = l + l2 = l + for { + l2 = *nextp(l2) + if l2 == nil { + break + } + l2 = *nextp(l2) + if l2 == nil { + break + } + l1 = *nextp(l1) + } + + l2 = *nextp(l1) + *nextp(l1) = nil + l1 = listsort(l, cmp, nextp) + l2 = listsort(l2, cmp, nextp) + + /* set up lead element */ + if cmp(l1, l2) < 0 { + l = l1 + l1 = *nextp(l1) + } else { + l = l2 + l2 = *nextp(l2) + } + + le = l + + for { + if l1 == nil { + for l2 != nil { + *nextp(le) = l2 + le = l2 + l2 = *nextp(l2) + } + + *nextp(le) = nil + break + } + + if l2 == nil { + for l1 != nil { + *nextp(le) = l1 + le = l1 + l1 = *nextp(l1) + } + + break + } + + if cmp(l1, l2) < 0 { + *nextp(le) = l1 + le = l1 + l1 = *nextp(l1) + } else { + *nextp(le) = l2 + le = l2 + l2 = *nextp(l2) + } + } + + *nextp(le) = nil + return l +} + +func relocsym(s *LSym) { + var r *Reloc + var rs *LSym + var i16 int16 + var ri int32 + var off int32 + var siz int32 + var fl int32 + var o int64 + + Ctxt.Cursym = s + for ri = 0; ri < int32(len(s.R)); ri++ { + r = &s.R[ri] + r.Done = 1 + off = r.Off + siz = int32(r.Siz) + if off < 0 || off+siz > int32(len(s.P)) { + Diag("%s: invalid relocation %d+%d not in [%d,%d)", s.Name, off, siz, 0, len(s.P)) + continue + } + + if r.Sym != nil && (r.Sym.Type&(SMASK|SHIDDEN) == 0 || r.Sym.Type&SMASK == SXREF) { + Diag("%s: not defined", r.Sym.Name) + continue + } + + if r.Type >= 256 { + continue + } + if r.Siz == 0 { // informational relocation - no work to do + continue + } + + // Solaris needs the ability to reference dynimport symbols. + if HEADTYPE != Hsolaris && r.Sym != nil && r.Sym.Type == SDYNIMPORT { + Diag("unhandled relocation for %s (type %d rtype %d)", r.Sym.Name, r.Sym.Type, r.Type) + } + if r.Sym != nil && r.Sym.Type != STLSBSS && !r.Sym.Reachable { + Diag("unreachable sym in relocation: %s %s", s.Name, r.Sym.Name) + } + + // Android emulates runtime.tlsg as a regular variable. + if r.Type == R_TLS && goos == "android" { + r.Type = R_ADDR + } + + switch r.Type { + default: + o = 0 + if Thearch.Archreloc(r, s, &o) < 0 { + Diag("unknown reloc %d", r.Type) + } + + case R_TLS: + if Linkmode == LinkInternal && Iself && Thearch.Thechar == '5' { + // On ELF ARM, the thread pointer is 8 bytes before + // the start of the thread-local data block, so add 8 + // to the actual TLS offset (r->sym->value). + // This 8 seems to be a fundamental constant of + // ELF on ARM (or maybe Glibc on ARM); it is not + // related to the fact that our own TLS storage happens + // to take up 8 bytes. + o = 8 + r.Sym.Value + + break + } + + r.Done = 0 + o = 0 + if Thearch.Thechar != '6' { + o = r.Add + } + + case R_TLS_LE: + if Linkmode == LinkExternal && Iself && HEADTYPE != Hopenbsd { + r.Done = 0 + r.Sym = Ctxt.Tlsg + r.Xsym = Ctxt.Tlsg + r.Xadd = r.Add + o = 0 + if Thearch.Thechar != '6' { + o = r.Add + } + break + } + + o = int64(Ctxt.Tlsoffset) + r.Add + + case R_TLS_IE: + if Linkmode == LinkExternal && Iself && HEADTYPE != Hopenbsd { + r.Done = 0 + r.Sym = Ctxt.Tlsg + r.Xsym = Ctxt.Tlsg + r.Xadd = r.Add + o = 0 + if Thearch.Thechar != '6' { + o = r.Add + } + break + } + + if Iself || Ctxt.Headtype == Hplan9 { + o = int64(Ctxt.Tlsoffset) + r.Add + } else if Ctxt.Headtype == Hwindows { + o = r.Add + } else { + log.Fatalf("unexpected R_TLS_IE relocation for %s", Headstr(Ctxt.Headtype)) + } + + case R_ADDR: + if Linkmode == LinkExternal && r.Sym.Type != SCONST { + r.Done = 0 + + // set up addend for eventual relocation via outer symbol. + rs = r.Sym + + r.Xadd = r.Add + for rs.Outer != nil { + r.Xadd += Symaddr(rs) - Symaddr(rs.Outer) + rs = rs.Outer + } + + if rs.Type != SHOSTOBJ && rs.Type != SDYNIMPORT && rs.Sect == nil { + Diag("missing section for %s", rs.Name) + } + r.Xsym = rs + + o = r.Xadd + if Iself { + if Thearch.Thechar == '6' { + o = 0 + } + } else if HEADTYPE == Hdarwin { + if rs.Type != SHOSTOBJ { + o += Symaddr(rs) + } + } else { + Diag("unhandled pcrel relocation for %s", headstring) + } + + break + } + + o = Symaddr(r.Sym) + r.Add + + // On amd64, 4-byte offsets will be sign-extended, so it is impossible to + // access more than 2GB of static data; fail at link time is better than + // fail at runtime. See http://golang.org/issue/7980. + // Instead of special casing only amd64, we treat this as an error on all + // 64-bit architectures so as to be future-proof. + if int32(o) < 0 && Thearch.Ptrsize > 4 && siz == 4 { + Diag("non-pc-relative relocation address is too big: %#x", uint64(o)) + Errorexit() + } + + // r->sym can be null when CALL $(constant) is transformed from absolute PC to relative PC call. + case R_CALL, + R_PCREL: + if Linkmode == LinkExternal && r.Sym != nil && r.Sym.Type != SCONST && r.Sym.Sect != Ctxt.Cursym.Sect { + r.Done = 0 + + // set up addend for eventual relocation via outer symbol. + rs = r.Sym + + r.Xadd = r.Add + for rs.Outer != nil { + r.Xadd += Symaddr(rs) - Symaddr(rs.Outer) + rs = rs.Outer + } + + r.Xadd -= int64(r.Siz) // relative to address after the relocated chunk + if rs.Type != SHOSTOBJ && rs.Type != SDYNIMPORT && rs.Sect == nil { + Diag("missing section for %s", rs.Name) + } + r.Xsym = rs + + o = r.Xadd + if Iself { + if Thearch.Thechar == '6' { + o = 0 + } + } else if HEADTYPE == Hdarwin { + if r.Type == R_CALL { + if rs.Type != SHOSTOBJ { + o += int64(uint64(Symaddr(rs)) - (rs.Sect.(*Section)).Vaddr) + } + o -= int64(r.Off) // relative to section offset, not symbol + } else { + o += int64(r.Siz) + } + } else { + Diag("unhandled pcrel relocation for %s", headstring) + } + + break + } + + o = 0 + if r.Sym != nil { + o += Symaddr(r.Sym) + } + + // NOTE: The (int32) cast on the next line works around a bug in Plan 9's 8c + // compiler. The expression s->value + r->off + r->siz is int32 + int32 + + // uchar, and Plan 9 8c incorrectly treats the expression as type uint32 + // instead of int32, causing incorrect values when sign extended for adding + // to o. The bug only occurs on Plan 9, because this C program is compiled by + // the standard host compiler (gcc on most other systems). + o += r.Add - (s.Value + int64(r.Off) + int64(int32(r.Siz))) + + case R_SIZE: + o = r.Sym.Size + r.Add + } + + if r.Variant != RV_NONE { + o = Thearch.Archrelocvariant(r, s, o) + } + + //print("relocate %s %#llux (%#llux+%#llux, size %d) => %s %#llux +%#llx [%llx]\n", s->name, (uvlong)(s->value+off), (uvlong)s->value, (uvlong)r->off, r->siz, r->sym ? r->sym->name : "", (uvlong)symaddr(r->sym), (vlong)r->add, (vlong)o); + switch siz { + default: + Ctxt.Cursym = s + Diag("bad reloc size %#x for %s", uint32(siz), r.Sym.Name) + fallthrough + + // TODO(rsc): Remove. + case 1: + s.P[off] = byte(int8(o)) + + case 2: + if o != int64(int16(o)) { + Diag("relocation address is too big: %#x", o) + } + i16 = int16(o) + Ctxt.Arch.ByteOrder.PutUint16(s.P[off:], uint16(i16)) + + case 4: + if r.Type == R_PCREL || r.Type == R_CALL { + if o != int64(int32(o)) { + Diag("pc-relative relocation address is too big: %#x", o) + } + } else { + if o != int64(int32(o)) && o != int64(uint32(o)) { + Diag("non-pc-relative relocation address is too big: %#x", uint64(o)) + } + } + + fl = int32(o) + Ctxt.Arch.ByteOrder.PutUint32(s.P[off:], uint32(fl)) + + case 8: + Ctxt.Arch.ByteOrder.PutUint64(s.P[off:], uint64(o)) + } + } +} + +func reloc() { + var s *LSym + + if Debug['v'] != 0 { + fmt.Fprintf(&Bso, "%5.2f reloc\n", obj.Cputime()) + } + Bflush(&Bso) + + for s = Ctxt.Textp; s != nil; s = s.Next { + relocsym(s) + } + for s = datap; s != nil; s = s.Next { + relocsym(s) + } +} + +func dynrelocsym(s *LSym) { + var ri int + var r *Reloc + + if HEADTYPE == Hwindows { + var rel *LSym + var targ *LSym + + rel = Linklookup(Ctxt, ".rel", 0) + if s == rel { + return + } + for ri = 0; ri < len(s.R); ri++ { + r = &s.R[ri] + targ = r.Sym + if targ == nil { + continue + } + if !targ.Reachable { + Diag("internal inconsistency: dynamic symbol %s is not reachable.", targ.Name) + } + if r.Sym.Plt == -2 && r.Sym.Got != -2 { // make dynimport JMP table for PE object files. + targ.Plt = int32(rel.Size) + r.Sym = rel + r.Add = int64(targ.Plt) + + // jmp *addr + if Thearch.Thechar == '8' { + Adduint8(Ctxt, rel, 0xff) + Adduint8(Ctxt, rel, 0x25) + Addaddr(Ctxt, rel, targ) + Adduint8(Ctxt, rel, 0x90) + Adduint8(Ctxt, rel, 0x90) + } else { + Adduint8(Ctxt, rel, 0xff) + Adduint8(Ctxt, rel, 0x24) + Adduint8(Ctxt, rel, 0x25) + addaddrplus4(Ctxt, rel, targ, 0) + Adduint8(Ctxt, rel, 0x90) + } + } else if r.Sym.Plt >= 0 { + r.Sym = rel + r.Add = int64(targ.Plt) + } + } + + return + } + + for ri = 0; ri < len(s.R); ri++ { + r = &s.R[ri] + if r.Sym != nil && r.Sym.Type == SDYNIMPORT || r.Type >= 256 { + if r.Sym != nil && !r.Sym.Reachable { + Diag("internal inconsistency: dynamic symbol %s is not reachable.", r.Sym.Name) + } + Thearch.Adddynrel(s, r) + } + } +} + +func dynreloc() { + var s *LSym + + // -d suppresses dynamic loader format, so we may as well not + // compute these sections or mark their symbols as reachable. + if Debug['d'] != 0 && HEADTYPE != Hwindows { + return + } + if Debug['v'] != 0 { + fmt.Fprintf(&Bso, "%5.2f reloc\n", obj.Cputime()) + } + Bflush(&Bso) + + for s = Ctxt.Textp; s != nil; s = s.Next { + dynrelocsym(s) + } + for s = datap; s != nil; s = s.Next { + dynrelocsym(s) + } + if Iself { + elfdynhash() + } +} + +func blk(start *LSym, addr int64, size int64) { + var sym *LSym + var eaddr int64 + var p []byte + var ep []byte + + for sym = start; sym != nil; sym = sym.Next { + if sym.Type&SSUB == 0 && sym.Value >= addr { + break + } + } + + eaddr = addr + size + for ; sym != nil; sym = sym.Next { + if sym.Type&SSUB != 0 { + continue + } + if sym.Value >= eaddr { + break + } + Ctxt.Cursym = sym + if sym.Value < addr { + Diag("phase error: addr=%#x but sym=%#x type=%d", int64(addr), int64(sym.Value), sym.Type) + Errorexit() + } + + for ; addr < sym.Value; addr++ { + Cput(0) + } + p = sym.P + ep = p[len(sym.P):] + for -cap(p) < -cap(ep) { + Cput(uint8(p[0])) + p = p[1:] + } + addr += int64(len(sym.P)) + for ; addr < sym.Value+sym.Size; addr++ { + Cput(0) + } + if addr != sym.Value+sym.Size { + Diag("phase error: addr=%#x value+size=%#x", int64(addr), int64(sym.Value)+sym.Size) + Errorexit() + } + + if sym.Value+sym.Size >= eaddr { + break + } + } + + for ; addr < eaddr; addr++ { + Cput(0) + } + Cflush() +} + +func Codeblk(addr int64, size int64) { + var sym *LSym + var eaddr int64 + var n int64 + var q []byte + + if Debug['a'] != 0 { + fmt.Fprintf(&Bso, "codeblk [%#x,%#x) at offset %#x\n", addr, addr+size, Cpos()) + } + + blk(Ctxt.Textp, addr, size) + + /* again for printing */ + if Debug['a'] == 0 { + return + } + + for sym = Ctxt.Textp; sym != nil; sym = sym.Next { + if !sym.Reachable { + continue + } + if sym.Value >= addr { + break + } + } + + eaddr = addr + size + for ; sym != nil; sym = sym.Next { + if !sym.Reachable { + continue + } + if sym.Value >= eaddr { + break + } + + if addr < sym.Value { + fmt.Fprintf(&Bso, "%-20s %.8x|", "_", uint64(int64(addr))) + for ; addr < sym.Value; addr++ { + fmt.Fprintf(&Bso, " %.2x", 0) + } + fmt.Fprintf(&Bso, "\n") + } + + fmt.Fprintf(&Bso, "%.6x\t%-20s\n", uint64(int64(addr)), sym.Name) + n = sym.Size + q = sym.P + + for n >= 16 { + fmt.Fprintf(&Bso, "%.6x\t%%-20.16I\n", uint64(addr), q) + addr += 16 + q = q[16:] + n -= 16 + } + + if n > 0 { + fmt.Fprintf(&Bso, "%.6x\t%%-20.*I\n", uint64(addr), int(n), q) + } + addr += n + } + + if addr < eaddr { + fmt.Fprintf(&Bso, "%-20s %.8x|", "_", uint64(int64(addr))) + for ; addr < eaddr; addr++ { + fmt.Fprintf(&Bso, " %.2x", 0) + } + } + + Bflush(&Bso) +} + +func Datblk(addr int64, size int64) { + var sym *LSym + var i int64 + var eaddr int64 + var p []byte + var ep []byte + var typ string + var rsname string + var r *Reloc + + if Debug['a'] != 0 { + fmt.Fprintf(&Bso, "datblk [%#x,%#x) at offset %#x\n", addr, addr+size, Cpos()) + } + + blk(datap, addr, size) + + /* again for printing */ + if Debug['a'] == 0 { + return + } + + for sym = datap; sym != nil; sym = sym.Next { + if sym.Value >= addr { + break + } + } + + eaddr = addr + size + for ; sym != nil; sym = sym.Next { + if sym.Value >= eaddr { + break + } + if addr < sym.Value { + fmt.Fprintf(&Bso, "\t%.8x| 00 ...\n", uint64(addr)) + addr = sym.Value + } + + fmt.Fprintf(&Bso, "%s\n\t%.8x|", sym.Name, uint(addr)) + p = sym.P + ep = p[len(sym.P):] + for -cap(p) < -cap(ep) { + if -cap(p) > -cap(sym.P) && int(-cap(p)+cap(sym.P))%16 == 0 { + fmt.Fprintf(&Bso, "\n\t%.8x|", uint(addr+int64(-cap(p)+cap(sym.P)))) + } + fmt.Fprintf(&Bso, " %.2x", p[0]) + p = p[1:] + } + + addr += int64(len(sym.P)) + for ; addr < sym.Value+sym.Size; addr++ { + fmt.Fprintf(&Bso, " %.2x", 0) + } + fmt.Fprintf(&Bso, "\n") + + if Linkmode == LinkExternal { + for i = 0; i < int64(len(sym.R)); i++ { + r = &sym.R[i] + rsname = "" + if r.Sym != nil { + rsname = r.Sym.Name + } + typ = "?" + switch r.Type { + case R_ADDR: + typ = "addr" + + case R_PCREL: + typ = "pcrel" + + case R_CALL: + typ = "call" + } + + fmt.Fprintf(&Bso, "\treloc %.8x/%d %s %s+%#x [%#x]\n", uint(sym.Value+int64(r.Off)), r.Siz, typ, rsname, int64(r.Add), int64(r.Sym.Value+r.Add)) + } + } + } + + if addr < eaddr { + fmt.Fprintf(&Bso, "\t%.8x| 00 ...\n", uint(addr)) + } + fmt.Fprintf(&Bso, "\t%.8x|\n", uint(eaddr)) +} + +func strnput(s string, n int) { + for ; n > 0 && s != ""; s = s[1:] { + Cput(uint8(s[0])) + n-- + } + + for n > 0 { + Cput(0) + n-- + } +} + +var addstrdata_name string + +func addstrdata1(arg string) { + if strings.HasPrefix(arg, "VALUE:") { + addstrdata(addstrdata_name, arg[6:]) + } else { + addstrdata_name = arg + } +} + +func addstrdata(name string, value string) { + var s *LSym + var sp *LSym + var p string + var reachable bool + + p = fmt.Sprintf("%s.str", name) + sp = Linklookup(Ctxt, p, 0) + + Addstring(sp, value) + sp.Type = SRODATA + + s = Linklookup(Ctxt, name, 0) + s.Size = 0 + s.Dupok = 1 + reachable = s.Reachable + Addaddr(Ctxt, s, sp) + adduintxx(Ctxt, s, uint64(len(value)), Thearch.Ptrsize) + + // addstring, addaddr, etc., mark the symbols as reachable. + // In this case that is not necessarily true, so stick to what + // we know before entering this function. + s.Reachable = reachable + + sp.Reachable = reachable +} + +func Addstring(s *LSym, str string) int64 { + var n int + var r int32 + + if s.Type == 0 { + s.Type = SNOPTRDATA + } + s.Reachable = true + r = int32(s.Size) + n = len(str) + 1 + if s.Name == ".shstrtab" { + elfsetstring(str, int(r)) + } + Symgrow(Ctxt, s, int64(r)+int64(n)) + copy(s.P[r:], str) + s.P[int(r)+len(str)] = 0 + s.Size += int64(n) + return int64(r) +} + +func dosymtype() { + var s *LSym + + for s = Ctxt.Allsym; s != nil; s = s.Allsym { + if len(s.P) > 0 { + if s.Type == SBSS { + s.Type = SDATA + } + if s.Type == SNOPTRBSS { + s.Type = SNOPTRDATA + } + } + } +} + +func symalign(s *LSym) int32 { + var align int32 + + if s.Align != 0 { + return s.Align + } + + align = int32(Thearch.Maxalign) + for int64(align) > s.Size && align > 1 { + align >>= 1 + } + if align < s.Align { + align = s.Align + } + return align +} + +func aligndatsize(datsize int64, s *LSym) int64 { + return Rnd(datsize, int64(symalign(s))) +} + +// maxalign returns the maximum required alignment for +// the list of symbols s; the list stops when s->type exceeds type. +func maxalign(s *LSym, type_ int) int32 { + var align int32 + var max int32 + + max = 0 + for ; s != nil && int(s.Type) <= type_; s = s.Next { + align = symalign(s) + if max < align { + max = align + } + } + + return max +} + +// Helper object for building GC type programs. +type ProgGen struct { + s *LSym + datasize int32 + data [256 / obj.PointersPerByte]uint8 + pos int64 +} + +func proggeninit(g *ProgGen, s *LSym) { + g.s = s + g.datasize = 0 + g.pos = 0 + g.data = [256 / obj.PointersPerByte]uint8{} +} + +func proggenemit(g *ProgGen, v uint8) { + Adduint8(Ctxt, g.s, v) +} + +// Writes insData block from g->data. +func proggendataflush(g *ProgGen) { + var i int32 + var s int32 + + if g.datasize == 0 { + return + } + proggenemit(g, obj.InsData) + proggenemit(g, uint8(g.datasize)) + s = (g.datasize + obj.PointersPerByte - 1) / obj.PointersPerByte + for i = 0; i < s; i++ { + proggenemit(g, g.data[i]) + } + g.datasize = 0 + g.data = [256 / obj.PointersPerByte]uint8{} +} + +func proggendata(g *ProgGen, d uint8) { + g.data[g.datasize/obj.PointersPerByte] |= d << uint((g.datasize%obj.PointersPerByte)*obj.BitsPerPointer) + g.datasize++ + if g.datasize == 255 { + proggendataflush(g) + } +} + +// Skip v bytes due to alignment, etc. +func proggenskip(g *ProgGen, off int64, v int64) { + var i int64 + + for i = off; i < off+v; i++ { + if (i % int64(Thearch.Ptrsize)) == 0 { + proggendata(g, obj.BitsScalar) + } + } +} + +// Emit insArray instruction. +func proggenarray(g *ProgGen, length int64) { + var i int32 + + proggendataflush(g) + proggenemit(g, obj.InsArray) + for i = 0; i < int32(Thearch.Ptrsize); (func() { i++; length >>= 8 })() { + proggenemit(g, uint8(length)) + } +} + +func proggenarrayend(g *ProgGen) { + proggendataflush(g) + proggenemit(g, obj.InsArrayEnd) +} + +func proggenfini(g *ProgGen, size int64) { + proggenskip(g, g.pos, size-g.pos) + proggendataflush(g) + proggenemit(g, obj.InsEnd) +} + +// This function generates GC pointer info for global variables. +func proggenaddsym(g *ProgGen, s *LSym) { + var gcprog *LSym + var mask []byte + var i int64 + var size int64 + + if s.Size == 0 { + return + } + + // Skip alignment hole from the previous symbol. + proggenskip(g, g.pos, s.Value-g.pos) + + g.pos += s.Value - g.pos + + // The test for names beginning with . here is meant + // to keep .dynamic and .dynsym from turning up as + // conservative symbols. They should be marked SELFSECT + // and not SDATA, but sometimes that doesn't happen. + // Leave debugging the SDATA issue for the Go rewrite. + + if s.Gotype == nil && s.Size >= int64(Thearch.Ptrsize) && s.Name[0] != '.' { + // conservative scan + Diag("missing Go type information for global symbol: %s size %d", s.Name, int(s.Size)) + + if (s.Size%int64(Thearch.Ptrsize) != 0) || (g.pos%int64(Thearch.Ptrsize) != 0) { + Diag("proggenaddsym: unaligned conservative symbol %s: size=%d pos=%d", s.Name, s.Size, g.pos) + } + size = (s.Size + int64(Thearch.Ptrsize) - 1) / int64(Thearch.Ptrsize) * int64(Thearch.Ptrsize) + if size < int64(32*Thearch.Ptrsize) { + // Emit small symbols as data. + for i = 0; i < size/int64(Thearch.Ptrsize); i++ { + proggendata(g, obj.BitsPointer) + } + } else { + // Emit large symbols as array. + proggenarray(g, size/int64(Thearch.Ptrsize)) + + proggendata(g, obj.BitsPointer) + proggenarrayend(g) + } + + g.pos = s.Value + size + } else if s.Gotype == nil || decodetype_noptr(s.Gotype) != 0 || s.Size < int64(Thearch.Ptrsize) || s.Name[0] == '.' { + // no scan + if s.Size < int64(32*Thearch.Ptrsize) { + // Emit small symbols as data. + // This case also handles unaligned and tiny symbols, so tread carefully. + for i = s.Value; i < s.Value+s.Size; i++ { + if (i % int64(Thearch.Ptrsize)) == 0 { + proggendata(g, obj.BitsScalar) + } + } + } else { + // Emit large symbols as array. + if (s.Size%int64(Thearch.Ptrsize) != 0) || (g.pos%int64(Thearch.Ptrsize) != 0) { + Diag("proggenaddsym: unaligned noscan symbol %s: size=%d pos=%d", s.Name, s.Size, g.pos) + } + proggenarray(g, s.Size/int64(Thearch.Ptrsize)) + proggendata(g, obj.BitsScalar) + proggenarrayend(g) + } + + g.pos = s.Value + s.Size + } else if decodetype_usegcprog(s.Gotype) != 0 { + // gc program, copy directly + proggendataflush(g) + + gcprog = decodetype_gcprog(s.Gotype) + size = decodetype_size(s.Gotype) + if (size%int64(Thearch.Ptrsize) != 0) || (g.pos%int64(Thearch.Ptrsize) != 0) { + Diag("proggenaddsym: unaligned gcprog symbol %s: size=%d pos=%d", s.Name, s.Size, g.pos) + } + for i = 0; i < int64(len(gcprog.P)-1); i++ { + proggenemit(g, uint8(gcprog.P[i])) + } + g.pos = s.Value + size + } else { + // gc mask, it's small so emit as data + mask = decodetype_gcmask(s.Gotype) + + size = decodetype_size(s.Gotype) + if (size%int64(Thearch.Ptrsize) != 0) || (g.pos%int64(Thearch.Ptrsize) != 0) { + Diag("proggenaddsym: unaligned gcmask symbol %s: size=%d pos=%d", s.Name, s.Size, g.pos) + } + for i = 0; i < size; i += int64(Thearch.Ptrsize) { + proggendata(g, uint8((mask[i/int64(Thearch.Ptrsize)/2]>>uint64((i/int64(Thearch.Ptrsize)%2)*4+2))&obj.BitsMask)) + } + g.pos = s.Value + size + } +} + +func growdatsize(datsizep *int64, s *LSym) { + var datsize int64 + + datsize = *datsizep + if s.Size < 0 { + Diag("negative size (datsize = %d, s->size = %d)", datsize, s.Size) + } + if datsize+s.Size < datsize { + Diag("symbol too large (datsize = %d, s->size = %d)", datsize, s.Size) + } + *datsizep = datsize + s.Size +} + +func dodata() { + var n int32 + var datsize int64 + var sect *Section + var segro *Segment + var s *LSym + var last *LSym + var l **LSym + var toc *LSym + var gcdata *LSym + var gcbss *LSym + var gen ProgGen + + if Debug['v'] != 0 { + fmt.Fprintf(&Bso, "%5.2f dodata\n", obj.Cputime()) + } + Bflush(&Bso) + + last = nil + datap = nil + + for s = Ctxt.Allsym; s != nil; s = s.Allsym { + if !s.Reachable || s.Special != 0 { + continue + } + if STEXT < s.Type && s.Type < SXREF { + if s.Onlist != 0 { + log.Fatalf("symbol %s listed multiple times", s.Name) + } + s.Onlist = 1 + if last == nil { + datap = s + } else { + last.Next = s + } + s.Next = nil + last = s + } + } + + for s = datap; s != nil; s = s.Next { + if int64(len(s.P)) > s.Size { + Diag("%s: initialize bounds (%d < %d)", s.Name, int64(s.Size), len(s.P)) + } + } + + /* + * now that we have the datap list, but before we start + * to assign addresses, record all the necessary + * dynamic relocations. these will grow the relocation + * symbol, which is itself data. + * + * on darwin, we need the symbol table numbers for dynreloc. + */ + if HEADTYPE == Hdarwin { + machosymorder() + } + dynreloc() + + /* some symbols may no longer belong in datap (Mach-O) */ + for l = &datap; ; { + s = *l + if s == nil { + break + } + + if s.Type <= STEXT || SXREF <= s.Type { + *l = s.Next + } else { + l = &s.Next + } + } + + *l = nil + + datap = listsort(datap, datcmp, listnextp) + + /* + * allocate sections. list is sorted by type, + * so we can just walk it for each piece we want to emit. + * segdata is processed before segtext, because we need + * to see all symbols in the .data and .bss sections in order + * to generate garbage collection information. + */ + + /* begin segdata */ + + /* skip symbols belonging to segtext */ + s = datap + + for ; s != nil && s.Type < SELFSECT; s = s.Next { + } + + /* writable ELF sections */ + datsize = 0 + + for ; s != nil && s.Type < SELFGOT; s = s.Next { + sect = addsection(&Segdata, s.Name, 06) + sect.Align = symalign(s) + datsize = Rnd(datsize, int64(sect.Align)) + sect.Vaddr = uint64(datsize) + s.Sect = sect + s.Type = SDATA + s.Value = int64(uint64(datsize) - sect.Vaddr) + growdatsize(&datsize, s) + sect.Length = uint64(datsize) - sect.Vaddr + } + + /* .got (and .toc on ppc64) */ + if s.Type == SELFGOT { + sect = addsection(&Segdata, ".got", 06) + sect.Align = maxalign(s, SELFGOT) + datsize = Rnd(datsize, int64(sect.Align)) + sect.Vaddr = uint64(datsize) + for ; s != nil && s.Type == SELFGOT; s = s.Next { + datsize = aligndatsize(datsize, s) + s.Sect = sect + s.Type = SDATA + s.Value = int64(uint64(datsize) - sect.Vaddr) + + // Resolve .TOC. symbol for this object file (ppc64) + toc = Linkrlookup(Ctxt, ".TOC.", int(s.Version)) + + if toc != nil { + toc.Sect = sect + toc.Outer = s + toc.Sub = s.Sub + s.Sub = toc + + toc.Value = 0x8000 + } + + growdatsize(&datsize, s) + } + + sect.Length = uint64(datsize) - sect.Vaddr + } + + /* pointer-free data */ + sect = addsection(&Segdata, ".noptrdata", 06) + + sect.Align = maxalign(s, SINITARR-1) + datsize = Rnd(datsize, int64(sect.Align)) + sect.Vaddr = uint64(datsize) + Linklookup(Ctxt, "runtime.noptrdata", 0).Sect = sect + Linklookup(Ctxt, "runtime.enoptrdata", 0).Sect = sect + for ; s != nil && s.Type < SINITARR; s = s.Next { + datsize = aligndatsize(datsize, s) + s.Sect = sect + s.Type = SDATA + s.Value = int64(uint64(datsize) - sect.Vaddr) + growdatsize(&datsize, s) + } + + sect.Length = uint64(datsize) - sect.Vaddr + + /* shared library initializer */ + if Flag_shared != 0 { + sect = addsection(&Segdata, ".init_array", 06) + sect.Align = maxalign(s, SINITARR) + datsize = Rnd(datsize, int64(sect.Align)) + sect.Vaddr = uint64(datsize) + for ; s != nil && s.Type == SINITARR; s = s.Next { + datsize = aligndatsize(datsize, s) + s.Sect = sect + s.Value = int64(uint64(datsize) - sect.Vaddr) + growdatsize(&datsize, s) + } + + sect.Length = uint64(datsize) - sect.Vaddr + } + + /* data */ + sect = addsection(&Segdata, ".data", 06) + + sect.Align = maxalign(s, SBSS-1) + datsize = Rnd(datsize, int64(sect.Align)) + sect.Vaddr = uint64(datsize) + Linklookup(Ctxt, "runtime.data", 0).Sect = sect + Linklookup(Ctxt, "runtime.edata", 0).Sect = sect + gcdata = Linklookup(Ctxt, "runtime.gcdata", 0) + proggeninit(&gen, gcdata) + for ; s != nil && s.Type < SBSS; s = s.Next { + if s.Type == SINITARR { + Ctxt.Cursym = s + Diag("unexpected symbol type %d", s.Type) + } + + s.Sect = sect + s.Type = SDATA + datsize = aligndatsize(datsize, s) + s.Value = int64(uint64(datsize) - sect.Vaddr) + proggenaddsym(&gen, s) // gc + growdatsize(&datsize, s) + } + + sect.Length = uint64(datsize) - sect.Vaddr + proggenfini(&gen, int64(sect.Length)) // gc + + /* bss */ + sect = addsection(&Segdata, ".bss", 06) + + sect.Align = maxalign(s, SNOPTRBSS-1) + datsize = Rnd(datsize, int64(sect.Align)) + sect.Vaddr = uint64(datsize) + Linklookup(Ctxt, "runtime.bss", 0).Sect = sect + Linklookup(Ctxt, "runtime.ebss", 0).Sect = sect + gcbss = Linklookup(Ctxt, "runtime.gcbss", 0) + proggeninit(&gen, gcbss) + for ; s != nil && s.Type < SNOPTRBSS; s = s.Next { + s.Sect = sect + datsize = aligndatsize(datsize, s) + s.Value = int64(uint64(datsize) - sect.Vaddr) + proggenaddsym(&gen, s) // gc + growdatsize(&datsize, s) + } + + sect.Length = uint64(datsize) - sect.Vaddr + proggenfini(&gen, int64(sect.Length)) // gc + + /* pointer-free bss */ + sect = addsection(&Segdata, ".noptrbss", 06) + + sect.Align = maxalign(s, SNOPTRBSS) + datsize = Rnd(datsize, int64(sect.Align)) + sect.Vaddr = uint64(datsize) + Linklookup(Ctxt, "runtime.noptrbss", 0).Sect = sect + Linklookup(Ctxt, "runtime.enoptrbss", 0).Sect = sect + for ; s != nil && s.Type == SNOPTRBSS; s = s.Next { + datsize = aligndatsize(datsize, s) + s.Sect = sect + s.Value = int64(uint64(datsize) - sect.Vaddr) + growdatsize(&datsize, s) + } + + sect.Length = uint64(datsize) - sect.Vaddr + Linklookup(Ctxt, "runtime.end", 0).Sect = sect + + // 6g uses 4-byte relocation offsets, so the entire segment must fit in 32 bits. + if datsize != int64(uint32(datsize)) { + Diag("data or bss segment too large") + } + + if Iself && Linkmode == LinkExternal && s != nil && s.Type == STLSBSS && HEADTYPE != Hopenbsd { + sect = addsection(&Segdata, ".tbss", 06) + sect.Align = int32(Thearch.Ptrsize) + sect.Vaddr = 0 + datsize = 0 + for ; s != nil && s.Type == STLSBSS; s = s.Next { + datsize = aligndatsize(datsize, s) + s.Sect = sect + s.Value = int64(uint64(datsize) - sect.Vaddr) + growdatsize(&datsize, s) + } + + sect.Length = uint64(datsize) + } else { + // Might be internal linking but still using cgo. + // In that case, the only possible STLSBSS symbol is runtime.tlsg. + // Give it offset 0, because it's the only thing here. + if s != nil && s.Type == STLSBSS && s.Name == "runtime.tlsg" { + s.Value = 0 + s = s.Next + } + } + + if s != nil { + Ctxt.Cursym = nil + Diag("unexpected symbol type %d for %s", s.Type, s.Name) + } + + /* + * We finished data, begin read-only data. + * Not all systems support a separate read-only non-executable data section. + * ELF systems do. + * OS X and Plan 9 do not. + * Windows PE may, but if so we have not implemented it. + * And if we're using external linking mode, the point is moot, + * since it's not our decision; that code expects the sections in + * segtext. + */ + if Iself && Linkmode == LinkInternal { + segro = &Segrodata + } else { + segro = &Segtext + } + + s = datap + + datsize = 0 + + /* read-only executable ELF, Mach-O sections */ + for ; s != nil && s.Type < STYPE; s = s.Next { + sect = addsection(&Segtext, s.Name, 04) + sect.Align = symalign(s) + datsize = Rnd(datsize, int64(sect.Align)) + sect.Vaddr = uint64(datsize) + s.Sect = sect + s.Type = SRODATA + s.Value = int64(uint64(datsize) - sect.Vaddr) + growdatsize(&datsize, s) + sect.Length = uint64(datsize) - sect.Vaddr + } + + /* read-only data */ + sect = addsection(segro, ".rodata", 04) + + sect.Align = maxalign(s, STYPELINK-1) + datsize = Rnd(datsize, int64(sect.Align)) + sect.Vaddr = 0 + Linklookup(Ctxt, "runtime.rodata", 0).Sect = sect + Linklookup(Ctxt, "runtime.erodata", 0).Sect = sect + for ; s != nil && s.Type < STYPELINK; s = s.Next { + datsize = aligndatsize(datsize, s) + s.Sect = sect + s.Type = SRODATA + s.Value = int64(uint64(datsize) - sect.Vaddr) + growdatsize(&datsize, s) + } + + sect.Length = uint64(datsize) - sect.Vaddr + + /* typelink */ + sect = addsection(segro, ".typelink", 04) + + sect.Align = maxalign(s, STYPELINK) + datsize = Rnd(datsize, int64(sect.Align)) + sect.Vaddr = uint64(datsize) + Linklookup(Ctxt, "runtime.typelink", 0).Sect = sect + Linklookup(Ctxt, "runtime.etypelink", 0).Sect = sect + for ; s != nil && s.Type == STYPELINK; s = s.Next { + datsize = aligndatsize(datsize, s) + s.Sect = sect + s.Type = SRODATA + s.Value = int64(uint64(datsize) - sect.Vaddr) + growdatsize(&datsize, s) + } + + sect.Length = uint64(datsize) - sect.Vaddr + + /* gosymtab */ + sect = addsection(segro, ".gosymtab", 04) + + sect.Align = maxalign(s, SPCLNTAB-1) + datsize = Rnd(datsize, int64(sect.Align)) + sect.Vaddr = uint64(datsize) + Linklookup(Ctxt, "runtime.symtab", 0).Sect = sect + Linklookup(Ctxt, "runtime.esymtab", 0).Sect = sect + for ; s != nil && s.Type < SPCLNTAB; s = s.Next { + datsize = aligndatsize(datsize, s) + s.Sect = sect + s.Type = SRODATA + s.Value = int64(uint64(datsize) - sect.Vaddr) + growdatsize(&datsize, s) + } + + sect.Length = uint64(datsize) - sect.Vaddr + + /* gopclntab */ + sect = addsection(segro, ".gopclntab", 04) + + sect.Align = maxalign(s, SELFROSECT-1) + datsize = Rnd(datsize, int64(sect.Align)) + sect.Vaddr = uint64(datsize) + Linklookup(Ctxt, "runtime.pclntab", 0).Sect = sect + Linklookup(Ctxt, "runtime.epclntab", 0).Sect = sect + for ; s != nil && s.Type < SELFROSECT; s = s.Next { + datsize = aligndatsize(datsize, s) + s.Sect = sect + s.Type = SRODATA + s.Value = int64(uint64(datsize) - sect.Vaddr) + growdatsize(&datsize, s) + } + + sect.Length = uint64(datsize) - sect.Vaddr + + /* read-only ELF, Mach-O sections */ + for ; s != nil && s.Type < SELFSECT; s = s.Next { + sect = addsection(segro, s.Name, 04) + sect.Align = symalign(s) + datsize = Rnd(datsize, int64(sect.Align)) + sect.Vaddr = uint64(datsize) + s.Sect = sect + s.Type = SRODATA + s.Value = int64(uint64(datsize) - sect.Vaddr) + growdatsize(&datsize, s) + sect.Length = uint64(datsize) - sect.Vaddr + } + + // 6g uses 4-byte relocation offsets, so the entire segment must fit in 32 bits. + if datsize != int64(uint32(datsize)) { + Diag("read-only data segment too large") + } + + /* number the sections */ + n = 1 + + for sect = Segtext.Sect; sect != nil; sect = sect.Next { + sect.Extnum = int16(n) + n++ + } + for sect = Segrodata.Sect; sect != nil; sect = sect.Next { + sect.Extnum = int16(n) + n++ + } + for sect = Segdata.Sect; sect != nil; sect = sect.Next { + sect.Extnum = int16(n) + n++ + } +} + +// assign addresses to text +func textaddress() { + var va uint64 + var sect *Section + var sym *LSym + var sub *LSym + + addsection(&Segtext, ".text", 05) + + // Assign PCs in text segment. + // Could parallelize, by assigning to text + // and then letting threads copy down, but probably not worth it. + sect = Segtext.Sect + + sect.Align = int32(Funcalign) + Linklookup(Ctxt, "runtime.text", 0).Sect = sect + Linklookup(Ctxt, "runtime.etext", 0).Sect = sect + va = uint64(INITTEXT) + sect.Vaddr = va + for sym = Ctxt.Textp; sym != nil; sym = sym.Next { + sym.Sect = sect + if sym.Type&SSUB != 0 { + continue + } + if sym.Align != 0 { + va = uint64(Rnd(int64(va), int64(sym.Align))) + } else { + va = uint64(Rnd(int64(va), int64(Funcalign))) + } + sym.Value = 0 + for sub = sym; sub != nil; sub = sub.Sub { + sub.Value += int64(va) + } + if sym.Size == 0 && sym.Sub != nil { + Ctxt.Cursym = sym + } + if sym.Size < MINFUNC { + va += MINFUNC // spacing required for findfunctab + } else { + va += uint64(sym.Size) + } + } + + sect.Length = va - sect.Vaddr +} + +// assign addresses +func address() { + var s *Section + var text *Section + var data *Section + var rodata *Section + var symtab *Section + var pclntab *Section + var noptr *Section + var bss *Section + var noptrbss *Section + var typelink *Section + var sym *LSym + var sub *LSym + var va uint64 + var vlen int64 + + va = uint64(INITTEXT) + Segtext.Rwx = 05 + Segtext.Vaddr = va + Segtext.Fileoff = uint64(HEADR) + for s = Segtext.Sect; s != nil; s = s.Next { + va = uint64(Rnd(int64(va), int64(s.Align))) + s.Vaddr = va + va += s.Length + } + + Segtext.Length = va - uint64(INITTEXT) + Segtext.Filelen = Segtext.Length + if HEADTYPE == Hnacl { + va += 32 // room for the "halt sled" + } + + if Segrodata.Sect != nil { + // align to page boundary so as not to mix + // rodata and executable text. + va = uint64(Rnd(int64(va), int64(INITRND))) + + Segrodata.Rwx = 04 + Segrodata.Vaddr = va + Segrodata.Fileoff = va - Segtext.Vaddr + Segtext.Fileoff + Segrodata.Filelen = 0 + for s = Segrodata.Sect; s != nil; s = s.Next { + va = uint64(Rnd(int64(va), int64(s.Align))) + s.Vaddr = va + va += s.Length + } + + Segrodata.Length = va - Segrodata.Vaddr + Segrodata.Filelen = Segrodata.Length + } + + va = uint64(Rnd(int64(va), int64(INITRND))) + Segdata.Rwx = 06 + Segdata.Vaddr = va + Segdata.Fileoff = va - Segtext.Vaddr + Segtext.Fileoff + Segdata.Filelen = 0 + if HEADTYPE == Hwindows { + Segdata.Fileoff = Segtext.Fileoff + uint64(Rnd(int64(Segtext.Length), PEFILEALIGN)) + } + if HEADTYPE == Hplan9 { + Segdata.Fileoff = Segtext.Fileoff + Segtext.Filelen + } + data = nil + noptr = nil + bss = nil + noptrbss = nil + for s = Segdata.Sect; s != nil; s = s.Next { + vlen = int64(s.Length) + if s.Next != nil { + vlen = int64(s.Next.Vaddr - s.Vaddr) + } + s.Vaddr = va + va += uint64(vlen) + Segdata.Length = va - Segdata.Vaddr + if s.Name == ".data" { + data = s + } + if s.Name == ".noptrdata" { + noptr = s + } + if s.Name == ".bss" { + bss = s + } + if s.Name == ".noptrbss" { + noptrbss = s + } + } + + Segdata.Filelen = bss.Vaddr - Segdata.Vaddr + + text = Segtext.Sect + if Segrodata.Sect != nil { + rodata = Segrodata.Sect + } else { + rodata = text.Next + } + typelink = rodata.Next + symtab = typelink.Next + pclntab = symtab.Next + + for sym = datap; sym != nil; sym = sym.Next { + Ctxt.Cursym = sym + if sym.Sect != nil { + sym.Value += int64((sym.Sect.(*Section)).Vaddr) + } + for sub = sym.Sub; sub != nil; sub = sub.Sub { + sub.Value += sym.Value + } + } + + xdefine("runtime.text", STEXT, int64(text.Vaddr)) + xdefine("runtime.etext", STEXT, int64(text.Vaddr+text.Length)) + xdefine("runtime.rodata", SRODATA, int64(rodata.Vaddr)) + xdefine("runtime.erodata", SRODATA, int64(rodata.Vaddr+rodata.Length)) + xdefine("runtime.typelink", SRODATA, int64(typelink.Vaddr)) + xdefine("runtime.etypelink", SRODATA, int64(typelink.Vaddr+typelink.Length)) + + sym = Linklookup(Ctxt, "runtime.gcdata", 0) + xdefine("runtime.egcdata", SRODATA, Symaddr(sym)+sym.Size) + Linklookup(Ctxt, "runtime.egcdata", 0).Sect = sym.Sect + + sym = Linklookup(Ctxt, "runtime.gcbss", 0) + xdefine("runtime.egcbss", SRODATA, Symaddr(sym)+sym.Size) + Linklookup(Ctxt, "runtime.egcbss", 0).Sect = sym.Sect + + xdefine("runtime.symtab", SRODATA, int64(symtab.Vaddr)) + xdefine("runtime.esymtab", SRODATA, int64(symtab.Vaddr+symtab.Length)) + xdefine("runtime.pclntab", SRODATA, int64(pclntab.Vaddr)) + xdefine("runtime.epclntab", SRODATA, int64(pclntab.Vaddr+pclntab.Length)) + xdefine("runtime.noptrdata", SNOPTRDATA, int64(noptr.Vaddr)) + xdefine("runtime.enoptrdata", SNOPTRDATA, int64(noptr.Vaddr+noptr.Length)) + xdefine("runtime.bss", SBSS, int64(bss.Vaddr)) + xdefine("runtime.ebss", SBSS, int64(bss.Vaddr+bss.Length)) + xdefine("runtime.data", SDATA, int64(data.Vaddr)) + xdefine("runtime.edata", SDATA, int64(data.Vaddr+data.Length)) + xdefine("runtime.noptrbss", SNOPTRBSS, int64(noptrbss.Vaddr)) + xdefine("runtime.enoptrbss", SNOPTRBSS, int64(noptrbss.Vaddr+noptrbss.Length)) + xdefine("runtime.end", SBSS, int64(Segdata.Vaddr+Segdata.Length)) +} diff --git a/src/cmd/internal/ld/decodesym.go b/src/cmd/internal/ld/decodesym.go new file mode 100644 index 0000000000..c7b1a2f169 --- /dev/null +++ b/src/cmd/internal/ld/decodesym.go @@ -0,0 +1,183 @@ +// Copyright 2012 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 ld + +import "cmd/internal/obj" + +// Decoding the type.* symbols. This has to be in sync with +// ../../runtime/type.go, or more specifically, with what +// ../gc/reflect.c stuffs in these. + +func decode_reloc(s *LSym, off int32) *Reloc { + var i int + + for i = 0; i < len(s.R); i++ { + if s.R[i].Off == off { + return &s.R[i:][0] + } + } + return nil +} + +func decode_reloc_sym(s *LSym, off int32) *LSym { + var r *Reloc + + r = decode_reloc(s, off) + if r == nil { + return nil + } + return r.Sym +} + +func decode_inuxi(p []byte, sz int) uint64 { + switch sz { + case 2: + return uint64(Ctxt.Arch.ByteOrder.Uint16(p)) + case 4: + return uint64(Ctxt.Arch.ByteOrder.Uint32(p)) + case 8: + return Ctxt.Arch.ByteOrder.Uint64(p) + } + Diag("dwarf: decode inuxi %d", sz) + Errorexit() + return 0 +} + +func commonsize() int { + return 8*Thearch.Ptrsize + 8 +} + +// Type.commonType.kind +func decodetype_kind(s *LSym) uint8 { + return uint8(s.P[1*Thearch.Ptrsize+7] & obj.KindMask) // 0x13 / 0x1f +} + +// Type.commonType.kind +func decodetype_noptr(s *LSym) uint8 { + return uint8(s.P[1*Thearch.Ptrsize+7] & obj.KindNoPointers) // 0x13 / 0x1f +} + +// Type.commonType.kind +func decodetype_usegcprog(s *LSym) uint8 { + return uint8(s.P[1*Thearch.Ptrsize+7] & obj.KindGCProg) // 0x13 / 0x1f +} + +// Type.commonType.size +func decodetype_size(s *LSym) int64 { + return int64(decode_inuxi(s.P, Thearch.Ptrsize)) // 0x8 / 0x10 +} + +// Type.commonType.gc +func decodetype_gcprog(s *LSym) *LSym { + return decode_reloc_sym(s, 1*int32(Thearch.Ptrsize)+8+2*int32(Thearch.Ptrsize)) +} + +func decodetype_gcmask(s *LSym) []byte { + var mask *LSym + + mask = decode_reloc_sym(s, 1*int32(Thearch.Ptrsize)+8+1*int32(Thearch.Ptrsize)) + return mask.P +} + +// Type.ArrayType.elem and Type.SliceType.Elem +func decodetype_arrayelem(s *LSym) *LSym { + return decode_reloc_sym(s, int32(commonsize())) // 0x1c / 0x30 +} + +func decodetype_arraylen(s *LSym) int64 { + return int64(decode_inuxi(s.P[commonsize()+2*Thearch.Ptrsize:], Thearch.Ptrsize)) +} + +// Type.PtrType.elem +func decodetype_ptrelem(s *LSym) *LSym { + return decode_reloc_sym(s, int32(commonsize())) // 0x1c / 0x30 +} + +// Type.MapType.key, elem +func decodetype_mapkey(s *LSym) *LSym { + return decode_reloc_sym(s, int32(commonsize())) // 0x1c / 0x30 +} + +func decodetype_mapvalue(s *LSym) *LSym { + return decode_reloc_sym(s, int32(commonsize())+int32(Thearch.Ptrsize)) // 0x20 / 0x38 +} + +// Type.ChanType.elem +func decodetype_chanelem(s *LSym) *LSym { + return decode_reloc_sym(s, int32(commonsize())) // 0x1c / 0x30 +} + +// Type.FuncType.dotdotdot +func decodetype_funcdotdotdot(s *LSym) int { + return int(s.P[commonsize()]) +} + +// Type.FuncType.in.length +func decodetype_funcincount(s *LSym) int { + return int(decode_inuxi(s.P[commonsize()+2*Thearch.Ptrsize:], Thearch.Intsize)) +} + +func decodetype_funcoutcount(s *LSym) int { + return int(decode_inuxi(s.P[commonsize()+3*Thearch.Ptrsize+2*Thearch.Intsize:], Thearch.Intsize)) +} + +func decodetype_funcintype(s *LSym, i int) *LSym { + var r *Reloc + + r = decode_reloc(s, int32(commonsize())+int32(Thearch.Ptrsize)) + if r == nil { + return nil + } + return decode_reloc_sym(r.Sym, int32(r.Add+int64(int32(i)*int32(Thearch.Ptrsize)))) +} + +func decodetype_funcouttype(s *LSym, i int) *LSym { + var r *Reloc + + r = decode_reloc(s, int32(commonsize())+2*int32(Thearch.Ptrsize)+2*int32(Thearch.Intsize)) + if r == nil { + return nil + } + return decode_reloc_sym(r.Sym, int32(r.Add+int64(int32(i)*int32(Thearch.Ptrsize)))) +} + +// Type.StructType.fields.Slice::length +func decodetype_structfieldcount(s *LSym) int { + return int(decode_inuxi(s.P[commonsize()+Thearch.Ptrsize:], Thearch.Intsize)) +} + +func structfieldsize() int { + return 5 * Thearch.Ptrsize +} + +// Type.StructType.fields[]-> name, typ and offset. +func decodetype_structfieldname(s *LSym, i int) string { + var r *Reloc + + // go.string."foo" 0x28 / 0x40 + s = decode_reloc_sym(s, int32(commonsize())+int32(Thearch.Ptrsize)+2*int32(Thearch.Intsize)+int32(i)*int32(structfieldsize())) + + if s == nil { // embedded structs have a nil name. + return "" + } + r = decode_reloc(s, 0) // s has a pointer to the string data at offset 0 + if r == nil { // shouldn't happen. + return "" + } + return cstring(r.Sym.P[r.Add:]) +} + +func decodetype_structfieldtype(s *LSym, i int) *LSym { + return decode_reloc_sym(s, int32(commonsize())+int32(Thearch.Ptrsize)+2*int32(Thearch.Intsize)+int32(i)*int32(structfieldsize())+2*int32(Thearch.Ptrsize)) +} + +func decodetype_structfieldoffs(s *LSym, i int) int64 { + return int64(decode_inuxi(s.P[commonsize()+Thearch.Ptrsize+2*Thearch.Intsize+i*structfieldsize()+4*Thearch.Ptrsize:], Thearch.Intsize)) +} + +// InterfaceTYpe.methods.length +func decodetype_ifacemethodcount(s *LSym) int64 { + return int64(decode_inuxi(s.P[commonsize()+Thearch.Ptrsize:], Thearch.Intsize)) +} diff --git a/src/cmd/internal/ld/dwarf.go b/src/cmd/internal/ld/dwarf.go new file mode 100644 index 0000000000..490795c782 --- /dev/null +++ b/src/cmd/internal/ld/dwarf.go @@ -0,0 +1,2744 @@ +// Copyright 2010 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 ld + +import ( + "cmd/internal/obj" + "fmt" + "strings" +) + +/* + * Offsets and sizes of the debug_* sections in the cout file. + */ +var abbrevo int64 + +var abbrevsize int64 + +var abbrevsym *LSym + +var abbrevsympos int64 + +var lineo int64 + +var linesize int64 + +var linesym *LSym + +var linesympos int64 + +var infoo int64 // also the base for DWDie->offs and reference attributes. + +var infosize int64 + +var infosym *LSym + +var infosympos int64 + +var frameo int64 + +var framesize int64 + +var framesym *LSym + +var framesympos int64 + +var pubnameso int64 + +var pubnamessize int64 + +var pubtypeso int64 + +var pubtypessize int64 + +var arangeso int64 + +var arangessize int64 + +var gdbscripto int64 + +var gdbscriptsize int64 + +var infosec *LSym + +var inforeloco int64 + +var inforelocsize int64 + +var arangessec *LSym + +var arangesreloco int64 + +var arangesrelocsize int64 + +var linesec *LSym + +var linereloco int64 + +var linerelocsize int64 + +var framesec *LSym + +var framereloco int64 + +var framerelocsize int64 + +var gdbscript string + +/* + * Basic I/O + */ +func addrput(addr int64) { + switch Thearch.Ptrsize { + case 4: + Thearch.Lput(uint32(addr)) + + case 8: + Thearch.Vput(uint64(addr)) + } +} + +func uleb128enc(v uint64, dst []byte) int { + var c uint8 + var length uint8 + + length = 0 + for { + c = uint8(v & 0x7f) + v >>= 7 + if v != 0 { + c |= 0x80 + } + if dst != nil { + dst[0] = byte(c) + dst = dst[1:] + } + length++ + if c&0x80 == 0 { + break + } + } + + return int(length) +} + +func sleb128enc(v int64, dst []byte) int { + var c uint8 + var s uint8 + var length uint8 + + length = 0 + for { + c = uint8(v & 0x7f) + s = uint8(v & 0x40) + v >>= 7 + if (v != -1 || s == 0) && (v != 0 || s != 0) { + c |= 0x80 + } + if dst != nil { + dst[0] = byte(c) + dst = dst[1:] + } + length++ + if c&0x80 == 0 { + break + } + } + + return int(length) +} + +func uleb128put(v int64) { + var buf [10]byte + n := uleb128enc(uint64(v), buf[:]) + Cwrite(buf[:n]) +} + +func sleb128put(v int64) { + var buf [10]byte + n := sleb128enc(v, buf[:]) + Cwrite(buf[:n]) +} + +/* + * Defining Abbrevs. This is hardcoded, and there will be + * only a handful of them. The DWARF spec places no restriction on + * the ordering of attributes in the Abbrevs and DIEs, and we will + * always write them out in the order of declaration in the abbrev. + */ +type DWAttrForm struct { + attr uint16 + form uint8 +} + +// Go-specific type attributes. +const ( + DW_AT_go_kind = 0x2900 + DW_AT_go_key = 0x2901 + DW_AT_go_elem = 0x2902 + DW_AT_internal_location = 253 +) + +// Index into the abbrevs table below. +// Keep in sync with ispubname() and ispubtype() below. +// ispubtype considers >= NULLTYPE public +const ( + DW_ABRV_NULL = iota + DW_ABRV_COMPUNIT + DW_ABRV_FUNCTION + DW_ABRV_VARIABLE + DW_ABRV_AUTO + DW_ABRV_PARAM + DW_ABRV_STRUCTFIELD + DW_ABRV_FUNCTYPEPARAM + DW_ABRV_DOTDOTDOT + DW_ABRV_ARRAYRANGE + DW_ABRV_NULLTYPE + DW_ABRV_BASETYPE + DW_ABRV_ARRAYTYPE + DW_ABRV_CHANTYPE + DW_ABRV_FUNCTYPE + DW_ABRV_IFACETYPE + DW_ABRV_MAPTYPE + DW_ABRV_PTRTYPE + DW_ABRV_BARE_PTRTYPE + DW_ABRV_SLICETYPE + DW_ABRV_STRINGTYPE + DW_ABRV_STRUCTTYPE + DW_ABRV_TYPEDECL + DW_NABRV +) + +type DWAbbrev struct { + tag uint8 + children uint8 + attr [30]DWAttrForm +} + +var abbrevs = [DW_NABRV]struct { + tag uint8 + children uint8 + attr [30]DWAttrForm +}{ + /* The mandatory DW_ABRV_NULL entry. */ + struct { + tag uint8 + children uint8 + attr [30]DWAttrForm + }{0, 0, [30]DWAttrForm{}}, + + /* COMPUNIT */ + struct { + tag uint8 + children uint8 + attr [30]DWAttrForm + }{ + DW_TAG_compile_unit, + DW_CHILDREN_yes, + [30]DWAttrForm{ + DWAttrForm{DW_AT_name, DW_FORM_string}, + DWAttrForm{DW_AT_language, DW_FORM_data1}, + DWAttrForm{DW_AT_low_pc, DW_FORM_addr}, + DWAttrForm{DW_AT_high_pc, DW_FORM_addr}, + DWAttrForm{DW_AT_stmt_list, DW_FORM_data4}, + DWAttrForm{0, 0}, + }, + }, + + /* FUNCTION */ + struct { + tag uint8 + children uint8 + attr [30]DWAttrForm + }{ + DW_TAG_subprogram, + DW_CHILDREN_yes, + [30]DWAttrForm{ + DWAttrForm{DW_AT_name, DW_FORM_string}, + DWAttrForm{DW_AT_low_pc, DW_FORM_addr}, + DWAttrForm{DW_AT_high_pc, DW_FORM_addr}, + DWAttrForm{DW_AT_external, DW_FORM_flag}, + DWAttrForm{0, 0}, + }, + }, + + /* VARIABLE */ + struct { + tag uint8 + children uint8 + attr [30]DWAttrForm + }{ + DW_TAG_variable, + DW_CHILDREN_no, + [30]DWAttrForm{ + DWAttrForm{DW_AT_name, DW_FORM_string}, + DWAttrForm{DW_AT_location, DW_FORM_block1}, + DWAttrForm{DW_AT_type, DW_FORM_ref_addr}, + DWAttrForm{DW_AT_external, DW_FORM_flag}, + DWAttrForm{0, 0}, + }, + }, + + /* AUTO */ + struct { + tag uint8 + children uint8 + attr [30]DWAttrForm + }{ + DW_TAG_variable, + DW_CHILDREN_no, + [30]DWAttrForm{ + DWAttrForm{DW_AT_name, DW_FORM_string}, + DWAttrForm{DW_AT_location, DW_FORM_block1}, + DWAttrForm{DW_AT_type, DW_FORM_ref_addr}, + DWAttrForm{0, 0}, + }, + }, + + /* PARAM */ + struct { + tag uint8 + children uint8 + attr [30]DWAttrForm + }{ + DW_TAG_formal_parameter, + DW_CHILDREN_no, + [30]DWAttrForm{ + DWAttrForm{DW_AT_name, DW_FORM_string}, + DWAttrForm{DW_AT_location, DW_FORM_block1}, + DWAttrForm{DW_AT_type, DW_FORM_ref_addr}, + DWAttrForm{0, 0}, + }, + }, + + /* STRUCTFIELD */ + struct { + tag uint8 + children uint8 + attr [30]DWAttrForm + }{ + DW_TAG_member, + DW_CHILDREN_no, + [30]DWAttrForm{ + DWAttrForm{DW_AT_name, DW_FORM_string}, + DWAttrForm{DW_AT_data_member_location, DW_FORM_block1}, + DWAttrForm{DW_AT_type, DW_FORM_ref_addr}, + DWAttrForm{0, 0}, + }, + }, + + /* FUNCTYPEPARAM */ + struct { + tag uint8 + children uint8 + attr [30]DWAttrForm + }{ + DW_TAG_formal_parameter, + DW_CHILDREN_no, + + // No name! + [30]DWAttrForm{ + DWAttrForm{DW_AT_type, DW_FORM_ref_addr}, + DWAttrForm{0, 0}, + }, + }, + + /* DOTDOTDOT */ + struct { + tag uint8 + children uint8 + attr [30]DWAttrForm + }{ + DW_TAG_unspecified_parameters, + DW_CHILDREN_no, + [30]DWAttrForm{DWAttrForm{0, 0}}, + }, + + /* ARRAYRANGE */ + struct { + tag uint8 + children uint8 + attr [30]DWAttrForm + }{ + DW_TAG_subrange_type, + DW_CHILDREN_no, + + // No name! + [30]DWAttrForm{ + DWAttrForm{DW_AT_type, DW_FORM_ref_addr}, + DWAttrForm{DW_AT_count, DW_FORM_udata}, + DWAttrForm{0, 0}, + }, + }, + + // Below here are the types considered public by ispubtype + /* NULLTYPE */ + struct { + tag uint8 + children uint8 + attr [30]DWAttrForm + }{ + DW_TAG_unspecified_type, + DW_CHILDREN_no, + [30]DWAttrForm{ + DWAttrForm{DW_AT_name, DW_FORM_string}, + DWAttrForm{0, 0}, + }, + }, + + /* BASETYPE */ + struct { + tag uint8 + children uint8 + attr [30]DWAttrForm + }{ + DW_TAG_base_type, + DW_CHILDREN_no, + [30]DWAttrForm{ + DWAttrForm{DW_AT_name, DW_FORM_string}, + DWAttrForm{DW_AT_encoding, DW_FORM_data1}, + DWAttrForm{DW_AT_byte_size, DW_FORM_data1}, + DWAttrForm{DW_AT_go_kind, DW_FORM_data1}, + DWAttrForm{0, 0}, + }, + }, + + /* ARRAYTYPE */ + // child is subrange with upper bound + struct { + tag uint8 + children uint8 + attr [30]DWAttrForm + }{ + DW_TAG_array_type, + DW_CHILDREN_yes, + [30]DWAttrForm{ + DWAttrForm{DW_AT_name, DW_FORM_string}, + DWAttrForm{DW_AT_type, DW_FORM_ref_addr}, + DWAttrForm{DW_AT_byte_size, DW_FORM_udata}, + DWAttrForm{DW_AT_go_kind, DW_FORM_data1}, + DWAttrForm{0, 0}, + }, + }, + + /* CHANTYPE */ + struct { + tag uint8 + children uint8 + attr [30]DWAttrForm + }{ + DW_TAG_typedef, + DW_CHILDREN_no, + [30]DWAttrForm{ + DWAttrForm{DW_AT_name, DW_FORM_string}, + DWAttrForm{DW_AT_type, DW_FORM_ref_addr}, + DWAttrForm{DW_AT_go_kind, DW_FORM_data1}, + DWAttrForm{DW_AT_go_elem, DW_FORM_ref_addr}, + DWAttrForm{0, 0}, + }, + }, + + /* FUNCTYPE */ + struct { + tag uint8 + children uint8 + attr [30]DWAttrForm + }{ + DW_TAG_subroutine_type, + DW_CHILDREN_yes, + [30]DWAttrForm{ + DWAttrForm{DW_AT_name, DW_FORM_string}, + + // {DW_AT_type, DW_FORM_ref_addr}, + DWAttrForm{DW_AT_go_kind, DW_FORM_data1}, + DWAttrForm{0, 0}, + }, + }, + + /* IFACETYPE */ + struct { + tag uint8 + children uint8 + attr [30]DWAttrForm + }{ + DW_TAG_typedef, + DW_CHILDREN_yes, + [30]DWAttrForm{ + DWAttrForm{DW_AT_name, DW_FORM_string}, + DWAttrForm{DW_AT_type, DW_FORM_ref_addr}, + DWAttrForm{DW_AT_go_kind, DW_FORM_data1}, + DWAttrForm{0, 0}, + }, + }, + + /* MAPTYPE */ + struct { + tag uint8 + children uint8 + attr [30]DWAttrForm + }{ + DW_TAG_typedef, + DW_CHILDREN_no, + [30]DWAttrForm{ + DWAttrForm{DW_AT_name, DW_FORM_string}, + DWAttrForm{DW_AT_type, DW_FORM_ref_addr}, + DWAttrForm{DW_AT_go_kind, DW_FORM_data1}, + DWAttrForm{DW_AT_go_key, DW_FORM_ref_addr}, + DWAttrForm{DW_AT_go_elem, DW_FORM_ref_addr}, + DWAttrForm{0, 0}, + }, + }, + + /* PTRTYPE */ + struct { + tag uint8 + children uint8 + attr [30]DWAttrForm + }{ + DW_TAG_pointer_type, + DW_CHILDREN_no, + [30]DWAttrForm{ + DWAttrForm{DW_AT_name, DW_FORM_string}, + DWAttrForm{DW_AT_type, DW_FORM_ref_addr}, + DWAttrForm{DW_AT_go_kind, DW_FORM_data1}, + DWAttrForm{0, 0}, + }, + }, + + /* BARE_PTRTYPE */ + struct { + tag uint8 + children uint8 + attr [30]DWAttrForm + }{ + DW_TAG_pointer_type, + DW_CHILDREN_no, + [30]DWAttrForm{ + DWAttrForm{DW_AT_name, DW_FORM_string}, + DWAttrForm{0, 0}, + }, + }, + + /* SLICETYPE */ + struct { + tag uint8 + children uint8 + attr [30]DWAttrForm + }{ + DW_TAG_structure_type, + DW_CHILDREN_yes, + [30]DWAttrForm{ + DWAttrForm{DW_AT_name, DW_FORM_string}, + DWAttrForm{DW_AT_byte_size, DW_FORM_udata}, + DWAttrForm{DW_AT_go_kind, DW_FORM_data1}, + DWAttrForm{DW_AT_go_elem, DW_FORM_ref_addr}, + DWAttrForm{0, 0}, + }, + }, + + /* STRINGTYPE */ + struct { + tag uint8 + children uint8 + attr [30]DWAttrForm + }{ + DW_TAG_structure_type, + DW_CHILDREN_yes, + [30]DWAttrForm{ + DWAttrForm{DW_AT_name, DW_FORM_string}, + DWAttrForm{DW_AT_byte_size, DW_FORM_udata}, + DWAttrForm{DW_AT_go_kind, DW_FORM_data1}, + DWAttrForm{0, 0}, + }, + }, + + /* STRUCTTYPE */ + struct { + tag uint8 + children uint8 + attr [30]DWAttrForm + }{ + DW_TAG_structure_type, + DW_CHILDREN_yes, + [30]DWAttrForm{ + DWAttrForm{DW_AT_name, DW_FORM_string}, + DWAttrForm{DW_AT_byte_size, DW_FORM_udata}, + DWAttrForm{DW_AT_go_kind, DW_FORM_data1}, + DWAttrForm{0, 0}, + }, + }, + + /* TYPEDECL */ + struct { + tag uint8 + children uint8 + attr [30]DWAttrForm + }{ + DW_TAG_typedef, + DW_CHILDREN_no, + [30]DWAttrForm{ + DWAttrForm{DW_AT_name, DW_FORM_string}, + DWAttrForm{DW_AT_type, DW_FORM_ref_addr}, + DWAttrForm{0, 0}, + }, + }, +} + +func writeabbrev() { + var i int + var j int + var f *DWAttrForm + + abbrevo = Cpos() + for i = 1; i < DW_NABRV; i++ { + // See section 7.5.3 + uleb128put(int64(i)) + + uleb128put(int64(abbrevs[i].tag)) + Cput(abbrevs[i].children) + for j = 0; j < len(abbrevs[i].attr); j++ { + f = &abbrevs[i].attr[j] + uleb128put(int64(f.attr)) + uleb128put(int64(f.form)) + if f.attr == 0 { + break + } + } + } + + Cput(0) + abbrevsize = Cpos() - abbrevo +} + +/* + * Debugging Information Entries and their attributes. + */ +const ( + HASHSIZE = 107 +) + +func dwarfhashstr(s string) uint32 { + var h uint32 + + h = 0 + for s != "" { + h = h + h + h + uint32(s[0]) + s = s[1:] + } + return h % HASHSIZE +} + +// For DW_CLS_string and _block, value should contain the length, and +// data the data, for _reference, value is 0 and data is a DWDie* to +// the referenced instance, for all others, value is the whole thing +// and data is null. + +type DWAttr struct { + link *DWAttr + atr uint16 + cls uint8 + value int64 + data interface{} +} + +type DWDie struct { + abbrev int + link *DWDie + child *DWDie + attr *DWAttr + offs int64 + hash []*DWDie + hlink *DWDie +} + +/* + * Root DIEs for compilation units, types and global variables. + */ +var dwroot DWDie + +var dwtypes DWDie + +var dwglobals DWDie + +func newattr(die *DWDie, attr uint16, cls int, value int64, data interface{}) *DWAttr { + var a *DWAttr + + a = new(DWAttr) + a.link = die.attr + die.attr = a + a.atr = attr + a.cls = uint8(cls) + a.value = value + a.data = data + return a +} + +// Each DIE (except the root ones) has at least 1 attribute: its +// name. getattr moves the desired one to the front so +// frequently searched ones are found faster. +func getattr(die *DWDie, attr uint16) *DWAttr { + var a *DWAttr + var b *DWAttr + + if die.attr.atr == attr { + return die.attr + } + + a = die.attr + b = a.link + for b != nil { + if b.atr == attr { + a.link = b.link + b.link = die.attr + die.attr = b + return b + } + + a = b + b = b.link + } + + return nil +} + +// Every DIE has at least a DW_AT_name attribute (but it will only be +// written out if it is listed in the abbrev). If its parent is +// keeping an index, the new DIE will be inserted there. +func newdie(parent *DWDie, abbrev int, name string) *DWDie { + var die *DWDie + var h int + + die = new(DWDie) + die.abbrev = abbrev + die.link = parent.child + parent.child = die + + newattr(die, DW_AT_name, DW_CLS_STRING, int64(len(name)), name) + + if parent.hash != nil { + h = int(dwarfhashstr(name)) + die.hlink = parent.hash[h] + parent.hash[h] = die + } + + return die +} + +func mkindex(die *DWDie) { + die.hash = make([]*DWDie, HASHSIZE) +} + +func walktypedef(die *DWDie) *DWDie { + var attr *DWAttr + + // Resolve typedef if present. + if die.abbrev == DW_ABRV_TYPEDECL { + for attr = die.attr; attr != nil; attr = attr.link { + if attr.atr == DW_AT_type && attr.cls == DW_CLS_REFERENCE && attr.data != nil { + return attr.data.(*DWDie) + } + } + } + + return die +} + +// Find child by AT_name using hashtable if available or linear scan +// if not. +func find(die *DWDie, name string) *DWDie { + var a *DWDie + var b *DWDie + var die2 *DWDie + var h int + +top: + if die.hash == nil { + for a = die.child; a != nil; a = a.link { + if name == getattr(a, DW_AT_name).data { + return a + } + } + goto notfound + } + + h = int(dwarfhashstr(name)) + a = die.hash[h] + + if a == nil { + goto notfound + } + + if name == getattr(a, DW_AT_name).data { + return a + } + + // Move found ones to head of the list. + b = a.hlink + + for b != nil { + if name == getattr(b, DW_AT_name).data { + a.hlink = b.hlink + b.hlink = die.hash[h] + die.hash[h] = b + return b + } + + a = b + b = b.hlink + } + +notfound: + die2 = walktypedef(die) + if die2 != die { + die = die2 + goto top + } + + return nil +} + +func find_or_diag(die *DWDie, name string) *DWDie { + var r *DWDie + r = find(die, name) + if r == nil { + Diag("dwarf find: %s %p has no %s", getattr(die, DW_AT_name).data, die, name) + Errorexit() + } + + return r +} + +func adddwarfrel(sec *LSym, sym *LSym, offsetbase int64, siz int, addend int64) { + var r *Reloc + + r = Addrel(sec) + r.Sym = sym + r.Xsym = sym + r.Off = int32(Cpos() - offsetbase) + r.Siz = uint8(siz) + r.Type = R_ADDR + r.Add = addend + r.Xadd = addend + if Iself && Thearch.Thechar == '6' { + addend = 0 + } + switch siz { + case 4: + Thearch.Lput(uint32(addend)) + + case 8: + Thearch.Vput(uint64(addend)) + + default: + Diag("bad size in adddwarfrel") + } +} + +func newrefattr(die *DWDie, attr uint16, ref *DWDie) *DWAttr { + if ref == nil { + return nil + } + return newattr(die, attr, DW_CLS_REFERENCE, 0, ref) +} + +var fwdcount int + +func putattr(abbrev int, form int, cls int, value int64, data interface{}) { + var off int64 + var p []byte + var i int + + switch form { + case DW_FORM_addr: // address + if Linkmode == LinkExternal { + value -= (data.(*LSym)).Value + adddwarfrel(infosec, data.(*LSym), infoo, Thearch.Ptrsize, value) + break + } + + addrput(value) + + case DW_FORM_block1: // block + if cls == DW_CLS_ADDRESS { + Cput(uint8(1 + Thearch.Ptrsize)) + Cput(DW_OP_addr) + if Linkmode == LinkExternal { + value -= (data.(*LSym)).Value + adddwarfrel(infosec, data.(*LSym), infoo, Thearch.Ptrsize, value) + break + } + + addrput(value) + break + } + + value &= 0xff + Cput(uint8(value)) + p = data.([]byte) + for i = 0; int64(i) < value; i++ { + Cput(uint8(p[i])) + } + + case DW_FORM_block2: // block + value &= 0xffff + + Thearch.Wput(uint16(value)) + p = data.([]byte) + for i = 0; int64(i) < value; i++ { + Cput(uint8(p[i])) + } + + case DW_FORM_block4: // block + value &= 0xffffffff + + Thearch.Lput(uint32(value)) + p = data.([]byte) + for i = 0; int64(i) < value; i++ { + Cput(uint8(p[i])) + } + + case DW_FORM_block: // block + uleb128put(value) + + p = data.([]byte) + for i = 0; int64(i) < value; i++ { + Cput(uint8(p[i])) + } + + case DW_FORM_data1: // constant + Cput(uint8(value)) + + case DW_FORM_data2: // constant + Thearch.Wput(uint16(value)) + + case DW_FORM_data4: // constant, {line,loclist,mac,rangelist}ptr + if Linkmode == LinkExternal && cls == DW_CLS_PTR { + adddwarfrel(infosec, linesym, infoo, 4, value) + break + } + + Thearch.Lput(uint32(value)) + + case DW_FORM_data8: // constant, {line,loclist,mac,rangelist}ptr + Thearch.Vput(uint64(value)) + + case DW_FORM_sdata: // constant + sleb128put(value) + + case DW_FORM_udata: // constant + uleb128put(value) + + case DW_FORM_string: // string + strnput(data.(string), int(value+1)) + + case DW_FORM_flag: // flag + if value != 0 { + Cput(1) + } else { + Cput(0) + } + + // In DWARF 2 (which is what we claim to generate), + // the ref_addr is the same size as a normal address. + // In DWARF 3 it is always 32 bits, unless emitting a large + // (> 4 GB of debug info aka "64-bit") unit, which we don't implement. + case DW_FORM_ref_addr: // reference to a DIE in the .info section + if data == nil { + Diag("dwarf: null reference in %d", abbrev) + if Thearch.Ptrsize == 8 { + Thearch.Vput(0) // invalid dwarf, gdb will complain. + } else { + Thearch.Lput(0) // invalid dwarf, gdb will complain. + } + } else { + off = (data.(*DWDie)).offs + if off == 0 { + fwdcount++ + } + if Linkmode == LinkExternal { + adddwarfrel(infosec, infosym, infoo, Thearch.Ptrsize, off) + break + } + + addrput(off) + } + + case DW_FORM_ref1, // reference within the compilation unit + DW_FORM_ref2, // reference + DW_FORM_ref4, // reference + DW_FORM_ref8, // reference + DW_FORM_ref_udata, // reference + + DW_FORM_strp, // string + DW_FORM_indirect: // (see Section 7.5.3) + fallthrough + default: + Diag("dwarf: unsupported attribute form %d / class %d", form, cls) + + Errorexit() + } +} + +// Note that we can (and do) add arbitrary attributes to a DIE, but +// only the ones actually listed in the Abbrev will be written out. +func putattrs(abbrev int, attr *DWAttr) { + var af []DWAttrForm + var ap *DWAttr + + for af = abbrevs[abbrev].attr[:]; af[0].attr != 0; af = af[1:] { + for ap = attr; ap != nil; ap = ap.link { + if ap.atr == af[0].attr { + putattr(abbrev, int(af[0].form), int(ap.cls), ap.value, ap.data) + goto done + } + } + + putattr(abbrev, int(af[0].form), 0, 0, nil) + done: + } +} + +func putdies(die *DWDie) { + for ; die != nil; die = die.link { + putdie(die) + } +} + +func putdie(die *DWDie) { + die.offs = Cpos() - infoo + uleb128put(int64(die.abbrev)) + putattrs(die.abbrev, die.attr) + if abbrevs[die.abbrev].children != 0 { + putdies(die.child) + Cput(0) + } +} + +func reverselist(list **DWDie) { + var curr *DWDie + var prev *DWDie + + curr = *list + prev = nil + for curr != nil { + var next *DWDie = curr.link + curr.link = prev + prev = curr + curr = next + } + + *list = prev +} + +func reversetree(list **DWDie) { + var die *DWDie + + reverselist(list) + for die = *list; die != nil; die = die.link { + if abbrevs[die.abbrev].children != 0 { + reversetree(&die.child) + } + } +} + +func newmemberoffsetattr(die *DWDie, offs int32) { + var block [20]byte + var i int + + i = 0 + block[i] = DW_OP_plus_uconst + i++ + i += uleb128enc(uint64(offs), block[i:]) + newattr(die, DW_AT_data_member_location, DW_CLS_BLOCK, int64(i), block[:i]) +} + +// GDB doesn't like DW_FORM_addr for DW_AT_location, so emit a +// location expression that evals to a const. +func newabslocexprattr(die *DWDie, addr int64, sym *LSym) { + newattr(die, DW_AT_location, DW_CLS_ADDRESS, addr, sym) + // below +} + +// Lookup predefined types +func lookup_or_diag(n string) *LSym { + var s *LSym + + s = Linkrlookup(Ctxt, n, 0) + if s == nil || s.Size == 0 { + Diag("dwarf: missing type: %s", n) + Errorexit() + } + + return s +} + +func dotypedef(parent *DWDie, name string, def *DWDie) { + var die *DWDie + + // Only emit typedefs for real names. + if strings.HasPrefix(name, "map[") { + return + } + if strings.HasPrefix(name, "struct {") { + return + } + if strings.HasPrefix(name, "chan ") { + return + } + if name[0] == '[' || name[0] == '*' { + return + } + if def == nil { + Diag("dwarf: bad def in dotypedef") + } + + // The typedef entry must be created after the def, + // so that future lookups will find the typedef instead + // of the real definition. This hooks the typedef into any + // circular definition loops, so that gdb can understand them. + die = newdie(parent, DW_ABRV_TYPEDECL, name) + + newrefattr(die, DW_AT_type, def) +} + +// Define gotype, for composite ones recurse into constituents. +func defgotype(gotype *LSym) *DWDie { + var die *DWDie + var fld *DWDie + var s *LSym + var name string + var f string + var kind uint8 + var bytesize int64 + var i int + var nfields int + + if gotype == nil { + return find_or_diag(&dwtypes, "") + } + + if !strings.HasPrefix(gotype.Name, "type.") { + Diag("dwarf: type name doesn't start with \".type\": %s", gotype.Name) + return find_or_diag(&dwtypes, "") + } + + name = gotype.Name[5:] // could also decode from Type.string + + die = find(&dwtypes, name) + + if die != nil { + return die + } + + if false && Debug['v'] > 2 { + fmt.Printf("new type: %%Y\n", gotype) + } + + kind = decodetype_kind(gotype) + bytesize = decodetype_size(gotype) + + switch kind { + case obj.KindBool: + die = newdie(&dwtypes, DW_ABRV_BASETYPE, name) + newattr(die, DW_AT_encoding, DW_CLS_CONSTANT, DW_ATE_boolean, 0) + newattr(die, DW_AT_byte_size, DW_CLS_CONSTANT, bytesize, 0) + + case obj.KindInt, + obj.KindInt8, + obj.KindInt16, + obj.KindInt32, + obj.KindInt64: + die = newdie(&dwtypes, DW_ABRV_BASETYPE, name) + newattr(die, DW_AT_encoding, DW_CLS_CONSTANT, DW_ATE_signed, 0) + newattr(die, DW_AT_byte_size, DW_CLS_CONSTANT, bytesize, 0) + + case obj.KindUint, + obj.KindUint8, + obj.KindUint16, + obj.KindUint32, + obj.KindUint64, + obj.KindUintptr: + die = newdie(&dwtypes, DW_ABRV_BASETYPE, name) + newattr(die, DW_AT_encoding, DW_CLS_CONSTANT, DW_ATE_unsigned, 0) + newattr(die, DW_AT_byte_size, DW_CLS_CONSTANT, bytesize, 0) + + case obj.KindFloat32, + obj.KindFloat64: + die = newdie(&dwtypes, DW_ABRV_BASETYPE, name) + newattr(die, DW_AT_encoding, DW_CLS_CONSTANT, DW_ATE_float, 0) + newattr(die, DW_AT_byte_size, DW_CLS_CONSTANT, bytesize, 0) + + case obj.KindComplex64, + obj.KindComplex128: + die = newdie(&dwtypes, DW_ABRV_BASETYPE, name) + newattr(die, DW_AT_encoding, DW_CLS_CONSTANT, DW_ATE_complex_float, 0) + newattr(die, DW_AT_byte_size, DW_CLS_CONSTANT, bytesize, 0) + + case obj.KindArray: + die = newdie(&dwtypes, DW_ABRV_ARRAYTYPE, name) + dotypedef(&dwtypes, name, die) + newattr(die, DW_AT_byte_size, DW_CLS_CONSTANT, bytesize, 0) + s = decodetype_arrayelem(gotype) + newrefattr(die, DW_AT_type, defgotype(s)) + fld = newdie(die, DW_ABRV_ARRAYRANGE, "range") + + // use actual length not upper bound; correct for 0-length arrays. + newattr(fld, DW_AT_count, DW_CLS_CONSTANT, decodetype_arraylen(gotype), 0) + + newrefattr(fld, DW_AT_type, find_or_diag(&dwtypes, "uintptr")) + + case obj.KindChan: + die = newdie(&dwtypes, DW_ABRV_CHANTYPE, name) + newattr(die, DW_AT_byte_size, DW_CLS_CONSTANT, bytesize, 0) + s = decodetype_chanelem(gotype) + newrefattr(die, DW_AT_go_elem, defgotype(s)) + + case obj.KindFunc: + die = newdie(&dwtypes, DW_ABRV_FUNCTYPE, name) + dotypedef(&dwtypes, name, die) + newrefattr(die, DW_AT_type, find_or_diag(&dwtypes, "void")) + nfields = decodetype_funcincount(gotype) + for i = 0; i < nfields; i++ { + s = decodetype_funcintype(gotype, i) + fld = newdie(die, DW_ABRV_FUNCTYPEPARAM, s.Name[5:]) + newrefattr(fld, DW_AT_type, defgotype(s)) + } + + if decodetype_funcdotdotdot(gotype) != 0 { + newdie(die, DW_ABRV_DOTDOTDOT, "...") + } + nfields = decodetype_funcoutcount(gotype) + for i = 0; i < nfields; i++ { + s = decodetype_funcouttype(gotype, i) + fld = newdie(die, DW_ABRV_FUNCTYPEPARAM, s.Name[5:]) + newrefattr(fld, DW_AT_type, defptrto(defgotype(s))) + } + + case obj.KindInterface: + die = newdie(&dwtypes, DW_ABRV_IFACETYPE, name) + dotypedef(&dwtypes, name, die) + newattr(die, DW_AT_byte_size, DW_CLS_CONSTANT, bytesize, 0) + nfields = int(decodetype_ifacemethodcount(gotype)) + if nfields == 0 { + s = lookup_or_diag("type.runtime.eface") + } else { + s = lookup_or_diag("type.runtime.iface") + } + newrefattr(die, DW_AT_type, defgotype(s)) + + case obj.KindMap: + die = newdie(&dwtypes, DW_ABRV_MAPTYPE, name) + s = decodetype_mapkey(gotype) + newrefattr(die, DW_AT_go_key, defgotype(s)) + s = decodetype_mapvalue(gotype) + newrefattr(die, DW_AT_go_elem, defgotype(s)) + + case obj.KindPtr: + die = newdie(&dwtypes, DW_ABRV_PTRTYPE, name) + dotypedef(&dwtypes, name, die) + s = decodetype_ptrelem(gotype) + newrefattr(die, DW_AT_type, defgotype(s)) + + case obj.KindSlice: + die = newdie(&dwtypes, DW_ABRV_SLICETYPE, name) + dotypedef(&dwtypes, name, die) + newattr(die, DW_AT_byte_size, DW_CLS_CONSTANT, bytesize, 0) + s = decodetype_arrayelem(gotype) + newrefattr(die, DW_AT_go_elem, defgotype(s)) + + case obj.KindString: + die = newdie(&dwtypes, DW_ABRV_STRINGTYPE, name) + newattr(die, DW_AT_byte_size, DW_CLS_CONSTANT, bytesize, 0) + + case obj.KindStruct: + die = newdie(&dwtypes, DW_ABRV_STRUCTTYPE, name) + dotypedef(&dwtypes, name, die) + newattr(die, DW_AT_byte_size, DW_CLS_CONSTANT, bytesize, 0) + nfields = decodetype_structfieldcount(gotype) + for i = 0; i < nfields; i++ { + f = decodetype_structfieldname(gotype, i) + s = decodetype_structfieldtype(gotype, i) + if f == "" { + f = s.Name[5:] // skip "type." + } + fld = newdie(die, DW_ABRV_STRUCTFIELD, f) + newrefattr(fld, DW_AT_type, defgotype(s)) + newmemberoffsetattr(fld, int32(decodetype_structfieldoffs(gotype, i))) + } + + case obj.KindUnsafePointer: + die = newdie(&dwtypes, DW_ABRV_BARE_PTRTYPE, name) + + default: + Diag("dwarf: definition of unknown kind %d: %s", kind, gotype.Name) + die = newdie(&dwtypes, DW_ABRV_TYPEDECL, name) + newrefattr(die, DW_AT_type, find_or_diag(&dwtypes, "")) + } + + newattr(die, DW_AT_go_kind, DW_CLS_CONSTANT, int64(kind), 0) + + return die +} + +// Find or construct *T given T. +func defptrto(dwtype *DWDie) *DWDie { + var ptrname string + var die *DWDie + + ptrname = fmt.Sprintf("*%s", getattr(dwtype, DW_AT_name).data) + die = find(&dwtypes, ptrname) + if die == nil { + die = newdie(&dwtypes, DW_ABRV_PTRTYPE, ptrname) + newrefattr(die, DW_AT_type, dwtype) + } + + return die +} + +// Copies src's children into dst. Copies attributes by value. +// DWAttr.data is copied as pointer only. If except is one of +// the top-level children, it will not be copied. +func copychildrenexcept(dst *DWDie, src *DWDie, except *DWDie) { + var c *DWDie + var a *DWAttr + + for src = src.child; src != nil; src = src.link { + if src == except { + continue + } + c = newdie(dst, src.abbrev, getattr(src, DW_AT_name).data.(string)) + for a = src.attr; a != nil; a = a.link { + newattr(c, a.atr, int(a.cls), a.value, a.data) + } + copychildrenexcept(c, src, nil) + } + + reverselist(&dst.child) +} + +func copychildren(dst *DWDie, src *DWDie) { + copychildrenexcept(dst, src, nil) +} + +// Search children (assumed to have DW_TAG_member) for the one named +// field and set its DW_AT_type to dwtype +func substitutetype(structdie *DWDie, field string, dwtype *DWDie) { + var child *DWDie + var a *DWAttr + + child = find_or_diag(structdie, field) + if child == nil { + return + } + + a = getattr(child, DW_AT_type) + if a != nil { + a.data = dwtype + } else { + newrefattr(child, DW_AT_type, dwtype) + } +} + +func synthesizestringtypes(die *DWDie) { + var prototype *DWDie + + prototype = walktypedef(defgotype(lookup_or_diag("type.runtime._string"))) + if prototype == nil { + return + } + + for ; die != nil; die = die.link { + if die.abbrev != DW_ABRV_STRINGTYPE { + continue + } + copychildren(die, prototype) + } +} + +func synthesizeslicetypes(die *DWDie) { + var prototype *DWDie + var elem *DWDie + + prototype = walktypedef(defgotype(lookup_or_diag("type.runtime.slice"))) + if prototype == nil { + return + } + + for ; die != nil; die = die.link { + if die.abbrev != DW_ABRV_SLICETYPE { + continue + } + copychildren(die, prototype) + elem = getattr(die, DW_AT_go_elem).data.(*DWDie) + substitutetype(die, "array", defptrto(elem)) + } +} + +func mkinternaltypename(base string, arg1 string, arg2 string) string { + var buf string + var n string + + if arg2 == "" { + buf = fmt.Sprintf("%s<%s>", base, arg1) + } else { + buf = fmt.Sprintf("%s<%s,%s>", base, arg1, arg2) + } + n = buf + return n +} + +// synthesizemaptypes is way too closely married to runtime/hashmap.c +const ( + MaxKeySize = 128 + MaxValSize = 128 + BucketSize = 8 +) + +func synthesizemaptypes(die *DWDie) { + var hash *DWDie + var bucket *DWDie + var dwh *DWDie + var dwhk *DWDie + var dwhv *DWDie + var dwhb *DWDie + var keytype *DWDie + var valtype *DWDie + var fld *DWDie + var t *DWDie + var indirect_key int + var indirect_val int + var keysize int + var valsize int + var a *DWAttr + + hash = walktypedef(defgotype(lookup_or_diag("type.runtime.hmap"))) + bucket = walktypedef(defgotype(lookup_or_diag("type.runtime.bmap"))) + + if hash == nil { + return + } + + for ; die != nil; die = die.link { + if die.abbrev != DW_ABRV_MAPTYPE { + continue + } + + keytype = walktypedef(getattr(die, DW_AT_go_key).data.(*DWDie)) + valtype = walktypedef(getattr(die, DW_AT_go_elem).data.(*DWDie)) + + // compute size info like hashmap.c does. + a = getattr(keytype, DW_AT_byte_size) + + if a != nil { + keysize = int(a.value) + } else { + keysize = Thearch.Ptrsize + } + a = getattr(valtype, DW_AT_byte_size) + if a != nil { + valsize = int(a.value) + } else { + valsize = Thearch.Ptrsize + } + indirect_key = 0 + indirect_val = 0 + if keysize > MaxKeySize { + keysize = Thearch.Ptrsize + indirect_key = 1 + } + + if valsize > MaxValSize { + valsize = Thearch.Ptrsize + indirect_val = 1 + } + + // Construct type to represent an array of BucketSize keys + dwhk = newdie(&dwtypes, DW_ABRV_ARRAYTYPE, mkinternaltypename("[]key", getattr(keytype, DW_AT_name).data.(string), "")) + + newattr(dwhk, DW_AT_byte_size, DW_CLS_CONSTANT, BucketSize*int64(keysize), 0) + t = keytype + if indirect_key != 0 { + t = defptrto(keytype) + } + newrefattr(dwhk, DW_AT_type, t) + fld = newdie(dwhk, DW_ABRV_ARRAYRANGE, "size") + newattr(fld, DW_AT_count, DW_CLS_CONSTANT, BucketSize, 0) + newrefattr(fld, DW_AT_type, find_or_diag(&dwtypes, "uintptr")) + + // Construct type to represent an array of BucketSize values + dwhv = newdie(&dwtypes, DW_ABRV_ARRAYTYPE, mkinternaltypename("[]val", getattr(valtype, DW_AT_name).data.(string), "")) + + newattr(dwhv, DW_AT_byte_size, DW_CLS_CONSTANT, BucketSize*int64(valsize), 0) + t = valtype + if indirect_val != 0 { + t = defptrto(valtype) + } + newrefattr(dwhv, DW_AT_type, t) + fld = newdie(dwhv, DW_ABRV_ARRAYRANGE, "size") + newattr(fld, DW_AT_count, DW_CLS_CONSTANT, BucketSize, 0) + newrefattr(fld, DW_AT_type, find_or_diag(&dwtypes, "uintptr")) + + // Construct bucket + dwhb = newdie(&dwtypes, DW_ABRV_STRUCTTYPE, mkinternaltypename("bucket", getattr(keytype, DW_AT_name).data.(string), getattr(valtype, DW_AT_name).data.(string))) + + // Copy over all fields except the field "data" from the generic bucket. + // "data" will be replaced with keys/values below. + copychildrenexcept(dwhb, bucket, find(bucket, "data")) + + fld = newdie(dwhb, DW_ABRV_STRUCTFIELD, "keys") + newrefattr(fld, DW_AT_type, dwhk) + newmemberoffsetattr(fld, BucketSize) + fld = newdie(dwhb, DW_ABRV_STRUCTFIELD, "values") + newrefattr(fld, DW_AT_type, dwhv) + newmemberoffsetattr(fld, BucketSize+BucketSize*int32(keysize)) + fld = newdie(dwhb, DW_ABRV_STRUCTFIELD, "overflow") + newrefattr(fld, DW_AT_type, defptrto(dwhb)) + newmemberoffsetattr(fld, BucketSize+BucketSize*(int32(keysize)+int32(valsize))) + if Thearch.Regsize > Thearch.Ptrsize { + fld = newdie(dwhb, DW_ABRV_STRUCTFIELD, "pad") + newrefattr(fld, DW_AT_type, find_or_diag(&dwtypes, "uintptr")) + newmemberoffsetattr(fld, BucketSize+BucketSize*(int32(keysize)+int32(valsize))+int32(Thearch.Ptrsize)) + } + + newattr(dwhb, DW_AT_byte_size, DW_CLS_CONSTANT, BucketSize+BucketSize*int64(keysize)+BucketSize*int64(valsize)+int64(Thearch.Regsize), 0) + + // Construct hash + dwh = newdie(&dwtypes, DW_ABRV_STRUCTTYPE, mkinternaltypename("hash", getattr(keytype, DW_AT_name).data.(string), getattr(valtype, DW_AT_name).data.(string))) + + copychildren(dwh, hash) + substitutetype(dwh, "buckets", defptrto(dwhb)) + substitutetype(dwh, "oldbuckets", defptrto(dwhb)) + newattr(dwh, DW_AT_byte_size, DW_CLS_CONSTANT, getattr(hash, DW_AT_byte_size).value, nil) + + // make map type a pointer to hash + newrefattr(die, DW_AT_type, defptrto(dwh)) + } +} + +func synthesizechantypes(die *DWDie) { + var sudog *DWDie + var waitq *DWDie + var hchan *DWDie + var dws *DWDie + var dww *DWDie + var dwh *DWDie + var elemtype *DWDie + var a *DWAttr + var elemsize int + var sudogsize int + + sudog = walktypedef(defgotype(lookup_or_diag("type.runtime.sudog"))) + waitq = walktypedef(defgotype(lookup_or_diag("type.runtime.waitq"))) + hchan = walktypedef(defgotype(lookup_or_diag("type.runtime.hchan"))) + if sudog == nil || waitq == nil || hchan == nil { + return + } + + sudogsize = int(getattr(sudog, DW_AT_byte_size).value) + + for ; die != nil; die = die.link { + if die.abbrev != DW_ABRV_CHANTYPE { + continue + } + elemtype = getattr(die, DW_AT_go_elem).data.(*DWDie) + a = getattr(elemtype, DW_AT_byte_size) + if a != nil { + elemsize = int(a.value) + } else { + elemsize = Thearch.Ptrsize + } + + // sudog + dws = newdie(&dwtypes, DW_ABRV_STRUCTTYPE, mkinternaltypename("sudog", getattr(elemtype, DW_AT_name).data.(string), "")) + + copychildren(dws, sudog) + substitutetype(dws, "elem", elemtype) + if elemsize > 8 { + elemsize -= 8 + } else { + elemsize = 0 + } + newattr(dws, DW_AT_byte_size, DW_CLS_CONSTANT, int64(sudogsize)+int64(elemsize), nil) + + // waitq + dww = newdie(&dwtypes, DW_ABRV_STRUCTTYPE, mkinternaltypename("waitq", getattr(elemtype, DW_AT_name).data.(string), "")) + + copychildren(dww, waitq) + substitutetype(dww, "first", defptrto(dws)) + substitutetype(dww, "last", defptrto(dws)) + newattr(dww, DW_AT_byte_size, DW_CLS_CONSTANT, getattr(waitq, DW_AT_byte_size).value, nil) + + // hchan + dwh = newdie(&dwtypes, DW_ABRV_STRUCTTYPE, mkinternaltypename("hchan", getattr(elemtype, DW_AT_name).data.(string), "")) + + copychildren(dwh, hchan) + substitutetype(dwh, "recvq", dww) + substitutetype(dwh, "sendq", dww) + newattr(dwh, DW_AT_byte_size, DW_CLS_CONSTANT, getattr(hchan, DW_AT_byte_size).value, nil) + + newrefattr(die, DW_AT_type, defptrto(dwh)) + } +} + +// For use with pass.c::genasmsym +func defdwsymb(sym *LSym, s string, t int, v int64, size int64, ver int, gotype *LSym) { + var dv *DWDie + var dt *DWDie + + if strings.HasPrefix(s, "go.string.") { + return + } + + if strings.HasPrefix(s, "type.") && s != "type.*" && !strings.HasPrefix(s, "type..") { + defgotype(sym) + return + } + + dv = nil + + switch t { + default: + return + + case 'd', + 'b', + 'D', + 'B': + dv = newdie(&dwglobals, DW_ABRV_VARIABLE, s) + newabslocexprattr(dv, v, sym) + if ver == 0 { + newattr(dv, DW_AT_external, DW_CLS_FLAG, 1, 0) + } + fallthrough + + // fallthrough + case 'a', + 'p': + dt = defgotype(gotype) + } + + if dv != nil { + newrefattr(dv, DW_AT_type, dt) + } +} + +func movetomodule(parent *DWDie) { + var die *DWDie + + die = dwroot.child.child + for die.link != nil { + die = die.link + } + die.link = parent.child +} + +// If the pcln table contains runtime/runtime.go, use that to set gdbscript path. +func finddebugruntimepath(s *LSym) { + var i int + var p string + var f *LSym + + if gdbscript != "" { + return + } + + for i = 0; i < s.Pcln.Nfile; i++ { + f = s.Pcln.File[i] + _ = p + if i := strings.Index(f.Name, "runtime/runtime.go"); i >= 0 { + gdbscript = f.Name[:i] + "runtime/runtime-gdb.py" + break + } + } +} + +/* + * Generate short opcodes when possible, long ones when necessary. + * See section 6.2.5 + */ +const ( + LINE_BASE = -1 + LINE_RANGE = 4 + OPCODE_BASE = 10 +) + +func putpclcdelta(delta_pc int64, delta_lc int64) { + if LINE_BASE <= delta_lc && delta_lc < LINE_BASE+LINE_RANGE { + var opcode int64 = OPCODE_BASE + (delta_lc - LINE_BASE) + (LINE_RANGE * delta_pc) + if OPCODE_BASE <= opcode && opcode < 256 { + Cput(uint8(opcode)) + return + } + } + + if delta_pc != 0 { + Cput(DW_LNS_advance_pc) + sleb128put(delta_pc) + } + + Cput(DW_LNS_advance_line) + sleb128put(delta_lc) + Cput(DW_LNS_copy) +} + +func newcfaoffsetattr(die *DWDie, offs int32) { + var block [20]byte + var i int + + i = 0 + + block[i] = DW_OP_call_frame_cfa + i++ + if offs != 0 { + block[i] = DW_OP_consts + i++ + i += sleb128enc(int64(offs), block[i:]) + block[i] = DW_OP_plus + i++ + } + + newattr(die, DW_AT_location, DW_CLS_BLOCK, int64(i), block[:i]) +} + +func mkvarname(name string, da int) string { + var buf string + var n string + + buf = fmt.Sprintf("%s#%d", name, da) + n = buf + return n +} + +/* + * Walk prog table, emit line program and build DIE tree. + */ + +// flush previous compilation unit. +func flushunit(dwinfo *DWDie, pc int64, pcsym *LSym, unitstart int64, header_length int32) { + var here int64 + + if dwinfo != nil && pc != 0 { + newattr(dwinfo, DW_AT_high_pc, DW_CLS_ADDRESS, pc+1, pcsym) + } + + if unitstart >= 0 { + Cput(0) // start extended opcode + uleb128put(1) + Cput(DW_LNE_end_sequence) + + here = Cpos() + Cseek(unitstart) + Thearch.Lput(uint32(here - unitstart - 4)) // unit_length + Thearch.Wput(2) // dwarf version + Thearch.Lput(uint32(header_length)) // header length starting here + Cseek(here) + } +} + +func writelines() { + var s *LSym + var epcs *LSym + var a *Auto + var unitstart int64 + var headerend int64 + var offs int64 + var pc int64 + var epc int64 + var i int + var lang int + var da int + var dt int + var line int + var file int + var dwinfo *DWDie + var dwfunc *DWDie + var dwvar *DWDie + var dws **DWDie + var varhash [HASHSIZE]*DWDie + var n string + var nn string + var pcfile Pciter + var pcline Pciter + var files []*LSym + var f *LSym + + if linesec == nil { + linesec = Linklookup(Ctxt, ".dwarfline", 0) + } + linesec.R = linesec.R[:0] + + unitstart = -1 + headerend = -1 + epc = 0 + epcs = nil + lineo = Cpos() + dwinfo = nil + + flushunit(dwinfo, epc, epcs, unitstart, int32(headerend-unitstart-10)) + unitstart = Cpos() + + lang = DW_LANG_Go + + s = Ctxt.Textp + + dwinfo = newdie(&dwroot, DW_ABRV_COMPUNIT, "go") + newattr(dwinfo, DW_AT_language, DW_CLS_CONSTANT, int64(lang), 0) + newattr(dwinfo, DW_AT_stmt_list, DW_CLS_PTR, unitstart-lineo, 0) + newattr(dwinfo, DW_AT_low_pc, DW_CLS_ADDRESS, s.Value, s) + + // Write .debug_line Line Number Program Header (sec 6.2.4) + // Fields marked with (*) must be changed for 64-bit dwarf + Thearch.Lput(0) // unit_length (*), will be filled in by flushunit. + Thearch.Wput(2) // dwarf version (appendix F) + Thearch.Lput(0) // header_length (*), filled in by flushunit. + + // cpos == unitstart + 4 + 2 + 4 + Cput(1) // minimum_instruction_length + Cput(1) // default_is_stmt + Cput(LINE_BASE & 0xFF) // line_base + Cput(LINE_RANGE) // line_range + Cput(OPCODE_BASE) // opcode_base + Cput(0) // standard_opcode_lengths[1] + Cput(1) // standard_opcode_lengths[2] + Cput(1) // standard_opcode_lengths[3] + Cput(1) // standard_opcode_lengths[4] + Cput(1) // standard_opcode_lengths[5] + Cput(0) // standard_opcode_lengths[6] + Cput(0) // standard_opcode_lengths[7] + Cput(0) // standard_opcode_lengths[8] + Cput(1) // standard_opcode_lengths[9] + Cput(0) // include_directories (empty) + + files = make([]*LSym, Ctxt.Nhistfile) + + for f = Ctxt.Filesyms; f != nil; f = f.Next { + files[f.Value-1] = f + } + + for i = 0; int32(i) < Ctxt.Nhistfile; i++ { + strnput(files[i].Name, len(files[i].Name)+4) + } + + // 4 zeros: the string termination + 3 fields. + Cput(0) + // terminate file_names. + headerend = Cpos() + + Cput(0) // start extended opcode + uleb128put(1 + int64(Thearch.Ptrsize)) + Cput(DW_LNE_set_address) + + pc = s.Value + line = 1 + file = 1 + if Linkmode == LinkExternal { + adddwarfrel(linesec, s, lineo, Thearch.Ptrsize, 0) + } else { + addrput(pc) + } + + for Ctxt.Cursym = Ctxt.Textp; Ctxt.Cursym != nil; Ctxt.Cursym = Ctxt.Cursym.Next { + s = Ctxt.Cursym + + dwfunc = newdie(dwinfo, DW_ABRV_FUNCTION, s.Name) + newattr(dwfunc, DW_AT_low_pc, DW_CLS_ADDRESS, s.Value, s) + epc = s.Value + s.Size + epcs = s + newattr(dwfunc, DW_AT_high_pc, DW_CLS_ADDRESS, epc, s) + if s.Version == 0 { + newattr(dwfunc, DW_AT_external, DW_CLS_FLAG, 1, 0) + } + + if s.Pcln == nil { + continue + } + + finddebugruntimepath(s) + + pciterinit(Ctxt, &pcfile, &s.Pcln.Pcfile) + pciterinit(Ctxt, &pcline, &s.Pcln.Pcline) + epc = pc + for pcfile.done == 0 && pcline.done == 0 { + if epc-s.Value >= int64(pcfile.nextpc) { + pciternext(&pcfile) + continue + } + + if epc-s.Value >= int64(pcline.nextpc) { + pciternext(&pcline) + continue + } + + if int32(file) != pcfile.value { + Cput(DW_LNS_set_file) + uleb128put(int64(pcfile.value)) + file = int(pcfile.value) + } + + putpclcdelta(s.Value+int64(pcline.pc)-pc, int64(pcline.value)-int64(line)) + + pc = s.Value + int64(pcline.pc) + line = int(pcline.value) + if pcfile.nextpc < pcline.nextpc { + epc = int64(pcfile.nextpc) + } else { + epc = int64(pcline.nextpc) + } + epc += s.Value + } + + da = 0 + dwfunc.hash = varhash[:] // enable indexing of children by name + varhash = [HASHSIZE]*DWDie{} + for a = s.Autom; a != nil; a = a.Link { + switch a.Name { + case A_AUTO: + dt = DW_ABRV_AUTO + offs = int64(a.Aoffset) - int64(Thearch.Ptrsize) + + case A_PARAM: + dt = DW_ABRV_PARAM + offs = int64(a.Aoffset) + + default: + continue + } + + if strings.Contains(a.Asym.Name, ".autotmp_") { + continue + } + if find(dwfunc, a.Asym.Name) != nil { + n = mkvarname(a.Asym.Name, da) + } else { + n = a.Asym.Name + } + + // Drop the package prefix from locals and arguments. + _ = nn + if i := strings.LastIndex(n, "."); i >= 0 { + n = n[i+1:] + } + + dwvar = newdie(dwfunc, dt, n) + newcfaoffsetattr(dwvar, int32(offs)) + newrefattr(dwvar, DW_AT_type, defgotype(a.Gotype)) + + // push dwvar down dwfunc->child to preserve order + newattr(dwvar, DW_AT_internal_location, DW_CLS_CONSTANT, offs, nil) + + dwfunc.child = dwvar.link // take dwvar out from the top of the list + for dws = &dwfunc.child; *dws != nil; dws = &(*dws).link { + if offs > getattr(*dws, DW_AT_internal_location).value { + break + } + } + dwvar.link = *dws + *dws = dwvar + + da++ + } + + dwfunc.hash = nil + } + + flushunit(dwinfo, epc, epcs, unitstart, int32(headerend-unitstart-10)) + linesize = Cpos() - lineo +} + +/* + * Emit .debug_frame + */ +const ( + CIERESERVE = 16 + DATAALIGNMENTFACTOR = -4 + FAKERETURNCOLUMN = 16 +) + +func putpccfadelta(deltapc int64, cfa int64) { + Cput(DW_CFA_def_cfa_offset_sf) + sleb128put(cfa / DATAALIGNMENTFACTOR) + + if deltapc < 0x40 { + Cput(uint8(DW_CFA_advance_loc + deltapc)) + } else if deltapc < 0x100 { + Cput(DW_CFA_advance_loc1) + Cput(uint8(deltapc)) + } else if deltapc < 0x10000 { + Cput(DW_CFA_advance_loc2) + Thearch.Wput(uint16(deltapc)) + } else { + Cput(DW_CFA_advance_loc4) + Thearch.Lput(uint32(deltapc)) + } +} + +func writeframes() { + var s *LSym + var fdeo int64 + var fdesize int64 + var pad int64 + var pcsp Pciter + var nextpc uint32 + + if framesec == nil { + framesec = Linklookup(Ctxt, ".dwarfframe", 0) + } + framesec.R = framesec.R[:0] + frameo = Cpos() + + // Emit the CIE, Section 6.4.1 + Thearch.Lput(CIERESERVE) // initial length, must be multiple of thearch.ptrsize + Thearch.Lput(0xffffffff) // cid. + Cput(3) // dwarf version (appendix F) + Cput(0) // augmentation "" + uleb128put(1) // code_alignment_factor + sleb128put(DATAALIGNMENTFACTOR) // guess + uleb128put(FAKERETURNCOLUMN) // return_address_register + + Cput(DW_CFA_def_cfa) + + uleb128put(int64(Thearch.Dwarfregsp)) // register SP (**ABI-dependent, defined in l.h) + uleb128put(int64(Thearch.Ptrsize)) // offset + + Cput(DW_CFA_offset + FAKERETURNCOLUMN) // return address + uleb128put(int64(-Thearch.Ptrsize) / DATAALIGNMENTFACTOR) // at cfa - x*4 + + // 4 is to exclude the length field. + pad = CIERESERVE + frameo + 4 - Cpos() + + if pad < 0 { + Diag("dwarf: CIERESERVE too small by %d bytes.", -pad) + Errorexit() + } + + strnput("", int(pad)) + + for Ctxt.Cursym = Ctxt.Textp; Ctxt.Cursym != nil; Ctxt.Cursym = Ctxt.Cursym.Next { + s = Ctxt.Cursym + if s.Pcln == nil { + continue + } + + fdeo = Cpos() + + // Emit a FDE, Section 6.4.1, starting wit a placeholder. + Thearch.Lput(0) // length, must be multiple of thearch.ptrsize + Thearch.Lput(0) // Pointer to the CIE above, at offset 0 + addrput(0) // initial location + addrput(0) // address range + + for pciterinit(Ctxt, &pcsp, &s.Pcln.Pcsp); pcsp.done == 0; pciternext(&pcsp) { + nextpc = pcsp.nextpc + + // pciterinit goes up to the end of the function, + // but DWARF expects us to stop just before the end. + if int64(nextpc) == s.Size { + nextpc-- + if nextpc < pcsp.pc { + continue + } + } + + putpccfadelta(int64(nextpc)-int64(pcsp.pc), int64(Thearch.Ptrsize)+int64(pcsp.value)) + } + + fdesize = Cpos() - fdeo - 4 // exclude the length field. + pad = Rnd(fdesize, int64(Thearch.Ptrsize)) - fdesize + strnput("", int(pad)) + fdesize += pad + + // Emit the FDE header for real, Section 6.4.1. + Cseek(fdeo) + + Thearch.Lput(uint32(fdesize)) + if Linkmode == LinkExternal { + adddwarfrel(framesec, framesym, frameo, 4, 0) + adddwarfrel(framesec, s, frameo, Thearch.Ptrsize, 0) + } else { + Thearch.Lput(0) + addrput(s.Value) + } + + addrput(s.Size) + Cseek(fdeo + 4 + fdesize) + } + + Cflush() + framesize = Cpos() - frameo +} + +/* + * Walk DWarfDebugInfoEntries, and emit .debug_info + */ +const ( + COMPUNITHEADERSIZE = 4 + 2 + 4 + 1 +) + +func writeinfo() { + var compunit *DWDie + var unitstart int64 + var here int64 + + fwdcount = 0 + if infosec == nil { + infosec = Linklookup(Ctxt, ".dwarfinfo", 0) + } + infosec.R = infosec.R[:0] + + if arangessec == nil { + arangessec = Linklookup(Ctxt, ".dwarfaranges", 0) + } + arangessec.R = arangessec.R[:0] + + for compunit = dwroot.child; compunit != nil; compunit = compunit.link { + unitstart = Cpos() + + // Write .debug_info Compilation Unit Header (sec 7.5.1) + // Fields marked with (*) must be changed for 64-bit dwarf + // This must match COMPUNITHEADERSIZE above. + Thearch.Lput(0) // unit_length (*), will be filled in later. + Thearch.Wput(2) // dwarf version (appendix F) + + // debug_abbrev_offset (*) + if Linkmode == LinkExternal { + adddwarfrel(infosec, abbrevsym, infoo, 4, 0) + } else { + Thearch.Lput(0) + } + + Cput(uint8(Thearch.Ptrsize)) // address_size + + putdie(compunit) + + here = Cpos() + Cseek(unitstart) + Thearch.Lput(uint32(here - unitstart - 4)) // exclude the length field. + Cseek(here) + } + + Cflush() +} + +/* + * Emit .debug_pubnames/_types. _info must have been written before, + * because we need die->offs and infoo/infosize; + */ +func ispubname(die *DWDie) bool { + var a *DWAttr + + switch die.abbrev { + case DW_ABRV_FUNCTION, + DW_ABRV_VARIABLE: + a = getattr(die, DW_AT_external) + return a != nil && a.value != 0 + } + + return false +} + +func ispubtype(die *DWDie) bool { + return die.abbrev >= DW_ABRV_NULLTYPE +} + +func writepub(ispub func(*DWDie) bool) int64 { + var compunit *DWDie + var die *DWDie + var dwa *DWAttr + var unitstart int64 + var unitend int64 + var sectionstart int64 + var here int64 + + sectionstart = Cpos() + + for compunit = dwroot.child; compunit != nil; compunit = compunit.link { + unitstart = compunit.offs - COMPUNITHEADERSIZE + if compunit.link != nil { + unitend = compunit.link.offs - COMPUNITHEADERSIZE + } else { + unitend = infoo + infosize + } + + // Write .debug_pubnames/types Header (sec 6.1.1) + Thearch.Lput(0) // unit_length (*), will be filled in later. + Thearch.Wput(2) // dwarf version (appendix F) + Thearch.Lput(uint32(unitstart)) // debug_info_offset (of the Comp unit Header) + Thearch.Lput(uint32(unitend - unitstart)) // debug_info_length + + for die = compunit.child; die != nil; die = die.link { + if !ispub(die) { + continue + } + Thearch.Lput(uint32(die.offs - unitstart)) + dwa = getattr(die, DW_AT_name) + strnput(dwa.data.(string), int(dwa.value+1)) + } + + Thearch.Lput(0) + + here = Cpos() + Cseek(sectionstart) + Thearch.Lput(uint32(here - sectionstart - 4)) // exclude the length field. + Cseek(here) + } + + return sectionstart +} + +/* + * emit .debug_aranges. _info must have been written before, + * because we need die->offs of dw_globals. + */ +func writearanges() int64 { + var compunit *DWDie + var b *DWAttr + var e *DWAttr + var headersize int + var sectionstart int64 + var value int64 + + sectionstart = Cpos() + headersize = int(Rnd(4+2+4+1+1, int64(Thearch.Ptrsize))) // don't count unit_length field itself + + for compunit = dwroot.child; compunit != nil; compunit = compunit.link { + b = getattr(compunit, DW_AT_low_pc) + if b == nil { + continue + } + e = getattr(compunit, DW_AT_high_pc) + if e == nil { + continue + } + + // Write .debug_aranges Header + entry (sec 6.1.2) + Thearch.Lput(uint32(headersize) + 4*uint32(Thearch.Ptrsize) - 4) // unit_length (*) + Thearch.Wput(2) // dwarf version (appendix F) + + value = compunit.offs - COMPUNITHEADERSIZE // debug_info_offset + if Linkmode == LinkExternal { + adddwarfrel(arangessec, infosym, sectionstart, 4, value) + } else { + Thearch.Lput(uint32(value)) + } + + Cput(uint8(Thearch.Ptrsize)) // address_size + Cput(0) // segment_size + strnput("", headersize-(4+2+4+1+1)) // align to thearch.ptrsize + + if Linkmode == LinkExternal { + adddwarfrel(arangessec, b.data.(*LSym), sectionstart, Thearch.Ptrsize, b.value-(b.data.(*LSym)).Value) + } else { + addrput(b.value) + } + + addrput(e.value - b.value) + addrput(0) + addrput(0) + } + + Cflush() + return sectionstart +} + +func writegdbscript() int64 { + var sectionstart int64 + + sectionstart = Cpos() + + if gdbscript != "" { + Cput(1) // magic 1 byte? + strnput(gdbscript, len(gdbscript)+1) + Cflush() + } + + return sectionstart +} + +func align(size int64) { + if HEADTYPE == Hwindows { // Only Windows PE need section align. + strnput("", int(Rnd(size, PEFILEALIGN)-size)) + } +} + +func writedwarfreloc(s *LSym) int64 { + var i int + var ri int + var start int64 + var r *Reloc + + start = Cpos() + for ri = 0; ri < len(s.R); ri++ { + r = &s.R[ri] + if Iself { + i = Thearch.Elfreloc1(r, int64(r.Off)) + } else if HEADTYPE == Hdarwin { + i = Thearch.Machoreloc1(r, int64(r.Off)) + } else { + i = -1 + } + if i < 0 { + Diag("unsupported obj reloc %d/%d to %s", r.Type, r.Siz, r.Sym.Name) + } + } + + return start +} + +/* + * This is the main entry point for generating dwarf. After emitting + * the mandatory debug_abbrev section, it calls writelines() to set up + * the per-compilation unit part of the DIE tree, while simultaneously + * emitting the debug_line section. When the final tree contains + * forward references, it will write the debug_info section in 2 + * passes. + * + */ +func Dwarfemitdebugsections() { + var infoe int64 + var die *DWDie + + if Debug['w'] != 0 { // disable dwarf + return + } + + if Linkmode == LinkExternal && !Iself { + return + } + + // For diagnostic messages. + newattr(&dwtypes, DW_AT_name, DW_CLS_STRING, int64(len("dwtypes")), "dwtypes") + + mkindex(&dwroot) + mkindex(&dwtypes) + mkindex(&dwglobals) + + // Some types that must exist to define other ones. + newdie(&dwtypes, DW_ABRV_NULLTYPE, "") + + newdie(&dwtypes, DW_ABRV_NULLTYPE, "void") + newdie(&dwtypes, DW_ABRV_BARE_PTRTYPE, "unsafe.Pointer") + + die = newdie(&dwtypes, DW_ABRV_BASETYPE, "uintptr") // needed for array size + newattr(die, DW_AT_encoding, DW_CLS_CONSTANT, DW_ATE_unsigned, 0) + newattr(die, DW_AT_byte_size, DW_CLS_CONSTANT, int64(Thearch.Ptrsize), 0) + newattr(die, DW_AT_go_kind, DW_CLS_CONSTANT, obj.KindUintptr, 0) + + // Needed by the prettyprinter code for interface inspection. + defgotype(lookup_or_diag("type.runtime._type")) + + defgotype(lookup_or_diag("type.runtime.interfacetype")) + defgotype(lookup_or_diag("type.runtime.itab")) + + genasmsym(defdwsymb) + + writeabbrev() + align(abbrevsize) + writelines() + align(linesize) + writeframes() + align(framesize) + + synthesizestringtypes(dwtypes.child) + synthesizeslicetypes(dwtypes.child) + synthesizemaptypes(dwtypes.child) + synthesizechantypes(dwtypes.child) + + reversetree(&dwroot.child) + reversetree(&dwtypes.child) + reversetree(&dwglobals.child) + + movetomodule(&dwtypes) + movetomodule(&dwglobals) + + infoo = Cpos() + writeinfo() + infoe = Cpos() + pubnameso = infoe + pubtypeso = infoe + arangeso = infoe + gdbscripto = infoe + + if fwdcount > 0 { + if Debug['v'] != 0 { + fmt.Fprintf(&Bso, "%5.2f dwarf pass 2.\n", obj.Cputime()) + } + Cseek(infoo) + writeinfo() + if fwdcount > 0 { + Diag("dwarf: unresolved references after first dwarf info pass") + Errorexit() + } + + if infoe != Cpos() { + Diag("dwarf: inconsistent second dwarf info pass") + Errorexit() + } + } + + infosize = infoe - infoo + align(infosize) + + pubnameso = writepub(ispubname) + pubnamessize = Cpos() - pubnameso + align(pubnamessize) + + pubtypeso = writepub(ispubtype) + pubtypessize = Cpos() - pubtypeso + align(pubtypessize) + + arangeso = writearanges() + arangessize = Cpos() - arangeso + align(arangessize) + + gdbscripto = writegdbscript() + gdbscriptsize = Cpos() - gdbscripto + align(gdbscriptsize) + + for Cpos()&7 != 0 { + Cput(0) + } + inforeloco = writedwarfreloc(infosec) + inforelocsize = Cpos() - inforeloco + align(inforelocsize) + + arangesreloco = writedwarfreloc(arangessec) + arangesrelocsize = Cpos() - arangesreloco + align(arangesrelocsize) + + linereloco = writedwarfreloc(linesec) + linerelocsize = Cpos() - linereloco + align(linerelocsize) + + framereloco = writedwarfreloc(framesec) + framerelocsize = Cpos() - framereloco + align(framerelocsize) +} + +/* + * Elf. + */ +const ( + ElfStrDebugAbbrev = iota + ElfStrDebugAranges + ElfStrDebugFrame + ElfStrDebugInfo + ElfStrDebugLine + ElfStrDebugLoc + ElfStrDebugMacinfo + ElfStrDebugPubNames + ElfStrDebugPubTypes + ElfStrDebugRanges + ElfStrDebugStr + ElfStrGDBScripts + ElfStrRelDebugInfo + ElfStrRelDebugAranges + ElfStrRelDebugLine + ElfStrRelDebugFrame + NElfStrDbg +) + +var elfstrdbg [NElfStrDbg]int64 + +func dwarfaddshstrings(shstrtab *LSym) { + if Debug['w'] != 0 { // disable dwarf + return + } + + elfstrdbg[ElfStrDebugAbbrev] = Addstring(shstrtab, ".debug_abbrev") + elfstrdbg[ElfStrDebugAranges] = Addstring(shstrtab, ".debug_aranges") + elfstrdbg[ElfStrDebugFrame] = Addstring(shstrtab, ".debug_frame") + elfstrdbg[ElfStrDebugInfo] = Addstring(shstrtab, ".debug_info") + elfstrdbg[ElfStrDebugLine] = Addstring(shstrtab, ".debug_line") + elfstrdbg[ElfStrDebugLoc] = Addstring(shstrtab, ".debug_loc") + elfstrdbg[ElfStrDebugMacinfo] = Addstring(shstrtab, ".debug_macinfo") + elfstrdbg[ElfStrDebugPubNames] = Addstring(shstrtab, ".debug_pubnames") + elfstrdbg[ElfStrDebugPubTypes] = Addstring(shstrtab, ".debug_pubtypes") + elfstrdbg[ElfStrDebugRanges] = Addstring(shstrtab, ".debug_ranges") + elfstrdbg[ElfStrDebugStr] = Addstring(shstrtab, ".debug_str") + elfstrdbg[ElfStrGDBScripts] = Addstring(shstrtab, ".debug_gdb_scripts") + if Linkmode == LinkExternal { + if Thearch.Thechar == '6' || Thearch.Thechar == '9' { + elfstrdbg[ElfStrRelDebugInfo] = Addstring(shstrtab, ".rela.debug_info") + elfstrdbg[ElfStrRelDebugAranges] = Addstring(shstrtab, ".rela.debug_aranges") + elfstrdbg[ElfStrRelDebugLine] = Addstring(shstrtab, ".rela.debug_line") + elfstrdbg[ElfStrRelDebugFrame] = Addstring(shstrtab, ".rela.debug_frame") + } else { + elfstrdbg[ElfStrRelDebugInfo] = Addstring(shstrtab, ".rel.debug_info") + elfstrdbg[ElfStrRelDebugAranges] = Addstring(shstrtab, ".rel.debug_aranges") + elfstrdbg[ElfStrRelDebugLine] = Addstring(shstrtab, ".rel.debug_line") + elfstrdbg[ElfStrRelDebugFrame] = Addstring(shstrtab, ".rel.debug_frame") + } + + infosym = Linklookup(Ctxt, ".debug_info", 0) + infosym.Hide = 1 + + abbrevsym = Linklookup(Ctxt, ".debug_abbrev", 0) + abbrevsym.Hide = 1 + + linesym = Linklookup(Ctxt, ".debug_line", 0) + linesym.Hide = 1 + + framesym = Linklookup(Ctxt, ".debug_frame", 0) + framesym.Hide = 1 + } +} + +// Add section symbols for DWARF debug info. This is called before +// dwarfaddelfheaders. +func dwarfaddelfsectionsyms() { + if infosym != nil { + infosympos = Cpos() + putelfsectionsym(infosym, 0) + } + + if abbrevsym != nil { + abbrevsympos = Cpos() + putelfsectionsym(abbrevsym, 0) + } + + if linesym != nil { + linesympos = Cpos() + putelfsectionsym(linesym, 0) + } + + if framesym != nil { + framesympos = Cpos() + putelfsectionsym(framesym, 0) + } +} + +func dwarfaddelfrelocheader(elfstr int, shdata *ElfShdr, off int64, size int64) { + var sh *ElfShdr + + sh = newElfShdr(elfstrdbg[elfstr]) + if Thearch.Thechar == '6' || Thearch.Thechar == '9' { + sh.type_ = SHT_RELA + } else { + sh.type_ = SHT_REL + } + + sh.entsize = uint64(Thearch.Ptrsize) * 2 + if sh.type_ == SHT_RELA { + sh.entsize += uint64(Thearch.Ptrsize) + } + sh.link = uint32(elfshname(".symtab").shnum) + sh.info = uint32(shdata.shnum) + sh.off = uint64(off) + sh.size = uint64(size) + sh.addralign = uint64(Thearch.Ptrsize) +} + +func dwarfaddelfheaders() { + var sh *ElfShdr + var shinfo *ElfShdr + var sharanges *ElfShdr + var shline *ElfShdr + var shframe *ElfShdr + + if Debug['w'] != 0 { // disable dwarf + return + } + + sh = newElfShdr(elfstrdbg[ElfStrDebugAbbrev]) + sh.type_ = SHT_PROGBITS + sh.off = uint64(abbrevo) + sh.size = uint64(abbrevsize) + sh.addralign = 1 + if abbrevsympos > 0 { + putelfsymshndx(abbrevsympos, sh.shnum) + } + + sh = newElfShdr(elfstrdbg[ElfStrDebugLine]) + sh.type_ = SHT_PROGBITS + sh.off = uint64(lineo) + sh.size = uint64(linesize) + sh.addralign = 1 + if linesympos > 0 { + putelfsymshndx(linesympos, sh.shnum) + } + shline = sh + + sh = newElfShdr(elfstrdbg[ElfStrDebugFrame]) + sh.type_ = SHT_PROGBITS + sh.off = uint64(frameo) + sh.size = uint64(framesize) + sh.addralign = 1 + if framesympos > 0 { + putelfsymshndx(framesympos, sh.shnum) + } + shframe = sh + + sh = newElfShdr(elfstrdbg[ElfStrDebugInfo]) + sh.type_ = SHT_PROGBITS + sh.off = uint64(infoo) + sh.size = uint64(infosize) + sh.addralign = 1 + if infosympos > 0 { + putelfsymshndx(infosympos, sh.shnum) + } + shinfo = sh + + if pubnamessize > 0 { + sh = newElfShdr(elfstrdbg[ElfStrDebugPubNames]) + sh.type_ = SHT_PROGBITS + sh.off = uint64(pubnameso) + sh.size = uint64(pubnamessize) + sh.addralign = 1 + } + + if pubtypessize > 0 { + sh = newElfShdr(elfstrdbg[ElfStrDebugPubTypes]) + sh.type_ = SHT_PROGBITS + sh.off = uint64(pubtypeso) + sh.size = uint64(pubtypessize) + sh.addralign = 1 + } + + sharanges = nil + if arangessize != 0 { + sh = newElfShdr(elfstrdbg[ElfStrDebugAranges]) + sh.type_ = SHT_PROGBITS + sh.off = uint64(arangeso) + sh.size = uint64(arangessize) + sh.addralign = 1 + sharanges = sh + } + + if gdbscriptsize != 0 { + sh = newElfShdr(elfstrdbg[ElfStrGDBScripts]) + sh.type_ = SHT_PROGBITS + sh.off = uint64(gdbscripto) + sh.size = uint64(gdbscriptsize) + sh.addralign = 1 + } + + if inforelocsize != 0 { + dwarfaddelfrelocheader(ElfStrRelDebugInfo, shinfo, inforeloco, inforelocsize) + } + + if arangesrelocsize != 0 { + dwarfaddelfrelocheader(ElfStrRelDebugAranges, sharanges, arangesreloco, arangesrelocsize) + } + + if linerelocsize != 0 { + dwarfaddelfrelocheader(ElfStrRelDebugLine, shline, linereloco, linerelocsize) + } + + if framerelocsize != 0 { + dwarfaddelfrelocheader(ElfStrRelDebugFrame, shframe, framereloco, framerelocsize) + } +} + +/* + * Macho + */ +func dwarfaddmachoheaders() { + var msect *MachoSect + var ms *MachoSeg + var fakestart int64 + var nsect int + + if Debug['w'] != 0 { // disable dwarf + return + } + + // Zero vsize segments won't be loaded in memory, even so they + // have to be page aligned in the file. + fakestart = abbrevo &^ 0xfff + + nsect = 4 + if pubnamessize > 0 { + nsect++ + } + if pubtypessize > 0 { + nsect++ + } + if arangessize > 0 { + nsect++ + } + if gdbscriptsize > 0 { + nsect++ + } + + ms = newMachoSeg("__DWARF", nsect) + ms.fileoffset = uint64(fakestart) + ms.filesize = uint64(abbrevo) - uint64(fakestart) + ms.vaddr = ms.fileoffset + Segdata.Vaddr - Segdata.Fileoff + + msect = newMachoSect(ms, "__debug_abbrev", "__DWARF") + msect.off = uint32(abbrevo) + msect.size = uint64(abbrevsize) + msect.addr = uint64(msect.off) + Segdata.Vaddr - Segdata.Fileoff + ms.filesize += msect.size + + msect = newMachoSect(ms, "__debug_line", "__DWARF") + msect.off = uint32(lineo) + msect.size = uint64(linesize) + msect.addr = uint64(msect.off) + Segdata.Vaddr - Segdata.Fileoff + ms.filesize += msect.size + + msect = newMachoSect(ms, "__debug_frame", "__DWARF") + msect.off = uint32(frameo) + msect.size = uint64(framesize) + msect.addr = uint64(msect.off) + Segdata.Vaddr - Segdata.Fileoff + ms.filesize += msect.size + + msect = newMachoSect(ms, "__debug_info", "__DWARF") + msect.off = uint32(infoo) + msect.size = uint64(infosize) + msect.addr = uint64(msect.off) + Segdata.Vaddr - Segdata.Fileoff + ms.filesize += msect.size + + if pubnamessize > 0 { + msect = newMachoSect(ms, "__debug_pubnames", "__DWARF") + msect.off = uint32(pubnameso) + msect.size = uint64(pubnamessize) + msect.addr = uint64(msect.off) + Segdata.Vaddr - Segdata.Fileoff + ms.filesize += msect.size + } + + if pubtypessize > 0 { + msect = newMachoSect(ms, "__debug_pubtypes", "__DWARF") + msect.off = uint32(pubtypeso) + msect.size = uint64(pubtypessize) + msect.addr = uint64(msect.off) + Segdata.Vaddr - Segdata.Fileoff + ms.filesize += msect.size + } + + if arangessize > 0 { + msect = newMachoSect(ms, "__debug_aranges", "__DWARF") + msect.off = uint32(arangeso) + msect.size = uint64(arangessize) + msect.addr = uint64(msect.off) + Segdata.Vaddr - Segdata.Fileoff + ms.filesize += msect.size + } + + // TODO(lvd) fix gdb/python to load MachO (16 char section name limit) + if gdbscriptsize > 0 { + msect = newMachoSect(ms, "__debug_gdb_scripts", "__DWARF") + msect.off = uint32(gdbscripto) + msect.size = uint64(gdbscriptsize) + msect.addr = uint64(msect.off) + Segdata.Vaddr - Segdata.Fileoff + ms.filesize += msect.size + } +} + +/* + * Windows PE + */ +func dwarfaddpeheaders() { + if Debug['w'] != 0 { // disable dwarf + return + } + + newPEDWARFSection(".debug_abbrev", abbrevsize) + newPEDWARFSection(".debug_line", linesize) + newPEDWARFSection(".debug_frame", framesize) + newPEDWARFSection(".debug_info", infosize) + newPEDWARFSection(".debug_pubnames", pubnamessize) + newPEDWARFSection(".debug_pubtypes", pubtypessize) + newPEDWARFSection(".debug_aranges", arangessize) + newPEDWARFSection(".debug_gdb_scripts", gdbscriptsize) +} diff --git a/src/cmd/internal/ld/dwarf_defs.go b/src/cmd/internal/ld/dwarf_defs.go new file mode 100644 index 0000000000..5be838965a --- /dev/null +++ b/src/cmd/internal/ld/dwarf_defs.go @@ -0,0 +1,500 @@ +// Copyright 2010 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 ld + +// TODO/NICETOHAVE: +// - eliminate DW_CLS_ if not used +// - package info in compilation units +// - assign global variables and types to their packages +// - gdb uses c syntax, meaning clumsy quoting is needed for go identifiers. eg +// ptype struct '[]uint8' and qualifiers need to be quoted away +// - lexical scoping is lost, so gdb gets confused as to which 'obj.i' you mean. +// - file:line info for variables +// - make strings a typedef so prettyprinters can see the underlying string type +// +// Copyright 2010 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. + +/* + * Emit debug_abbrevs, debug_info and debug_line sections to current + * offset in cout. + */ + +/* + * Add the dwarf section names to the ELF + * s[ection]h[eader]str[ing]tab. Prerequisite for + * dwarfaddelfheaders(). + */ + +/* + * Add section headers pointing to the sections emitted in + * dwarfemitdebugsections. + */ +// Copyright 2010 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. + +// Cut, pasted, tr-and-awk'ed from tables in +// http://dwarfstd.org/doc/Dwarf3.pdf + +// Table 18 +const ( + DW_TAG_array_type = 0x01 + DW_TAG_class_type = 0x02 + DW_TAG_entry_point = 0x03 + DW_TAG_enumeration_type = 0x04 + DW_TAG_formal_parameter = 0x05 + DW_TAG_imported_declaration = 0x08 + DW_TAG_label = 0x0a + DW_TAG_lexical_block = 0x0b + DW_TAG_member = 0x0d + DW_TAG_pointer_type = 0x0f + DW_TAG_reference_type = 0x10 + DW_TAG_compile_unit = 0x11 + DW_TAG_string_type = 0x12 + DW_TAG_structure_type = 0x13 + DW_TAG_subroutine_type = 0x15 + DW_TAG_typedef = 0x16 + DW_TAG_union_type = 0x17 + DW_TAG_unspecified_parameters = 0x18 + DW_TAG_variant = 0x19 + DW_TAG_common_block = 0x1a + DW_TAG_common_inclusion = 0x1b + DW_TAG_inheritance = 0x1c + DW_TAG_inlined_subroutine = 0x1d + DW_TAG_module = 0x1e + DW_TAG_ptr_to_member_type = 0x1f + DW_TAG_set_type = 0x20 + DW_TAG_subrange_type = 0x21 + DW_TAG_with_stmt = 0x22 + DW_TAG_access_declaration = 0x23 + DW_TAG_base_type = 0x24 + DW_TAG_catch_block = 0x25 + DW_TAG_const_type = 0x26 + DW_TAG_constant = 0x27 + DW_TAG_enumerator = 0x28 + DW_TAG_file_type = 0x29 + DW_TAG_friend = 0x2a + DW_TAG_namelist = 0x2b + DW_TAG_namelist_item = 0x2c + DW_TAG_packed_type = 0x2d + DW_TAG_subprogram = 0x2e + DW_TAG_template_type_parameter = 0x2f + DW_TAG_template_value_parameter = 0x30 + DW_TAG_thrown_type = 0x31 + DW_TAG_try_block = 0x32 + DW_TAG_variant_part = 0x33 + DW_TAG_variable = 0x34 + DW_TAG_volatile_type = 0x35 + DW_TAG_dwarf_procedure = 0x36 + DW_TAG_restrict_type = 0x37 + DW_TAG_interface_type = 0x38 + DW_TAG_namespace = 0x39 + DW_TAG_imported_module = 0x3a + DW_TAG_unspecified_type = 0x3b + DW_TAG_partial_unit = 0x3c + DW_TAG_imported_unit = 0x3d + DW_TAG_condition = 0x3f + DW_TAG_shared_type = 0x40 + DW_TAG_type_unit = 0x41 + DW_TAG_rvalue_reference_type = 0x42 + DW_TAG_template_alias = 0x43 + DW_TAG_lo_user = 0x4080 + DW_TAG_hi_user = 0xffff +) + +// Table 19 +const ( + DW_CHILDREN_no = 0x00 + DW_CHILDREN_yes = 0x01 +) + +// Not from the spec, but logicaly belongs here +const ( + DW_CLS_ADDRESS = 0x01 + iota + DW_CLS_BLOCK + DW_CLS_CONSTANT + DW_CLS_FLAG + DW_CLS_PTR + DW_CLS_REFERENCE + DW_CLS_ADDRLOC + DW_CLS_STRING +) + +// Table 20 +const ( + DW_AT_sibling = 0x01 + DW_AT_location = 0x02 + DW_AT_name = 0x03 + DW_AT_ordering = 0x09 + DW_AT_byte_size = 0x0b + DW_AT_bit_offset = 0x0c + DW_AT_bit_size = 0x0d + DW_AT_stmt_list = 0x10 + DW_AT_low_pc = 0x11 + DW_AT_high_pc = 0x12 + DW_AT_language = 0x13 + DW_AT_discr = 0x15 + DW_AT_discr_value = 0x16 + DW_AT_visibility = 0x17 + DW_AT_import = 0x18 + DW_AT_string_length = 0x19 + DW_AT_common_reference = 0x1a + DW_AT_comp_dir = 0x1b + DW_AT_const_value = 0x1c + DW_AT_containing_type = 0x1d + DW_AT_default_value = 0x1e + DW_AT_inline = 0x20 + DW_AT_is_optional = 0x21 + DW_AT_lower_bound = 0x22 + DW_AT_producer = 0x25 + DW_AT_prototyped = 0x27 + DW_AT_return_addr = 0x2a + DW_AT_start_scope = 0x2c + DW_AT_bit_stride = 0x2e + DW_AT_upper_bound = 0x2f + DW_AT_abstract_origin = 0x31 + DW_AT_accessibility = 0x32 + DW_AT_address_class = 0x33 + DW_AT_artificial = 0x34 + DW_AT_base_types = 0x35 + DW_AT_calling_convention = 0x36 + DW_AT_count = 0x37 + DW_AT_data_member_location = 0x38 + DW_AT_decl_column = 0x39 + DW_AT_decl_file = 0x3a + DW_AT_decl_line = 0x3b + DW_AT_declaration = 0x3c + DW_AT_discr_list = 0x3d + DW_AT_encoding = 0x3e + DW_AT_external = 0x3f + DW_AT_frame_base = 0x40 + DW_AT_friend = 0x41 + DW_AT_identifier_case = 0x42 + DW_AT_macro_info = 0x43 + DW_AT_namelist_item = 0x44 + DW_AT_priority = 0x45 + DW_AT_segment = 0x46 + DW_AT_specification = 0x47 + DW_AT_static_link = 0x48 + DW_AT_type = 0x49 + DW_AT_use_location = 0x4a + DW_AT_variable_parameter = 0x4b + DW_AT_virtuality = 0x4c + DW_AT_vtable_elem_location = 0x4d + DW_AT_allocated = 0x4e + DW_AT_associated = 0x4f + DW_AT_data_location = 0x50 + DW_AT_byte_stride = 0x51 + DW_AT_entry_pc = 0x52 + DW_AT_use_UTF8 = 0x53 + DW_AT_extension = 0x54 + DW_AT_ranges = 0x55 + DW_AT_trampoline = 0x56 + DW_AT_call_column = 0x57 + DW_AT_call_file = 0x58 + DW_AT_call_line = 0x59 + DW_AT_description = 0x5a + DW_AT_binary_scale = 0x5b + DW_AT_decimal_scale = 0x5c + DW_AT_small = 0x5d + DW_AT_decimal_sign = 0x5e + DW_AT_digit_count = 0x5f + DW_AT_picture_string = 0x60 + DW_AT_mutable = 0x61 + DW_AT_threads_scaled = 0x62 + DW_AT_explicit = 0x63 + DW_AT_object_pointer = 0x64 + DW_AT_endianity = 0x65 + DW_AT_elemental = 0x66 + DW_AT_pure = 0x67 + DW_AT_recursive = 0x68 + DW_AT_lo_user = 0x2000 + DW_AT_hi_user = 0x3fff +) + +// Table 21 +const ( + DW_FORM_addr = 0x01 + DW_FORM_block2 = 0x03 + DW_FORM_block4 = 0x04 + DW_FORM_data2 = 0x05 + DW_FORM_data4 = 0x06 + DW_FORM_data8 = 0x07 + DW_FORM_string = 0x08 + DW_FORM_block = 0x09 + DW_FORM_block1 = 0x0a + DW_FORM_data1 = 0x0b + DW_FORM_flag = 0x0c + DW_FORM_sdata = 0x0d + DW_FORM_strp = 0x0e + DW_FORM_udata = 0x0f + DW_FORM_ref_addr = 0x10 + DW_FORM_ref1 = 0x11 + DW_FORM_ref2 = 0x12 + DW_FORM_ref4 = 0x13 + DW_FORM_ref8 = 0x14 + DW_FORM_ref_udata = 0x15 + DW_FORM_indirect = 0x16 +) + +// Table 24 (#operands, notes) +const ( + DW_OP_addr = 0x03 + DW_OP_deref = 0x06 + DW_OP_const1u = 0x08 + DW_OP_const1s = 0x09 + DW_OP_const2u = 0x0a + DW_OP_const2s = 0x0b + DW_OP_const4u = 0x0c + DW_OP_const4s = 0x0d + DW_OP_const8u = 0x0e + DW_OP_const8s = 0x0f + DW_OP_constu = 0x10 + DW_OP_consts = 0x11 + DW_OP_dup = 0x12 + DW_OP_drop = 0x13 + DW_OP_over = 0x14 + DW_OP_pick = 0x15 + DW_OP_swap = 0x16 + DW_OP_rot = 0x17 + DW_OP_xderef = 0x18 + DW_OP_abs = 0x19 + DW_OP_and = 0x1a + DW_OP_div = 0x1b + DW_OP_minus = 0x1c + DW_OP_mod = 0x1d + DW_OP_mul = 0x1e + DW_OP_neg = 0x1f + DW_OP_not = 0x20 + DW_OP_or = 0x21 + DW_OP_plus = 0x22 + DW_OP_plus_uconst = 0x23 + DW_OP_shl = 0x24 + DW_OP_shr = 0x25 + DW_OP_shra = 0x26 + DW_OP_xor = 0x27 + DW_OP_skip = 0x2f + DW_OP_bra = 0x28 + DW_OP_eq = 0x29 + DW_OP_ge = 0x2a + DW_OP_gt = 0x2b + DW_OP_le = 0x2c + DW_OP_lt = 0x2d + DW_OP_ne = 0x2e + DW_OP_lit0 = 0x30 + DW_OP_lit31 = 0x4f + DW_OP_reg0 = 0x50 + DW_OP_reg31 = 0x6f + DW_OP_breg0 = 0x70 + DW_OP_breg31 = 0x8f + DW_OP_regx = 0x90 + DW_OP_fbreg = 0x91 + DW_OP_bregx = 0x92 + DW_OP_piece = 0x93 + DW_OP_deref_size = 0x94 + DW_OP_xderef_size = 0x95 + DW_OP_nop = 0x96 + DW_OP_push_object_address = 0x97 + DW_OP_call2 = 0x98 + DW_OP_call4 = 0x99 + DW_OP_call_ref = 0x9a + DW_OP_form_tls_address = 0x9b + DW_OP_call_frame_cfa = 0x9c + DW_OP_bit_piece = 0x9d + DW_OP_lo_user = 0xe0 + DW_OP_hi_user = 0xff +) + +// Table 25 +const ( + DW_ATE_address = 0x01 + DW_ATE_boolean = 0x02 + DW_ATE_complex_float = 0x03 + DW_ATE_float = 0x04 + DW_ATE_signed = 0x05 + DW_ATE_signed_char = 0x06 + DW_ATE_unsigned = 0x07 + DW_ATE_unsigned_char = 0x08 + DW_ATE_imaginary_float = 0x09 + DW_ATE_packed_decimal = 0x0a + DW_ATE_numeric_string = 0x0b + DW_ATE_edited = 0x0c + DW_ATE_signed_fixed = 0x0d + DW_ATE_unsigned_fixed = 0x0e + DW_ATE_decimal_float = 0x0f + DW_ATE_lo_user = 0x80 + DW_ATE_hi_user = 0xff +) + +// Table 26 +const ( + DW_DS_unsigned = 0x01 + DW_DS_leading_overpunch = 0x02 + DW_DS_trailing_overpunch = 0x03 + DW_DS_leading_separate = 0x04 + DW_DS_trailing_separate = 0x05 +) + +// Table 27 +const ( + DW_END_default = 0x00 + DW_END_big = 0x01 + DW_END_little = 0x02 + DW_END_lo_user = 0x40 + DW_END_hi_user = 0xff +) + +// Table 28 +const ( + DW_ACCESS_public = 0x01 + DW_ACCESS_protected = 0x02 + DW_ACCESS_private = 0x03 +) + +// Table 29 +const ( + DW_VIS_local = 0x01 + DW_VIS_exported = 0x02 + DW_VIS_qualified = 0x03 +) + +// Table 30 +const ( + DW_VIRTUALITY_none = 0x00 + DW_VIRTUALITY_virtual = 0x01 + DW_VIRTUALITY_pure_virtual = 0x02 +) + +// Table 31 +const ( + DW_LANG_C89 = 0x0001 + DW_LANG_C = 0x0002 + DW_LANG_Ada83 = 0x0003 + DW_LANG_C_plus_plus = 0x0004 + DW_LANG_Cobol74 = 0x0005 + DW_LANG_Cobol85 = 0x0006 + DW_LANG_Fortran77 = 0x0007 + DW_LANG_Fortran90 = 0x0008 + DW_LANG_Pascal83 = 0x0009 + DW_LANG_Modula2 = 0x000a + DW_LANG_Java = 0x000b + DW_LANG_C99 = 0x000c + DW_LANG_Ada95 = 0x000d + DW_LANG_Fortran95 = 0x000e + DW_LANG_PLI = 0x000f + DW_LANG_ObjC = 0x0010 + DW_LANG_ObjC_plus_plus = 0x0011 + DW_LANG_UPC = 0x0012 + DW_LANG_D = 0x0013 + DW_LANG_Python = 0x0014 + DW_LANG_Go = 0x0016 + DW_LANG_lo_user = 0x8000 + DW_LANG_hi_user = 0xffff +) + +// Table 32 +const ( + DW_ID_case_sensitive = 0x00 + DW_ID_up_case = 0x01 + DW_ID_down_case = 0x02 + DW_ID_case_insensitive = 0x03 +) + +// Table 33 +const ( + DW_CC_normal = 0x01 + DW_CC_program = 0x02 + DW_CC_nocall = 0x03 + DW_CC_lo_user = 0x40 + DW_CC_hi_user = 0xff +) + +// Table 34 +const ( + DW_INL_not_inlined = 0x00 + DW_INL_inlined = 0x01 + DW_INL_declared_not_inlined = 0x02 + DW_INL_declared_inlined = 0x03 +) + +// Table 35 +const ( + DW_ORD_row_major = 0x00 + DW_ORD_col_major = 0x01 +) + +// Table 36 +const ( + DW_DSC_label = 0x00 + DW_DSC_range = 0x01 +) + +// Table 37 +const ( + DW_LNS_copy = 0x01 + DW_LNS_advance_pc = 0x02 + DW_LNS_advance_line = 0x03 + DW_LNS_set_file = 0x04 + DW_LNS_set_column = 0x05 + DW_LNS_negate_stmt = 0x06 + DW_LNS_set_basic_block = 0x07 + DW_LNS_const_add_pc = 0x08 + DW_LNS_fixed_advance_pc = 0x09 + DW_LNS_set_prologue_end = 0x0a + DW_LNS_set_epilogue_begin = 0x0b + DW_LNS_set_isa = 0x0c +) + +// Table 38 +const ( + DW_LNE_end_sequence = 0x01 + DW_LNE_set_address = 0x02 + DW_LNE_define_file = 0x03 + DW_LNE_lo_user = 0x80 + DW_LNE_hi_user = 0xff +) + +// Table 39 +const ( + DW_MACINFO_define = 0x01 + DW_MACINFO_undef = 0x02 + DW_MACINFO_start_file = 0x03 + DW_MACINFO_end_file = 0x04 + DW_MACINFO_vendor_ext = 0xff +) + +// Table 40. +const ( + DW_CFA_nop = 0x00 + DW_CFA_set_loc = 0x01 + DW_CFA_advance_loc1 = 0x02 + DW_CFA_advance_loc2 = 0x03 + DW_CFA_advance_loc4 = 0x04 + DW_CFA_offset_extended = 0x05 + DW_CFA_restore_extended = 0x06 + DW_CFA_undefined = 0x07 + DW_CFA_same_value = 0x08 + DW_CFA_register = 0x09 + DW_CFA_remember_state = 0x0a + DW_CFA_restore_state = 0x0b + DW_CFA_def_cfa = 0x0c + DW_CFA_def_cfa_register = 0x0d + DW_CFA_def_cfa_offset = 0x0e + DW_CFA_def_cfa_expression = 0x0f + DW_CFA_expression = 0x10 + DW_CFA_offset_extended_sf = 0x11 + DW_CFA_def_cfa_sf = 0x12 + DW_CFA_def_cfa_offset_sf = 0x13 + DW_CFA_val_offset = 0x14 + DW_CFA_val_offset_sf = 0x15 + DW_CFA_val_expression = 0x16 + DW_CFA_lo_user = 0x1c + DW_CFA_hi_user = 0x3f + DW_CFA_advance_loc = 0x1 << 6 + DW_CFA_offset = 0x2 << 6 + DW_CFA_restore = 0x3 << 6 +) diff --git a/src/cmd/internal/ld/elf.go b/src/cmd/internal/ld/elf.go new file mode 100644 index 0000000000..ca95fe0cc2 --- /dev/null +++ b/src/cmd/internal/ld/elf.go @@ -0,0 +1,2448 @@ +// 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 ld + +import ( + "fmt" + "os" +) + +/* + * Derived from: + * $FreeBSD: src/sys/sys/elf32.h,v 1.8.14.1 2005/12/30 22:13:58 marcel Exp $ + * $FreeBSD: src/sys/sys/elf64.h,v 1.10.14.1 2005/12/30 22:13:58 marcel Exp $ + * $FreeBSD: src/sys/sys/elf_common.h,v 1.15.8.1 2005/12/30 22:13:58 marcel Exp $ + * $FreeBSD: src/sys/alpha/include/elf.h,v 1.14 2003/09/25 01:10:22 peter Exp $ + * $FreeBSD: src/sys/amd64/include/elf.h,v 1.18 2004/08/03 08:21:48 dfr Exp $ + * $FreeBSD: src/sys/arm/include/elf.h,v 1.5.2.1 2006/06/30 21:42:52 cognet Exp $ + * $FreeBSD: src/sys/i386/include/elf.h,v 1.16 2004/08/02 19:12:17 dfr Exp $ + * $FreeBSD: src/sys/powerpc/include/elf.h,v 1.7 2004/11/02 09:47:01 ssouhlal Exp $ + * $FreeBSD: src/sys/sparc64/include/elf.h,v 1.12 2003/09/25 01:10:26 peter Exp $ + * + * Copyright (c) 1996-1998 John D. Polstra. All rights reserved. + * Copyright (c) 2001 David E. O'Brien + * Portions Copyright 2009 The Go Authors. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +/* + * ELF definitions that are independent of architecture or word size. + */ + +/* + * Note header. The ".note" section contains an array of notes. Each + * begins with this header, aligned to a word boundary. Immediately + * following the note header is n_namesz bytes of name, padded to the + * next word boundary. Then comes n_descsz bytes of descriptor, again + * padded to a word boundary. The values of n_namesz and n_descsz do + * not include the padding. + */ +type Elf_Note struct { + n_namesz uint32 + n_descsz uint32 + n_type uint32 +} + +const ( + EI_MAG0 = 0 + EI_MAG1 = 1 + EI_MAG2 = 2 + EI_MAG3 = 3 + EI_CLASS = 4 + EI_DATA = 5 + EI_VERSION = 6 + EI_OSABI = 7 + EI_ABIVERSION = 8 + OLD_EI_BRAND = 8 + EI_PAD = 9 + EI_NIDENT = 16 + ELFMAG0 = 0x7f + ELFMAG1 = 'E' + ELFMAG2 = 'L' + ELFMAG3 = 'F' + SELFMAG = 4 + EV_NONE = 0 + EV_CURRENT = 1 + ELFCLASSNONE = 0 + ELFCLASS32 = 1 + ELFCLASS64 = 2 + ELFDATANONE = 0 + ELFDATA2LSB = 1 + ELFDATA2MSB = 2 + ELFOSABI_NONE = 0 + ELFOSABI_HPUX = 1 + ELFOSABI_NETBSD = 2 + ELFOSABI_LINUX = 3 + ELFOSABI_HURD = 4 + ELFOSABI_86OPEN = 5 + ELFOSABI_SOLARIS = 6 + ELFOSABI_AIX = 7 + ELFOSABI_IRIX = 8 + ELFOSABI_FREEBSD = 9 + ELFOSABI_TRU64 = 10 + ELFOSABI_MODESTO = 11 + ELFOSABI_OPENBSD = 12 + ELFOSABI_OPENVMS = 13 + ELFOSABI_NSK = 14 + ELFOSABI_ARM = 97 + ELFOSABI_STANDALONE = 255 + ELFOSABI_SYSV = ELFOSABI_NONE + ELFOSABI_MONTEREY = ELFOSABI_AIX + ET_NONE = 0 + ET_REL = 1 + ET_EXEC = 2 + ET_DYN = 3 + ET_CORE = 4 + ET_LOOS = 0xfe00 + ET_HIOS = 0xfeff + ET_LOPROC = 0xff00 + ET_HIPROC = 0xffff + EM_NONE = 0 + EM_M32 = 1 + EM_SPARC = 2 + EM_386 = 3 + EM_68K = 4 + EM_88K = 5 + EM_860 = 7 + EM_MIPS = 8 + EM_S370 = 9 + EM_MIPS_RS3_LE = 10 + EM_PARISC = 15 + EM_VPP500 = 17 + EM_SPARC32PLUS = 18 + EM_960 = 19 + EM_PPC = 20 + EM_PPC64 = 21 + EM_S390 = 22 + EM_V800 = 36 + EM_FR20 = 37 + EM_RH32 = 38 + EM_RCE = 39 + EM_ARM = 40 + EM_SH = 42 + EM_SPARCV9 = 43 + EM_TRICORE = 44 + EM_ARC = 45 + EM_H8_300 = 46 + EM_H8_300H = 47 + EM_H8S = 48 + EM_H8_500 = 49 + EM_IA_64 = 50 + EM_MIPS_X = 51 + EM_COLDFIRE = 52 + EM_68HC12 = 53 + EM_MMA = 54 + EM_PCP = 55 + EM_NCPU = 56 + EM_NDR1 = 57 + EM_STARCORE = 58 + EM_ME16 = 59 + EM_ST100 = 60 + EM_TINYJ = 61 + EM_X86_64 = 62 + EM_486 = 6 + EM_MIPS_RS4_BE = 10 + EM_ALPHA_STD = 41 + EM_ALPHA = 0x9026 + SHN_UNDEF = 0 + SHN_LORESERVE = 0xff00 + SHN_LOPROC = 0xff00 + SHN_HIPROC = 0xff1f + SHN_LOOS = 0xff20 + SHN_HIOS = 0xff3f + SHN_ABS = 0xfff1 + SHN_COMMON = 0xfff2 + SHN_XINDEX = 0xffff + SHN_HIRESERVE = 0xffff + SHT_NULL = 0 + SHT_PROGBITS = 1 + SHT_SYMTAB = 2 + SHT_STRTAB = 3 + SHT_RELA = 4 + SHT_HASH = 5 + SHT_DYNAMIC = 6 + SHT_NOTE = 7 + SHT_NOBITS = 8 + SHT_REL = 9 + SHT_SHLIB = 10 + SHT_DYNSYM = 11 + SHT_INIT_ARRAY = 14 + SHT_FINI_ARRAY = 15 + SHT_PREINIT_ARRAY = 16 + SHT_GROUP = 17 + SHT_SYMTAB_SHNDX = 18 + SHT_LOOS = 0x60000000 + SHT_HIOS = 0x6fffffff + SHT_GNU_VERDEF = 0x6ffffffd + SHT_GNU_VERNEED = 0x6ffffffe + SHT_GNU_VERSYM = 0x6fffffff + SHT_LOPROC = 0x70000000 + SHT_HIPROC = 0x7fffffff + SHT_LOUSER = 0x80000000 + SHT_HIUSER = 0xffffffff + SHF_WRITE = 0x1 + SHF_ALLOC = 0x2 + SHF_EXECINSTR = 0x4 + SHF_MERGE = 0x10 + SHF_STRINGS = 0x20 + SHF_INFO_LINK = 0x40 + SHF_LINK_ORDER = 0x80 + SHF_OS_NONCONFORMING = 0x100 + SHF_GROUP = 0x200 + SHF_TLS = 0x400 + SHF_MASKOS = 0x0ff00000 + SHF_MASKPROC = 0xf0000000 + PT_NULL = 0 + PT_LOAD = 1 + PT_DYNAMIC = 2 + PT_INTERP = 3 + PT_NOTE = 4 + PT_SHLIB = 5 + PT_PHDR = 6 + PT_TLS = 7 + PT_LOOS = 0x60000000 + PT_HIOS = 0x6fffffff + PT_LOPROC = 0x70000000 + PT_HIPROC = 0x7fffffff + PT_GNU_STACK = 0x6474e551 + PT_PAX_FLAGS = 0x65041580 + PF_X = 0x1 + PF_W = 0x2 + PF_R = 0x4 + PF_MASKOS = 0x0ff00000 + PF_MASKPROC = 0xf0000000 + DT_NULL = 0 + DT_NEEDED = 1 + DT_PLTRELSZ = 2 + DT_PLTGOT = 3 + DT_HASH = 4 + DT_STRTAB = 5 + DT_SYMTAB = 6 + DT_RELA = 7 + DT_RELASZ = 8 + DT_RELAENT = 9 + DT_STRSZ = 10 + DT_SYMENT = 11 + DT_INIT = 12 + DT_FINI = 13 + DT_SONAME = 14 + DT_RPATH = 15 + DT_SYMBOLIC = 16 + DT_REL = 17 + DT_RELSZ = 18 + DT_RELENT = 19 + DT_PLTREL = 20 + DT_DEBUG = 21 + DT_TEXTREL = 22 + DT_JMPREL = 23 + DT_BIND_NOW = 24 + DT_INIT_ARRAY = 25 + DT_FINI_ARRAY = 26 + DT_INIT_ARRAYSZ = 27 + DT_FINI_ARRAYSZ = 28 + DT_RUNPATH = 29 + DT_FLAGS = 30 + DT_ENCODING = 32 + DT_PREINIT_ARRAY = 32 + DT_PREINIT_ARRAYSZ = 33 + DT_LOOS = 0x6000000d + DT_HIOS = 0x6ffff000 + DT_LOPROC = 0x70000000 + DT_HIPROC = 0x7fffffff + DT_VERNEED = 0x6ffffffe + DT_VERNEEDNUM = 0x6fffffff + DT_VERSYM = 0x6ffffff0 + DT_PPC64_GLINK = DT_LOPROC + 0 + DT_PPC64_OPT = DT_LOPROC + 3 + DF_ORIGIN = 0x0001 + DF_SYMBOLIC = 0x0002 + DF_TEXTREL = 0x0004 + DF_BIND_NOW = 0x0008 + DF_STATIC_TLS = 0x0010 + NT_PRSTATUS = 1 + NT_FPREGSET = 2 + NT_PRPSINFO = 3 + STB_LOCAL = 0 + STB_GLOBAL = 1 + STB_WEAK = 2 + STB_LOOS = 10 + STB_HIOS = 12 + STB_LOPROC = 13 + STB_HIPROC = 15 + STT_NOTYPE = 0 + STT_OBJECT = 1 + STT_FUNC = 2 + STT_SECTION = 3 + STT_FILE = 4 + STT_COMMON = 5 + STT_TLS = 6 + STT_LOOS = 10 + STT_HIOS = 12 + STT_LOPROC = 13 + STT_HIPROC = 15 + STV_DEFAULT = 0x0 + STV_INTERNAL = 0x1 + STV_HIDDEN = 0x2 + STV_PROTECTED = 0x3 + STN_UNDEF = 0 +) + +/* For accessing the fields of r_info. */ + +/* For constructing r_info from field values. */ + +/* + * Relocation types. + */ +const ( + R_X86_64_NONE = 0 + R_X86_64_64 = 1 + R_X86_64_PC32 = 2 + R_X86_64_GOT32 = 3 + R_X86_64_PLT32 = 4 + R_X86_64_COPY = 5 + R_X86_64_GLOB_DAT = 6 + R_X86_64_JMP_SLOT = 7 + R_X86_64_RELATIVE = 8 + R_X86_64_GOTPCREL = 9 + R_X86_64_32 = 10 + R_X86_64_32S = 11 + R_X86_64_16 = 12 + R_X86_64_PC16 = 13 + R_X86_64_8 = 14 + R_X86_64_PC8 = 15 + R_X86_64_DTPMOD64 = 16 + R_X86_64_DTPOFF64 = 17 + R_X86_64_TPOFF64 = 18 + R_X86_64_TLSGD = 19 + R_X86_64_TLSLD = 20 + R_X86_64_DTPOFF32 = 21 + R_X86_64_GOTTPOFF = 22 + R_X86_64_TPOFF32 = 23 + R_X86_64_COUNT = 24 + R_ALPHA_NONE = 0 + R_ALPHA_REFLONG = 1 + R_ALPHA_REFQUAD = 2 + R_ALPHA_GPREL32 = 3 + R_ALPHA_LITERAL = 4 + R_ALPHA_LITUSE = 5 + R_ALPHA_GPDISP = 6 + R_ALPHA_BRADDR = 7 + R_ALPHA_HINT = 8 + R_ALPHA_SREL16 = 9 + R_ALPHA_SREL32 = 10 + R_ALPHA_SREL64 = 11 + R_ALPHA_OP_PUSH = 12 + R_ALPHA_OP_STORE = 13 + R_ALPHA_OP_PSUB = 14 + R_ALPHA_OP_PRSHIFT = 15 + R_ALPHA_GPVALUE = 16 + R_ALPHA_GPRELHIGH = 17 + R_ALPHA_GPRELLOW = 18 + R_ALPHA_IMMED_GP_16 = 19 + R_ALPHA_IMMED_GP_HI32 = 20 + R_ALPHA_IMMED_SCN_HI32 = 21 + R_ALPHA_IMMED_BR_HI32 = 22 + R_ALPHA_IMMED_LO32 = 23 + R_ALPHA_COPY = 24 + R_ALPHA_GLOB_DAT = 25 + R_ALPHA_JMP_SLOT = 26 + R_ALPHA_RELATIVE = 27 + R_ALPHA_COUNT = 28 + R_ARM_NONE = 0 + R_ARM_PC24 = 1 + R_ARM_ABS32 = 2 + R_ARM_REL32 = 3 + R_ARM_PC13 = 4 + R_ARM_ABS16 = 5 + R_ARM_ABS12 = 6 + R_ARM_THM_ABS5 = 7 + R_ARM_ABS8 = 8 + R_ARM_SBREL32 = 9 + R_ARM_THM_PC22 = 10 + R_ARM_THM_PC8 = 11 + R_ARM_AMP_VCALL9 = 12 + R_ARM_SWI24 = 13 + R_ARM_THM_SWI8 = 14 + R_ARM_XPC25 = 15 + R_ARM_THM_XPC22 = 16 + R_ARM_COPY = 20 + R_ARM_GLOB_DAT = 21 + R_ARM_JUMP_SLOT = 22 + R_ARM_RELATIVE = 23 + R_ARM_GOTOFF = 24 + R_ARM_GOTPC = 25 + R_ARM_GOT32 = 26 + R_ARM_PLT32 = 27 + R_ARM_CALL = 28 + R_ARM_JUMP24 = 29 + R_ARM_V4BX = 40 + R_ARM_GOT_PREL = 96 + R_ARM_GNU_VTENTRY = 100 + R_ARM_GNU_VTINHERIT = 101 + R_ARM_TLS_IE32 = 107 + R_ARM_TLS_LE32 = 108 + R_ARM_RSBREL32 = 250 + R_ARM_THM_RPC22 = 251 + R_ARM_RREL32 = 252 + R_ARM_RABS32 = 253 + R_ARM_RPC24 = 254 + R_ARM_RBASE = 255 + R_ARM_COUNT = 38 + R_386_NONE = 0 + R_386_32 = 1 + R_386_PC32 = 2 + R_386_GOT32 = 3 + R_386_PLT32 = 4 + R_386_COPY = 5 + R_386_GLOB_DAT = 6 + R_386_JMP_SLOT = 7 + R_386_RELATIVE = 8 + R_386_GOTOFF = 9 + R_386_GOTPC = 10 + R_386_TLS_TPOFF = 14 + R_386_TLS_IE = 15 + R_386_TLS_GOTIE = 16 + R_386_TLS_LE = 17 + R_386_TLS_GD = 18 + R_386_TLS_LDM = 19 + R_386_TLS_GD_32 = 24 + R_386_TLS_GD_PUSH = 25 + R_386_TLS_GD_CALL = 26 + R_386_TLS_GD_POP = 27 + R_386_TLS_LDM_32 = 28 + R_386_TLS_LDM_PUSH = 29 + R_386_TLS_LDM_CALL = 30 + R_386_TLS_LDM_POP = 31 + R_386_TLS_LDO_32 = 32 + R_386_TLS_IE_32 = 33 + R_386_TLS_LE_32 = 34 + R_386_TLS_DTPMOD32 = 35 + R_386_TLS_DTPOFF32 = 36 + R_386_TLS_TPOFF32 = 37 + R_386_COUNT = 38 + R_PPC_NONE = 0 + R_PPC_ADDR32 = 1 + R_PPC_ADDR24 = 2 + R_PPC_ADDR16 = 3 + R_PPC_ADDR16_LO = 4 + R_PPC_ADDR16_HI = 5 + R_PPC_ADDR16_HA = 6 + R_PPC_ADDR14 = 7 + R_PPC_ADDR14_BRTAKEN = 8 + R_PPC_ADDR14_BRNTAKEN = 9 + R_PPC_REL24 = 10 + R_PPC_REL14 = 11 + R_PPC_REL14_BRTAKEN = 12 + R_PPC_REL14_BRNTAKEN = 13 + R_PPC_GOT16 = 14 + R_PPC_GOT16_LO = 15 + R_PPC_GOT16_HI = 16 + R_PPC_GOT16_HA = 17 + R_PPC_PLTREL24 = 18 + R_PPC_COPY = 19 + R_PPC_GLOB_DAT = 20 + R_PPC_JMP_SLOT = 21 + R_PPC_RELATIVE = 22 + R_PPC_LOCAL24PC = 23 + R_PPC_UADDR32 = 24 + R_PPC_UADDR16 = 25 + R_PPC_REL32 = 26 + R_PPC_PLT32 = 27 + R_PPC_PLTREL32 = 28 + R_PPC_PLT16_LO = 29 + R_PPC_PLT16_HI = 30 + R_PPC_PLT16_HA = 31 + R_PPC_SDAREL16 = 32 + R_PPC_SECTOFF = 33 + R_PPC_SECTOFF_LO = 34 + R_PPC_SECTOFF_HI = 35 + R_PPC_SECTOFF_HA = 36 + R_PPC_COUNT = 37 + R_PPC_TLS = 67 + R_PPC_DTPMOD32 = 68 + R_PPC_TPREL16 = 69 + R_PPC_TPREL16_LO = 70 + R_PPC_TPREL16_HI = 71 + R_PPC_TPREL16_HA = 72 + R_PPC_TPREL32 = 73 + R_PPC_DTPREL16 = 74 + R_PPC_DTPREL16_LO = 75 + R_PPC_DTPREL16_HI = 76 + R_PPC_DTPREL16_HA = 77 + R_PPC_DTPREL32 = 78 + R_PPC_GOT_TLSGD16 = 79 + R_PPC_GOT_TLSGD16_LO = 80 + R_PPC_GOT_TLSGD16_HI = 81 + R_PPC_GOT_TLSGD16_HA = 82 + R_PPC_GOT_TLSLD16 = 83 + R_PPC_GOT_TLSLD16_LO = 84 + R_PPC_GOT_TLSLD16_HI = 85 + R_PPC_GOT_TLSLD16_HA = 86 + R_PPC_GOT_TPREL16 = 87 + R_PPC_GOT_TPREL16_LO = 88 + R_PPC_GOT_TPREL16_HI = 89 + R_PPC_GOT_TPREL16_HA = 90 + R_PPC_EMB_NADDR32 = 101 + R_PPC_EMB_NADDR16 = 102 + R_PPC_EMB_NADDR16_LO = 103 + R_PPC_EMB_NADDR16_HI = 104 + R_PPC_EMB_NADDR16_HA = 105 + R_PPC_EMB_SDAI16 = 106 + R_PPC_EMB_SDA2I16 = 107 + R_PPC_EMB_SDA2REL = 108 + R_PPC_EMB_SDA21 = 109 + R_PPC_EMB_MRKREF = 110 + R_PPC_EMB_RELSEC16 = 111 + R_PPC_EMB_RELST_LO = 112 + R_PPC_EMB_RELST_HI = 113 + R_PPC_EMB_RELST_HA = 114 + R_PPC_EMB_BIT_FLD = 115 + R_PPC_EMB_RELSDA = 116 + R_PPC_EMB_COUNT = R_PPC_EMB_RELSDA - R_PPC_EMB_NADDR32 + 1 + R_PPC64_REL24 = R_PPC_REL24 + R_PPC64_JMP_SLOT = R_PPC_JMP_SLOT + R_PPC64_ADDR64 = 38 + R_PPC64_TOC16 = 47 + R_PPC64_TOC16_LO = 48 + R_PPC64_TOC16_HI = 49 + R_PPC64_TOC16_HA = 50 + R_PPC64_TOC16_DS = 63 + R_PPC64_TOC16_LO_DS = 64 + R_PPC64_REL16_LO = 250 + R_PPC64_REL16_HI = 251 + R_PPC64_REL16_HA = 252 + R_SPARC_NONE = 0 + R_SPARC_8 = 1 + R_SPARC_16 = 2 + R_SPARC_32 = 3 + R_SPARC_DISP8 = 4 + R_SPARC_DISP16 = 5 + R_SPARC_DISP32 = 6 + R_SPARC_WDISP30 = 7 + R_SPARC_WDISP22 = 8 + R_SPARC_HI22 = 9 + R_SPARC_22 = 10 + R_SPARC_13 = 11 + R_SPARC_LO10 = 12 + R_SPARC_GOT10 = 13 + R_SPARC_GOT13 = 14 + R_SPARC_GOT22 = 15 + R_SPARC_PC10 = 16 + R_SPARC_PC22 = 17 + R_SPARC_WPLT30 = 18 + R_SPARC_COPY = 19 + R_SPARC_GLOB_DAT = 20 + R_SPARC_JMP_SLOT = 21 + R_SPARC_RELATIVE = 22 + R_SPARC_UA32 = 23 + R_SPARC_PLT32 = 24 + R_SPARC_HIPLT22 = 25 + R_SPARC_LOPLT10 = 26 + R_SPARC_PCPLT32 = 27 + R_SPARC_PCPLT22 = 28 + R_SPARC_PCPLT10 = 29 + R_SPARC_10 = 30 + R_SPARC_11 = 31 + R_SPARC_64 = 32 + R_SPARC_OLO10 = 33 + R_SPARC_HH22 = 34 + R_SPARC_HM10 = 35 + R_SPARC_LM22 = 36 + R_SPARC_PC_HH22 = 37 + R_SPARC_PC_HM10 = 38 + R_SPARC_PC_LM22 = 39 + R_SPARC_WDISP16 = 40 + R_SPARC_WDISP19 = 41 + R_SPARC_GLOB_JMP = 42 + R_SPARC_7 = 43 + R_SPARC_5 = 44 + R_SPARC_6 = 45 + R_SPARC_DISP64 = 46 + R_SPARC_PLT64 = 47 + R_SPARC_HIX22 = 48 + R_SPARC_LOX10 = 49 + R_SPARC_H44 = 50 + R_SPARC_M44 = 51 + R_SPARC_L44 = 52 + R_SPARC_REGISTER = 53 + R_SPARC_UA64 = 54 + R_SPARC_UA16 = 55 + ARM_MAGIC_TRAMP_NUMBER = 0x5c000003 +) + +/* + * Symbol table entries. + */ + +/* For accessing the fields of st_info. */ + +/* For constructing st_info from field values. */ + +/* For accessing the fields of st_other. */ + +/* + * ELF header. + */ +type ElfEhdr struct { + ident [EI_NIDENT]uint8 + type_ uint16 + machine uint16 + version uint32 + entry uint64 + phoff uint64 + shoff uint64 + flags uint32 + ehsize uint16 + phentsize uint16 + phnum uint16 + shentsize uint16 + shnum uint16 + shstrndx uint16 +} + +/* + * Section header. + */ +type ElfShdr struct { + name uint32 + type_ uint32 + flags uint64 + addr uint64 + off uint64 + size uint64 + link uint32 + info uint32 + addralign uint64 + entsize uint64 + shnum int + secsym *LSym +} + +/* + * Program header. + */ +type ElfPhdr struct { + type_ uint32 + flags uint32 + off uint64 + vaddr uint64 + paddr uint64 + filesz uint64 + memsz uint64 + align uint64 +} + +/* For accessing the fields of r_info. */ + +/* For constructing r_info from field values. */ + +/* + * Symbol table entries. + */ + +/* For accessing the fields of st_info. */ + +/* For constructing st_info from field values. */ + +/* For accessing the fields of st_other. */ + +/* + * Go linker interface + */ +const ( + ELF64HDRSIZE = 64 + ELF64PHDRSIZE = 56 + ELF64SHDRSIZE = 64 + ELF64RELSIZE = 16 + ELF64RELASIZE = 24 + ELF64SYMSIZE = 24 + ELF32HDRSIZE = 52 + ELF32PHDRSIZE = 32 + ELF32SHDRSIZE = 40 + ELF32SYMSIZE = 16 + ELF32RELSIZE = 8 +) + +/* + * The interface uses the 64-bit structures always, + * to avoid code duplication. The writers know how to + * marshal a 32-bit representation from the 64-bit structure. + */ + +var numelfphdr int + +var numelfshdr int + +var elfstrsize int + +var Elfstrdat []byte + +/* + * Total amount of space to reserve at the start of the file + * for Header, PHeaders, SHeaders, and interp. + * May waste some. + * On FreeBSD, cannot be larger than a page. + */ +const ( + ELFRESERVE = 3072 +) + +/* + * We use the 64-bit data structures on both 32- and 64-bit machines + * in order to write the code just once. The 64-bit data structure is + * written in the 32-bit format on the 32-bit machines. + */ +const ( + NSECT = 48 +) + +var Iself bool + +var Nelfsym int = 1 + +var elf64 int + +var ehdr ElfEhdr + +var phdr [NSECT]*ElfPhdr + +var shdr [NSECT]*ElfShdr + +var interp string + +type Elfstring struct { + s string + off int +} + +var elfstr [100]Elfstring + +var nelfstr int + +var buildinfo []byte + +/* + Initialize the global variable that describes the ELF header. It will be updated as + we write section and prog headers. +*/ +func Elfinit() { + Iself = true + + switch Thearch.Thechar { + // 64-bit architectures + case '9': + if Ctxt.Arch.Endian == BigEndian { + ehdr.flags = 1 /* Version 1 ABI */ + } else { + ehdr.flags = 2 /* Version 2 ABI */ + } + fallthrough + + // fallthrough + case '6': + elf64 = 1 + + ehdr.phoff = ELF64HDRSIZE /* Must be be ELF64HDRSIZE: first PHdr must follow ELF header */ + ehdr.shoff = ELF64HDRSIZE /* Will move as we add PHeaders */ + ehdr.ehsize = ELF64HDRSIZE /* Must be ELF64HDRSIZE */ + ehdr.phentsize = ELF64PHDRSIZE /* Must be ELF64PHDRSIZE */ + ehdr.shentsize = ELF64SHDRSIZE /* Must be ELF64SHDRSIZE */ + + // we use EABI on both linux/arm and freebsd/arm. + // 32-bit architectures + case '5': + if HEADTYPE == Hlinux || HEADTYPE == Hfreebsd { + ehdr.flags = 0x5000002 // has entry point, Version5 EABI + } + fallthrough + + // fallthrough + default: + ehdr.phoff = ELF32HDRSIZE + /* Must be be ELF32HDRSIZE: first PHdr must follow ELF header */ + ehdr.shoff = ELF32HDRSIZE /* Will move as we add PHeaders */ + ehdr.ehsize = ELF32HDRSIZE /* Must be ELF32HDRSIZE */ + ehdr.phentsize = ELF32PHDRSIZE /* Must be ELF32PHDRSIZE */ + ehdr.shentsize = ELF32SHDRSIZE /* Must be ELF32SHDRSIZE */ + } +} + +func elf64phdr(e *ElfPhdr) { + Thearch.Lput(e.type_) + Thearch.Lput(e.flags) + Thearch.Vput(e.off) + Thearch.Vput(e.vaddr) + Thearch.Vput(e.paddr) + Thearch.Vput(e.filesz) + Thearch.Vput(e.memsz) + Thearch.Vput(e.align) +} + +func elf32phdr(e *ElfPhdr) { + var frag int + + if e.type_ == PT_LOAD { + // Correct ELF loaders will do this implicitly, + // but buggy ELF loaders like the one in some + // versions of QEMU won't. + frag = int(e.vaddr & (e.align - 1)) + + e.off -= uint64(frag) + e.vaddr -= uint64(frag) + e.paddr -= uint64(frag) + e.filesz += uint64(frag) + e.memsz += uint64(frag) + } + + Thearch.Lput(e.type_) + Thearch.Lput(uint32(e.off)) + Thearch.Lput(uint32(e.vaddr)) + Thearch.Lput(uint32(e.paddr)) + Thearch.Lput(uint32(e.filesz)) + Thearch.Lput(uint32(e.memsz)) + Thearch.Lput(e.flags) + Thearch.Lput(uint32(e.align)) +} + +func elf64shdr(e *ElfShdr) { + Thearch.Lput(e.name) + Thearch.Lput(e.type_) + Thearch.Vput(e.flags) + Thearch.Vput(e.addr) + Thearch.Vput(e.off) + Thearch.Vput(e.size) + Thearch.Lput(e.link) + Thearch.Lput(e.info) + Thearch.Vput(e.addralign) + Thearch.Vput(e.entsize) +} + +func elf32shdr(e *ElfShdr) { + Thearch.Lput(e.name) + Thearch.Lput(e.type_) + Thearch.Lput(uint32(e.flags)) + Thearch.Lput(uint32(e.addr)) + Thearch.Lput(uint32(e.off)) + Thearch.Lput(uint32(e.size)) + Thearch.Lput(e.link) + Thearch.Lput(e.info) + Thearch.Lput(uint32(e.addralign)) + Thearch.Lput(uint32(e.entsize)) +} + +func elfwriteshdrs() uint32 { + var i int + + if elf64 != 0 { + for i = 0; i < int(ehdr.shnum); i++ { + elf64shdr(shdr[i]) + } + return uint32(ehdr.shnum) * ELF64SHDRSIZE + } + + for i = 0; i < int(ehdr.shnum); i++ { + elf32shdr(shdr[i]) + } + return uint32(ehdr.shnum) * ELF32SHDRSIZE +} + +func elfsetstring(s string, off int) { + if nelfstr >= len(elfstr) { + Diag("too many elf strings") + Errorexit() + } + + elfstr[nelfstr].s = s + elfstr[nelfstr].off = off + nelfstr++ +} + +func elfwritephdrs() uint32 { + var i int + + if elf64 != 0 { + for i = 0; i < int(ehdr.phnum); i++ { + elf64phdr(phdr[i]) + } + return uint32(ehdr.phnum) * ELF64PHDRSIZE + } + + for i = 0; i < int(ehdr.phnum); i++ { + elf32phdr(phdr[i]) + } + return uint32(ehdr.phnum) * ELF32PHDRSIZE +} + +func newElfPhdr() *ElfPhdr { + var e *ElfPhdr + + e = new(ElfPhdr) + if ehdr.phnum >= NSECT { + Diag("too many phdrs") + } else { + phdr[ehdr.phnum] = e + ehdr.phnum++ + } + if elf64 != 0 { + ehdr.shoff += ELF64PHDRSIZE + } else { + ehdr.shoff += ELF32PHDRSIZE + } + return e +} + +func newElfShdr(name int64) *ElfShdr { + var e *ElfShdr + + e = new(ElfShdr) + e.name = uint32(name) + e.shnum = int(ehdr.shnum) + if ehdr.shnum >= NSECT { + Diag("too many shdrs") + } else { + shdr[ehdr.shnum] = e + ehdr.shnum++ + } + + return e +} + +func getElfEhdr() *ElfEhdr { + return &ehdr +} + +func elf64writehdr() uint32 { + var i int + + for i = 0; i < EI_NIDENT; i++ { + Cput(ehdr.ident[i]) + } + Thearch.Wput(ehdr.type_) + Thearch.Wput(ehdr.machine) + Thearch.Lput(ehdr.version) + Thearch.Vput(ehdr.entry) + Thearch.Vput(ehdr.phoff) + Thearch.Vput(ehdr.shoff) + Thearch.Lput(ehdr.flags) + Thearch.Wput(ehdr.ehsize) + Thearch.Wput(ehdr.phentsize) + Thearch.Wput(ehdr.phnum) + Thearch.Wput(ehdr.shentsize) + Thearch.Wput(ehdr.shnum) + Thearch.Wput(ehdr.shstrndx) + return ELF64HDRSIZE +} + +func elf32writehdr() uint32 { + var i int + + for i = 0; i < EI_NIDENT; i++ { + Cput(ehdr.ident[i]) + } + Thearch.Wput(ehdr.type_) + Thearch.Wput(ehdr.machine) + Thearch.Lput(ehdr.version) + Thearch.Lput(uint32(ehdr.entry)) + Thearch.Lput(uint32(ehdr.phoff)) + Thearch.Lput(uint32(ehdr.shoff)) + Thearch.Lput(ehdr.flags) + Thearch.Wput(ehdr.ehsize) + Thearch.Wput(ehdr.phentsize) + Thearch.Wput(ehdr.phnum) + Thearch.Wput(ehdr.shentsize) + Thearch.Wput(ehdr.shnum) + Thearch.Wput(ehdr.shstrndx) + return ELF32HDRSIZE +} + +func elfwritehdr() uint32 { + if elf64 != 0 { + return elf64writehdr() + } + return elf32writehdr() +} + +/* Taken directly from the definition document for ELF64 */ +func elfhash(name []byte) uint32 { + var h uint32 = 0 + var g uint32 + for len(name) != 0 { + h = (h << 4) + uint32(name[0]) + name = name[1:] + g = h & 0xf0000000 + if g != 0 { + h ^= g >> 24 + } + h &= 0x0fffffff + } + + return h +} + +func Elfwritedynent(s *LSym, tag int, val uint64) { + if elf64 != 0 { + Adduint64(Ctxt, s, uint64(tag)) + Adduint64(Ctxt, s, val) + } else { + Adduint32(Ctxt, s, uint32(tag)) + Adduint32(Ctxt, s, uint32(val)) + } +} + +func elfwritedynentsym(s *LSym, tag int, t *LSym) { + Elfwritedynentsymplus(s, tag, t, 0) +} + +func Elfwritedynentsymplus(s *LSym, tag int, t *LSym, add int64) { + if elf64 != 0 { + Adduint64(Ctxt, s, uint64(tag)) + } else { + Adduint32(Ctxt, s, uint32(tag)) + } + Addaddrplus(Ctxt, s, t, add) +} + +func elfwritedynentsymsize(s *LSym, tag int, t *LSym) { + if elf64 != 0 { + Adduint64(Ctxt, s, uint64(tag)) + } else { + Adduint32(Ctxt, s, uint32(tag)) + } + addsize(Ctxt, s, t) +} + +func elfinterp(sh *ElfShdr, startva uint64, resoff uint64, p string) int { + var n int + + interp = p + n = len(interp) + 1 + sh.addr = startva + resoff - uint64(n) + sh.off = resoff - uint64(n) + sh.size = uint64(n) + + return n +} + +func elfwriteinterp() int { + var sh *ElfShdr + + sh = elfshname(".interp") + Cseek(int64(sh.off)) + coutbuf.w.WriteString(interp) + Cput(0) + return int(sh.size) +} + +func elfnote(sh *ElfShdr, startva uint64, resoff uint64, sz int) int { + var n uint64 + + n = 3*4 + uint64(sz) + resoff%4 + + sh.type_ = SHT_NOTE + sh.flags = SHF_ALLOC + sh.addralign = 4 + sh.addr = startva + resoff - n + sh.off = resoff - n + sh.size = n - resoff%4 + + return int(n) +} + +func elfwritenotehdr(str string, namesz uint32, descsz uint32, tag uint32) *ElfShdr { + var sh *ElfShdr + + sh = elfshname(str) + + // Write Elf_Note header. + Cseek(int64(sh.off)) + + Thearch.Lput(namesz) + Thearch.Lput(descsz) + Thearch.Lput(tag) + + return sh +} + +// NetBSD Signature (as per sys/exec_elf.h) +const ( + ELF_NOTE_NETBSD_NAMESZ = 7 + ELF_NOTE_NETBSD_DESCSZ = 4 + ELF_NOTE_NETBSD_TAG = 1 + ELF_NOTE_NETBSD_VERSION = 599000000 +) + +var ELF_NOTE_NETBSD_NAME = []byte("NetBSD\x00") + +func elfnetbsdsig(sh *ElfShdr, startva uint64, resoff uint64) int { + var n int + + n = int(Rnd(ELF_NOTE_NETBSD_NAMESZ, 4) + Rnd(ELF_NOTE_NETBSD_DESCSZ, 4)) + return elfnote(sh, startva, resoff, n) +} + +func elfwritenetbsdsig() int { + var sh *ElfShdr + + // Write Elf_Note header. + sh = elfwritenotehdr(".note.netbsd.ident", ELF_NOTE_NETBSD_NAMESZ, ELF_NOTE_NETBSD_DESCSZ, ELF_NOTE_NETBSD_TAG) + + if sh == nil { + return 0 + } + + // Followed by NetBSD string and version. + Cwrite(ELF_NOTE_NETBSD_NAME) + Cput(0) + + Thearch.Lput(ELF_NOTE_NETBSD_VERSION) + + return int(sh.size) +} + +// OpenBSD Signature +const ( + ELF_NOTE_OPENBSD_NAMESZ = 8 + ELF_NOTE_OPENBSD_DESCSZ = 4 + ELF_NOTE_OPENBSD_TAG = 1 + ELF_NOTE_OPENBSD_VERSION = 0 +) + +var ELF_NOTE_OPENBSD_NAME = []byte("OpenBSD\x00") + +func elfopenbsdsig(sh *ElfShdr, startva uint64, resoff uint64) int { + var n int + + n = ELF_NOTE_OPENBSD_NAMESZ + ELF_NOTE_OPENBSD_DESCSZ + return elfnote(sh, startva, resoff, n) +} + +func elfwriteopenbsdsig() int { + var sh *ElfShdr + + // Write Elf_Note header. + sh = elfwritenotehdr(".note.openbsd.ident", ELF_NOTE_OPENBSD_NAMESZ, ELF_NOTE_OPENBSD_DESCSZ, ELF_NOTE_OPENBSD_TAG) + + if sh == nil { + return 0 + } + + // Followed by OpenBSD string and version. + Cwrite(ELF_NOTE_OPENBSD_NAME) + + Thearch.Lput(ELF_NOTE_OPENBSD_VERSION) + + return int(sh.size) +} + +func addbuildinfo(val string) { + var ov string + var i int + var b int + var j int + + if val[0] != '0' || val[1] != 'x' { + fmt.Fprintf(os.Stderr, "%s: -B argument must start with 0x: %s\n", os.Args[0], val) + Exit(2) + } + + ov = val + val = val[2:] + i = 0 + for val != "" { + if len(val) == 1 { + fmt.Fprintf(os.Stderr, "%s: -B argument must have even number of digits: %s\n", os.Args[0], ov) + Exit(2) + } + + b = 0 + for j = 0; j < 2; (func() { j++; val = val[1:] })() { + b *= 16 + if val[0] >= '0' && val[0] <= '9' { + b += int(val[0]) - '0' + } else if val[0] >= 'a' && val[0] <= 'f' { + b += int(val[0]) - 'a' + 10 + } else if val[0] >= 'A' && val[0] <= 'F' { + b += int(val[0]) - 'A' + 10 + } else { + fmt.Fprintf(os.Stderr, "%s: -B argument contains invalid hex digit %c: %s\n", os.Args[0], val[0], ov) + Exit(2) + } + } + + const maxLen = 32 + if i >= maxLen { + fmt.Fprintf(os.Stderr, "%s: -B option too long (max %d digits): %s\n", os.Args[0], maxLen, ov) + Exit(2) + } + + buildinfo = append(buildinfo, uint8(b)) + i++ + } + + buildinfo = buildinfo[:i] +} + +// Build info note +const ( + ELF_NOTE_BUILDINFO_NAMESZ = 4 + ELF_NOTE_BUILDINFO_TAG = 3 +) + +var ELF_NOTE_BUILDINFO_NAME = []byte("GNU\x00") + +func elfbuildinfo(sh *ElfShdr, startva uint64, resoff uint64) int { + var n int + + n = int(ELF_NOTE_BUILDINFO_NAMESZ + Rnd(int64(len(buildinfo)), 4)) + return elfnote(sh, startva, resoff, n) +} + +func elfwritebuildinfo() int { + var sh *ElfShdr + + sh = elfwritenotehdr(".note.gnu.build-id", ELF_NOTE_BUILDINFO_NAMESZ, uint32(len(buildinfo)), ELF_NOTE_BUILDINFO_TAG) + if sh == nil { + return 0 + } + + Cwrite(ELF_NOTE_BUILDINFO_NAME) + Cwrite(buildinfo) + var zero = make([]byte, 4) + Cwrite(zero[:int(Rnd(int64(len(buildinfo)), 4)-int64(len(buildinfo)))]) + + return int(sh.size) +} + +var elfverneed int + +type Elfaux struct { + next *Elfaux + num int + vers string +} + +type Elflib struct { + next *Elflib + aux *Elfaux + file string +} + +func addelflib(list **Elflib, file string, vers string) *Elfaux { + var lib *Elflib + var aux *Elfaux + + for lib = *list; lib != nil; lib = lib.next { + if lib.file == file { + goto havelib + } + } + lib = new(Elflib) + lib.next = *list + lib.file = file + *list = lib + +havelib: + for aux = lib.aux; aux != nil; aux = aux.next { + if aux.vers == vers { + goto haveaux + } + } + aux = new(Elfaux) + aux.next = lib.aux + aux.vers = vers + lib.aux = aux + +haveaux: + return aux +} + +func elfdynhash() { + var s *LSym + var sy *LSym + var dynstr *LSym + var i int + var j int + var nbucket int + var b int + var nfile int + var hc uint32 + var chain []uint32 + var buckets []uint32 + var nsym int + var name string + var need []*Elfaux + var needlib *Elflib + var l *Elflib + var x *Elfaux + + if !Iself { + return + } + + nsym = Nelfsym + s = Linklookup(Ctxt, ".hash", 0) + s.Type = SELFROSECT + s.Reachable = true + + i = nsym + nbucket = 1 + for i > 0 { + nbucket++ + i >>= 1 + } + + needlib = nil + need = make([]*Elfaux, nsym) + chain = make([]uint32, nsym) + buckets = make([]uint32, nbucket) + if need == nil || chain == nil || buckets == nil { + Ctxt.Cursym = nil + Diag("out of memory") + Errorexit() + } + + for i = 0; i < nsym; i++ { + need[i] = nil + } + for i = 0; i < nsym; i++ { + chain[i] = 0 + } + for i = 0; i < nbucket; i++ { + buckets[i] = 0 + } + for sy = Ctxt.Allsym; sy != nil; sy = sy.Allsym { + if sy.Dynid <= 0 { + continue + } + + if sy.Dynimpvers != "" { + need[sy.Dynid] = addelflib(&needlib, sy.Dynimplib, sy.Dynimpvers) + } + + name = sy.Extname + hc = elfhash([]byte(name)) + + b = int(hc % uint32(nbucket)) + chain[sy.Dynid] = buckets[b] + buckets[b] = uint32(sy.Dynid) + } + + Adduint32(Ctxt, s, uint32(nbucket)) + Adduint32(Ctxt, s, uint32(nsym)) + for i = 0; i < nbucket; i++ { + Adduint32(Ctxt, s, buckets[i]) + } + for i = 0; i < nsym; i++ { + Adduint32(Ctxt, s, chain[i]) + } + + // version symbols + dynstr = Linklookup(Ctxt, ".dynstr", 0) + + s = Linklookup(Ctxt, ".gnu.version_r", 0) + i = 2 + nfile = 0 + for l = needlib; l != nil; l = l.next { + nfile++ + + // header + Adduint16(Ctxt, s, 1) // table version + j = 0 + for x = l.aux; x != nil; x = x.next { + j++ + } + Adduint16(Ctxt, s, uint16(j)) // aux count + Adduint32(Ctxt, s, uint32(Addstring(dynstr, l.file))) // file string offset + Adduint32(Ctxt, s, 16) // offset from header to first aux + if l.next != nil { + Adduint32(Ctxt, s, 16+uint32(j)*16) // offset from this header to next + } else { + Adduint32(Ctxt, s, 0) + } + + for x = l.aux; x != nil; x = x.next { + x.num = i + i++ + + // aux struct + Adduint32(Ctxt, s, elfhash([]byte(x.vers))) // hash + Adduint16(Ctxt, s, 0) // flags + Adduint16(Ctxt, s, uint16(x.num)) // other - index we refer to this by + Adduint32(Ctxt, s, uint32(Addstring(dynstr, x.vers))) // version string offset + if x.next != nil { + Adduint32(Ctxt, s, 16) // offset from this aux to next + } else { + Adduint32(Ctxt, s, 0) + } + } + } + + // version references + s = Linklookup(Ctxt, ".gnu.version", 0) + + for i = 0; i < nsym; i++ { + if i == 0 { + Adduint16(Ctxt, s, 0) // first entry - no symbol + } else if need[i] == nil { + Adduint16(Ctxt, s, 1) // global + } else { + Adduint16(Ctxt, s, uint16(need[i].num)) + } + } + + s = Linklookup(Ctxt, ".dynamic", 0) + elfverneed = nfile + if elfverneed != 0 { + elfwritedynentsym(s, DT_VERNEED, Linklookup(Ctxt, ".gnu.version_r", 0)) + Elfwritedynent(s, DT_VERNEEDNUM, uint64(nfile)) + elfwritedynentsym(s, DT_VERSYM, Linklookup(Ctxt, ".gnu.version", 0)) + } + + if Thearch.Thechar == '6' || Thearch.Thechar == '9' { + sy = Linklookup(Ctxt, ".rela.plt", 0) + if sy.Size > 0 { + Elfwritedynent(s, DT_PLTREL, DT_RELA) + elfwritedynentsymsize(s, DT_PLTRELSZ, sy) + elfwritedynentsym(s, DT_JMPREL, sy) + } + } else { + sy = Linklookup(Ctxt, ".rel.plt", 0) + if sy.Size > 0 { + Elfwritedynent(s, DT_PLTREL, DT_REL) + elfwritedynentsymsize(s, DT_PLTRELSZ, sy) + elfwritedynentsym(s, DT_JMPREL, sy) + } + } + + Elfwritedynent(s, DT_NULL, 0) +} + +func elfphload(seg *Segment) *ElfPhdr { + var ph *ElfPhdr + + ph = newElfPhdr() + ph.type_ = PT_LOAD + if seg.Rwx&4 != 0 { + ph.flags |= PF_R + } + if seg.Rwx&2 != 0 { + ph.flags |= PF_W + } + if seg.Rwx&1 != 0 { + ph.flags |= PF_X + } + ph.vaddr = seg.Vaddr + ph.paddr = seg.Vaddr + ph.memsz = seg.Length + ph.off = seg.Fileoff + ph.filesz = seg.Filelen + ph.align = uint64(INITRND) + + return ph +} + +func elfshname(name string) *ElfShdr { + var i int + var off int + var sh *ElfShdr + + for i = 0; i < nelfstr; i++ { + if name == elfstr[i].s { + off = elfstr[i].off + goto found + } + } + + Diag("cannot find elf name %s", name) + Errorexit() + return nil + +found: + for i = 0; i < int(ehdr.shnum); i++ { + sh = shdr[i] + if sh.name == uint32(off) { + return sh + } + } + + sh = newElfShdr(int64(off)) + return sh +} + +func elfshalloc(sect *Section) *ElfShdr { + var sh *ElfShdr + + sh = elfshname(sect.Name) + sect.Elfsect = sh + return sh +} + +func elfshbits(sect *Section) *ElfShdr { + var sh *ElfShdr + + sh = elfshalloc(sect) + if sh.type_ > 0 { + return sh + } + + if sect.Vaddr < sect.Seg.Vaddr+sect.Seg.Filelen { + sh.type_ = SHT_PROGBITS + } else { + sh.type_ = SHT_NOBITS + } + sh.flags = SHF_ALLOC + if sect.Rwx&1 != 0 { + sh.flags |= SHF_EXECINSTR + } + if sect.Rwx&2 != 0 { + sh.flags |= SHF_WRITE + } + if sect.Name == ".tbss" { + if goos != "android" { + sh.flags |= SHF_TLS // no TLS on android + } + sh.type_ = SHT_NOBITS + } + + if Linkmode != LinkExternal { + sh.addr = sect.Vaddr + } + sh.addralign = uint64(sect.Align) + sh.size = sect.Length + sh.off = sect.Seg.Fileoff + sect.Vaddr - sect.Seg.Vaddr + + return sh +} + +func elfshreloc(sect *Section) *ElfShdr { + var typ int + var sh *ElfShdr + var prefix string + var buf string + + // If main section is SHT_NOBITS, nothing to relocate. + // Also nothing to relocate in .shstrtab. + if sect.Vaddr >= sect.Seg.Vaddr+sect.Seg.Filelen { + return nil + } + if sect.Name == ".shstrtab" || sect.Name == ".tbss" { + return nil + } + + if Thearch.Thechar == '6' || Thearch.Thechar == '9' { + prefix = ".rela" + typ = SHT_RELA + } else { + prefix = ".rel" + typ = SHT_REL + } + + buf = fmt.Sprintf("%s%s", prefix, sect.Name) + sh = elfshname(buf) + sh.type_ = uint32(typ) + sh.entsize = uint64(Thearch.Regsize) * 2 + if typ == SHT_RELA { + sh.entsize += uint64(Thearch.Regsize) + } + sh.link = uint32(elfshname(".symtab").shnum) + sh.info = uint32((sect.Elfsect.(*ElfShdr)).shnum) + sh.off = sect.Reloff + sh.size = sect.Rellen + sh.addralign = uint64(Thearch.Regsize) + return sh +} + +func elfrelocsect(sect *Section, first *LSym) { + var ri int + var sym *LSym + var eaddr int32 + var r *Reloc + + // If main section is SHT_NOBITS, nothing to relocate. + // Also nothing to relocate in .shstrtab. + if sect.Vaddr >= sect.Seg.Vaddr+sect.Seg.Filelen { + return + } + if sect.Name == ".shstrtab" { + return + } + + sect.Reloff = uint64(Cpos()) + for sym = first; sym != nil; sym = sym.Next { + if !sym.Reachable { + continue + } + if uint64(sym.Value) >= sect.Vaddr { + break + } + } + + eaddr = int32(sect.Vaddr + sect.Length) + for ; sym != nil; sym = sym.Next { + if !sym.Reachable { + continue + } + if sym.Value >= int64(eaddr) { + break + } + Ctxt.Cursym = sym + + for ri = 0; ri < len(sym.R); ri++ { + r = &sym.R[ri] + if r.Done != 0 { + continue + } + if r.Xsym == nil { + Diag("missing xsym in relocation") + continue + } + + if r.Xsym.Elfsym == 0 { + Diag("reloc %d to non-elf symbol %s (outer=%s) %d", r.Type, r.Sym.Name, r.Xsym.Name, r.Sym.Type) + } + if Thearch.Elfreloc1(r, int64(uint64(sym.Value+int64(r.Off))-sect.Vaddr)) < 0 { + Diag("unsupported obj reloc %d/%d to %s", r.Type, r.Siz, r.Sym.Name) + } + } + } + + sect.Rellen = uint64(Cpos()) - sect.Reloff +} + +func Elfemitreloc() { + var sect *Section + + for Cpos()&7 != 0 { + Cput(0) + } + + elfrelocsect(Segtext.Sect, Ctxt.Textp) + for sect = Segtext.Sect.Next; sect != nil; sect = sect.Next { + elfrelocsect(sect, datap) + } + for sect = Segrodata.Sect; sect != nil; sect = sect.Next { + elfrelocsect(sect, datap) + } + for sect = Segdata.Sect; sect != nil; sect = sect.Next { + elfrelocsect(sect, datap) + } +} + +func doelf() { + var s *LSym + var shstrtab *LSym + var dynstr *LSym + + if !Iself { + return + } + + /* predefine strings we need for section headers */ + shstrtab = Linklookup(Ctxt, ".shstrtab", 0) + + shstrtab.Type = SELFROSECT + shstrtab.Reachable = true + + Addstring(shstrtab, "") + Addstring(shstrtab, ".text") + Addstring(shstrtab, ".noptrdata") + Addstring(shstrtab, ".data") + Addstring(shstrtab, ".bss") + Addstring(shstrtab, ".noptrbss") + + // generate .tbss section (except for OpenBSD where it's not supported) + // for dynamic internal linker or external linking, so that various + // binutils could correctly calculate PT_TLS size. + // see http://golang.org/issue/5200. + if HEADTYPE != Hopenbsd { + if Debug['d'] == 0 || Linkmode == LinkExternal { + Addstring(shstrtab, ".tbss") + } + } + if HEADTYPE == Hnetbsd { + Addstring(shstrtab, ".note.netbsd.ident") + } + if HEADTYPE == Hopenbsd { + Addstring(shstrtab, ".note.openbsd.ident") + } + if len(buildinfo) > 0 { + Addstring(shstrtab, ".note.gnu.build-id") + } + Addstring(shstrtab, ".elfdata") + Addstring(shstrtab, ".rodata") + Addstring(shstrtab, ".typelink") + Addstring(shstrtab, ".gosymtab") + Addstring(shstrtab, ".gopclntab") + + if Linkmode == LinkExternal { + debug_s = Debug['s'] + Debug['s'] = 0 + Debug['d'] = 1 + + if Thearch.Thechar == '6' || Thearch.Thechar == '9' { + Addstring(shstrtab, ".rela.text") + Addstring(shstrtab, ".rela.rodata") + Addstring(shstrtab, ".rela.typelink") + Addstring(shstrtab, ".rela.gosymtab") + Addstring(shstrtab, ".rela.gopclntab") + Addstring(shstrtab, ".rela.noptrdata") + Addstring(shstrtab, ".rela.data") + } else { + Addstring(shstrtab, ".rel.text") + Addstring(shstrtab, ".rel.rodata") + Addstring(shstrtab, ".rel.typelink") + Addstring(shstrtab, ".rel.gosymtab") + Addstring(shstrtab, ".rel.gopclntab") + Addstring(shstrtab, ".rel.noptrdata") + Addstring(shstrtab, ".rel.data") + } + + // add a .note.GNU-stack section to mark the stack as non-executable + Addstring(shstrtab, ".note.GNU-stack") + } + + if Flag_shared != 0 { + Addstring(shstrtab, ".init_array") + if Thearch.Thechar == '6' || Thearch.Thechar == '9' { + Addstring(shstrtab, ".rela.init_array") + } else { + Addstring(shstrtab, ".rel.init_array") + } + } + + if Debug['s'] == 0 { + Addstring(shstrtab, ".symtab") + Addstring(shstrtab, ".strtab") + dwarfaddshstrings(shstrtab) + } + + Addstring(shstrtab, ".shstrtab") + + if Debug['d'] == 0 { + Addstring(shstrtab, ".interp") + Addstring(shstrtab, ".hash") + Addstring(shstrtab, ".got") + if Thearch.Thechar == '9' { + Addstring(shstrtab, ".glink") + } + Addstring(shstrtab, ".got.plt") + Addstring(shstrtab, ".dynamic") + Addstring(shstrtab, ".dynsym") + Addstring(shstrtab, ".dynstr") + if Thearch.Thechar == '6' || Thearch.Thechar == '9' { + Addstring(shstrtab, ".rela") + Addstring(shstrtab, ".rela.plt") + } else { + Addstring(shstrtab, ".rel") + Addstring(shstrtab, ".rel.plt") + } + + Addstring(shstrtab, ".plt") + Addstring(shstrtab, ".gnu.version") + Addstring(shstrtab, ".gnu.version_r") + + /* dynamic symbol table - first entry all zeros */ + s = Linklookup(Ctxt, ".dynsym", 0) + + s.Type = SELFROSECT + s.Reachable = true + if Thearch.Thechar == '6' || Thearch.Thechar == '9' { + s.Size += ELF64SYMSIZE + } else { + s.Size += ELF32SYMSIZE + } + + /* dynamic string table */ + s = Linklookup(Ctxt, ".dynstr", 0) + + s.Type = SELFROSECT + s.Reachable = true + if s.Size == 0 { + Addstring(s, "") + } + dynstr = s + + /* relocation table */ + if Thearch.Thechar == '6' || Thearch.Thechar == '9' { + s = Linklookup(Ctxt, ".rela", 0) + } else { + s = Linklookup(Ctxt, ".rel", 0) + } + s.Reachable = true + s.Type = SELFROSECT + + /* global offset table */ + s = Linklookup(Ctxt, ".got", 0) + + s.Reachable = true + s.Type = SELFGOT // writable + + /* ppc64 glink resolver */ + if Thearch.Thechar == '9' { + s = Linklookup(Ctxt, ".glink", 0) + s.Reachable = true + s.Type = SELFRXSECT + } + + /* hash */ + s = Linklookup(Ctxt, ".hash", 0) + + s.Reachable = true + s.Type = SELFROSECT + + s = Linklookup(Ctxt, ".got.plt", 0) + s.Reachable = true + s.Type = SELFSECT // writable + + s = Linklookup(Ctxt, ".plt", 0) + + s.Reachable = true + if Thearch.Thechar == '9' { + // In the ppc64 ABI, .plt is a data section + // written by the dynamic linker. + s.Type = SELFSECT + } else { + s.Type = SELFRXSECT + } + + Thearch.Elfsetupplt() + + if Thearch.Thechar == '6' || Thearch.Thechar == '9' { + s = Linklookup(Ctxt, ".rela.plt", 0) + } else { + s = Linklookup(Ctxt, ".rel.plt", 0) + } + s.Reachable = true + s.Type = SELFROSECT + + s = Linklookup(Ctxt, ".gnu.version", 0) + s.Reachable = true + s.Type = SELFROSECT + + s = Linklookup(Ctxt, ".gnu.version_r", 0) + s.Reachable = true + s.Type = SELFROSECT + + /* define dynamic elf table */ + s = Linklookup(Ctxt, ".dynamic", 0) + + s.Reachable = true + s.Type = SELFSECT // writable + + /* + * .dynamic table + */ + elfwritedynentsym(s, DT_HASH, Linklookup(Ctxt, ".hash", 0)) + + elfwritedynentsym(s, DT_SYMTAB, Linklookup(Ctxt, ".dynsym", 0)) + if Thearch.Thechar == '6' || Thearch.Thechar == '9' { + Elfwritedynent(s, DT_SYMENT, ELF64SYMSIZE) + } else { + Elfwritedynent(s, DT_SYMENT, ELF32SYMSIZE) + } + elfwritedynentsym(s, DT_STRTAB, Linklookup(Ctxt, ".dynstr", 0)) + elfwritedynentsymsize(s, DT_STRSZ, Linklookup(Ctxt, ".dynstr", 0)) + if Thearch.Thechar == '6' || Thearch.Thechar == '9' { + elfwritedynentsym(s, DT_RELA, Linklookup(Ctxt, ".rela", 0)) + elfwritedynentsymsize(s, DT_RELASZ, Linklookup(Ctxt, ".rela", 0)) + Elfwritedynent(s, DT_RELAENT, ELF64RELASIZE) + } else { + elfwritedynentsym(s, DT_REL, Linklookup(Ctxt, ".rel", 0)) + elfwritedynentsymsize(s, DT_RELSZ, Linklookup(Ctxt, ".rel", 0)) + Elfwritedynent(s, DT_RELENT, ELF32RELSIZE) + } + + if rpath != "" { + Elfwritedynent(s, DT_RUNPATH, uint64(Addstring(dynstr, rpath))) + } + + if Thearch.Thechar == '9' { + elfwritedynentsym(s, DT_PLTGOT, Linklookup(Ctxt, ".plt", 0)) + } else { + elfwritedynentsym(s, DT_PLTGOT, Linklookup(Ctxt, ".got.plt", 0)) + } + + if Thearch.Thechar == '9' { + Elfwritedynent(s, DT_PPC64_OPT, 0) + } + + // Solaris dynamic linker can't handle an empty .rela.plt if + // DT_JMPREL is emitted so we have to defer generation of DT_PLTREL, + // DT_PLTRELSZ, and DT_JMPREL dynamic entries until after we know the + // size of .rel(a).plt section. + Elfwritedynent(s, DT_DEBUG, 0) + } +} + +// Do not write DT_NULL. elfdynhash will finish it. +func shsym(sh *ElfShdr, s *LSym) { + var addr int64 + addr = Symaddr(s) + if sh.flags&SHF_ALLOC != 0 { + sh.addr = uint64(addr) + } + sh.off = uint64(datoff(addr)) + sh.size = uint64(s.Size) +} + +func phsh(ph *ElfPhdr, sh *ElfShdr) { + ph.vaddr = sh.addr + ph.paddr = ph.vaddr + ph.off = sh.off + ph.filesz = sh.size + ph.memsz = sh.size + ph.align = sh.addralign +} + +func Asmbelfsetup() { + var sect *Section + + /* This null SHdr must appear before all others */ + elfshname("") + + for sect = Segtext.Sect; sect != nil; sect = sect.Next { + elfshalloc(sect) + } + for sect = Segrodata.Sect; sect != nil; sect = sect.Next { + elfshalloc(sect) + } + for sect = Segdata.Sect; sect != nil; sect = sect.Next { + elfshalloc(sect) + } +} + +func Asmbelf(symo int64) { + var a int64 + var o int64 + var startva int64 + var resoff int64 + var eh *ElfEhdr + var ph *ElfPhdr + var pph *ElfPhdr + var pnote *ElfPhdr + var sh *ElfShdr + var sect *Section + + eh = getElfEhdr() + switch Thearch.Thechar { + default: + Diag("unknown architecture in asmbelf") + Errorexit() + fallthrough + + case '5': + eh.machine = EM_ARM + + case '6': + eh.machine = EM_X86_64 + + case '8': + eh.machine = EM_386 + + case '9': + eh.machine = EM_PPC64 + } + + startva = INITTEXT - int64(HEADR) + resoff = ELFRESERVE + + pph = nil + if Linkmode == LinkExternal { + /* skip program headers */ + eh.phoff = 0 + + eh.phentsize = 0 + goto elfobj + } + + /* program header info */ + pph = newElfPhdr() + + pph.type_ = PT_PHDR + pph.flags = PF_R + pph.off = uint64(eh.ehsize) + pph.vaddr = uint64(INITTEXT) - uint64(HEADR) + pph.off + pph.paddr = uint64(INITTEXT) - uint64(HEADR) + pph.off + pph.align = uint64(INITRND) + + /* + * PHDR must be in a loaded segment. Adjust the text + * segment boundaries downwards to include it. + * Except on NaCl where it must not be loaded. + */ + if HEADTYPE != Hnacl { + o = int64(Segtext.Vaddr - pph.vaddr) + Segtext.Vaddr -= uint64(o) + Segtext.Length += uint64(o) + o = int64(Segtext.Fileoff - pph.off) + Segtext.Fileoff -= uint64(o) + Segtext.Filelen += uint64(o) + } + + if Debug['d'] == 0 { + /* interpreter */ + sh = elfshname(".interp") + + sh.type_ = SHT_PROGBITS + sh.flags = SHF_ALLOC + sh.addralign = 1 + if interpreter == "" { + switch HEADTYPE { + case Hlinux: + interpreter = Thearch.Linuxdynld + + case Hfreebsd: + interpreter = Thearch.Freebsddynld + + case Hnetbsd: + interpreter = Thearch.Netbsddynld + + case Hopenbsd: + interpreter = Thearch.Openbsddynld + + case Hdragonfly: + interpreter = Thearch.Dragonflydynld + + case Hsolaris: + interpreter = Thearch.Solarisdynld + } + } + + resoff -= int64(elfinterp(sh, uint64(startva), uint64(resoff), interpreter)) + + ph = newElfPhdr() + ph.type_ = PT_INTERP + ph.flags = PF_R + phsh(ph, sh) + } + + pnote = nil + if HEADTYPE == Hnetbsd || HEADTYPE == Hopenbsd { + sh = nil + switch HEADTYPE { + case Hnetbsd: + sh = elfshname(".note.netbsd.ident") + resoff -= int64(elfnetbsdsig(sh, uint64(startva), uint64(resoff))) + + case Hopenbsd: + sh = elfshname(".note.openbsd.ident") + resoff -= int64(elfopenbsdsig(sh, uint64(startva), uint64(resoff))) + } + + pnote = newElfPhdr() + pnote.type_ = PT_NOTE + pnote.flags = PF_R + phsh(pnote, sh) + } + + if len(buildinfo) > 0 { + sh = elfshname(".note.gnu.build-id") + resoff -= int64(elfbuildinfo(sh, uint64(startva), uint64(resoff))) + + if pnote == nil { + pnote = newElfPhdr() + pnote.type_ = PT_NOTE + pnote.flags = PF_R + } + + phsh(pnote, sh) + } + + // Additions to the reserved area must be above this line. + + elfphload(&Segtext) + if Segrodata.Sect != nil { + elfphload(&Segrodata) + } + elfphload(&Segdata) + + /* Dynamic linking sections */ + if Debug['d'] == 0 { + sh = elfshname(".dynsym") + sh.type_ = SHT_DYNSYM + sh.flags = SHF_ALLOC + if elf64 != 0 { + sh.entsize = ELF64SYMSIZE + } else { + sh.entsize = ELF32SYMSIZE + } + sh.addralign = uint64(Thearch.Regsize) + sh.link = uint32(elfshname(".dynstr").shnum) + + // sh->info = index of first non-local symbol (number of local symbols) + shsym(sh, Linklookup(Ctxt, ".dynsym", 0)) + + sh = elfshname(".dynstr") + sh.type_ = SHT_STRTAB + sh.flags = SHF_ALLOC + sh.addralign = 1 + shsym(sh, Linklookup(Ctxt, ".dynstr", 0)) + + if elfverneed != 0 { + sh = elfshname(".gnu.version") + sh.type_ = SHT_GNU_VERSYM + sh.flags = SHF_ALLOC + sh.addralign = 2 + sh.link = uint32(elfshname(".dynsym").shnum) + sh.entsize = 2 + shsym(sh, Linklookup(Ctxt, ".gnu.version", 0)) + + sh = elfshname(".gnu.version_r") + sh.type_ = SHT_GNU_VERNEED + sh.flags = SHF_ALLOC + sh.addralign = uint64(Thearch.Regsize) + sh.info = uint32(elfverneed) + sh.link = uint32(elfshname(".dynstr").shnum) + shsym(sh, Linklookup(Ctxt, ".gnu.version_r", 0)) + } + + switch eh.machine { + case EM_X86_64, + EM_PPC64: + sh = elfshname(".rela.plt") + sh.type_ = SHT_RELA + sh.flags = SHF_ALLOC + sh.entsize = ELF64RELASIZE + sh.addralign = uint64(Thearch.Regsize) + sh.link = uint32(elfshname(".dynsym").shnum) + sh.info = uint32(elfshname(".plt").shnum) + shsym(sh, Linklookup(Ctxt, ".rela.plt", 0)) + + sh = elfshname(".rela") + sh.type_ = SHT_RELA + sh.flags = SHF_ALLOC + sh.entsize = ELF64RELASIZE + sh.addralign = 8 + sh.link = uint32(elfshname(".dynsym").shnum) + shsym(sh, Linklookup(Ctxt, ".rela", 0)) + + default: + sh = elfshname(".rel.plt") + sh.type_ = SHT_REL + sh.flags = SHF_ALLOC + sh.entsize = ELF32RELSIZE + sh.addralign = 4 + sh.link = uint32(elfshname(".dynsym").shnum) + shsym(sh, Linklookup(Ctxt, ".rel.plt", 0)) + + sh = elfshname(".rel") + sh.type_ = SHT_REL + sh.flags = SHF_ALLOC + sh.entsize = ELF32RELSIZE + sh.addralign = 4 + sh.link = uint32(elfshname(".dynsym").shnum) + shsym(sh, Linklookup(Ctxt, ".rel", 0)) + } + + if eh.machine == EM_PPC64 { + sh = elfshname(".glink") + sh.type_ = SHT_PROGBITS + sh.flags = SHF_ALLOC + SHF_EXECINSTR + sh.addralign = 4 + shsym(sh, Linklookup(Ctxt, ".glink", 0)) + } + + sh = elfshname(".plt") + sh.type_ = SHT_PROGBITS + sh.flags = SHF_ALLOC + SHF_EXECINSTR + if eh.machine == EM_X86_64 { + sh.entsize = 16 + } else if eh.machine == EM_PPC64 { + // On ppc64, this is just a table of addresses + // filled by the dynamic linker + sh.type_ = SHT_NOBITS + + sh.flags = SHF_ALLOC + SHF_WRITE + sh.entsize = 8 + } else { + sh.entsize = 4 + } + sh.addralign = sh.entsize + shsym(sh, Linklookup(Ctxt, ".plt", 0)) + + // On ppc64, .got comes from the input files, so don't + // create it here, and .got.plt is not used. + if eh.machine != EM_PPC64 { + sh = elfshname(".got") + sh.type_ = SHT_PROGBITS + sh.flags = SHF_ALLOC + SHF_WRITE + sh.entsize = uint64(Thearch.Regsize) + sh.addralign = uint64(Thearch.Regsize) + shsym(sh, Linklookup(Ctxt, ".got", 0)) + + sh = elfshname(".got.plt") + sh.type_ = SHT_PROGBITS + sh.flags = SHF_ALLOC + SHF_WRITE + sh.entsize = uint64(Thearch.Regsize) + sh.addralign = uint64(Thearch.Regsize) + shsym(sh, Linklookup(Ctxt, ".got.plt", 0)) + } + + sh = elfshname(".hash") + sh.type_ = SHT_HASH + sh.flags = SHF_ALLOC + sh.entsize = 4 + sh.addralign = uint64(Thearch.Regsize) + sh.link = uint32(elfshname(".dynsym").shnum) + shsym(sh, Linklookup(Ctxt, ".hash", 0)) + + /* sh and PT_DYNAMIC for .dynamic section */ + sh = elfshname(".dynamic") + + sh.type_ = SHT_DYNAMIC + sh.flags = SHF_ALLOC + SHF_WRITE + sh.entsize = 2 * uint64(Thearch.Regsize) + sh.addralign = uint64(Thearch.Regsize) + sh.link = uint32(elfshname(".dynstr").shnum) + shsym(sh, Linklookup(Ctxt, ".dynamic", 0)) + ph = newElfPhdr() + ph.type_ = PT_DYNAMIC + ph.flags = PF_R + PF_W + phsh(ph, sh) + + /* + * Thread-local storage segment (really just size). + */ + // Do not emit PT_TLS for OpenBSD since ld.so(1) does + // not currently support it. This is handled + // appropriately in runtime/cgo. + if Ctxt.Tlsoffset != 0 && HEADTYPE != Hopenbsd { + ph = newElfPhdr() + ph.type_ = PT_TLS + ph.flags = PF_R + ph.memsz = uint64(-Ctxt.Tlsoffset) + ph.align = uint64(Thearch.Regsize) + } + } + + if HEADTYPE == Hlinux { + ph = newElfPhdr() + ph.type_ = PT_GNU_STACK + ph.flags = PF_W + PF_R + ph.align = uint64(Thearch.Regsize) + + ph = newElfPhdr() + ph.type_ = PT_PAX_FLAGS + ph.flags = 0x2a00 // mprotect, randexec, emutramp disabled + ph.align = uint64(Thearch.Regsize) + } + +elfobj: + sh = elfshname(".shstrtab") + sh.type_ = SHT_STRTAB + sh.addralign = 1 + shsym(sh, Linklookup(Ctxt, ".shstrtab", 0)) + eh.shstrndx = uint16(sh.shnum) + + // put these sections early in the list + if Debug['s'] == 0 { + elfshname(".symtab") + elfshname(".strtab") + } + + for sect = Segtext.Sect; sect != nil; sect = sect.Next { + elfshbits(sect) + } + for sect = Segrodata.Sect; sect != nil; sect = sect.Next { + elfshbits(sect) + } + for sect = Segdata.Sect; sect != nil; sect = sect.Next { + elfshbits(sect) + } + + if Linkmode == LinkExternal { + for sect = Segtext.Sect; sect != nil; sect = sect.Next { + elfshreloc(sect) + } + for sect = Segrodata.Sect; sect != nil; sect = sect.Next { + elfshreloc(sect) + } + for sect = Segdata.Sect; sect != nil; sect = sect.Next { + elfshreloc(sect) + } + + // add a .note.GNU-stack section to mark the stack as non-executable + sh = elfshname(".note.GNU-stack") + + sh.type_ = SHT_PROGBITS + sh.addralign = 1 + sh.flags = 0 + } + + // generate .tbss section for dynamic internal linking (except for OpenBSD) + // external linking generates .tbss in data.c + if Linkmode == LinkInternal && Debug['d'] == 0 && HEADTYPE != Hopenbsd { + sh = elfshname(".tbss") + sh.type_ = SHT_NOBITS + sh.addralign = uint64(Thearch.Regsize) + sh.size = uint64(-Ctxt.Tlsoffset) + sh.flags = SHF_ALLOC | SHF_TLS | SHF_WRITE + } + + if Debug['s'] == 0 { + sh = elfshname(".symtab") + sh.type_ = SHT_SYMTAB + sh.off = uint64(symo) + sh.size = uint64(Symsize) + sh.addralign = uint64(Thearch.Regsize) + sh.entsize = 8 + 2*uint64(Thearch.Regsize) + sh.link = uint32(elfshname(".strtab").shnum) + sh.info = uint32(elfglobalsymndx) + + sh = elfshname(".strtab") + sh.type_ = SHT_STRTAB + sh.off = uint64(symo) + uint64(Symsize) + sh.size = uint64(len(Elfstrdat)) + sh.addralign = 1 + + dwarfaddelfheaders() + } + + /* Main header */ + eh.ident[EI_MAG0] = '\177' + + eh.ident[EI_MAG1] = 'E' + eh.ident[EI_MAG2] = 'L' + eh.ident[EI_MAG3] = 'F' + if HEADTYPE == Hfreebsd { + eh.ident[EI_OSABI] = ELFOSABI_FREEBSD + } else if HEADTYPE == Hnetbsd { + eh.ident[EI_OSABI] = ELFOSABI_NETBSD + } else if HEADTYPE == Hopenbsd { + eh.ident[EI_OSABI] = ELFOSABI_OPENBSD + } else if HEADTYPE == Hdragonfly { + eh.ident[EI_OSABI] = ELFOSABI_NONE + } + if elf64 != 0 { + eh.ident[EI_CLASS] = ELFCLASS64 + } else { + eh.ident[EI_CLASS] = ELFCLASS32 + } + if Ctxt.Arch.Endian == BigEndian { + eh.ident[EI_DATA] = ELFDATA2MSB + } else { + eh.ident[EI_DATA] = ELFDATA2LSB + } + eh.ident[EI_VERSION] = EV_CURRENT + + if Linkmode == LinkExternal { + eh.type_ = ET_REL + } else { + eh.type_ = ET_EXEC + } + + if Linkmode != LinkExternal { + eh.entry = uint64(Entryvalue()) + } + + eh.version = EV_CURRENT + + if pph != nil { + pph.filesz = uint64(eh.phnum) * uint64(eh.phentsize) + pph.memsz = pph.filesz + } + + Cseek(0) + a = 0 + a += int64(elfwritehdr()) + a += int64(elfwritephdrs()) + a += int64(elfwriteshdrs()) + if Debug['d'] == 0 { + a += int64(elfwriteinterp()) + } + if Linkmode != LinkExternal { + if HEADTYPE == Hnetbsd { + a += int64(elfwritenetbsdsig()) + } + if HEADTYPE == Hopenbsd { + a += int64(elfwriteopenbsdsig()) + } + if len(buildinfo) > 0 { + a += int64(elfwritebuildinfo()) + } + } + + if a > ELFRESERVE { + Diag("ELFRESERVE too small: %d > %d", a, ELFRESERVE) + } +} + +func ELF32_R_SYM(info uint32) uint32 { + return info >> 8 +} + +func ELF32_R_TYPE(info uint32) uint32 { + return uint32(uint8(info)) +} + +func ELF32_R_INFO(sym uint32, type_ uint32) uint32 { + return sym<<8 | type_ +} + +func ELF32_ST_BIND(info uint8) uint8 { + return info >> 4 +} + +func ELF32_ST_TYPE(info uint8) uint8 { + return info & 0xf +} + +func ELF32_ST_INFO(bind uint8, type_ uint8) uint8 { + return bind<<4 | type_&0xf +} + +func ELF32_ST_VISIBILITY(oth uint8) uint8 { + return oth & 3 +} + +func ELF64_R_SYM(info uint64) uint32 { + return uint32(info >> 32) +} + +func ELF64_R_TYPE(info uint64) uint32 { + return uint32(info) +} + +func ELF64_R_INFO(sym uint32, type_ uint32) uint64 { + return uint64(sym)<<32 | uint64(type_) +} + +func ELF64_ST_BIND(info uint8) uint8 { + return info >> 4 +} + +func ELF64_ST_TYPE(info uint8) uint8 { + return info & 0xf +} + +func ELF64_ST_INFO(bind uint8, type_ uint8) uint8 { + return bind<<4 | type_&0xf +} + +func ELF64_ST_VISIBILITY(oth uint8) uint8 { + return oth & 3 +} diff --git a/src/cmd/internal/ld/fmt.go b/src/cmd/internal/ld/fmt.go new file mode 100644 index 0000000000..86096e610b --- /dev/null +++ b/src/cmd/internal/ld/fmt.go @@ -0,0 +1,60 @@ +/* + * The authors of this software are Rob Pike and Ken Thompson. + * Copyright (c) 2002 by Lucent Technologies. + * Permission to use, copy, modify, and distribute this software for any + * purpose without fee is hereby granted, provided that this entire notice + * is included in all copies of any software which is or includes a copy + * or modification of this software and in all copies of the supporting + * documentation for such software. + * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY + * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY + * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. + */ + +package ld + +// (The comments in this file were copied from the manpage files rune.3, +// isalpharune.3, and runestrcat.3. Some formatting changes were also made +// to conform to Google style. /JRM 11/11/05) + +type Fmt struct { + runes uint8 + start interface{} + to interface{} + stop interface{} + flush func(*Fmt) int + farg interface{} + nfmt int + args []interface{} + r uint + width int + prec int + flags uint32 + decimal string + thousands string + grouping string +} + +const ( + FmtWidth = 1 + FmtLeft = FmtWidth << 1 + FmtPrec = FmtLeft << 1 + FmtSharp = FmtPrec << 1 + FmtSpace = FmtSharp << 1 + FmtSign = FmtSpace << 1 + FmtApost = FmtSign << 1 + FmtZero = FmtApost << 1 + FmtUnsigned = FmtZero << 1 + FmtShort = FmtUnsigned << 1 + FmtLong = FmtShort << 1 + FmtVLong = FmtLong << 1 + FmtComma = FmtVLong << 1 + FmtByte = FmtComma << 1 + FmtLDouble = FmtByte << 1 + FmtFlag = FmtLDouble << 1 +) + +var fmtdoquote func(int) int + +/* Edit .+1,/^$/ | cfn $PLAN9/src/lib9/fmt/?*.c | grep -v static |grep -v __ */ diff --git a/src/cmd/internal/ld/go.go b/src/cmd/internal/ld/go.go new file mode 100644 index 0000000000..cd85c8b96e --- /dev/null +++ b/src/cmd/internal/ld/go.go @@ -0,0 +1,929 @@ +// 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 ld + +import ( + "bytes" + "cmd/internal/obj" + "fmt" + "os" + "strings" + "unicode/utf8" +) + +// go-specific code shared across loaders (5l, 6l, 8l). + +// replace all "". with pkg. +func expandpkg(t0 string, pkg string) string { + return strings.Replace(t0, `"".`, pkg+".", -1) +} + +// 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. + +// go-specific code shared across loaders (5l, 6l, 8l). + +// accumulate all type information from .6 files. +// check for inconsistencies. + +// TODO: +// generate debugging section in binary. +// once the dust settles, try to move some code to +// libmach, so that other linkers and ar can share. + +/* + * package import data + */ +type Import struct { + hash *Import + prefix string + name string + def string + file string +} + +const ( + NIHASH = 1024 +) + +var ihash [NIHASH]*Import + +var nimport int + +func hashstr(name string) int { + var h uint32 + var cp string + + h = 0 + for cp = name; cp != ""; cp = cp[1:] { + h = h*1119 + uint32(cp[0]) + } + h &= 0xffffff + return int(h) +} + +func ilookup(name string) *Import { + var h int + var x *Import + + h = hashstr(name) % NIHASH + for x = ihash[h]; x != nil; x = x.hash { + if x.name[0] == name[0] && x.name == name { + return x + } + } + x = new(Import) + x.name = name + x.hash = ihash[h] + ihash[h] = x + nimport++ + return x +} + +func ldpkg(f *Biobuf, pkg string, length int64, filename string, whence int) { + var bdata []byte + var data string + var p0, p1 int + var name string + + if Debug['g'] != 0 { + return + } + + if int64(int(length)) != length { + fmt.Fprintf(os.Stderr, "%s: too much pkg data in %s\n", os.Args[0], filename) + if Debug['u'] != 0 { + Errorexit() + } + return + } + + bdata = make([]byte, length) + if int64(Bread(f, bdata)) != length { + fmt.Fprintf(os.Stderr, "%s: short pkg read %s\n", os.Args[0], filename) + if Debug['u'] != 0 { + Errorexit() + } + return + } + data = string(bdata) + + // first \n$$ marks beginning of exports - skip rest of line + p0 = strings.Index(data, "\n$$") + if p0 < 0 { + if Debug['u'] != 0 && whence != ArchiveObj { + fmt.Fprintf(os.Stderr, "%s: cannot find export data in %s\n", os.Args[0], filename) + Errorexit() + } + return + } + + p0 += 3 + for p0 < len(data) && data[0] != '\n' { + p0++ + } + + // second marks end of exports / beginning of local data + p1 = strings.Index(data[p0:], "\n$$") + if p1 < 0 { + fmt.Fprintf(os.Stderr, "%s: cannot find end of exports in %s\n", os.Args[0], filename) + if Debug['u'] != 0 { + Errorexit() + } + return + } + p1 += p0 + + for p0 < p1 && (data[p0] == ' ' || data[0] == '\t' || data[0] == '\n') { + p0++ + } + if p0 < p1 { + if !strings.HasPrefix(data[p0:], "package ") { + fmt.Fprintf(os.Stderr, "%s: bad package section in %s - %.20s\n", os.Args[0], filename, data[p0:]) + if Debug['u'] != 0 { + Errorexit() + } + return + } + + p0 += 8 + for p0 < p1 && (data[p0] == ' ' || data[p0] == '\t' || data[p0] == '\n') { + p0++ + } + name = data[p0:] + for p0 < p1 && data[p0] != ' ' && data[p0] != '\t' && data[p0] != '\n' { + p0++ + } + if Debug['u'] != 0 && whence != ArchiveObj && (p0+6 > p1 || !strings.HasPrefix(data[p0:], " safe\n")) { + fmt.Fprintf(os.Stderr, "%s: load of unsafe package %s\n", os.Args[0], filename) + nerrors++ + Errorexit() + } + + name = name[:p1-p0] + if p0 < p1 { + if data[p0] == '\n' { + p0++ + } else { + p0++ + for p0 < p1 && data[p0] != '\n' { + p0++ + } + } + } + + if pkg == "main" && name != "main" { + fmt.Fprintf(os.Stderr, "%s: %s: not package main (package %s)\n", os.Args[0], filename, name) + nerrors++ + Errorexit() + } + + loadpkgdata(filename, pkg, data[p0:p1]) + } + + // __.PKGDEF has no cgo section - those are in the C compiler-generated object files. + if whence == Pkgdef { + return + } + + // look for cgo section + p0 = strings.Index(data[p1:], "\n$$ // cgo") + if p0 >= 0 { + p0 += p1 + i := strings.IndexByte(data[p0+1:], '\n') + if i < 0 { + fmt.Fprintf(os.Stderr, "%s: found $$ // cgo but no newline in %s\n", os.Args[0], filename) + if Debug['u'] != 0 { + Errorexit() + } + return + } + p0 += 1 + i + + p1 = strings.Index(data[p0:], "\n$$") + if p1 < 0 { + p1 = strings.Index(data[p0:], "\n!\n") + } + if p1 < 0 { + fmt.Fprintf(os.Stderr, "%s: cannot find end of // cgo section in %s\n", os.Args[0], filename) + if Debug['u'] != 0 { + Errorexit() + } + return + } + p1 += p0 + + loadcgo(filename, pkg, data[p0:p1]) + } +} + +func loadpkgdata(file string, pkg string, data string) { + var p string + var prefix string + var name string + var def string + var x *Import + + file = file + p = data + for parsepkgdata(file, pkg, &p, &prefix, &name, &def) > 0 { + x = ilookup(name) + if x.prefix == "" { + x.prefix = prefix + x.def = def + x.file = file + } else if x.prefix != prefix { + fmt.Fprintf(os.Stderr, "%s: conflicting definitions for %s\n", os.Args[0], name) + fmt.Fprintf(os.Stderr, "%s:\t%s %s ...\n", x.file, x.prefix, name) + fmt.Fprintf(os.Stderr, "%s:\t%s %s ...\n", file, prefix, name) + nerrors++ + } else if x.def != def { + fmt.Fprintf(os.Stderr, "%s: conflicting definitions for %s\n", os.Args[0], name) + fmt.Fprintf(os.Stderr, "%s:\t%s %s %s\n", x.file, x.prefix, name, x.def) + fmt.Fprintf(os.Stderr, "%s:\t%s %s %s\n", file, prefix, name, def) + nerrors++ + } + } +} + +func parsepkgdata(file string, pkg string, pp *string, prefixp *string, namep *string, defp *string) int { + var p string + var prefix string + var name string + var def string + var meth string + var inquote bool + + // skip white space + p = *pp + +loop: + for len(p) > 0 && (p[0] == ' ' || p[0] == '\t' || p[0] == '\n') { + p = p[1:] + } + if len(p) == 0 || strings.HasPrefix(p, "$$\n") { + return 0 + } + + // prefix: (var|type|func|const) + prefix = p + + if len(p) < 7 { + return -1 + } + if strings.HasPrefix(p, "var ") { + p = p[4:] + } else if strings.HasPrefix(p, "type ") { + p = p[5:] + } else if strings.HasPrefix(p, "func ") { + p = p[5:] + } else if strings.HasPrefix(p, "const ") { + p = p[6:] + } else if strings.HasPrefix(p, "import ") { + p = p[7:] + for len(p) > 0 && p[0] != ' ' { + p = p[1:] + } + p = p[1:] + name := p + for len(p) > 0 && p[0] != '\n' { + p = p[1:] + } + if len(p) == 0 { + fmt.Fprintf(os.Stderr, "%s: %s: confused in import line\n", os.Args[0], file) + nerrors++ + return -1 + } + name = name[:len(name)-len(p)] + p = p[1:] + imported(pkg, name) + goto loop + } else { + fmt.Fprintf(os.Stderr, "%s: %s: confused in pkg data near <<%.40s>>\n", os.Args[0], file, prefix) + nerrors++ + return -1 + } + + prefix = prefix[:len(prefix)-len(p)-1] + + // name: a.b followed by space + name = p + + inquote = false + for len(p) > 0 { + if p[0] == ' ' && !inquote { + break + } + + if p[0] == '\\' { + p = p[1:] + } else if p[0] == '"' { + inquote = !inquote + } + + p = p[1:] + } + + if len(p) == 0 { + return -1 + } + name = name[:len(name)-len(p)] + p = p[1:] + + // def: free form to new line + def = p + + for len(p) > 0 && p[0] != '\n' { + p = p[1:] + } + if len(p) == 0 { + return -1 + } + def = def[:len(def)-len(p)] + var defbuf *bytes.Buffer + p = p[1:] + + // include methods on successive lines in def of named type + for parsemethod(&p, &meth) > 0 { + if defbuf == nil { + defbuf = new(bytes.Buffer) + defbuf.WriteString(def) + } + defbuf.WriteString("\n\t") + defbuf.WriteString(meth) + } + if defbuf != nil { + def = defbuf.String() + } + + name = expandpkg(name, pkg) + def = expandpkg(def, pkg) + + // done + *pp = p + + *prefixp = prefix + *namep = name + *defp = def + return 1 +} + +func parsemethod(pp *string, methp *string) int { + var p string + + // skip white space + p = *pp + + for len(p) > 0 && (p[0] == ' ' || p[0] == '\t') { + p = p[1:] + } + if len(p) == 0 { + return 0 + } + + // might be a comment about the method + if strings.HasPrefix(p, "//") { + goto useline + } + + // if it says "func (", it's a method + if strings.HasPrefix(p, "func (") { + goto useline + } + return 0 + + // definition to end of line +useline: + *methp = p + + for len(p) > 0 && p[0] != '\n' { + p = p[1:] + } + if len(p) == 0 { + fmt.Fprintf(os.Stderr, "%s: lost end of line in method definition\n", os.Args[0]) + *pp = "" + return -1 + } + + *methp = (*methp)[:len(*methp)-len(p)] + *pp = p[1:] + return 1 +} + +func loadcgo(file string, pkg string, p string) { + var next string + var p0 string + var q string + var f []string + var local string + var remote string + var lib string + var s *LSym + + p0 = "" + for ; p != ""; p = next { + if i := strings.Index(p, "\n"); i >= 0 { + p, next = p[:i], p[i+1:] + } else { + next = "" + } + + p0 = p // save for error message + f = tokenize(p) + if len(f) == 0 { + continue + } + + if f[0] == "cgo_import_dynamic" { + if len(f) < 2 || len(f) > 4 { + goto err + } + + local = f[1] + remote = local + if len(f) > 2 { + remote = f[2] + } + lib = "" + if len(f) > 3 { + lib = f[3] + } + + if Debug['d'] != 0 { + fmt.Fprintf(os.Stderr, "%s: %s: cannot use dynamic imports with -d flag\n", os.Args[0], file) + nerrors++ + return + } + + if local == "_" && remote == "_" { + // allow #pragma dynimport _ _ "foo.so" + // to force a link of foo.so. + havedynamic = 1 + + Thearch.Adddynlib(lib) + continue + } + + local = expandpkg(local, pkg) + q = "" + if i := strings.Index(remote, "#"); i >= 0 { + remote, q = remote[:i], remote[i+1:] + } + s = Linklookup(Ctxt, local, 0) + if local != f[1] { + } + if s.Type == 0 || s.Type == SXREF || s.Type == SHOSTOBJ { + s.Dynimplib = lib + s.Extname = remote + s.Dynimpvers = q + if s.Type != SHOSTOBJ { + s.Type = SDYNIMPORT + } + havedynamic = 1 + } + + continue + } + + if f[0] == "cgo_import_static" { + if len(f) != 2 { + goto err + } + local = f[1] + s = Linklookup(Ctxt, local, 0) + s.Type = SHOSTOBJ + s.Size = 0 + continue + } + + if f[0] == "cgo_export_static" || f[0] == "cgo_export_dynamic" { + // TODO: Remove once we know Windows is okay. + if f[0] == "cgo_export_static" && HEADTYPE == Hwindows { + continue + } + + if len(f) < 2 || len(f) > 3 { + goto err + } + local = f[1] + if len(f) > 2 { + remote = f[2] + } else { + remote = local + } + local = expandpkg(local, pkg) + s = Linklookup(Ctxt, local, 0) + + if Flag_shared != 0 && s == Linklookup(Ctxt, "main", 0) { + continue + } + + // export overrides import, for openbsd/cgo. + // see issue 4878. + if s.Dynimplib != "" { + s.Dynimplib = "" + s.Extname = "" + s.Dynimpvers = "" + s.Type = 0 + } + + if s.Cgoexport == 0 { + s.Extname = remote + dynexp = append(dynexp, s) + } else if s.Extname != remote { + fmt.Fprintf(os.Stderr, "%s: conflicting cgo_export directives: %s as %s and %s\n", os.Args[0], s.Name, s.Extname, remote) + nerrors++ + return + } + + if f[0] == "cgo_export_static" { + s.Cgoexport |= CgoExportStatic + } else { + s.Cgoexport |= CgoExportDynamic + } + if local != f[1] { + } + continue + } + + if f[0] == "cgo_dynamic_linker" { + if len(f) != 2 { + goto err + } + + if Debug['I'] == 0 { + if interpreter != "" && interpreter != f[1] { + fmt.Fprintf(os.Stderr, "%s: conflict dynlinker: %s and %s\n", os.Args[0], interpreter, f[1]) + nerrors++ + return + } + + interpreter = f[1] + } + + continue + } + + if f[0] == "cgo_ldflag" { + if len(f) != 2 { + goto err + } + ldflag = append(ldflag, f[1]) + continue + } + } + + return + +err: + fmt.Fprintf(os.Stderr, "%s: %s: invalid dynimport line: %s\n", os.Args[0], file, p0) + nerrors++ +} + +var markq *LSym + +var emarkq *LSym + +func mark1(s *LSym, parent *LSym) { + if s == nil || s.Reachable { + return + } + if strings.HasPrefix(s.Name, "go.weak.") { + return + } + s.Reachable = true + s.Reachparent = parent + if markq == nil { + markq = s + } else { + emarkq.Queue = s + } + emarkq = s +} + +func mark(s *LSym) { + mark1(s, nil) +} + +func markflood() { + var a *Auto + var s *LSym + var i int + + for s = markq; s != nil; s = s.Queue { + if s.Type == STEXT { + if Debug['v'] > 1 { + fmt.Fprintf(&Bso, "marktext %s\n", s.Name) + } + for a = s.Autom; a != nil; a = a.Link { + mark1(a.Gotype, s) + } + } + + for i = 0; i < len(s.R); i++ { + mark1(s.R[i].Sym, s) + } + if s.Pcln != nil { + for i = 0; i < s.Pcln.Nfuncdata; i++ { + mark1(s.Pcln.Funcdata[i], s) + } + } + + mark1(s.Gotype, s) + mark1(s.Sub, s) + mark1(s.Outer, s) + } +} + +var markextra = []string{ + "runtime.morestack", + "runtime.morestackx", + "runtime.morestack00", + "runtime.morestack10", + "runtime.morestack01", + "runtime.morestack11", + "runtime.morestack8", + "runtime.morestack16", + "runtime.morestack24", + "runtime.morestack32", + "runtime.morestack40", + "runtime.morestack48", + // on arm, lock in the div/mod helpers too + "_div", + "_divu", + "_mod", + "_modu", +} + +func deadcode() { + var i int + var s *LSym + var last *LSym + var p *LSym + var fmt_ string + + if Debug['v'] != 0 { + fmt.Fprintf(&Bso, "%5.2f deadcode\n", obj.Cputime()) + } + + mark(Linklookup(Ctxt, INITENTRY, 0)) + for i = 0; i < len(markextra); i++ { + mark(Linklookup(Ctxt, markextra[i], 0)) + } + + for i = 0; i < len(dynexp); i++ { + mark(dynexp[i]) + } + + markflood() + + // keep each beginning with 'typelink.' if the symbol it points at is being kept. + for s = Ctxt.Allsym; s != nil; s = s.Allsym { + if strings.HasPrefix(s.Name, "go.typelink.") { + s.Reachable = len(s.R) == 1 && s.R[0].Sym.Reachable + } + } + + // remove dead text but keep file information (z symbols). + last = nil + + for s = Ctxt.Textp; s != nil; s = s.Next { + if !s.Reachable { + continue + } + + // NOTE: Removing s from old textp and adding to new, shorter textp. + if last == nil { + Ctxt.Textp = s + } else { + last.Next = s + } + last = s + } + + if last == nil { + Ctxt.Textp = nil + } else { + last.Next = nil + } + + for s = Ctxt.Allsym; s != nil; s = s.Allsym { + if strings.HasPrefix(s.Name, "go.weak.") { + s.Special = 1 // do not lay out in data segment + s.Reachable = true + s.Hide = 1 + } + } + + // record field tracking references + fmt_ = "" + + for s = Ctxt.Allsym; s != nil; s = s.Allsym { + if strings.HasPrefix(s.Name, "go.track.") { + s.Special = 1 // do not lay out in data segment + s.Hide = 1 + if s.Reachable { + fmt_ += fmt.Sprintf("%s", s.Name[9:]) + for p = s.Reachparent; p != nil; p = p.Reachparent { + fmt_ += fmt.Sprintf("\t%s", p.Name) + } + fmt_ += fmt.Sprintf("\n") + } + + s.Type = SCONST + s.Value = 0 + } + } + + if tracksym == "" { + return + } + s = Linklookup(Ctxt, tracksym, 0) + if !s.Reachable { + return + } + addstrdata(tracksym, fmt_) +} + +func doweak() { + var s *LSym + var t *LSym + + // resolve weak references only if + // target symbol will be in binary anyway. + for s = Ctxt.Allsym; s != nil; s = s.Allsym { + if strings.HasPrefix(s.Name, "go.weak.") { + t = Linkrlookup(Ctxt, s.Name[8:], int(s.Version)) + if t != nil && t.Type != 0 && t.Reachable { + s.Value = t.Value + s.Type = t.Type + s.Outer = t + } else { + s.Type = SCONST + s.Value = 0 + } + + continue + } + } +} + +func addexport() { + var i int + + if HEADTYPE == Hdarwin { + return + } + + for i = 0; i < len(dynexp); i++ { + Thearch.Adddynsym(Ctxt, dynexp[i]) + } +} + +/* %Z from gc, for quoting import paths */ +func Zconv(s string, flag int) string { + // NOTE: Keep in sync with gc Zconv. + var n int + var fp string + for i := 0; i < len(s); i += n { + var r rune + r, n = utf8.DecodeRuneInString(s[i:]) + switch r { + case utf8.RuneError: + if n == 1 { + fp += fmt.Sprintf("\\x%02x", s[i]) + break + } + fallthrough + + // fall through + default: + if r < ' ' { + fp += fmt.Sprintf("\\x%02x", r) + break + } + + fp += string(r) + + case '\t': + fp += "\\t" + + case '\n': + fp += "\\n" + + case '"', + '\\': + fp += `\` + string(r) + + case 0xFEFF: // BOM, basically disallowed in source code + fp += "\\uFEFF" + } + } + + return fp +} + +type Pkg struct { + mark uint8 + checked uint8 + next *Pkg + path_ string + impby []*Pkg + all *Pkg +} + +var phash [1024]*Pkg + +var pkgall *Pkg + +func getpkg(path_ string) *Pkg { + var p *Pkg + var h int + + h = hashstr(path_) % len(phash) + for p = phash[h]; p != nil; p = p.next { + if p.path_ == path_ { + return p + } + } + p = new(Pkg) + p.path_ = path_ + p.next = phash[h] + phash[h] = p + p.all = pkgall + pkgall = p + return p +} + +func imported(pkg string, import_ string) { + var p *Pkg + var i *Pkg + + // everyone imports runtime, even runtime. + if import_ == "\"runtime\"" { + return + } + + pkg = fmt.Sprintf("\"%v\"", Zconv(pkg, 0)) // turn pkg path into quoted form, freed below + p = getpkg(pkg) + i = getpkg(import_) + i.impby = append(i.impby, p) +} + +func cycle(p *Pkg) *Pkg { + var i int + var bad *Pkg + + if p.checked != 0 { + return nil + } + + if p.mark != 0 { + nerrors++ + fmt.Printf("import cycle:\n") + fmt.Printf("\t%s\n", p.path_) + return p + } + + p.mark = 1 + for i = 0; i < len(p.impby); i++ { + bad = cycle(p.impby[i]) + if bad != nil { + p.mark = 0 + p.checked = 1 + fmt.Printf("\timports %s\n", p.path_) + if bad == p { + return nil + } + return bad + } + } + + p.checked = 1 + p.mark = 0 + return nil +} + +func importcycles() { + var p *Pkg + + for p = pkgall; p != nil; p = p.all { + cycle(p) + } +} + +func setlinkmode(arg string) { + if arg == "internal" { + Linkmode = LinkInternal + } else if arg == "external" { + Linkmode = LinkExternal + } else if arg == "auto" { + Linkmode = LinkAuto + } else { + fmt.Fprintf(os.Stderr, "unknown link mode -linkmode %s\n", arg) + Errorexit() + } +} diff --git a/src/cmd/internal/ld/ld.go b/src/cmd/internal/ld/ld.go new file mode 100644 index 0000000000..99f2fab947 --- /dev/null +++ b/src/cmd/internal/ld/ld.go @@ -0,0 +1,123 @@ +// Derived from Inferno utils/6l/obj.c and utils/6l/span.c +// http://code.google.com/p/inferno-os/source/browse/utils/6l/obj.c +// http://code.google.com/p/inferno-os/source/browse/utils/6l/span.c +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package ld + +import ( + "cmd/internal/obj" + "fmt" + "os" + "path" + "strconv" + "strings" +) + +func addlib(ctxt *Link, src string, obj string, pathname string) { + name := path.Clean(pathname) + + // runtime.a -> runtime, runtime.6 -> runtime + pkg := name + if len(pkg) >= 2 && pkg[len(pkg)-2] == '.' { + pkg = pkg[:len(pkg)-2] + } + + // already loaded? + for i := 0; i < len(ctxt.Library); i++ { + if ctxt.Library[i].Pkg == pkg { + return + } + } + + var pname string + if (ctxt.Windows == 0 && strings.HasPrefix(name, "/")) || (ctxt.Windows != 0 && len(name) >= 2 && name[1] == ':') { + pname = name + } else { + // try dot, -L "libdir", and then goroot. + for _, dir := range ctxt.Libdir { + pname = dir + "/" + name + if _, err := os.Stat(pname); err == nil { + break + } + } + } + + pname = path.Clean(pname) + + if ctxt.Debugvlog > 1 && ctxt.Bso != nil { + fmt.Fprintf(ctxt.Bso, "%5.2f addlib: %s %s pulls in %s\n", elapsed(), obj, src, pname) + } + + addlibpath(ctxt, src, obj, pname, pkg) +} + +/* + * add library to library list. + * srcref: src file referring to package + * objref: object file referring to package + * file: object file, e.g., /home/rsc/go/pkg/container/vector.a + * pkg: package import path, e.g. container/vector + */ +func addlibpath(ctxt *Link, srcref string, objref string, file string, pkg string) { + var i int + var l *Library + + for i = 0; i < len(ctxt.Library); i++ { + if file == ctxt.Library[i].File { + return + } + } + + if ctxt.Debugvlog > 1 && ctxt.Bso != nil { + fmt.Fprintf(ctxt.Bso, "%5.2f addlibpath: srcref: %s objref: %s file: %s pkg: %s\n", obj.Cputime(), srcref, objref, file, pkg) + } + + ctxt.Library = append(ctxt.Library, Library{}) + l = &ctxt.Library[len(ctxt.Library)-1] + l.Objref = objref + l.Srcref = srcref + l.File = file + l.Pkg = pkg +} + +var fnuxi8 [8]uint8 + +var fnuxi4 [4]uint8 + +var inuxi1 [1]uint8 + +var inuxi2 [2]uint8 + +var inuxi8 [8]uint8 + +func atolwhex(s string) int64 { + n, _ := strconv.ParseInt(s, 0, 64) + return n +} diff --git a/src/cmd/internal/ld/ldelf.go b/src/cmd/internal/ld/ldelf.go new file mode 100644 index 0000000000..c50e995168 --- /dev/null +++ b/src/cmd/internal/ld/ldelf.go @@ -0,0 +1,1004 @@ +package ld + +import ( + "bytes" + "cmd/internal/obj" + "encoding/binary" + "fmt" + "log" + "sort" + "strings" +) + +/* +Derived from Plan 9 from User Space's src/libmach/elf.h, elf.c +http://code.swtch.com/plan9port/src/tip/src/libmach/ + + Copyright © 2004 Russ Cox. + Portions Copyright © 2008-2010 Google Inc. + Portions Copyright © 2010 The Go Authors. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ +const ( + ElfClassNone = 0 + iota + ElfClass32 + ElfClass64 + ElfDataNone = 0 + iota - 3 + ElfDataLsb + ElfDataMsb + ElfTypeNone = 0 + iota - 6 + ElfTypeRelocatable + ElfTypeExecutable + ElfTypeSharedObject + ElfTypeCore + ElfMachNone = 0 + iota - 11 + ElfMach32100 + ElfMachSparc + ElfMach386 + ElfMach68000 + ElfMach88000 + ElfMach486 + ElfMach860 + ElfMachMips + ElfMachS370 + ElfMachMipsLe + ElfMachParisc = 15 + ElfMachVpp500 = 17 + iota - 23 + ElfMachSparc32Plus + ElfMach960 + ElfMachPower + ElfMachPower64 + ElfMachS390 + ElfMachV800 = 36 + iota - 29 + ElfMachFr20 + ElfMachRh32 + ElfMachRce + ElfMachArm + ElfMachAlpha + ElfMachSH + ElfMachSparc9 + ElfMachAmd64 = 62 + ElfAbiNone = 0 + ElfAbiSystemV = 0 + iota - 39 + ElfAbiHPUX + ElfAbiNetBSD + ElfAbiLinux + ElfAbiSolaris = 6 + iota - 43 + ElfAbiAix + ElfAbiIrix + ElfAbiFreeBSD + ElfAbiTru64 + ElfAbiModesto + ElfAbiOpenBSD + ElfAbiARM = 97 + ElfAbiEmbedded = 255 + ElfSectNone = 0 + iota - 52 + ElfSectProgbits + ElfSectSymtab + ElfSectStrtab + ElfSectRela + ElfSectHash + ElfSectDynamic + ElfSectNote + ElfSectNobits + ElfSectRel + ElfSectShlib + ElfSectDynsym + ElfSectFlagWrite = 0x1 + ElfSectFlagAlloc = 0x2 + ElfSectFlagExec = 0x4 + ElfSymBindLocal = 0 + iota - 67 + ElfSymBindGlobal + ElfSymBindWeak + ElfSymTypeNone = 0 + iota - 70 + ElfSymTypeObject + ElfSymTypeFunc + ElfSymTypeSection + ElfSymTypeFile + ElfSymShnNone = 0 + ElfSymShnAbs = 0xFFF1 + ElfSymShnCommon = 0xFFF2 + ElfProgNone = 0 + iota - 78 + ElfProgLoad + ElfProgDynamic + ElfProgInterp + ElfProgNote + ElfProgShlib + ElfProgPhdr + ElfProgFlagExec = 0x1 + ElfProgFlagWrite = 0x2 + ElfProgFlagRead = 0x4 + ElfNotePrStatus = 1 + ElfNotePrFpreg = 2 + ElfNotePrPsinfo = 3 + ElfNotePrTaskstruct = 4 + ElfNotePrAuxv = 6 + ElfNotePrXfpreg = 0x46e62b7f +) + +type ElfHdrBytes struct { + Ident [16]uint8 + Type [2]uint8 + Machine [2]uint8 + Version [4]uint8 + Entry [4]uint8 + Phoff [4]uint8 + Shoff [4]uint8 + Flags [4]uint8 + Ehsize [2]uint8 + Phentsize [2]uint8 + Phnum [2]uint8 + Shentsize [2]uint8 + Shnum [2]uint8 + Shstrndx [2]uint8 +} + +type ElfSectBytes struct { + Name [4]uint8 + Type [4]uint8 + Flags [4]uint8 + Addr [4]uint8 + Off [4]uint8 + Size [4]uint8 + Link [4]uint8 + Info [4]uint8 + Align [4]uint8 + Entsize [4]uint8 +} + +type ElfProgBytes struct { +} + +type ElfSymBytes struct { + Name [4]uint8 + Value [4]uint8 + Size [4]uint8 + Info uint8 + Other uint8 + Shndx [2]uint8 +} + +type ElfHdrBytes64 struct { + Ident [16]uint8 + Type [2]uint8 + Machine [2]uint8 + Version [4]uint8 + Entry [8]uint8 + Phoff [8]uint8 + Shoff [8]uint8 + Flags [4]uint8 + Ehsize [2]uint8 + Phentsize [2]uint8 + Phnum [2]uint8 + Shentsize [2]uint8 + Shnum [2]uint8 + Shstrndx [2]uint8 +} + +type ElfSectBytes64 struct { + Name [4]uint8 + Type [4]uint8 + Flags [8]uint8 + Addr [8]uint8 + Off [8]uint8 + Size [8]uint8 + Link [4]uint8 + Info [4]uint8 + Align [8]uint8 + Entsize [8]uint8 +} + +type ElfProgBytes64 struct { +} + +type ElfSymBytes64 struct { + Name [4]uint8 + Info uint8 + Other uint8 + Shndx [2]uint8 + Value [8]uint8 + Size [8]uint8 +} + +type ElfSect struct { + name string + nameoff uint32 + type_ uint32 + flags uint64 + addr uint64 + off uint64 + size uint64 + link uint32 + info uint32 + align uint64 + entsize uint64 + base []byte + sym *LSym +} + +type ElfObj struct { + f *Biobuf + base int64 + length int64 + is64 int + name string + e binary.ByteOrder + sect []ElfSect + nsect uint + shstrtab string + nsymtab int + symtab *ElfSect + symstr *ElfSect + type_ uint32 + machine uint32 + version uint32 + entry uint64 + phoff uint64 + shoff uint64 + flags uint32 + ehsize uint32 + phentsize uint32 + phnum uint32 + shentsize uint32 + shnum uint32 + shstrndx uint32 +} + +type ElfSym struct { + name string + value uint64 + size uint64 + bind uint8 + type_ uint8 + other uint8 + shndx uint16 + sym *LSym +} + +var ElfMagic = [4]uint8{0x7F, 'E', 'L', 'F'} + +func valuecmp(a *LSym, b *LSym) int { + if a.Value < b.Value { + return -1 + } + if a.Value > b.Value { + return +1 + } + return 0 +} + +func ldelf(f *Biobuf, pkg string, length int64, pn string) { + var err error + var base int32 + var add uint64 + var info uint64 + var name string + var i int + var j int + var rela int + var is64 int + var n int + var flag int + var hdrbuf [64]uint8 + var p []byte + var hdr *ElfHdrBytes + var elfobj *ElfObj + var sect *ElfSect + var rsect *ElfSect + var sym ElfSym + var e binary.ByteOrder + var r []Reloc + var rp *Reloc + var s *LSym + var symbols []*LSym + + symbols = nil + + if Debug['v'] != 0 { + fmt.Fprintf(&Bso, "%5.2f ldelf %s\n", obj.Cputime(), pn) + } + + Ctxt.Version++ + base = int32(Boffset(f)) + + if Bread(f, hdrbuf[:]) != len(hdrbuf) { + goto bad + } + hdr = new(ElfHdrBytes) + binary.Read(bytes.NewReader(hdrbuf[:]), binary.BigEndian, hdr) // only byte arrays; byte order doesn't matter + if string(hdr.Ident[:4]) != "\x7FELF" { + goto bad + } + switch hdr.Ident[5] { + case ElfDataLsb: + e = binary.LittleEndian + + case ElfDataMsb: + e = binary.BigEndian + + default: + goto bad + } + + // read header + elfobj = new(ElfObj) + + elfobj.e = e + elfobj.f = f + elfobj.base = int64(base) + elfobj.length = length + elfobj.name = pn + + is64 = 0 + if hdr.Ident[4] == ElfClass64 { + var hdr *ElfHdrBytes64 + + is64 = 1 + hdr = new(ElfHdrBytes64) + binary.Read(bytes.NewReader(hdrbuf[:]), binary.BigEndian, hdr) // only byte arrays; byte order doesn't matter + elfobj.type_ = uint32(e.Uint16(hdr.Type[:])) + elfobj.machine = uint32(e.Uint16(hdr.Machine[:])) + elfobj.version = e.Uint32(hdr.Version[:]) + elfobj.phoff = e.Uint64(hdr.Phoff[:]) + elfobj.shoff = e.Uint64(hdr.Shoff[:]) + elfobj.flags = e.Uint32(hdr.Flags[:]) + elfobj.ehsize = uint32(e.Uint16(hdr.Ehsize[:])) + elfobj.phentsize = uint32(e.Uint16(hdr.Phentsize[:])) + elfobj.phnum = uint32(e.Uint16(hdr.Phnum[:])) + elfobj.shentsize = uint32(e.Uint16(hdr.Shentsize[:])) + elfobj.shnum = uint32(e.Uint16(hdr.Shnum[:])) + elfobj.shstrndx = uint32(e.Uint16(hdr.Shstrndx[:])) + } else { + elfobj.type_ = uint32(e.Uint16(hdr.Type[:])) + elfobj.machine = uint32(e.Uint16(hdr.Machine[:])) + elfobj.version = e.Uint32(hdr.Version[:]) + elfobj.entry = uint64(e.Uint32(hdr.Entry[:])) + elfobj.phoff = uint64(e.Uint32(hdr.Phoff[:])) + elfobj.shoff = uint64(e.Uint32(hdr.Shoff[:])) + elfobj.flags = e.Uint32(hdr.Flags[:]) + elfobj.ehsize = uint32(e.Uint16(hdr.Ehsize[:])) + elfobj.phentsize = uint32(e.Uint16(hdr.Phentsize[:])) + elfobj.phnum = uint32(e.Uint16(hdr.Phnum[:])) + elfobj.shentsize = uint32(e.Uint16(hdr.Shentsize[:])) + elfobj.shnum = uint32(e.Uint16(hdr.Shnum[:])) + elfobj.shstrndx = uint32(e.Uint16(hdr.Shstrndx[:])) + } + + elfobj.is64 = is64 + + if uint32(hdr.Ident[6]) != elfobj.version { + goto bad + } + + if e.Uint16(hdr.Type[:]) != ElfTypeRelocatable { + Diag("%s: elf but not elf relocatable object", pn) + return + } + + switch Thearch.Thechar { + default: + Diag("%s: elf %s unimplemented", pn, Thestring) + return + + case '5': + if e != binary.LittleEndian || elfobj.machine != ElfMachArm || hdr.Ident[4] != ElfClass32 { + Diag("%s: elf object but not arm", pn) + return + } + + case '6': + if e != binary.LittleEndian || elfobj.machine != ElfMachAmd64 || hdr.Ident[4] != ElfClass64 { + Diag("%s: elf object but not amd64", pn) + return + } + + case '8': + if e != binary.LittleEndian || elfobj.machine != ElfMach386 || hdr.Ident[4] != ElfClass32 { + Diag("%s: elf object but not 386", pn) + return + } + + case '9': + if elfobj.machine != ElfMachPower64 || hdr.Ident[4] != ElfClass64 { + Diag("%s: elf object but not ppc64", pn) + return + } + } + + // load section list into memory. + elfobj.sect = make([]ElfSect, elfobj.shnum) + + elfobj.nsect = uint(elfobj.shnum) + for i = 0; uint(i) < elfobj.nsect; i++ { + if Bseek(f, int64(uint64(base)+elfobj.shoff+uint64(int64(i)*int64(elfobj.shentsize))), 0) < 0 { + goto bad + } + sect = &elfobj.sect[i] + if is64 != 0 { + var b ElfSectBytes64 + + if err = binary.Read(f, e, &b); err != nil { + goto bad + } + + sect.nameoff = uint32(e.Uint32(b.Name[:])) + sect.type_ = e.Uint32(b.Type[:]) + sect.flags = e.Uint64(b.Flags[:]) + sect.addr = e.Uint64(b.Addr[:]) + sect.off = e.Uint64(b.Off[:]) + sect.size = e.Uint64(b.Size[:]) + sect.link = e.Uint32(b.Link[:]) + sect.info = e.Uint32(b.Info[:]) + sect.align = e.Uint64(b.Align[:]) + sect.entsize = e.Uint64(b.Entsize[:]) + } else { + var b ElfSectBytes + + if err = binary.Read(f, e, &b); err != nil { + goto bad + } + + sect.nameoff = uint32(e.Uint32(b.Name[:])) + sect.type_ = e.Uint32(b.Type[:]) + sect.flags = uint64(e.Uint32(b.Flags[:])) + sect.addr = uint64(e.Uint32(b.Addr[:])) + sect.off = uint64(e.Uint32(b.Off[:])) + sect.size = uint64(e.Uint32(b.Size[:])) + sect.link = e.Uint32(b.Link[:]) + sect.info = e.Uint32(b.Info[:]) + sect.align = uint64(e.Uint32(b.Align[:])) + sect.entsize = uint64(e.Uint32(b.Entsize[:])) + } + } + + // read section string table and translate names + if elfobj.shstrndx >= uint32(elfobj.nsect) { + err = fmt.Errorf("shstrndx out of range %d >= %d", elfobj.shstrndx, elfobj.nsect) + goto bad + } + + sect = &elfobj.sect[elfobj.shstrndx] + if err = elfmap(elfobj, sect); err != nil { + goto bad + } + for i = 0; uint(i) < elfobj.nsect; i++ { + if elfobj.sect[i].nameoff != 0 { + elfobj.sect[i].name = cstring(sect.base[elfobj.sect[i].nameoff:]) + } + } + + // load string table for symbols into memory. + elfobj.symtab = section(elfobj, ".symtab") + + if elfobj.symtab == nil { + // our work is done here - no symbols means nothing can refer to this file + return + } + + if elfobj.symtab.link <= 0 || elfobj.symtab.link >= uint32(elfobj.nsect) { + Diag("%s: elf object has symbol table with invalid string table link", pn) + return + } + + elfobj.symstr = &elfobj.sect[elfobj.symtab.link] + if is64 != 0 { + elfobj.nsymtab = int(elfobj.symtab.size / ELF64SYMSIZE) + } else { + elfobj.nsymtab = int(elfobj.symtab.size / ELF32SYMSIZE) + } + + if err = elfmap(elfobj, elfobj.symtab); err != nil { + goto bad + } + if err = elfmap(elfobj, elfobj.symstr); err != nil { + goto bad + } + + // load text and data segments into memory. + // they are not as small as the section lists, but we'll need + // the memory anyway for the symbol images, so we might + // as well use one large chunk. + + // create symbols for elfmapped sections + for i = 0; uint(i) < elfobj.nsect; i++ { + sect = &elfobj.sect[i] + if (sect.type_ != ElfSectProgbits && sect.type_ != ElfSectNobits) || sect.flags&ElfSectFlagAlloc == 0 { + continue + } + if sect.type_ != ElfSectNobits { + if err = elfmap(elfobj, sect); err != nil { + goto bad + } + } + + name = fmt.Sprintf("%s(%s)", pkg, sect.name) + s = Linklookup(Ctxt, name, Ctxt.Version) + + switch int(sect.flags) & (ElfSectFlagAlloc | ElfSectFlagWrite | ElfSectFlagExec) { + default: + err = fmt.Errorf("unexpected flags for ELF section %s", sect.name) + goto bad + + case ElfSectFlagAlloc: + s.Type = SRODATA + + case ElfSectFlagAlloc + ElfSectFlagWrite: + if sect.type_ == ElfSectNobits { + s.Type = SNOPTRBSS + } else { + s.Type = SNOPTRDATA + } + + case ElfSectFlagAlloc + ElfSectFlagExec: + s.Type = STEXT + } + + if sect.name == ".got" || sect.name == ".toc" { + s.Type = SELFGOT + } + if sect.type_ == ElfSectProgbits { + s.P = sect.base + s.P = s.P[:sect.size] + } + + s.Size = int64(sect.size) + s.Align = int32(sect.align) + sect.sym = s + } + + // enter sub-symbols into symbol table. + // symbol 0 is the null symbol. + symbols = make([]*LSym, elfobj.nsymtab) + + if symbols == nil { + Diag("out of memory") + Errorexit() + } + + for i = 1; i < elfobj.nsymtab; i++ { + if err = readelfsym(elfobj, i, &sym, 1); err != nil { + goto bad + } + symbols[i] = sym.sym + if sym.type_ != ElfSymTypeFunc && sym.type_ != ElfSymTypeObject && sym.type_ != ElfSymTypeNone { + continue + } + if sym.shndx == ElfSymShnCommon { + s = sym.sym + if uint64(s.Size) < sym.size { + s.Size = int64(sym.size) + } + if s.Type == 0 || s.Type == SXREF { + s.Type = SNOPTRBSS + } + continue + } + + if uint(sym.shndx) >= elfobj.nsect || sym.shndx == 0 { + continue + } + + // even when we pass needSym == 1 to readelfsym, it might still return nil to skip some unwanted symbols + if sym.sym == nil { + continue + } + sect = &elfobj.sect[sym.shndx:][0] + if sect.sym == nil { + if strings.HasPrefix(sym.name, ".Linfo_string") { + continue + } + Diag("%s: sym#%d: ignoring %s in section %d (type %d)", pn, i, sym.name, sym.shndx, sym.type_) + continue + } + + s = sym.sym + if s.Outer != nil { + if s.Dupok != 0 { + continue + } + Diag("%s: duplicate symbol reference: %s in both %s and %s", pn, s.Name, s.Outer.Name, sect.sym.Name) + Errorexit() + } + + s.Sub = sect.sym.Sub + sect.sym.Sub = s + s.Type = sect.sym.Type | s.Type&^SMASK | SSUB + if s.Cgoexport&CgoExportDynamic == 0 { + s.Dynimplib = "" // satisfy dynimport + } + s.Value = int64(sym.value) + s.Size = int64(sym.size) + s.Outer = sect.sym + if sect.sym.Type == STEXT { + if s.External != 0 && s.Dupok == 0 { + Diag("%s: duplicate definition of %s", pn, s.Name) + } + s.External = 1 + } + + if elfobj.machine == ElfMachPower64 { + flag = int(sym.other) >> 5 + if 2 <= flag && flag <= 6 { + s.Localentry = 1 << uint(flag-2) + } else if flag == 7 { + Diag("%s: invalid sym.other 0x%x for %s", pn, sym.other, s.Name) + } + } + } + + // Sort outer lists by address, adding to textp. + // This keeps textp in increasing address order. + for i = 0; uint(i) < elfobj.nsect; i++ { + s = elfobj.sect[i].sym + if s == nil { + continue + } + if s.Sub != nil { + s.Sub = listsort(s.Sub, valuecmp, listsubp) + } + if s.Type == STEXT { + if s.Onlist != 0 { + log.Fatalf("symbol %s listed multiple times", s.Name) + } + s.Onlist = 1 + if Ctxt.Etextp != nil { + Ctxt.Etextp.Next = s + } else { + Ctxt.Textp = s + } + Ctxt.Etextp = s + for s = s.Sub; s != nil; s = s.Sub { + if s.Onlist != 0 { + log.Fatalf("symbol %s listed multiple times", s.Name) + } + s.Onlist = 1 + Ctxt.Etextp.Next = s + Ctxt.Etextp = s + } + } + } + + // load relocations + for i = 0; uint(i) < elfobj.nsect; i++ { + rsect = &elfobj.sect[i] + if rsect.type_ != ElfSectRela && rsect.type_ != ElfSectRel { + continue + } + if rsect.info >= uint32(elfobj.nsect) || elfobj.sect[rsect.info].base == nil { + continue + } + sect = &elfobj.sect[rsect.info] + if err = elfmap(elfobj, rsect); err != nil { + goto bad + } + rela = 0 + if rsect.type_ == ElfSectRela { + rela = 1 + } + n = int(rsect.size / uint64(4+4*is64) / uint64(2+rela)) + r = make([]Reloc, n) + p = rsect.base + for j = 0; j < n; j++ { + add = 0 + rp = &r[j] + if is64 != 0 { + // 64-bit rel/rela + rp.Off = int32(e.Uint64(p)) + + p = p[8:] + info = e.Uint64(p) + p = p[8:] + if rela != 0 { + add = e.Uint64(p) + p = p[8:] + } + } else { + // 32-bit rel/rela + rp.Off = int32(e.Uint32(p)) + + p = p[4:] + info = uint64(e.Uint32(p)) + info = info>>8<<32 | info&0xff // convert to 64-bit info + p = p[4:] + if rela != 0 { + add = uint64(e.Uint32(p)) + p = p[4:] + } + } + + if info&0xffffffff == 0 { // skip R_*_NONE relocation + j-- + n-- + continue + } + + if info>>32 == 0 { // absolute relocation, don't bother reading the null symbol + rp.Sym = nil + } else { + if err = readelfsym(elfobj, int(info>>32), &sym, 0); err != nil { + goto bad + } + sym.sym = symbols[info>>32] + if sym.sym == nil { + err = fmt.Errorf("%s#%d: reloc of invalid sym #%d %s shndx=%d type=%d", sect.sym.Name, j, int(info>>32), sym.name, sym.shndx, sym.type_) + goto bad + } + + rp.Sym = sym.sym + } + + rp.Type = int32(reltype(pn, int(uint32(info)), &rp.Siz)) + if rela != 0 { + rp.Add = int64(add) + } else { + // load addend from image + if rp.Siz == 4 { + rp.Add = int64(e.Uint32(sect.base[rp.Off:])) + } else if rp.Siz == 8 { + rp.Add = int64(e.Uint64(sect.base[rp.Off:])) + } else { + Diag("invalid rela size %d", rp.Siz) + } + } + + if rp.Siz == 2 { + rp.Add = int64(int16(rp.Add)) + } + if rp.Siz == 4 { + rp.Add = int64(int32(rp.Add)) + } + } + + //print("rel %s %d %d %s %#llx\n", sect->sym->name, rp->type, rp->siz, rp->sym->name, rp->add); + sort.Sort(rbyoff(r[:n])) + // just in case + + s = sect.sym + s.R = r + s.R = s.R[:n] + } + + return + +bad: + Diag("%s: malformed elf file: %v", pn, err) +} + +func section(elfobj *ElfObj, name string) *ElfSect { + var i int + + for i = 0; uint(i) < elfobj.nsect; i++ { + if elfobj.sect[i].name != "" && name != "" && elfobj.sect[i].name == name { + return &elfobj.sect[i] + } + } + return nil +} + +func elfmap(elfobj *ElfObj, sect *ElfSect) (err error) { + if sect.base != nil { + return nil + } + + if sect.off+sect.size > uint64(elfobj.length) { + err = fmt.Errorf("elf section past end of file") + return err + } + + sect.base = make([]byte, sect.size) + err = fmt.Errorf("short read") + if Bseek(elfobj.f, int64(uint64(elfobj.base)+sect.off), 0) < 0 || Bread(elfobj.f, sect.base) != len(sect.base) { + return err + } + + return nil +} + +func readelfsym(elfobj *ElfObj, i int, sym *ElfSym, needSym int) (err error) { + var s *LSym + + if i >= elfobj.nsymtab || i < 0 { + err = fmt.Errorf("invalid elf symbol index") + return err + } + + if i == 0 { + Diag("readym: read null symbol!") + } + + if elfobj.is64 != 0 { + b := new(ElfSymBytes64) + binary.Read(bytes.NewReader(elfobj.symtab.base[i*ELF64SYMSIZE:(i+1)*ELF64SYMSIZE]), elfobj.e, b) + sym.name = cstring(elfobj.symstr.base[elfobj.e.Uint32(b.Name[:]):]) + sym.value = elfobj.e.Uint64(b.Value[:]) + sym.size = elfobj.e.Uint64(b.Size[:]) + sym.shndx = elfobj.e.Uint16(b.Shndx[:]) + sym.bind = b.Info >> 4 + sym.type_ = b.Info & 0xf + sym.other = b.Other + } else { + b := new(ElfSymBytes) + binary.Read(bytes.NewReader(elfobj.symtab.base[i*ELF32SYMSIZE:(i+1)*ELF32SYMSIZE]), elfobj.e, b) + sym.name = cstring(elfobj.symstr.base[elfobj.e.Uint32(b.Name[:]):]) + sym.value = uint64(elfobj.e.Uint32(b.Value[:])) + sym.size = uint64(elfobj.e.Uint32(b.Size[:])) + sym.shndx = elfobj.e.Uint16(b.Shndx[:]) + sym.bind = b.Info >> 4 + sym.type_ = b.Info & 0xf + sym.other = b.Other + } + + s = nil + if sym.name == "_GLOBAL_OFFSET_TABLE_" { + sym.name = ".got" + } + if sym.name == ".TOC." { + // Magic symbol on ppc64. Will be set to this object + // file's .got+0x8000. + sym.bind = ElfSymBindLocal + } + + switch sym.type_ { + case ElfSymTypeSection: + s = elfobj.sect[sym.shndx].sym + + case ElfSymTypeObject, + ElfSymTypeFunc, + ElfSymTypeNone: + switch sym.bind { + case ElfSymBindGlobal: + if needSym != 0 { + s = Linklookup(Ctxt, sym.name, 0) + + // for global scoped hidden symbols we should insert it into + // symbol hash table, but mark them as hidden. + // __i686.get_pc_thunk.bx is allowed to be duplicated, to + // workaround that we set dupok. + // TODO(minux): correctly handle __i686.get_pc_thunk.bx without + // set dupok generally. See http://codereview.appspot.com/5823055/ + // comment #5 for details. + if s != nil && sym.other == 2 { + s.Type |= SHIDDEN + s.Dupok = 1 + } + } + + case ElfSymBindLocal: + if Thearch.Thechar == '5' && (strings.HasPrefix(sym.name, "$a") || strings.HasPrefix(sym.name, "$d")) { + // binutils for arm generate these elfmapping + // symbols, ignore these + break + } + + if sym.name == ".TOC." { + // We need to be able to look this up, + // so put it in the hash table. + if needSym != 0 { + s = Linklookup(Ctxt, sym.name, Ctxt.Version) + s.Type |= SHIDDEN + } + + break + } + + if needSym != 0 { + // local names and hidden visiblity global names are unique + // and should only reference by its index, not name, so we + // don't bother to add them into hash table + s = linknewsym(Ctxt, sym.name, Ctxt.Version) + + s.Type |= SHIDDEN + } + + case ElfSymBindWeak: + if needSym != 0 { + s = linknewsym(Ctxt, sym.name, 0) + if sym.other == 2 { + s.Type |= SHIDDEN + } + } + + default: + err = fmt.Errorf("%s: invalid symbol binding %d", sym.name, sym.bind) + return err + } + } + + if s != nil && s.Type == 0 && sym.type_ != ElfSymTypeSection { + s.Type = SXREF + } + sym.sym = s + + return nil +} + +type rbyoff []Reloc + +func (x rbyoff) Len() int { + return len(x) +} + +func (x rbyoff) Swap(i, j int) { + x[i], x[j] = x[j], x[i] +} + +func (x rbyoff) Less(i, j int) bool { + var a *Reloc + var b *Reloc + + a = &x[i] + b = &x[j] + if a.Off < b.Off { + return true + } + if a.Off > b.Off { + return false + } + return false +} + +func reltype(pn string, elftype int, siz *uint8) int { + switch uint32(Thearch.Thechar) | uint32(elftype)<<24 { + default: + Diag("%s: unknown relocation type %d; compiled without -fpic?", pn, elftype) + fallthrough + + case '9' | R_PPC64_TOC16<<24, + '9' | R_PPC64_TOC16_LO<<24, + '9' | R_PPC64_TOC16_HI<<24, + '9' | R_PPC64_TOC16_HA<<24, + '9' | R_PPC64_TOC16_DS<<24, + '9' | R_PPC64_TOC16_LO_DS<<24, + '9' | R_PPC64_REL16_LO<<24, + '9' | R_PPC64_REL16_HI<<24, + '9' | R_PPC64_REL16_HA<<24: + *siz = 2 + + case '5' | R_ARM_ABS32<<24, + '5' | R_ARM_GOT32<<24, + '5' | R_ARM_PLT32<<24, + '5' | R_ARM_GOTOFF<<24, + '5' | R_ARM_GOTPC<<24, + '5' | R_ARM_THM_PC22<<24, + '5' | R_ARM_REL32<<24, + '5' | R_ARM_CALL<<24, + '5' | R_ARM_V4BX<<24, + '5' | R_ARM_GOT_PREL<<24, + '5' | R_ARM_PC24<<24, + '5' | R_ARM_JUMP24<<24, + '6' | R_X86_64_PC32<<24, + '6' | R_X86_64_PLT32<<24, + '6' | R_X86_64_GOTPCREL<<24, + '8' | R_386_32<<24, + '8' | R_386_PC32<<24, + '8' | R_386_GOT32<<24, + '8' | R_386_PLT32<<24, + '8' | R_386_GOTOFF<<24, + '8' | R_386_GOTPC<<24, + '9' | R_PPC64_REL24<<24: + *siz = 4 + + case '6' | R_X86_64_64<<24, + '9' | R_PPC64_ADDR64<<24: + *siz = 8 + } + + return 256 + elftype +} diff --git a/src/cmd/internal/ld/ldmacho.go b/src/cmd/internal/ld/ldmacho.go new file mode 100644 index 0000000000..e762318b1e --- /dev/null +++ b/src/cmd/internal/ld/ldmacho.go @@ -0,0 +1,919 @@ +package ld + +import ( + "encoding/binary" + "fmt" + "log" + "sort" +) + +/* +Derived from Plan 9 from User Space's src/libmach/elf.h, elf.c +http://code.swtch.com/plan9port/src/tip/src/libmach/ + + Copyright © 2004 Russ Cox. + Portions Copyright © 2008-2010 Google Inc. + Portions Copyright © 2010 The Go Authors. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ +const ( + N_EXT = 0x01 + N_TYPE = 0x1e + N_STAB = 0xe0 +) + +type LdMachoObj struct { + f *Biobuf + base int64 + length int64 + is64 bool + name string + e binary.ByteOrder + cputype uint + subcputype uint + filetype uint32 + flags uint32 + cmd []LdMachoCmd + ncmd uint +} + +type LdMachoCmd struct { + type_ int + off uint32 + size uint32 + seg LdMachoSeg + sym LdMachoSymtab + dsym LdMachoDysymtab +} + +type LdMachoSeg struct { + name string + vmaddr uint64 + vmsize uint64 + fileoff uint32 + filesz uint32 + maxprot uint32 + initprot uint32 + nsect uint32 + flags uint32 + sect []LdMachoSect +} + +type LdMachoSect struct { + name string + segname string + addr uint64 + size uint64 + off uint32 + align uint32 + reloff uint32 + nreloc uint32 + flags uint32 + res1 uint32 + res2 uint32 + sym *LSym + rel []LdMachoRel +} + +type LdMachoRel struct { + addr uint32 + symnum uint32 + pcrel uint8 + length uint8 + extrn uint8 + type_ uint8 + scattered uint8 + value uint32 +} + +type LdMachoSymtab struct { + symoff uint32 + nsym uint32 + stroff uint32 + strsize uint32 + str []byte + sym []LdMachoSym +} + +type LdMachoSym struct { + name string + type_ uint8 + sectnum uint8 + desc uint16 + kind int8 + value uint64 + sym *LSym +} + +type LdMachoDysymtab struct { + ilocalsym uint32 + nlocalsym uint32 + iextdefsym uint32 + nextdefsym uint32 + iundefsym uint32 + nundefsym uint32 + tocoff uint32 + ntoc uint32 + modtaboff uint32 + nmodtab uint32 + extrefsymoff uint32 + nextrefsyms uint32 + indirectsymoff uint32 + nindirectsyms uint32 + extreloff uint32 + nextrel uint32 + locreloff uint32 + nlocrel uint32 + indir []uint32 +} + +const ( + LdMachoCpuVax = 1 + LdMachoCpu68000 = 6 + LdMachoCpu386 = 7 + LdMachoCpuAmd64 = 0x1000007 + LdMachoCpuMips = 8 + LdMachoCpu98000 = 10 + LdMachoCpuHppa = 11 + LdMachoCpuArm = 12 + LdMachoCpu88000 = 13 + LdMachoCpuSparc = 14 + LdMachoCpu860 = 15 + LdMachoCpuAlpha = 16 + LdMachoCpuPower = 18 + LdMachoCmdSegment = 1 + LdMachoCmdSymtab = 2 + LdMachoCmdSymseg = 3 + LdMachoCmdThread = 4 + LdMachoCmdDysymtab = 11 + LdMachoCmdSegment64 = 25 + LdMachoFileObject = 1 + LdMachoFileExecutable = 2 + LdMachoFileFvmlib = 3 + LdMachoFileCore = 4 + LdMachoFilePreload = 5 +) + +func unpackcmd(p []byte, m *LdMachoObj, c *LdMachoCmd, type_ uint, sz uint) int { + var e4 func([]byte) uint32 + var e8 func([]byte) uint64 + var s *LdMachoSect + var i int + + e4 = m.e.Uint32 + e8 = m.e.Uint64 + + c.type_ = int(type_) + c.size = uint32(sz) + switch type_ { + default: + return -1 + + case LdMachoCmdSegment: + if sz < 56 { + return -1 + } + c.seg.name = cstring(p[8:24]) + c.seg.vmaddr = uint64(e4(p[24:])) + c.seg.vmsize = uint64(e4(p[28:])) + c.seg.fileoff = e4(p[32:]) + c.seg.filesz = e4(p[36:]) + c.seg.maxprot = e4(p[40:]) + c.seg.initprot = e4(p[44:]) + c.seg.nsect = e4(p[48:]) + c.seg.flags = e4(p[52:]) + c.seg.sect = make([]LdMachoSect, c.seg.nsect) + if uint32(sz) < 56+c.seg.nsect*68 { + return -1 + } + p = p[56:] + for i = 0; uint32(i) < c.seg.nsect; i++ { + s = &c.seg.sect[i] + s.name = cstring(p[0:16]) + s.segname = cstring(p[16:32]) + s.addr = uint64(e4(p[32:])) + s.size = uint64(e4(p[36:])) + s.off = e4(p[40:]) + s.align = e4(p[44:]) + s.reloff = e4(p[48:]) + s.nreloc = e4(p[52:]) + s.flags = e4(p[56:]) + s.res1 = e4(p[60:]) + s.res2 = e4(p[64:]) + p = p[68:] + } + + case LdMachoCmdSegment64: + if sz < 72 { + return -1 + } + c.seg.name = cstring(p[8:24]) + c.seg.vmaddr = e8(p[24:]) + c.seg.vmsize = e8(p[32:]) + c.seg.fileoff = uint32(e8(p[40:])) + c.seg.filesz = uint32(e8(p[48:])) + c.seg.maxprot = e4(p[56:]) + c.seg.initprot = e4(p[60:]) + c.seg.nsect = e4(p[64:]) + c.seg.flags = e4(p[68:]) + c.seg.sect = make([]LdMachoSect, c.seg.nsect) + if uint32(sz) < 72+c.seg.nsect*80 { + return -1 + } + p = p[72:] + for i = 0; uint32(i) < c.seg.nsect; i++ { + s = &c.seg.sect[i] + s.name = cstring(p[0:16]) + s.segname = cstring(p[16:32]) + s.addr = e8(p[32:]) + s.size = e8(p[40:]) + s.off = e4(p[48:]) + s.align = e4(p[52:]) + s.reloff = e4(p[56:]) + s.nreloc = e4(p[60:]) + s.flags = e4(p[64:]) + s.res1 = e4(p[68:]) + s.res2 = e4(p[72:]) + + // p+76 is reserved + p = p[80:] + } + + case LdMachoCmdSymtab: + if sz < 24 { + return -1 + } + c.sym.symoff = e4(p[8:]) + c.sym.nsym = e4(p[12:]) + c.sym.stroff = e4(p[16:]) + c.sym.strsize = e4(p[20:]) + + case LdMachoCmdDysymtab: + if sz < 80 { + return -1 + } + c.dsym.ilocalsym = e4(p[8:]) + c.dsym.nlocalsym = e4(p[12:]) + c.dsym.iextdefsym = e4(p[16:]) + c.dsym.nextdefsym = e4(p[20:]) + c.dsym.iundefsym = e4(p[24:]) + c.dsym.nundefsym = e4(p[28:]) + c.dsym.tocoff = e4(p[32:]) + c.dsym.ntoc = e4(p[36:]) + c.dsym.modtaboff = e4(p[40:]) + c.dsym.nmodtab = e4(p[44:]) + c.dsym.extrefsymoff = e4(p[48:]) + c.dsym.nextrefsyms = e4(p[52:]) + c.dsym.indirectsymoff = e4(p[56:]) + c.dsym.nindirectsyms = e4(p[60:]) + c.dsym.extreloff = e4(p[64:]) + c.dsym.nextrel = e4(p[68:]) + c.dsym.locreloff = e4(p[72:]) + c.dsym.nlocrel = e4(p[76:]) + } + + return 0 +} + +func macholoadrel(m *LdMachoObj, sect *LdMachoSect) int { + var rel []LdMachoRel + var r *LdMachoRel + var buf []byte + var p []byte + var i int + var n int + var v uint32 + + if sect.rel != nil || sect.nreloc == 0 { + return 0 + } + rel = make([]LdMachoRel, sect.nreloc) + n = int(sect.nreloc * 8) + buf = make([]byte, n) + if Bseek(m.f, m.base+int64(sect.reloff), 0) < 0 || Bread(m.f, buf) != n { + return -1 + } + for i = 0; uint32(i) < sect.nreloc; i++ { + r = &rel[i] + p = buf[i*8:] + r.addr = m.e.Uint32(p) + + // TODO(rsc): Wrong interpretation for big-endian bitfields? + if r.addr&0x80000000 != 0 { + // scatterbrained relocation + r.scattered = 1 + + v = r.addr >> 24 + r.addr &= 0xFFFFFF + r.type_ = uint8(v & 0xF) + v >>= 4 + r.length = 1 << (v & 3) + v >>= 2 + r.pcrel = uint8(v & 1) + r.value = m.e.Uint32(p[4:]) + } else { + v = m.e.Uint32(p[4:]) + r.symnum = v & 0xFFFFFF + v >>= 24 + r.pcrel = uint8(v & 1) + v >>= 1 + r.length = 1 << (v & 3) + v >>= 2 + r.extrn = uint8(v & 1) + v >>= 1 + r.type_ = uint8(v) + } + } + + sect.rel = rel + return 0 +} + +func macholoaddsym(m *LdMachoObj, d *LdMachoDysymtab) int { + var p []byte + var i int + var n int + + n = int(d.nindirectsyms) + + p = make([]byte, n*4) + if Bseek(m.f, m.base+int64(d.indirectsymoff), 0) < 0 || Bread(m.f, p) != len(p) { + return -1 + } + + d.indir = make([]uint32, n) + for i = 0; i < n; i++ { + d.indir[i] = m.e.Uint32(p[4*i:]) + } + return 0 +} + +func macholoadsym(m *LdMachoObj, symtab *LdMachoSymtab) int { + var strbuf []byte + var symbuf []byte + var p []byte + var i int + var n int + var symsize int + var sym []LdMachoSym + var s *LdMachoSym + var v uint32 + + if symtab.sym != nil { + return 0 + } + + strbuf = make([]byte, symtab.strsize) + if Bseek(m.f, m.base+int64(symtab.stroff), 0) < 0 || Bread(m.f, strbuf) != len(strbuf) { + return -1 + } + + symsize = 12 + if m.is64 { + symsize = 16 + } + n = int(symtab.nsym * uint32(symsize)) + symbuf = make([]byte, n) + if Bseek(m.f, m.base+int64(symtab.symoff), 0) < 0 || Bread(m.f, symbuf) != len(symbuf) { + return -1 + } + sym = make([]LdMachoSym, symtab.nsym) + p = symbuf + for i = 0; uint32(i) < symtab.nsym; i++ { + s = &sym[i] + v = m.e.Uint32(p) + if v >= symtab.strsize { + return -1 + } + s.name = cstring(strbuf[v:]) + s.type_ = uint8(p[4]) + s.sectnum = uint8(p[5]) + s.desc = m.e.Uint16(p[6:]) + if m.is64 { + s.value = m.e.Uint64(p[8:]) + } else { + s.value = uint64(m.e.Uint32(p[8:])) + } + p = p[symsize:] + } + + symtab.str = strbuf + symtab.sym = sym + return 0 +} + +func ldmacho(f *Biobuf, pkg string, length int64, pn string) { + var err error + var i int + var j int + var is64 bool + var secaddr uint64 + var hdr [7 * 4]uint8 + var cmdp []byte + var tmp [4]uint8 + var dat []byte + var ncmd uint32 + var cmdsz uint32 + var ty uint32 + var sz uint32 + var off uint32 + var m *LdMachoObj + var e binary.ByteOrder + var base int64 + var sect *LdMachoSect + var rel *LdMachoRel + var rpi int + var s *LSym + var s1 *LSym + var outer *LSym + var c *LdMachoCmd + var symtab *LdMachoSymtab + var dsymtab *LdMachoDysymtab + var sym *LdMachoSym + var r []Reloc + var rp *Reloc + var name string + + Ctxt.Version++ + base = Boffset(f) + if Bread(f, hdr[:]) != len(hdr) { + goto bad + } + + if binary.BigEndian.Uint32(hdr[:])&^1 == 0xFEEDFACE { + e = binary.BigEndian + } else if binary.LittleEndian.Uint32(hdr[:])&^1 == 0xFEEDFACE { + e = binary.LittleEndian + } else { + err = fmt.Errorf("bad magic - not mach-o file") + goto bad + } + + is64 = e.Uint32(hdr[:]) == 0xFEEDFACF + ncmd = e.Uint32([]byte(hdr[4*4:])) + cmdsz = e.Uint32([]byte(hdr[5*4:])) + if ncmd > 0x10000 || cmdsz >= 0x01000000 { + err = fmt.Errorf("implausible mach-o header ncmd=%d cmdsz=%d", ncmd, cmdsz) + goto bad + } + + if is64 { + Bread(f, tmp[:4]) // skip reserved word in header + } + + m = new(LdMachoObj) + + m.f = f + m.e = e + m.cputype = uint(e.Uint32([]byte(hdr[1*4:]))) + m.subcputype = uint(e.Uint32([]byte(hdr[2*4:]))) + m.filetype = e.Uint32([]byte(hdr[3*4:])) + m.ncmd = uint(ncmd) + m.flags = e.Uint32([]byte(hdr[6*4:])) + m.is64 = is64 + m.base = base + m.length = length + m.name = pn + + switch Thearch.Thechar { + default: + Diag("%s: mach-o %s unimplemented", pn, Thestring) + return + + case '6': + if e != binary.LittleEndian || m.cputype != LdMachoCpuAmd64 { + Diag("%s: mach-o object but not amd64", pn) + return + } + + case '8': + if e != binary.LittleEndian || m.cputype != LdMachoCpu386 { + Diag("%s: mach-o object but not 386", pn) + return + } + } + + m.cmd = make([]LdMachoCmd, ncmd) + off = uint32(len(hdr)) + cmdp = make([]byte, cmdsz) + if Bread(f, cmdp) != len(cmdp) { + err = fmt.Errorf("reading cmds: %v", err) + goto bad + } + + // read and parse load commands + c = nil + + symtab = nil + dsymtab = nil + + for i = 0; uint32(i) < ncmd; i++ { + ty = e.Uint32(cmdp) + sz = e.Uint32(cmdp[4:]) + m.cmd[i].off = off + unpackcmd(cmdp, m, &m.cmd[i], uint(ty), uint(sz)) + cmdp = cmdp[sz:] + off += sz + if ty == LdMachoCmdSymtab { + if symtab != nil { + err = fmt.Errorf("multiple symbol tables") + goto bad + } + + symtab = &m.cmd[i].sym + macholoadsym(m, symtab) + } + + if ty == LdMachoCmdDysymtab { + dsymtab = &m.cmd[i].dsym + macholoaddsym(m, dsymtab) + } + + if (is64 && ty == LdMachoCmdSegment64) || (!is64 && ty == LdMachoCmdSegment) { + if c != nil { + err = fmt.Errorf("multiple load commands") + goto bad + } + + c = &m.cmd[i] + } + } + + // load text and data segments into memory. + // they are not as small as the load commands, but we'll need + // the memory anyway for the symbol images, so we might + // as well use one large chunk. + if c == nil { + err = fmt.Errorf("no load command") + goto bad + } + + if symtab == nil { + // our work is done here - no symbols means nothing can refer to this file + return + } + + if int64(c.seg.fileoff+c.seg.filesz) >= length { + err = fmt.Errorf("load segment out of range") + goto bad + } + + dat = make([]byte, c.seg.filesz) + if Bseek(f, m.base+int64(c.seg.fileoff), 0) < 0 || Bread(f, dat) != len(dat) { + err = fmt.Errorf("cannot load object data: %v", err) + goto bad + } + + for i = 0; uint32(i) < c.seg.nsect; i++ { + sect = &c.seg.sect[i] + if sect.segname != "__TEXT" && sect.segname != "__DATA" { + continue + } + if sect.name == "__eh_frame" { + continue + } + name = fmt.Sprintf("%s(%s/%s)", pkg, sect.segname, sect.name) + s = Linklookup(Ctxt, name, Ctxt.Version) + if s.Type != 0 { + err = fmt.Errorf("duplicate %s/%s", sect.segname, sect.name) + goto bad + } + + if sect.flags&0xff == 1 { // S_ZEROFILL + s.P = make([]byte, sect.size) + } else { + s.P = dat[sect.addr-c.seg.vmaddr:][:sect.size] + } + s.Size = int64(len(s.P)) + + if sect.segname == "__TEXT" { + if sect.name == "__text" { + s.Type = STEXT + } else { + s.Type = SRODATA + } + } else { + if sect.name == "__bss" { + s.Type = SNOPTRBSS + s.P = s.P[:0] + } else { + s.Type = SNOPTRDATA + } + } + + sect.sym = s + } + + // enter sub-symbols into symbol table. + // have to guess sizes from next symbol. + for i = 0; uint32(i) < symtab.nsym; i++ { + var v int + sym = &symtab.sym[i] + if sym.type_&N_STAB != 0 { + continue + } + + // TODO: check sym->type against outer->type. + name = sym.name + + if name[0] == '_' && name[1] != '\x00' { + name = name[1:] + } + v = 0 + if sym.type_&N_EXT == 0 { + v = Ctxt.Version + } + s = Linklookup(Ctxt, name, v) + if sym.type_&N_EXT == 0 { + s.Dupok = 1 + } + sym.sym = s + if sym.sectnum == 0 { // undefined + continue + } + if uint32(sym.sectnum) > c.seg.nsect { + err = fmt.Errorf("reference to invalid section %d", sym.sectnum) + goto bad + } + + sect = &c.seg.sect[sym.sectnum-1] + outer = sect.sym + if outer == nil { + err = fmt.Errorf("reference to invalid section %s/%s", sect.segname, sect.name) + continue + } + + if s.Outer != nil { + if s.Dupok != 0 { + continue + } + Diag("%s: duplicate symbol reference: %s in both %s and %s", pn, s.Name, s.Outer.Name, sect.sym.Name) + Errorexit() + } + + s.Type = outer.Type | SSUB + s.Sub = outer.Sub + outer.Sub = s + s.Outer = outer + s.Value = int64(sym.value - sect.addr) + if s.Cgoexport&CgoExportDynamic == 0 { + s.Dynimplib = "" // satisfy dynimport + } + if outer.Type == STEXT { + if s.External != 0 && s.Dupok == 0 { + Diag("%s: duplicate definition of %s", pn, s.Name) + } + s.External = 1 + } + + sym.sym = s + } + + // Sort outer lists by address, adding to textp. + // This keeps textp in increasing address order. + for i = 0; uint32(i) < c.seg.nsect; i++ { + sect = &c.seg.sect[i] + s = sect.sym + if s == nil { + continue + } + if s.Sub != nil { + s.Sub = listsort(s.Sub, valuecmp, listsubp) + + // assign sizes, now that we know symbols in sorted order. + for s1 = s.Sub; s1 != nil; s1 = s1.Sub { + if s1.Sub != nil { + s1.Size = s1.Sub.Value - s1.Value + } else { + s1.Size = s.Value + s.Size - s1.Value + } + } + } + + if s.Type == STEXT { + if s.Onlist != 0 { + log.Fatalf("symbol %s listed multiple times", s.Name) + } + s.Onlist = 1 + if Ctxt.Etextp != nil { + Ctxt.Etextp.Next = s + } else { + Ctxt.Textp = s + } + Ctxt.Etextp = s + for s1 = s.Sub; s1 != nil; s1 = s1.Sub { + if s1.Onlist != 0 { + log.Fatalf("symbol %s listed multiple times", s1.Name) + } + s1.Onlist = 1 + Ctxt.Etextp.Next = s1 + Ctxt.Etextp = s1 + } + } + } + + // load relocations + for i = 0; uint32(i) < c.seg.nsect; i++ { + sect = &c.seg.sect[i] + s = sect.sym + if s == nil { + continue + } + macholoadrel(m, sect) + if sect.rel == nil { + continue + } + r = make([]Reloc, sect.nreloc) + rpi = 0 + for j = 0; uint32(j) < sect.nreloc; j++ { + rp = &r[rpi] + rel = §.rel[j] + if rel.scattered != 0 { + var k int + var ks *LdMachoSect + + if Thearch.Thechar != '8' { + // mach-o only uses scattered relocation on 32-bit platforms + Diag("unexpected scattered relocation") + + continue + } + + // on 386, rewrite scattered 4/1 relocation and some + // scattered 2/1 relocation into the pseudo-pc-relative + // reference that it is. + // assume that the second in the pair is in this section + // and use that as the pc-relative base. + if uint32(j+1) >= sect.nreloc { + err = fmt.Errorf("unsupported scattered relocation %d", int(rel.type_)) + goto bad + } + + if sect.rel[j+1].scattered == 0 || sect.rel[j+1].type_ != 1 || (rel.type_ != 4 && rel.type_ != 2) || uint64(sect.rel[j+1].value) < sect.addr || uint64(sect.rel[j+1].value) >= sect.addr+sect.size { + err = fmt.Errorf("unsupported scattered relocation %d/%d", int(rel.type_), int(sect.rel[j+1].type_)) + goto bad + } + + rp.Siz = rel.length + rp.Off = int32(rel.addr) + + // NOTE(rsc): I haven't worked out why (really when) + // we should ignore the addend on a + // scattered relocation, but it seems that the + // common case is we ignore it. + // It's likely that this is not strictly correct + // and that the math should look something + // like the non-scattered case below. + rp.Add = 0 + + // want to make it pc-relative aka relative to rp->off+4 + // but the scatter asks for relative to off = sect->rel[j+1].value - sect->addr. + // adjust rp->add accordingly. + rp.Type = R_PCREL + + rp.Add += int64(uint64(int64(rp.Off)+4) - (uint64(sect.rel[j+1].value) - sect.addr)) + + // now consider the desired symbol. + // find the section where it lives. + for k = 0; uint32(k) < c.seg.nsect; k++ { + ks = &c.seg.sect[k] + if ks.addr <= uint64(rel.value) && uint64(rel.value) < ks.addr+ks.size { + goto foundk + } + } + + err = fmt.Errorf("unsupported scattered relocation: invalid address %#x", rel.addr) + goto bad + + foundk: + if ks.sym != nil { + rp.Sym = ks.sym + rp.Add += int64(uint64(rel.value) - ks.addr) + } else if ks.segname == "__IMPORT" && ks.name == "__pointers" { + // handle reference to __IMPORT/__pointers. + // how much worse can this get? + // why are we supporting 386 on the mac anyway? + rp.Type = 512 + MACHO_FAKE_GOTPCREL + + // figure out which pointer this is a reference to. + k = int(uint64(ks.res1) + (uint64(rel.value)-ks.addr)/4) + + // load indirect table for __pointers + // fetch symbol number + if dsymtab == nil || k < 0 || uint32(k) >= dsymtab.nindirectsyms || dsymtab.indir == nil { + err = fmt.Errorf("invalid scattered relocation: indirect symbol reference out of range") + goto bad + } + + k = int(dsymtab.indir[k]) + if k < 0 || uint32(k) >= symtab.nsym { + err = fmt.Errorf("invalid scattered relocation: symbol reference out of range") + goto bad + } + + rp.Sym = symtab.sym[k].sym + } else { + err = fmt.Errorf("unsupported scattered relocation: reference to %s/%s", ks.segname, ks.name) + goto bad + } + + rpi++ + + // skip #1 of 2 rel; continue skips #2 of 2. + j++ + + continue + } + + rp.Siz = rel.length + rp.Type = 512 + (int32(rel.type_) << 1) + int32(rel.pcrel) + rp.Off = int32(rel.addr) + + // Handle X86_64_RELOC_SIGNED referencing a section (rel->extrn == 0). + if Thearch.Thechar == '6' && rel.extrn == 0 && rel.type_ == 1 { + // Calculate the addend as the offset into the section. + // + // The rip-relative offset stored in the object file is encoded + // as follows: + // + // movsd 0x00000360(%rip),%xmm0 + // + // To get the absolute address of the value this rip-relative address is pointing + // to, we must add the address of the next instruction to it. This is done by + // taking the address of the relocation and adding 4 to it (since the rip-relative + // offset can at most be 32 bits long). To calculate the offset into the section the + // relocation is referencing, we subtract the vaddr of the start of the referenced + // section found in the original object file. + // + // [For future reference, see Darwin's /usr/include/mach-o/x86_64/reloc.h] + secaddr = c.seg.sect[rel.symnum-1].addr + + rp.Add = int64(uint64(int64(int32(e.Uint32(s.P[rp.Off:])))+int64(rp.Off)+4) - secaddr) + } else { + rp.Add = int64(int32(e.Uint32(s.P[rp.Off:]))) + } + + // For i386 Mach-O PC-relative, the addend is written such that + // it *is* the PC being subtracted. Use that to make + // it match our version of PC-relative. + if rel.pcrel != 0 && Thearch.Thechar == '8' { + rp.Add += int64(rp.Off) + int64(rp.Siz) + } + if rel.extrn == 0 { + if rel.symnum < 1 || rel.symnum > c.seg.nsect { + err = fmt.Errorf("invalid relocation: section reference out of range %d vs %d", rel.symnum, c.seg.nsect) + goto bad + } + + rp.Sym = c.seg.sect[rel.symnum-1].sym + if rp.Sym == nil { + err = fmt.Errorf("invalid relocation: %s", c.seg.sect[rel.symnum-1].name) + goto bad + } + + // References to symbols in other sections + // include that information in the addend. + // We only care about the delta from the + // section base. + if Thearch.Thechar == '8' { + rp.Add -= int64(c.seg.sect[rel.symnum-1].addr) + } + } else { + if rel.symnum >= symtab.nsym { + err = fmt.Errorf("invalid relocation: symbol reference out of range") + goto bad + } + + rp.Sym = symtab.sym[rel.symnum].sym + } + + rpi++ + } + + sort.Sort(rbyoff(r[:rpi])) + s.R = r + s.R = s.R[:rpi] + } + + return + +bad: + Diag("%s: malformed mach-o file: %v", pn, err) +} diff --git a/src/cmd/internal/ld/ldpe.go b/src/cmd/internal/ld/ldpe.go new file mode 100644 index 0000000000..247e8292ad --- /dev/null +++ b/src/cmd/internal/ld/ldpe.go @@ -0,0 +1,550 @@ +// Copyright 2010 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 ld + +import ( + "cmd/internal/obj" + "encoding/binary" + "fmt" + "log" + "sort" + "strings" +) + +const ( + IMAGE_SYM_UNDEFINED = 0 + IMAGE_SYM_ABSOLUTE = -1 + IMAGE_SYM_DEBUG = -2 + IMAGE_SYM_TYPE_NULL = 0 + IMAGE_SYM_TYPE_VOID = 1 + IMAGE_SYM_TYPE_CHAR = 2 + IMAGE_SYM_TYPE_SHORT = 3 + IMAGE_SYM_TYPE_INT = 4 + IMAGE_SYM_TYPE_LONG = 5 + IMAGE_SYM_TYPE_FLOAT = 6 + IMAGE_SYM_TYPE_DOUBLE = 7 + IMAGE_SYM_TYPE_STRUCT = 8 + IMAGE_SYM_TYPE_UNION = 9 + IMAGE_SYM_TYPE_ENUM = 10 + IMAGE_SYM_TYPE_MOE = 11 + IMAGE_SYM_TYPE_BYTE = 12 + IMAGE_SYM_TYPE_WORD = 13 + IMAGE_SYM_TYPE_UINT = 14 + IMAGE_SYM_TYPE_DWORD = 15 + IMAGE_SYM_TYPE_PCODE = 32768 + IMAGE_SYM_DTYPE_NULL = 0 + IMAGE_SYM_DTYPE_POINTER = 0x10 + IMAGE_SYM_DTYPE_FUNCTION = 0x20 + IMAGE_SYM_DTYPE_ARRAY = 0x30 + IMAGE_SYM_CLASS_END_OF_FUNCTION = -1 + IMAGE_SYM_CLASS_NULL = 0 + IMAGE_SYM_CLASS_AUTOMATIC = 1 + IMAGE_SYM_CLASS_EXTERNAL = 2 + IMAGE_SYM_CLASS_STATIC = 3 + IMAGE_SYM_CLASS_REGISTER = 4 + IMAGE_SYM_CLASS_EXTERNAL_DEF = 5 + IMAGE_SYM_CLASS_LABEL = 6 + IMAGE_SYM_CLASS_UNDEFINED_LABEL = 7 + IMAGE_SYM_CLASS_MEMBER_OF_STRUCT = 8 + IMAGE_SYM_CLASS_ARGUMENT = 9 + IMAGE_SYM_CLASS_STRUCT_TAG = 10 + IMAGE_SYM_CLASS_MEMBER_OF_UNION = 11 + IMAGE_SYM_CLASS_UNION_TAG = 12 + IMAGE_SYM_CLASS_TYPE_DEFINITION = 13 + IMAGE_SYM_CLASS_UNDEFINED_STATIC = 14 + IMAGE_SYM_CLASS_ENUM_TAG = 15 + IMAGE_SYM_CLASS_MEMBER_OF_ENUM = 16 + IMAGE_SYM_CLASS_REGISTER_PARAM = 17 + IMAGE_SYM_CLASS_BIT_FIELD = 18 + IMAGE_SYM_CLASS_FAR_EXTERNAL = 68 + IMAGE_SYM_CLASS_BLOCK = 100 + IMAGE_SYM_CLASS_FUNCTION = 101 + IMAGE_SYM_CLASS_END_OF_STRUCT = 102 + IMAGE_SYM_CLASS_FILE = 103 + IMAGE_SYM_CLASS_SECTION = 104 + IMAGE_SYM_CLASS_WEAK_EXTERNAL = 105 + IMAGE_SYM_CLASS_CLR_TOKEN = 107 + IMAGE_REL_I386_ABSOLUTE = 0x0000 + IMAGE_REL_I386_DIR16 = 0x0001 + IMAGE_REL_I386_REL16 = 0x0002 + IMAGE_REL_I386_DIR32 = 0x0006 + IMAGE_REL_I386_DIR32NB = 0x0007 + IMAGE_REL_I386_SEG12 = 0x0009 + IMAGE_REL_I386_SECTION = 0x000A + IMAGE_REL_I386_SECREL = 0x000B + IMAGE_REL_I386_TOKEN = 0x000C + IMAGE_REL_I386_SECREL7 = 0x000D + IMAGE_REL_I386_REL32 = 0x0014 + IMAGE_REL_AMD64_ABSOLUTE = 0x0000 + IMAGE_REL_AMD64_ADDR64 = 0x0001 + IMAGE_REL_AMD64_ADDR32 = 0x0002 + IMAGE_REL_AMD64_ADDR32NB = 0x0003 + IMAGE_REL_AMD64_REL32 = 0x0004 + IMAGE_REL_AMD64_REL32_1 = 0x0005 + IMAGE_REL_AMD64_REL32_2 = 0x0006 + IMAGE_REL_AMD64_REL32_3 = 0x0007 + IMAGE_REL_AMD64_REL32_4 = 0x0008 + IMAGE_REL_AMD64_REL32_5 = 0x0009 + IMAGE_REL_AMD64_SECTION = 0x000A + IMAGE_REL_AMD64_SECREL = 0x000B + IMAGE_REL_AMD64_SECREL7 = 0x000C + IMAGE_REL_AMD64_TOKEN = 0x000D + IMAGE_REL_AMD64_SREL32 = 0x000E + IMAGE_REL_AMD64_PAIR = 0x000F + IMAGE_REL_AMD64_SSPAN32 = 0x0010 +) + +type PeSym struct { + name string + value uint32 + sectnum uint16 + type_ uint16 + sclass uint8 + aux uint8 + sym *LSym +} + +type PeSect struct { + name string + base []byte + size uint64 + sym *LSym + sh IMAGE_SECTION_HEADER +} + +type PeObj struct { + f *Biobuf + name string + base uint32 + sect []PeSect + nsect uint + pesym []PeSym + npesym uint + fh IMAGE_FILE_HEADER + snames []byte +} + +func ldpe(f *Biobuf, pkg string, length int64, pn string) { + var err error + var name string + var base int32 + var l uint32 + var i int + var j int + var numaux int + var peobj *PeObj + var sect *PeSect + var rsect *PeSect + var symbuf [18]uint8 + var s *LSym + var r []Reloc + var rp *Reloc + var sym *PeSym + + if Debug['v'] != 0 { + fmt.Fprintf(&Bso, "%5.2f ldpe %s\n", obj.Cputime(), pn) + } + + sect = nil + Ctxt.Version++ + base = int32(Boffset(f)) + + peobj = new(PeObj) + peobj.f = f + peobj.base = uint32(base) + peobj.name = pn + + // read header + if err = binary.Read(f, binary.LittleEndian, &peobj.fh); err != nil { + goto bad + } + + // load section list + peobj.sect = make([]PeSect, peobj.fh.NumberOfSections) + + peobj.nsect = uint(peobj.fh.NumberOfSections) + for i = 0; i < int(peobj.fh.NumberOfSections); i++ { + if err = binary.Read(f, binary.LittleEndian, &peobj.sect[i].sh); err != nil { + goto bad + } + peobj.sect[i].size = uint64(peobj.sect[i].sh.SizeOfRawData) + peobj.sect[i].name = cstring(peobj.sect[i].sh.Name[:]) + } + + // TODO return error if found .cormeta + + // load string table + Bseek(f, int64(base)+int64(peobj.fh.PointerToSymbolTable)+int64(len(symbuf))*int64(peobj.fh.NumberOfSymbols), 0) + + if Bread(f, symbuf[:4]) != 4 { + goto bad + } + l = Le32(symbuf[:]) + peobj.snames = make([]byte, l) + Bseek(f, int64(base)+int64(peobj.fh.PointerToSymbolTable)+int64(len(symbuf))*int64(peobj.fh.NumberOfSymbols), 0) + if Bread(f, peobj.snames) != len(peobj.snames) { + goto bad + } + + // rewrite section names if they start with / + for i = 0; i < int(peobj.fh.NumberOfSections); i++ { + if peobj.sect[i].name == "" { + continue + } + if peobj.sect[i].name[0] != '/' { + continue + } + l = uint32(obj.Atoi(peobj.sect[i].name[1:])) + peobj.sect[i].name = cstring(peobj.snames[l:]) + } + + // read symbols + peobj.pesym = make([]PeSym, peobj.fh.NumberOfSymbols) + + peobj.npesym = uint(peobj.fh.NumberOfSymbols) + Bseek(f, int64(base)+int64(peobj.fh.PointerToSymbolTable), 0) + for i = 0; uint32(i) < peobj.fh.NumberOfSymbols; i += numaux + 1 { + Bseek(f, int64(base)+int64(peobj.fh.PointerToSymbolTable)+int64(len(symbuf))*int64(i), 0) + if Bread(f, symbuf[:]) != len(symbuf) { + goto bad + } + + if (symbuf[0] == 0) && (symbuf[1] == 0) && (symbuf[2] == 0) && (symbuf[3] == 0) { + l = Le32(symbuf[4:]) + peobj.pesym[i].name = cstring(peobj.snames[l:]) // sym name length <= 8 + } else { + peobj.pesym[i].name = cstring(symbuf[:8]) + } + + peobj.pesym[i].value = Le32(symbuf[8:]) + peobj.pesym[i].sectnum = Le16(symbuf[12:]) + peobj.pesym[i].sclass = symbuf[16] + peobj.pesym[i].aux = symbuf[17] + peobj.pesym[i].type_ = Le16(symbuf[14:]) + numaux = int(peobj.pesym[i].aux) + if numaux < 0 { + numaux = 0 + } + } + + // create symbols for mapped sections + for i = 0; uint(i) < peobj.nsect; i++ { + sect = &peobj.sect[i] + if sect.sh.Characteristics&IMAGE_SCN_MEM_DISCARDABLE != 0 { + continue + } + + if sect.sh.Characteristics&(IMAGE_SCN_CNT_CODE|IMAGE_SCN_CNT_INITIALIZED_DATA|IMAGE_SCN_CNT_UNINITIALIZED_DATA) == 0 { + // This has been seen for .idata sections, which we + // want to ignore. See issues 5106 and 5273. + continue + } + + if pemap(peobj, sect) < 0 { + goto bad + } + + name = fmt.Sprintf("%s(%s)", pkg, sect.name) + s = Linklookup(Ctxt, name, Ctxt.Version) + + switch sect.sh.Characteristics & (IMAGE_SCN_CNT_UNINITIALIZED_DATA | IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE | IMAGE_SCN_CNT_CODE | IMAGE_SCN_MEM_EXECUTE) { + case IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ: //.rdata + s.Type = SRODATA + + case IMAGE_SCN_CNT_UNINITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE: //.bss + s.Type = SNOPTRBSS + + case IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE: //.data + s.Type = SNOPTRDATA + + case IMAGE_SCN_CNT_CODE | IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ: //.text + s.Type = STEXT + + default: + err = fmt.Errorf("unexpected flags %#06x for PE section %s", sect.sh.Characteristics, sect.name) + goto bad + } + + s.P = sect.base + s.P = s.P[:sect.size] + s.Size = int64(sect.size) + sect.sym = s + if sect.name == ".rsrc" { + setpersrc(sect.sym) + } + } + + // load relocations + for i = 0; uint(i) < peobj.nsect; i++ { + rsect = &peobj.sect[i] + if rsect.sym == nil || rsect.sh.NumberOfRelocations == 0 { + continue + } + if rsect.sh.Characteristics&IMAGE_SCN_MEM_DISCARDABLE != 0 { + continue + } + if sect.sh.Characteristics&(IMAGE_SCN_CNT_CODE|IMAGE_SCN_CNT_INITIALIZED_DATA|IMAGE_SCN_CNT_UNINITIALIZED_DATA) == 0 { + // This has been seen for .idata sections, which we + // want to ignore. See issues 5106 and 5273. + continue + } + + r = make([]Reloc, rsect.sh.NumberOfRelocations) + Bseek(f, int64(peobj.base)+int64(rsect.sh.PointerToRelocations), 0) + for j = 0; j < int(rsect.sh.NumberOfRelocations); j++ { + rp = &r[j] + if Bread(f, symbuf[:10]) != 10 { + goto bad + } + var rva uint32 + var symindex uint32 + var type_ uint16 + rva = Le32(symbuf[0:]) + symindex = Le32(symbuf[4:]) + type_ = Le16(symbuf[8:]) + if err = readpesym(peobj, int(symindex), &sym); err != nil { + goto bad + } + if sym.sym == nil { + err = fmt.Errorf("reloc of invalid sym %s idx=%d type=%d", sym.name, symindex, sym.type_) + goto bad + } + + rp.Sym = sym.sym + rp.Siz = 4 + rp.Off = int32(rva) + switch type_ { + default: + Diag("%s: unknown relocation type %d;", pn, type_) + fallthrough + + case IMAGE_REL_I386_REL32, + IMAGE_REL_AMD64_REL32, + IMAGE_REL_AMD64_ADDR32, // R_X86_64_PC32 + IMAGE_REL_AMD64_ADDR32NB: + rp.Type = R_PCREL + + rp.Add = int64(int32(Le32(rsect.base[rp.Off:]))) + + case IMAGE_REL_I386_DIR32NB, + IMAGE_REL_I386_DIR32: + rp.Type = R_ADDR + + // load addend from image + rp.Add = int64(int32(Le32(rsect.base[rp.Off:]))) + + case IMAGE_REL_AMD64_ADDR64: // R_X86_64_64 + rp.Siz = 8 + + rp.Type = R_ADDR + + // load addend from image + rp.Add = int64(Le64(rsect.base[rp.Off:])) + } + + // ld -r could generate multiple section symbols for the + // same section but with different values, we have to take + // that into account + if issect(&peobj.pesym[symindex]) { + rp.Add += int64(peobj.pesym[symindex].value) + } + } + + sort.Sort(rbyoff(r[:rsect.sh.NumberOfRelocations])) + + s = rsect.sym + s.R = r + s.R = s.R[:rsect.sh.NumberOfRelocations] + } + + // enter sub-symbols into symbol table. + for i = 0; uint(i) < peobj.npesym; i++ { + if peobj.pesym[i].name == "" { + continue + } + if issect(&peobj.pesym[i]) { + continue + } + if uint(peobj.pesym[i].sectnum) > peobj.nsect { + continue + } + if peobj.pesym[i].sectnum > 0 { + sect = &peobj.sect[peobj.pesym[i].sectnum-1] + if sect.sym == nil { + continue + } + } + + if err = readpesym(peobj, i, &sym); err != nil { + goto bad + } + + s = sym.sym + if sym.sectnum == 0 { // extern + if s.Type == SDYNIMPORT { + s.Plt = -2 // flag for dynimport in PE object files. + } + if s.Type == SXREF && sym.value > 0 { // global data + s.Type = SNOPTRDATA + s.Size = int64(sym.value) + } + + continue + } else if sym.sectnum > 0 && uint(sym.sectnum) <= peobj.nsect { + sect = &peobj.sect[sym.sectnum-1] + if sect.sym == nil { + Diag("%s: %s sym == 0!", pn, s.Name) + } + } else { + Diag("%s: %s sectnum < 0!", pn, s.Name) + } + + if sect == nil { + return + } + + if s.Outer != nil { + if s.Dupok != 0 { + continue + } + Diag("%s: duplicate symbol reference: %s in both %s and %s", pn, s.Name, s.Outer.Name, sect.sym.Name) + Errorexit() + } + + s.Sub = sect.sym.Sub + sect.sym.Sub = s + s.Type = sect.sym.Type | SSUB + s.Value = int64(sym.value) + s.Size = 4 + s.Outer = sect.sym + if sect.sym.Type == STEXT { + if s.External != 0 && s.Dupok == 0 { + Diag("%s: duplicate definition of %s", pn, s.Name) + } + s.External = 1 + } + } + + // Sort outer lists by address, adding to textp. + // This keeps textp in increasing address order. + for i = 0; uint(i) < peobj.nsect; i++ { + s = peobj.sect[i].sym + if s == nil { + continue + } + if s.Sub != nil { + s.Sub = listsort(s.Sub, valuecmp, listsubp) + } + if s.Type == STEXT { + if s.Onlist != 0 { + log.Fatalf("symbol %s listed multiple times", s.Name) + } + s.Onlist = 1 + if Ctxt.Etextp != nil { + Ctxt.Etextp.Next = s + } else { + Ctxt.Textp = s + } + Ctxt.Etextp = s + for s = s.Sub; s != nil; s = s.Sub { + if s.Onlist != 0 { + log.Fatalf("symbol %s listed multiple times", s.Name) + } + s.Onlist = 1 + Ctxt.Etextp.Next = s + Ctxt.Etextp = s + } + } + } + + return + +bad: + Diag("%s: malformed pe file: %v", pn, err) +} + +func pemap(peobj *PeObj, sect *PeSect) int { + if sect.base != nil { + return 0 + } + + sect.base = make([]byte, sect.sh.SizeOfRawData) + if sect.sh.PointerToRawData == 0 { // .bss doesn't have data in object file + return 0 + } + if Bseek(peobj.f, int64(peobj.base)+int64(sect.sh.PointerToRawData), 0) < 0 || Bread(peobj.f, sect.base) != len(sect.base) { + return -1 + } + + return 0 +} + +func issect(s *PeSym) bool { + return s.sclass == IMAGE_SYM_CLASS_STATIC && s.type_ == 0 && s.name[0] == '.' +} + +func readpesym(peobj *PeObj, i int, y **PeSym) (err error) { + var s *LSym + var sym *PeSym + var name string + + if uint(i) >= peobj.npesym || i < 0 { + err = fmt.Errorf("invalid pe symbol index") + return err + } + + sym = &peobj.pesym[i] + *y = sym + + if issect(sym) { + name = peobj.sect[sym.sectnum-1].sym.Name + } else { + name = sym.name + if strings.HasPrefix(name, "__imp_") { + name = name[6:] // __imp_Name => Name + } + if Thearch.Thechar == '8' && name[0] == '_' { + name = name[1:] // _Name => Name + } + } + + // remove last @XXX + if i := strings.LastIndex(name, "@"); i >= 0 { + name = name[:i] + } + + switch sym.type_ { + default: + err = fmt.Errorf("%s: invalid symbol type %d", sym.name, sym.type_) + return err + + case IMAGE_SYM_DTYPE_FUNCTION, + IMAGE_SYM_DTYPE_NULL: + switch sym.sclass { + case IMAGE_SYM_CLASS_EXTERNAL: //global + s = Linklookup(Ctxt, name, 0) + + case IMAGE_SYM_CLASS_NULL, + IMAGE_SYM_CLASS_STATIC, + IMAGE_SYM_CLASS_LABEL: + s = Linklookup(Ctxt, name, Ctxt.Version) + s.Dupok = 1 + + default: + err = fmt.Errorf("%s: invalid symbol binding %d", sym.name, sym.sclass) + return err + } + } + + if s != nil && s.Type == 0 && (sym.sclass != IMAGE_SYM_CLASS_STATIC || sym.value != 0) { + s.Type = SXREF + } + if strings.HasPrefix(sym.name, "__imp_") { + s.Got = -2 // flag for __imp_ + } + sym.sym = s + + return nil +} diff --git a/src/cmd/internal/ld/lib.go b/src/cmd/internal/ld/lib.go new file mode 100644 index 0000000000..bc58ffff6f --- /dev/null +++ b/src/cmd/internal/ld/lib.go @@ -0,0 +1,1751 @@ +// Inferno utils/8l/asm.c +// http://code.google.com/p/inferno-os/source/browse/utils/8l/asm.c +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package ld + +import ( + "bytes" + "cmd/internal/obj" + "fmt" + "io/ioutil" + "log" + "os" + "os/exec" + "strings" +) + +// Data layout and relocation. + +// Derived from Inferno utils/6l/l.h +// http://code.google.com/p/inferno-os/source/browse/utils/6l/l.h +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +type Arch struct { + Thechar int + Ptrsize int + Intsize int + Regsize int + Funcalign int + Maxalign int + Minlc int + Dwarfregsp int + Linuxdynld string + Freebsddynld string + Netbsddynld string + Openbsddynld string + Dragonflydynld string + Solarisdynld string + Adddynlib func(string) + Adddynrel func(*LSym, *Reloc) + Adddynsym func(*Link, *LSym) + Archinit func() + Archreloc func(*Reloc, *LSym, *int64) int + Archrelocvariant func(*Reloc, *LSym, int64) int64 + Asmb func() + Elfreloc1 func(*Reloc, int64) int + Elfsetupplt func() + Gentext func() + Machoreloc1 func(*Reloc, int64) int + Lput func(uint32) + Wput func(uint16) + Vput func(uint64) +} + +var Thearch Arch + +var datap *LSym + +var Debug [128]int + +var literal string + +var Lcsize int32 + +var rpath string + +var Spsize int32 + +var symlist *LSym + +var Symsize int32 + +// Terrible but standard terminology. +// A segment describes a block of file to load into memory. +// A section further describes the pieces of that block for +// use in debuggers and such. + +const ( + MAXIO = 8192 + MINFUNC = 16 +) + +type Segment struct { + Rwx uint8 + Vaddr uint64 + Length uint64 + Fileoff uint64 + Filelen uint64 + Sect *Section +} + +type Section struct { + Rwx uint8 + Extnum int16 + Align int32 + Name string + Vaddr uint64 + Length uint64 + Next *Section + Seg *Segment + Elfsect interface{} + Reloff uint64 + Rellen uint64 +} + +var Thestring string + +var Thelinkarch *LinkArch + +var outfile string + +var ndynexp int + +var dynexp []*LSym + +var nldflag int + +var ldflag []string + +var havedynamic int + +var Funcalign int + +var iscgo bool + +var elfglobalsymndx int + +var flag_installsuffix string + +var flag_race int + +var Flag_shared int + +var tracksym string + +var interpreter string + +var tmpdir string + +var extld string + +var extldflags string + +var debug_s int // backup old value of debug['s'] + +var Ctxt *Link + +var HEADR int32 + +var HEADTYPE int32 + +var INITRND int32 + +var INITTEXT int64 + +var INITDAT int64 + +var INITENTRY string /* entry point */ + +var nerrors int + +var Linkmode int + +var liveness int64 + +// for dynexport field of LSym +const ( + CgoExportDynamic = 1 << 0 + CgoExportStatic = 1 << 1 +) + +var Segtext Segment + +var Segrodata Segment + +var Segdata Segment + +var Segdwarf Segment + +type Endian struct { + e16 func([]byte) uint16 + e32 func([]byte) uint32 + e64 func([]byte) uint64 +} + +/* set by call to mywhatsys() */ + +/* whence for ldpkg */ +const ( + FileObj = 0 + iota + ArchiveObj + Pkgdef +) + +var headstring string + +// buffered output + +var Bso Biobuf + +var coutbuf Biobuf + +const ( + AssumeGoldLinker = 0 +) + +var symname string = "__.GOSYMDEF" + +var pkgname string = "__.PKGDEF" + +var cout *os.File + +var version int + +// Set if we see an object compiled by the host compiler that is not +// from a package that is known to support internal linking mode. +var externalobj int = 0 + +var goroot string + +var goarch string + +var goos string + +var theline string + +func Lflag(arg string) { + Ctxt.Libdir = append(Ctxt.Libdir, arg) +} + +/* + * Unix doesn't like it when we write to a running (or, sometimes, + * recently run) binary, so remove the output file before writing it. + * On Windows 7, remove() can force a subsequent create() to fail. + * S_ISREG() does not exist on Plan 9. + */ +func mayberemoveoutfile() { + if fi, err := os.Lstat(outfile); err == nil && !fi.Mode().IsRegular() { + return + } + os.Remove(outfile) +} + +func libinit() { + var suffix string + var suffixsep string + + Funcalign = Thearch.Funcalign + mywhatsys() // get goroot, goarch, goos + + // add goroot to the end of the libdir list. + suffix = "" + + suffixsep = "" + if flag_installsuffix != "" { + suffixsep = "_" + suffix = flag_installsuffix + } else if flag_race != 0 { + suffixsep = "_" + suffix = "race" + } + + Lflag(fmt.Sprintf("%s/pkg/%s_%s%s%s", goroot, goos, goarch, suffixsep, suffix)) + + mayberemoveoutfile() + f, err := os.OpenFile(outfile, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0775) + if err != nil { + Diag("cannot create %s: %v", outfile, err) + Errorexit() + } + + cout = f + coutbuf = *Binitw(f) + + if INITENTRY == "" { + if Flag_shared == 0 { + INITENTRY = fmt.Sprintf("_rt0_%s_%s", goarch, goos) + } else { + INITENTRY = fmt.Sprintf("_rt0_%s_%s_lib", goarch, goos) + } + } + + Linklookup(Ctxt, INITENTRY, 0).Type = SXREF +} + +func Errorexit() { + if cout != nil { + // For rmtemp run at atexit time on Windows. + cout.Close() + } + + if nerrors != 0 { + if cout != nil { + mayberemoveoutfile() + } + Exit(2) + } + + Exit(0) +} + +func loadinternal(name string) { + var pname string + var i int + var found int + + found = 0 + for i = 0; i < len(Ctxt.Libdir); i++ { + pname = fmt.Sprintf("%s/%s.a", Ctxt.Libdir[i], name) + if Debug['v'] != 0 { + fmt.Fprintf(&Bso, "searching for %s.a in %s\n", name, pname) + } + if obj.Access(pname, obj.AEXIST) >= 0 { + addlibpath(Ctxt, "internal", "internal", pname, name) + found = 1 + break + } + } + + if found == 0 { + fmt.Fprintf(&Bso, "warning: unable to find %s.a\n", name) + } +} + +func loadlib() { + var i int + var w int + var x int + var s *LSym + var tlsg *LSym + var cgostrsym string + + if Flag_shared != 0 { + s = Linklookup(Ctxt, "runtime.islibrary", 0) + s.Dupok = 1 + Adduint8(Ctxt, s, 1) + } + + loadinternal("runtime") + if Thearch.Thechar == '5' { + loadinternal("math") + } + if flag_race != 0 { + loadinternal("runtime/race") + } + + for i = 0; i < len(Ctxt.Library); i++ { + if Debug['v'] > 1 { + fmt.Fprintf(&Bso, "%5.2f autolib: %s (from %s)\n", obj.Cputime(), Ctxt.Library[i].File, Ctxt.Library[i].Objref) + } + iscgo = iscgo || Ctxt.Library[i].Pkg == "runtime/cgo" + objfile(Ctxt.Library[i].File, Ctxt.Library[i].Pkg) + } + + if Linkmode == LinkAuto { + if iscgo && externalobj != 0 { + Linkmode = LinkExternal + } else { + Linkmode = LinkInternal + } + + // Force external linking for android. + if goos == "android" { + Linkmode = LinkExternal + } + + // cgo on Darwin must use external linking + // we can always use external linking, but then there will be circular + // dependency problems when compiling natively (external linking requires + // runtime/cgo, runtime/cgo requires cmd/cgo, but cmd/cgo needs to be + // compiled using external linking.) + if Thearch.Thechar == '5' && HEADTYPE == Hdarwin && iscgo { + Linkmode = LinkExternal + } + } + + if Linkmode == LinkExternal && !iscgo { + // This indicates a user requested -linkmode=external. + // The startup code uses an import of runtime/cgo to decide + // whether to initialize the TLS. So give it one. This could + // be handled differently but it's an unusual case. + loadinternal("runtime/cgo") + + if i < len(Ctxt.Library) { + objfile(Ctxt.Library[i].File, Ctxt.Library[i].Pkg) + } + + // Pretend that we really imported the package. + s = Linklookup(Ctxt, "go.importpath.runtime/cgo.", 0) + + s.Type = SDATA + s.Dupok = 1 + s.Reachable = true + + // Provided by the code that imports the package. + // Since we are simulating the import, we have to provide this string. + cgostrsym = "go.string.\"runtime/cgo\"" + + if Linkrlookup(Ctxt, cgostrsym, 0) == nil { + s = Linklookup(Ctxt, cgostrsym, 0) + s.Type = SRODATA + s.Reachable = true + addstrdata(cgostrsym, "runtime/cgo") + } + } + + if Linkmode == LinkInternal { + // Drop all the cgo_import_static declarations. + // Turns out we won't be needing them. + for s = Ctxt.Allsym; s != nil; s = s.Allsym { + if s.Type == SHOSTOBJ { + // If a symbol was marked both + // cgo_import_static and cgo_import_dynamic, + // then we want to make it cgo_import_dynamic + // now. + if s.Extname != "" && s.Dynimplib != "" && s.Cgoexport == 0 { + s.Type = SDYNIMPORT + } else { + s.Type = 0 + } + } + } + } + + tlsg = Linklookup(Ctxt, "runtime.tlsg", 0) + + // For most ports, runtime.tlsg is a placeholder symbol for TLS + // relocation. However, the Android and Darwin arm ports need it + // to be a real variable. + // + // TODO(crawshaw): android should require leaving the tlsg->type + // alone (as the runtime-provided SNOPTRBSS) just like darwin/arm. + // But some other part of the linker is expecting STLSBSS. + if goos != "darwin" || Thearch.Thechar != '5' { + tlsg.Type = STLSBSS + } + tlsg.Size = int64(Thearch.Ptrsize) + tlsg.Hide = 1 + tlsg.Reachable = true + Ctxt.Tlsg = tlsg + + // Now that we know the link mode, trim the dynexp list. + x = CgoExportDynamic + + if Linkmode == LinkExternal { + x = CgoExportStatic + } + w = 0 + for i = 0; i < len(dynexp); i++ { + if int(dynexp[i].Cgoexport)&x != 0 { + dynexp[w] = dynexp[i] + w++ + } + } + dynexp = dynexp[:w] + + // In internal link mode, read the host object files. + if Linkmode == LinkInternal { + hostobjs() + } else { + hostlinksetup() + } + + // We've loaded all the code now. + // If there are no dynamic libraries needed, gcc disables dynamic linking. + // Because of this, glibc's dynamic ELF loader occasionally (like in version 2.13) + // assumes that a dynamic binary always refers to at least one dynamic library. + // Rather than be a source of test cases for glibc, disable dynamic linking + // the same way that gcc would. + // + // Exception: on OS X, programs such as Shark only work with dynamic + // binaries, so leave it enabled on OS X (Mach-O) binaries. + // Also leave it enabled on Solaris which doesn't support + // statically linked binaries. + if Flag_shared == 0 && havedynamic == 0 && HEADTYPE != Hdarwin && HEADTYPE != Hsolaris { + Debug['d'] = 1 + } + + importcycles() +} + +/* + * look for the next file in an archive. + * adapted from libmach. + */ +func nextar(bp *Biobuf, off int64, a *ArHdr) int64 { + if off&1 != 0 { + off++ + } + Bseek(bp, off, 0) + buf := make([]byte, SAR_HDR) + if n := Bread(bp, buf); n < len(buf) { + if n >= 0 { + return 0 + } + return -1 + } + + a.name = artrim(buf[0:16]) + a.date = artrim(buf[16:28]) + a.uid = artrim(buf[28:34]) + a.gid = artrim(buf[34:40]) + a.mode = artrim(buf[40:48]) + a.size = artrim(buf[48:58]) + a.fmag = artrim(buf[58:60]) + + arsize := atolwhex(a.size) + if arsize&1 != 0 { + arsize++ + } + return int64(arsize) + SAR_HDR +} + +func objfile(file string, pkg string) { + var off int64 + var l int64 + var f *Biobuf + var pname string + var arhdr ArHdr + + pkg = pathtoprefix(pkg) + + if Debug['v'] > 1 { + fmt.Fprintf(&Bso, "%5.2f ldobj: %s (%s)\n", obj.Cputime(), file, pkg) + } + Bflush(&Bso) + var err error + f, err = Bopenr(file) + if err != nil { + Diag("cannot open file %s: %v", file, err) + Errorexit() + } + + magbuf := make([]byte, len(ARMAG)) + if Bread(f, magbuf) != len(magbuf) || !strings.HasPrefix(string(magbuf), ARMAG) { + /* load it as a regular file */ + l = Bseek(f, 0, 2) + + Bseek(f, 0, 0) + ldobj(f, pkg, l, file, file, FileObj) + Bterm(f) + + return + } + + /* skip over optional __.GOSYMDEF and process __.PKGDEF */ + off = Boffset(f) + + l = nextar(f, off, &arhdr) + if l <= 0 { + Diag("%s: short read on archive file symbol header", file) + goto out + } + + if strings.HasPrefix(arhdr.name, symname) { + off += l + l = nextar(f, off, &arhdr) + if l <= 0 { + Diag("%s: short read on archive file symbol header", file) + goto out + } + } + + if !strings.HasPrefix(arhdr.name, pkgname) { + Diag("%s: cannot find package header", file) + goto out + } + + off += l + + if Debug['u'] != 0 { + ldpkg(f, pkg, atolwhex(arhdr.size), file, Pkgdef) + } + + /* + * load all the object files from the archive now. + * this gives us sequential file access and keeps us + * from needing to come back later to pick up more + * objects. it breaks the usual C archive model, but + * this is Go, not C. the common case in Go is that + * we need to load all the objects, and then we throw away + * the individual symbols that are unused. + * + * loading every object will also make it possible to + * load foreign objects not referenced by __.GOSYMDEF. + */ + for { + l = nextar(f, off, &arhdr) + if l == 0 { + break + } + if l < 0 { + Diag("%s: malformed archive", file) + Errorexit() + goto out + } + + off += l + + pname = fmt.Sprintf("%s(%s)", file, arhdr.name) + l = atolwhex(arhdr.size) + ldobj(f, pkg, l, pname, file, ArchiveObj) + } + +out: + Bterm(f) +} + +type Hostobj struct { + ld func(*Biobuf, string, int64, string) + pkg string + pn string + file string + off int64 + length int64 +} + +var hostobj []Hostobj + +var nhostobj int + +var mhostobj int + +// These packages can use internal linking mode. +// Others trigger external mode. +var internalpkg = []string{ + "crypto/x509", + "net", + "os/user", + "runtime/cgo", + "runtime/race", +} + +func ldhostobj(ld func(*Biobuf, string, int64, string), f *Biobuf, pkg string, length int64, pn string, file string) { + var i int + var isinternal int + var h *Hostobj + + isinternal = 0 + for i = 0; i < len(internalpkg); i++ { + if pkg == internalpkg[i] { + isinternal = 1 + break + } + } + + // DragonFly declares errno with __thread, which results in a symbol + // type of R_386_TLS_GD or R_X86_64_TLSGD. The Go linker does not + // currently know how to handle TLS relocations, hence we have to + // force external linking for any libraries that link in code that + // uses errno. This can be removed if the Go linker ever supports + // these relocation types. + if HEADTYPE == Hdragonfly { + if pkg == "net" || pkg == "os/user" { + isinternal = 0 + } + } + + if isinternal == 0 { + externalobj = 1 + } + + hostobj = append(hostobj, Hostobj{}) + h = &hostobj[len(hostobj)-1] + h.ld = ld + h.pkg = pkg + h.pn = pn + h.file = file + h.off = Boffset(f) + h.length = length +} + +func hostobjs() { + var i int + var f *Biobuf + var h *Hostobj + + for i = 0; i < len(hostobj); i++ { + h = &hostobj[i] + var err error + f, err = Bopenr(h.file) + if f == nil { + Ctxt.Cursym = nil + Diag("cannot reopen %s: %v", h.pn, err) + Errorexit() + } + + Bseek(f, h.off, 0) + h.ld(f, h.pkg, h.length, h.pn) + Bterm(f) + } +} + +// provided by lib9 + +func rmtemp() { + os.RemoveAll(tmpdir) +} + +func hostlinksetup() { + var p string + + if Linkmode != LinkExternal { + return + } + + // create temporary directory and arrange cleanup + if tmpdir == "" { + dir, err := ioutil.TempDir("", "go-link-") + if err != nil { + log.Fatal(err) + } + tmpdir = dir + AtExit(rmtemp) + } + + // change our output to temporary object file + cout.Close() + + p = fmt.Sprintf("%s/go.o", tmpdir) + var err error + cout, err = os.OpenFile(p, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0775) + if err != nil { + Diag("cannot create %s: %v", p, err) + Errorexit() + } + + coutbuf = *Binitw(cout) +} + +var hostlink_buf = make([]byte, 64*1024) + +func hostlink() { + var p string + var argv []string + var i int + var n int + var length int + var h *Hostobj + var f *Biobuf + + if Linkmode != LinkExternal || nerrors > 0 { + return + } + + if extld == "" { + extld = "gcc" + } + argv = append(argv, extld) + switch Thearch.Thechar { + case '8': + argv = append(argv, "-m32") + + case '6', + '9': + argv = append(argv, "-m64") + + case '5': + argv = append(argv, "-marm") + } + + if Debug['s'] == 0 && debug_s == 0 { + argv = append(argv, "-gdwarf-2") + } else { + argv = append(argv, "-s") + } + + if HEADTYPE == Hdarwin { + argv = append(argv, "-Wl,-no_pie,-pagezero_size,4000000") + } + if HEADTYPE == Hopenbsd { + argv = append(argv, "-Wl,-nopie") + } + + if Iself && AssumeGoldLinker != 0 /*TypeKind(100016)*/ { + argv = append(argv, "-Wl,--rosegment") + } + + if Flag_shared != 0 { + argv = append(argv, "-Wl,-Bsymbolic") + argv = append(argv, "-shared") + } + + argv = append(argv, "-o") + argv = append(argv, outfile) + + if rpath != "" { + argv = append(argv, fmt.Sprintf("-Wl,-rpath,%s", rpath)) + } + + // Force global symbols to be exported for dlopen, etc. + if Iself { + argv = append(argv, "-rdynamic") + } + + if strings.Contains(argv[0], "clang") { + argv = append(argv, "-Qunused-arguments") + } + + // already wrote main object file + // copy host objects to temporary directory + for i = 0; i < len(hostobj); i++ { + h = &hostobj[i] + var err error + f, err = Bopenr(h.file) + if f == nil { + Ctxt.Cursym = nil + Diag("cannot reopen %s: %v", h.pn, err) + Errorexit() + } + + Bseek(f, h.off, 0) + p = fmt.Sprintf("%s/%06d.o", tmpdir, i) + argv = append(argv, p) + w, err := os.Create(p) + if err != nil { + Ctxt.Cursym = nil + Diag("cannot create %s: %v", p, err) + Errorexit() + } + + length = int(h.length) + for length > 0 { + n = Bread(f, hostlink_buf) + if n <= 0 { + break + } + if n > length { + n = length + } + if _, err = w.Write(hostlink_buf[:n]); err != nil { + log.Fatal(err) + } + length -= n + } + + if err = w.Close(); err != nil { + Ctxt.Cursym = nil + Diag("cannot write %s: %v", p, err) + Errorexit() + } + + Bterm(f) + } + + argv = append(argv, fmt.Sprintf("%s/go.o", tmpdir)) + for i = 0; i < len(ldflag); i++ { + argv = append(argv, ldflag[i]) + } + + for _, p = range strings.Fields(extldflags) { + argv = append(argv, p) + + // clang, unlike GCC, passes -rdynamic to the linker + // even when linking with -static, causing a linker + // error when using GNU ld. So take out -rdynamic if + // we added it. We do it in this order, rather than + // only adding -rdynamic later, so that -extldflags + // can override -rdynamic without using -static. + if Iself && p == "-static" { + for i = range argv { + if argv[i] == "-rdynamic" { + argv[i] = "-static" + } + } + } + } + + if Debug['v'] != 0 { + fmt.Fprintf(&Bso, "host link:") + for i = range argv { + fmt.Fprintf(&Bso, " %v", plan9quote(argv[i])) + } + fmt.Fprintf(&Bso, "\n") + Bflush(&Bso) + } + + if out, err := exec.Command(argv[0], argv[1:]...).CombinedOutput(); err != nil { + Ctxt.Cursym = nil + Diag("%s: running %s failed: %v\n%s", os.Args[0], argv[0], err, out) + Errorexit() + } +} + +func ldobj(f *Biobuf, pkg string, length int64, pn string, file string, whence int) { + var line string + var c1 int + var c2 int + var c3 int + var c4 int + var magic uint32 + var import0 int64 + var import1 int64 + var eof int64 + var start int64 + var t string + + eof = Boffset(f) + length + + pn = pn + + start = Boffset(f) + c1 = Bgetc(f) + c2 = Bgetc(f) + c3 = Bgetc(f) + c4 = Bgetc(f) + Bseek(f, start, 0) + + magic = uint32(c1)<<24 | uint32(c2)<<16 | uint32(c3)<<8 | uint32(c4) + if magic == 0x7f454c46 { // \x7F E L F + ldhostobj(ldelf, f, pkg, length, pn, file) + return + } + + if magic&^1 == 0xfeedface || magic&^0x01000000 == 0xcefaedfe { + ldhostobj(ldmacho, f, pkg, length, pn, file) + return + } + + if c1 == 0x4c && c2 == 0x01 || c1 == 0x64 && c2 == 0x86 { + ldhostobj(ldpe, f, pkg, length, pn, file) + return + } + + /* check the header */ + line = Brdline(f, '\n') + + if line == "" { + if Blinelen(f) > 0 { + Diag("%s: not an object file", pn) + return + } + + goto eof + } + + if !strings.HasPrefix(line, "go object ") { + if strings.HasSuffix(pn, ".go") { + fmt.Printf("%cl: input %s is not .%c file (use %cg to compile .go files)\n", Thearch.Thechar, pn, Thearch.Thechar, Thearch.Thechar) + Errorexit() + } + + if line == Thestring { + // old header format: just $GOOS + Diag("%s: stale object file", pn) + + return + } + + Diag("%s: not an object file", pn) + + return + } + + // First, check that the basic goos, goarch, and version match. + t = fmt.Sprintf("%s %s %s ", goos, obj.Getgoarch(), obj.Getgoversion()) + + line = strings.TrimRight(line, "\n") + if !strings.HasPrefix(line[10:]+" ", t) && Debug['f'] == 0 { + Diag("%s: object is [%s] expected [%s]", pn, line[10:], t) + return + } + + // Second, check that longer lines match each other exactly, + // so that the Go compiler and write additional information + // that must be the same from run to run. + if len(line) >= len(t)+10 { + if theline == "" { + theline = line[10:] + } else if theline != line[10:] { + Diag("%s: object is [%s] expected [%s]", pn, line[10:], theline) + return + } + } + + /* skip over exports and other info -- ends with \n!\n */ + import0 = Boffset(f) + + c1 = '\n' // the last line ended in \n + c2 = Bgetc(f) + c3 = Bgetc(f) + for c1 != '\n' || c2 != '!' || c3 != '\n' { + c1 = c2 + c2 = c3 + c3 = Bgetc(f) + if c3 == Beof { + goto eof + } + } + + import1 = Boffset(f) + + Bseek(f, import0, 0) + ldpkg(f, pkg, import1-import0-2, pn, whence) // -2 for !\n + Bseek(f, import1, 0) + + ldobjfile(Ctxt, f, pkg, eof-Boffset(f), pn) + + return + +eof: + Diag("truncated object file: %s", pn) +} + +func zerosig(sp string) { + var s *LSym + + s = Linklookup(Ctxt, sp, 0) + s.Sig = 0 +} + +func mywhatsys() { + goroot = obj.Getgoroot() + goos = obj.Getgoos() + goarch = obj.Getgoarch() + + if !strings.HasPrefix(goarch, Thestring) { + log.Fatalf("cannot use %cc with GOARCH=%s", Thearch.Thechar, goarch) + } +} + +func pathchar() int { + return '/' +} + +var hunk []byte + +var nhunk uint32 + +const ( + NHUNK = 10 << 20 +) + +// Copied from ../gc/subr.c:/^pathtoprefix; must stay in sync. +/* + * Convert raw string to the prefix that will be used in the symbol table. + * Invalid bytes turn into %xx. Right now the only bytes that need + * escaping are %, ., and ", but we escape all control characters too. + * + * If you edit this, edit ../gc/subr.c:/^pathtoprefix too. + * If you edit this, edit ../../debug/goobj/read.go:/importPathToPrefix too. + */ +func pathtoprefix(s string) string { + slash := strings.LastIndex(s, "/") + for i := 0; i < len(s); i++ { + c := s[i] + if c <= ' ' || i >= slash && c == '.' || c == '%' || c == '"' || c >= 0x7F { + goto escape + } + } + return s + +escape: + var buf bytes.Buffer + for i := 0; i < len(s); i++ { + c := s[i] + if c <= ' ' || i >= slash && c == '.' || c == '%' || c == '"' || c >= 0x7F { + fmt.Fprintf(&buf, "%%%02x", c) + continue + } + buf.WriteByte(c) + } + return buf.String() +} + +func iconv(p string) string { + var fp string + + if p == "" { + fp += "" + return fp + } + + p = pathtoprefix(p) + fp += p + return fp +} + +func addsection(seg *Segment, name string, rwx int) *Section { + var l **Section + var sect *Section + + for l = &seg.Sect; *l != nil; l = &(*l).Next { + } + sect = new(Section) + sect.Rwx = uint8(rwx) + sect.Name = name + sect.Seg = seg + sect.Align = int32(Thearch.Ptrsize) // everything is at least pointer-aligned + *l = sect + return sect +} + +func Le16(b []byte) uint16 { + return uint16(b[0]) | uint16(b[1])<<8 +} + +func Le32(b []byte) uint32 { + return uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24 +} + +func Le64(b []byte) uint64 { + return uint64(Le32(b)) | uint64(Le32(b[4:]))<<32 +} + +func Be16(b []byte) uint16 { + return uint16(b[0])<<8 | uint16(b[1]) +} + +func Be32(b []byte) uint32 { + return uint32(b[0])<<24 | uint32(b[1])<<16 | uint32(b[2])<<8 | uint32(b[3]) +} + +func Be64(b []byte) uint64 { + return uint64(Be32(b))<<32 | uint64(Be32(b[4:])) +} + +var be = Endian{Be16, Be32, Be64} + +var le = Endian{Le16, Le32, Le64} + +type Chain struct { + sym *LSym + up *Chain + limit int +} + +var morestack *LSym + +var newstack *LSym + +// TODO: Record enough information in new object files to +// allow stack checks here. + +func haslinkregister() bool { + return Thearch.Thechar == '5' || Thearch.Thechar == '9' +} + +func callsize() int { + if haslinkregister() { + return 0 + } + return Thearch.Regsize +} + +func dostkcheck() { + var ch Chain + var s *LSym + + morestack = Linklookup(Ctxt, "runtime.morestack", 0) + newstack = Linklookup(Ctxt, "runtime.newstack", 0) + + // Every splitting function ensures that there are at least StackLimit + // bytes available below SP when the splitting prologue finishes. + // If the splitting function calls F, then F begins execution with + // at least StackLimit - callsize() bytes available. + // Check that every function behaves correctly with this amount + // of stack, following direct calls in order to piece together chains + // of non-splitting functions. + ch.up = nil + + ch.limit = obj.StackLimit - callsize() + + // Check every function, but do the nosplit functions in a first pass, + // to make the printed failure chains as short as possible. + for s = Ctxt.Textp; s != nil; s = s.Next { + // runtime.racesymbolizethunk is called from gcc-compiled C + // code running on the operating system thread stack. + // It uses more than the usual amount of stack but that's okay. + if s.Name == "runtime.racesymbolizethunk" { + continue + } + + if s.Nosplit != 0 { + Ctxt.Cursym = s + ch.sym = s + stkcheck(&ch, 0) + } + } + + for s = Ctxt.Textp; s != nil; s = s.Next { + if s.Nosplit == 0 { + Ctxt.Cursym = s + ch.sym = s + stkcheck(&ch, 0) + } + } +} + +func stkcheck(up *Chain, depth int) int { + var ch Chain + var ch1 Chain + var s *LSym + var limit int + var r *Reloc + var ri int + var endr int + var pcsp Pciter + + limit = up.limit + s = up.sym + + // Don't duplicate work: only need to consider each + // function at top of safe zone once. + if limit == obj.StackLimit-callsize() { + if s.Stkcheck != 0 { + return 0 + } + s.Stkcheck = 1 + } + + if depth > 100 { + Diag("nosplit stack check too deep") + stkbroke(up, 0) + return -1 + } + + if s.External != 0 || s.Pcln == nil { + // external function. + // should never be called directly. + // only diagnose the direct caller. + if depth == 1 && s.Type != SXREF { + Diag("call to external function %s", s.Name) + } + return -1 + } + + if limit < 0 { + stkbroke(up, limit) + return -1 + } + + // morestack looks like it calls functions, + // but it switches the stack pointer first. + if s == morestack { + return 0 + } + + ch.up = up + + // Walk through sp adjustments in function, consuming relocs. + ri = 0 + + endr = len(s.R) + for pciterinit(Ctxt, &pcsp, &s.Pcln.Pcsp); pcsp.done == 0; pciternext(&pcsp) { + // pcsp.value is in effect for [pcsp.pc, pcsp.nextpc). + + // Check stack size in effect for this span. + if int32(limit)-pcsp.value < 0 { + stkbroke(up, int(int32(limit)-pcsp.value)) + return -1 + } + + // Process calls in this span. + for ; ri < endr && uint32(s.R[ri].Off) < pcsp.nextpc; ri++ { + r = &s.R[ri] + switch r.Type { + // Direct call. + case R_CALL, + R_CALLARM, + R_CALLPOWER: + ch.limit = int(int32(limit) - pcsp.value - int32(callsize())) + + ch.sym = r.Sym + if stkcheck(&ch, depth+1) < 0 { + return -1 + } + + // If this is a call to morestack, we've just raised our limit back + // to StackLimit beyond the frame size. + if strings.HasPrefix(r.Sym.Name, "runtime.morestack") { + limit = int(obj.StackLimit + s.Locals) + if haslinkregister() { + limit += Thearch.Regsize + } + } + + // Indirect call. Assume it is a call to a splitting function, + // so we have to make sure it can call morestack. + // Arrange the data structures to report both calls, so that + // if there is an error, stkprint shows all the steps involved. + case R_CALLIND: + ch.limit = int(int32(limit) - pcsp.value - int32(callsize())) + + ch.sym = nil + ch1.limit = ch.limit - callsize() // for morestack in called prologue + ch1.up = &ch + ch1.sym = morestack + if stkcheck(&ch1, depth+2) < 0 { + return -1 + } + } + } + } + + return 0 +} + +func stkbroke(ch *Chain, limit int) { + Diag("nosplit stack overflow") + stkprint(ch, limit) +} + +func stkprint(ch *Chain, limit int) { + var name string + + if ch.sym != nil { + name = ch.sym.Name + } else { + name = "function pointer" + } + + if ch.up == nil { + // top of chain. ch->sym != nil. + if ch.sym.Nosplit != 0 { + fmt.Printf("\t%d\tassumed on entry to %s\n", ch.limit, name) + } else { + fmt.Printf("\t%d\tguaranteed after split check in %s\n", ch.limit, name) + } + } else { + stkprint(ch.up, ch.limit+callsize()) + if !haslinkregister() { + fmt.Printf("\t%d\ton entry to %s\n", ch.limit, name) + } + } + + if ch.limit != limit { + fmt.Printf("\t%d\tafter %s uses %d\n", limit, name, ch.limit-limit) + } +} + +func Yconv(s *LSym) string { + var fp string + + var fmt_ string + var i int + var str string + + if s == nil { + fp += fmt.Sprintf("") + } else { + fmt_ = "" + fmt_ += fmt.Sprintf("%s @0x%08x [%d]", s.Name, int64(s.Value), int64(s.Size)) + for i = 0; int64(i) < s.Size; i++ { + if i%8 == 0 { + fmt_ += fmt.Sprintf("\n\t0x%04x ", i) + } + fmt_ += fmt.Sprintf("%02x ", s.P[i]) + } + + fmt_ += fmt.Sprintf("\n") + for i = 0; i < len(s.R); i++ { + fmt_ += fmt.Sprintf("\t0x%04x[%x] %d %s[%x]\n", s.R[i].Off, s.R[i].Siz, s.R[i].Type, s.R[i].Sym.Name, int64(s.R[i].Add)) + } + + str = fmt_ + fp += str + } + + return fp +} + +func Cflush() { + Bflush(&coutbuf) +} + +func Cpos() int64 { + return Boffset(&coutbuf) +} + +func Cseek(p int64) { + Bseek(&coutbuf, p, 0) +} + +func Cwrite(p []byte) { + Bwrite(&coutbuf, p) +} + +func Cput(c uint8) { + Bputc(&coutbuf, c) +} + +func usage() { + fmt.Fprintf(os.Stderr, "usage: %cl [options] obj.%c\n", Thearch.Thechar, Thearch.Thechar) + obj.Flagprint(2) + Exit(2) +} + +func setheadtype(s string) { + var h int + + h = headtype(s) + if h < 0 { + fmt.Fprintf(os.Stderr, "unknown header type -H %s\n", s) + Errorexit() + } + + headstring = s + HEADTYPE = int32(headtype(s)) +} + +func setinterp(s string) { + Debug['I'] = 1 // denote cmdline interpreter override + interpreter = s +} + +func doversion() { + fmt.Printf("%cl version %s\n", Thearch.Thechar, obj.Getgoversion()) + Errorexit() +} + +func genasmsym(put func(*LSym, string, int, int64, int64, int, *LSym)) { + var a *Auto + var s *LSym + var off int32 + + // These symbols won't show up in the first loop below because we + // skip STEXT symbols. Normal STEXT symbols are emitted by walking textp. + s = Linklookup(Ctxt, "runtime.text", 0) + + if s.Type == STEXT { + put(s, s.Name, 'T', s.Value, s.Size, int(s.Version), nil) + } + s = Linklookup(Ctxt, "runtime.etext", 0) + if s.Type == STEXT { + put(s, s.Name, 'T', s.Value, s.Size, int(s.Version), nil) + } + + for s = Ctxt.Allsym; s != nil; s = s.Allsym { + if s.Hide != 0 || (s.Name[0] == '.' && s.Version == 0 && s.Name != ".rathole") { + continue + } + switch s.Type & SMASK { + case SCONST, + SRODATA, + SSYMTAB, + SPCLNTAB, + SDATA, + SNOPTRDATA, + SELFROSECT, + SMACHOGOT, + STYPE, + SSTRING, + SGOSTRING, + SWINDOWS: + if !s.Reachable { + continue + } + put(s, s.Name, 'D', Symaddr(s), s.Size, int(s.Version), s.Gotype) + continue + + case SBSS, + SNOPTRBSS: + if !s.Reachable { + continue + } + if len(s.P) > 0 { + Diag("%s should not be bss (size=%d type=%d special=%d)", s.Name, int(len(s.P)), s.Type, s.Special) + } + put(s, s.Name, 'B', Symaddr(s), s.Size, int(s.Version), s.Gotype) + continue + + case SFILE: + put(nil, s.Name, 'f', s.Value, 0, int(s.Version), nil) + continue + } + } + + for s = Ctxt.Textp; s != nil; s = s.Next { + put(s, s.Name, 'T', s.Value, s.Size, int(s.Version), s.Gotype) + + // NOTE(ality): acid can't produce a stack trace without .frame symbols + put(nil, ".frame", 'm', int64(s.Locals)+int64(Thearch.Ptrsize), 0, 0, nil) + + for a = s.Autom; a != nil; a = a.Link { + // Emit a or p according to actual offset, even if label is wrong. + // This avoids negative offsets, which cannot be encoded. + if a.Name != A_AUTO && a.Name != A_PARAM { + continue + } + + // compute offset relative to FP + if a.Name == A_PARAM { + off = a.Aoffset + } else { + off = a.Aoffset - int32(Thearch.Ptrsize) + } + + // FP + if off >= 0 { + put(nil, a.Asym.Name, 'p', int64(off), 0, 0, a.Gotype) + continue + } + + // SP + if off <= int32(-Thearch.Ptrsize) { + put(nil, a.Asym.Name, 'a', -(int64(off) + int64(Thearch.Ptrsize)), 0, 0, a.Gotype) + continue + } + } + } + + // Otherwise, off is addressing the saved program counter. + // Something underhanded is going on. Say nothing. + if Debug['v'] != 0 || Debug['n'] != 0 { + fmt.Fprintf(&Bso, "%5.2f symsize = %d\n", obj.Cputime(), uint32(Symsize)) + } + Bflush(&Bso) +} + +func Symaddr(s *LSym) int64 { + if !s.Reachable { + Diag("unreachable symbol in symaddr - %s", s.Name) + } + return s.Value +} + +func xdefine(p string, t int, v int64) { + var s *LSym + + s = Linklookup(Ctxt, p, 0) + s.Type = int16(t) + s.Value = v + s.Reachable = true + s.Special = 1 +} + +func datoff(addr int64) int64 { + if uint64(addr) >= Segdata.Vaddr { + return int64(uint64(addr) - Segdata.Vaddr + Segdata.Fileoff) + } + if uint64(addr) >= Segtext.Vaddr { + return int64(uint64(addr) - Segtext.Vaddr + Segtext.Fileoff) + } + Diag("datoff %#x", addr) + return 0 +} + +func Entryvalue() int64 { + var a string + var s *LSym + + a = INITENTRY + if a[0] >= '0' && a[0] <= '9' { + return atolwhex(a) + } + s = Linklookup(Ctxt, a, 0) + if s.Type == 0 { + return INITTEXT + } + if s.Type != STEXT { + Diag("entry not text: %s", s.Name) + } + return s.Value +} + +func undefsym(s *LSym) { + var i int + var r *Reloc + + Ctxt.Cursym = s + for i = 0; i < len(s.R); i++ { + r = &s.R[i] + if r.Sym == nil { // happens for some external ARM relocs + continue + } + if r.Sym.Type == Sxxx || r.Sym.Type == SXREF { + Diag("undefined: %s", r.Sym.Name) + } + if !r.Sym.Reachable { + Diag("use of unreachable symbol: %s", r.Sym.Name) + } + } +} + +func undef() { + var s *LSym + + for s = Ctxt.Textp; s != nil; s = s.Next { + undefsym(s) + } + for s = datap; s != nil; s = s.Next { + undefsym(s) + } + if nerrors > 0 { + Errorexit() + } +} + +func callgraph() { + var s *LSym + var r *Reloc + var i int + + if Debug['c'] == 0 { + return + } + + for s = Ctxt.Textp; s != nil; s = s.Next { + for i = 0; i < len(s.R); i++ { + r = &s.R[i] + if r.Sym == nil { + continue + } + if (r.Type == R_CALL || r.Type == R_CALLARM || r.Type == R_CALLPOWER) && r.Sym.Type == STEXT { + fmt.Fprintf(&Bso, "%s calls %s\n", s.Name, r.Sym.Name) + } + } + } +} + +func Diag(format string, args ...interface{}) { + tn := "" + sep := "" + if Ctxt.Cursym != nil { + tn = Ctxt.Cursym.Name + sep = ": " + } + fmt.Printf("%s%s%s\n", tn, sep, fmt.Sprintf(format, args...)) + + nerrors++ + if nerrors > 20 { + fmt.Printf("too many errors\n") + Errorexit() + } +} + +func checkgo() { + var s *LSym + var r *Reloc + var i int + var changed int + + if Debug['C'] == 0 { + return + } + + // TODO(rsc,khr): Eventually we want to get to no Go-called C functions at all, + // which would simplify this logic quite a bit. + + // Mark every Go-called C function with cfunc=2, recursively. + for { + changed = 0 + for s = Ctxt.Textp; s != nil; s = s.Next { + if s.Cfunc == 0 || (s.Cfunc == 2 && s.Nosplit != 0) { + for i = 0; i < len(s.R); i++ { + r = &s.R[i] + if r.Sym == nil { + continue + } + if (r.Type == R_CALL || r.Type == R_CALLARM) && r.Sym.Type == STEXT { + if r.Sym.Cfunc == 1 { + changed = 1 + r.Sym.Cfunc = 2 + } + } + } + } + } + if changed == 0 { + break + } + } + + // Complain about Go-called C functions that can split the stack + // (that can be preempted for garbage collection or trigger a stack copy). + for s = Ctxt.Textp; s != nil; s = s.Next { + if s.Cfunc == 0 || (s.Cfunc == 2 && s.Nosplit != 0) { + for i = 0; i < len(s.R); i++ { + r = &s.R[i] + if r.Sym == nil { + continue + } + if (r.Type == R_CALL || r.Type == R_CALLARM) && r.Sym.Type == STEXT { + if s.Cfunc == 0 && r.Sym.Cfunc == 2 && r.Sym.Nosplit == 0 { + fmt.Printf("Go %s calls C %s\n", s.Name, r.Sym.Name) + } else if s.Cfunc == 2 && s.Nosplit != 0 && r.Sym.Nosplit == 0 { + fmt.Printf("Go calls C %s calls %s\n", s.Name, r.Sym.Name) + } + } + } + } + } +} + +func Rnd(v int64, r int64) int64 { + var c int64 + + if r <= 0 { + return v + } + v += r - 1 + c = v % r + if c < 0 { + c += r + } + v -= c + return v +} diff --git a/src/cmd/internal/ld/link.go b/src/cmd/internal/ld/link.go new file mode 100644 index 0000000000..69702fb458 --- /dev/null +++ b/src/cmd/internal/ld/link.go @@ -0,0 +1,299 @@ +// Derived from Inferno utils/6l/l.h and related files. +// http://code.google.com/p/inferno-os/source/browse/utils/6l/l.h +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package ld + +import "encoding/binary" + +type LSym struct { + Name string + Extname string + Type int16 + Version int16 + Dupok uint8 + Cfunc uint8 + External uint8 + Nosplit uint8 + Reachable bool + Cgoexport uint8 + Special uint8 + Stkcheck uint8 + Hide uint8 + Leaf uint8 + Localentry uint8 + Onlist uint8 + Dynid int32 + Sig int32 + Plt int32 + Got int32 + Align int32 + Elfsym int32 + Args int32 + Locals int32 + Value int64 + Size int64 + Hash *LSym + Allsym *LSym + Next *LSym + Sub *LSym + Outer *LSym + Gotype *LSym + Reachparent *LSym + Queue *LSym + File string + Dynimplib string + Dynimpvers string + Sect interface{} + Autom *Auto + Pcln *Pcln + P []byte + R []Reloc +} + +type Reloc struct { + Off int32 + Siz uint8 + Done uint8 + Type int32 + Variant int32 + Add int64 + Xadd int64 + Sym *LSym + Xsym *LSym +} + +type Auto struct { + Asym *LSym + Link *Auto + Aoffset int32 + Name int16 + Gotype *LSym +} + +type Link struct { + Thechar int32 + Thestring string + Goarm int32 + Headtype int + Arch *LinkArch + Debugasm int32 + Debugvlog int32 + Bso *Biobuf + Windows int32 + Goroot string + Hash map[symVer]*LSym + Allsym *LSym + Nsymbol int32 + Tlsg *LSym + Libdir []string + Library []Library + Tlsoffset int + Diag func(string, ...interface{}) + Cursym *LSym + Version int + Textp *LSym + Etextp *LSym + Nhistfile int32 + Filesyms *LSym +} + +type LinkArch struct { + ByteOrder binary.ByteOrder + Name string + Thechar int + Endian int32 + Minlc int + Ptrsize int + Regsize int +} + +type Library struct { + Objref string + Srcref string + File string + Pkg string +} + +type Pcln struct { + Pcsp Pcdata + Pcfile Pcdata + Pcline Pcdata + Pcdata []Pcdata + Npcdata int + Funcdata []*LSym + Funcdataoff []int64 + Nfuncdata int + File []*LSym + Nfile int + Mfile int + Lastfile *LSym + Lastindex int +} + +type Pcdata struct { + P []byte +} + +type Pciter struct { + d Pcdata + p []byte + pc uint32 + nextpc uint32 + pcscale uint32 + value int32 + start int + done int +} + +// LSym.type +const ( + Sxxx = iota + STEXT + SELFRXSECT + STYPE + SSTRING + SGOSTRING + SGOFUNC + SRODATA + SFUNCTAB + STYPELINK + SSYMTAB + SPCLNTAB + SELFROSECT + SMACHOPLT + SELFSECT + SMACHO + SMACHOGOT + SWINDOWS + SELFGOT + SNOPTRDATA + SINITARR + SDATA + SBSS + SNOPTRBSS + STLSBSS + SXREF + SMACHOSYMSTR + SMACHOSYMTAB + SMACHOINDIRECTPLT + SMACHOINDIRECTGOT + SFILE + SFILEPATH + SCONST + SDYNIMPORT + SHOSTOBJ + SSUB = 1 << 8 + SMASK = SSUB - 1 + SHIDDEN = 1 << 9 +) + +// Reloc.type +const ( + R_ADDR = 1 + iota + R_ADDRPOWER + R_SIZE + R_CALL + R_CALLARM + R_CALLIND + R_CALLPOWER + R_CONST + R_PCREL + R_TLS + R_TLS_LE + R_TLS_IE + R_GOTOFF + R_PLT0 + R_PLT1 + R_PLT2 + R_USEFIELD + R_POWER_TOC +) + +// Reloc.variant +const ( + RV_NONE = iota + RV_POWER_LO + RV_POWER_HI + RV_POWER_HA + RV_POWER_DS + RV_CHECK_OVERFLOW = 1 << 8 + RV_TYPE_MASK = RV_CHECK_OVERFLOW - 1 +) + +// Auto.name +const ( + A_AUTO = 1 + iota + A_PARAM +) + +const ( + LINKHASH = 100003 +) + +// Pcdata iterator. +// for(pciterinit(ctxt, &it, &pcd); !it.done; pciternext(&it)) { it.value holds in [it.pc, it.nextpc) } + +// symbol version, incremented each time a file is loaded. +// version==1 is reserved for savehist. +const ( + HistVersion = 1 +) + +// Link holds the context for writing object code from a compiler +// to be linker input or for reading that input into the linker. + +const ( + LittleEndian = 0x04030201 + BigEndian = 0x01020304 +) + +// LinkArch is the definition of a single architecture. + +/* executable header types */ +const ( + Hunknown = 0 + iota + Hdarwin + Hdragonfly + Helf + Hfreebsd + Hlinux + Hnacl + Hnetbsd + Hopenbsd + Hplan9 + Hsolaris + Hwindows +) + +const ( + LinkAuto = 0 + iota + LinkInternal + LinkExternal +) diff --git a/src/cmd/internal/ld/macho.go b/src/cmd/internal/ld/macho.go new file mode 100644 index 0000000000..6349642b93 --- /dev/null +++ b/src/cmd/internal/ld/macho.go @@ -0,0 +1,892 @@ +// 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 ld + +import ( + "sort" + "strings" +) + +type MachoHdr struct { + cpu uint32 + subcpu uint32 +} + +type MachoSect struct { + name string + segname string + addr uint64 + size uint64 + off uint32 + align uint32 + reloc uint32 + nreloc uint32 + flag uint32 + res1 uint32 + res2 uint32 +} + +type MachoSeg struct { + name string + vsize uint64 + vaddr uint64 + fileoffset uint64 + filesize uint64 + prot1 uint32 + prot2 uint32 + nsect uint32 + msect uint32 + sect []MachoSect + flag uint32 +} + +type MachoLoad struct { + type_ uint32 + data []uint32 +} + +/* + * Total amount of space to reserve at the start of the file + * for Header, PHeaders, and SHeaders. + * May waste some. + */ +const ( + INITIAL_MACHO_HEADR = 4 * 1024 +) + +const ( + MACHO_CPU_AMD64 = 1<<24 | 7 + MACHO_CPU_386 = 7 + MACHO_SUBCPU_X86 = 3 + MACHO_CPU_ARM = 12 + MACHO_SUBCPU_ARM = 0 + MACHO_SUBCPU_ARMV7 = 9 + MACHO32SYMSIZE = 12 + MACHO64SYMSIZE = 16 + MACHO_X86_64_RELOC_UNSIGNED = 0 + MACHO_X86_64_RELOC_SIGNED = 1 + MACHO_X86_64_RELOC_BRANCH = 2 + MACHO_X86_64_RELOC_GOT_LOAD = 3 + MACHO_X86_64_RELOC_GOT = 4 + MACHO_X86_64_RELOC_SUBTRACTOR = 5 + MACHO_X86_64_RELOC_SIGNED_1 = 6 + MACHO_X86_64_RELOC_SIGNED_2 = 7 + MACHO_X86_64_RELOC_SIGNED_4 = 8 + MACHO_ARM_RELOC_VANILLA = 0 + MACHO_ARM_RELOC_BR24 = 5 + MACHO_GENERIC_RELOC_VANILLA = 0 + MACHO_FAKE_GOTPCREL = 100 +) + +// 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. + +// Mach-O file writing +// http://developer.apple.com/mac/library/DOCUMENTATION/DeveloperTools/Conceptual/MachORuntime/Reference/reference.html + +var macho64 bool + +var machohdr MachoHdr + +var load []MachoLoad + +var seg [16]MachoSeg + +var nload int + +var mload int + +var nseg int + +var ndebug int + +var nsect int + +const ( + SymKindLocal = 0 + iota + SymKindExtdef + SymKindUndef + NumSymKind +) + +var nkind [NumSymKind]int + +var sortsym []*LSym + +var nsortsym int + +// Amount of space left for adding load commands +// that refer to dynamic libraries. Because these have +// to go in the Mach-O header, we can't just pick a +// "big enough" header size. The initial header is +// one page, the non-dynamic library stuff takes +// up about 1300 bytes; we overestimate that as 2k. +var load_budget int = INITIAL_MACHO_HEADR - 2*1024 + +func Machoinit() { + switch Thearch.Thechar { + // 64-bit architectures + case '6', + '9': + macho64 = true + + // 32-bit architectures + default: + break + } +} + +func getMachoHdr() *MachoHdr { + return &machohdr +} + +func newMachoLoad(type_ uint32, ndata uint32) *MachoLoad { + if macho64 && (ndata&1 != 0) { + ndata++ + } + + load = append(load, MachoLoad{}) + l := &load[len(load)-1] + l.type_ = type_ + l.data = make([]uint32, ndata) + return l +} + +func newMachoSeg(name string, msect int) *MachoSeg { + var s *MachoSeg + + if nseg >= len(seg) { + Diag("too many segs") + Errorexit() + } + + s = &seg[nseg] + nseg++ + s.name = name + s.msect = uint32(msect) + s.sect = make([]MachoSect, msect) + return s +} + +func newMachoSect(seg *MachoSeg, name string, segname string) *MachoSect { + var s *MachoSect + + if seg.nsect >= seg.msect { + Diag("too many sects in segment %s", seg.name) + Errorexit() + } + + s = &seg.sect[seg.nsect] + seg.nsect++ + s.name = name + s.segname = segname + nsect++ + return s +} + +// Generic linking code. + +var dylib []string + +var ndylib int + +var linkoff int64 + +func machowrite() int { + var o1 int64 + var loadsize int + var i int + var j int + var s *MachoSeg + var t *MachoSect + var l *MachoLoad + + o1 = Cpos() + + loadsize = 4 * 4 * ndebug + for i = 0; i < len(load); i++ { + loadsize += 4 * (len(load[i].data) + 2) + } + if macho64 { + loadsize += 18 * 4 * nseg + loadsize += 20 * 4 * nsect + } else { + loadsize += 14 * 4 * nseg + loadsize += 17 * 4 * nsect + } + + if macho64 { + Thearch.Lput(0xfeedfacf) + } else { + Thearch.Lput(0xfeedface) + } + Thearch.Lput(machohdr.cpu) + Thearch.Lput(machohdr.subcpu) + if Linkmode == LinkExternal { + Thearch.Lput(1) /* file type - mach object */ + } else { + Thearch.Lput(2) /* file type - mach executable */ + } + Thearch.Lput(uint32(len(load)) + uint32(nseg) + uint32(ndebug)) + Thearch.Lput(uint32(loadsize)) + Thearch.Lput(1) /* flags - no undefines */ + if macho64 { + Thearch.Lput(0) /* reserved */ + } + + for i = 0; i < nseg; i++ { + s = &seg[i] + if macho64 { + Thearch.Lput(25) /* segment 64 */ + Thearch.Lput(72 + 80*s.nsect) + strnput(s.name, 16) + Thearch.Vput(s.vaddr) + Thearch.Vput(s.vsize) + Thearch.Vput(s.fileoffset) + Thearch.Vput(s.filesize) + Thearch.Lput(s.prot1) + Thearch.Lput(s.prot2) + Thearch.Lput(s.nsect) + Thearch.Lput(s.flag) + } else { + Thearch.Lput(1) /* segment 32 */ + Thearch.Lput(56 + 68*s.nsect) + strnput(s.name, 16) + Thearch.Lput(uint32(s.vaddr)) + Thearch.Lput(uint32(s.vsize)) + Thearch.Lput(uint32(s.fileoffset)) + Thearch.Lput(uint32(s.filesize)) + Thearch.Lput(s.prot1) + Thearch.Lput(s.prot2) + Thearch.Lput(s.nsect) + Thearch.Lput(s.flag) + } + + for j = 0; uint32(j) < s.nsect; j++ { + t = &s.sect[j] + if macho64 { + strnput(t.name, 16) + strnput(t.segname, 16) + Thearch.Vput(t.addr) + Thearch.Vput(t.size) + Thearch.Lput(t.off) + Thearch.Lput(t.align) + Thearch.Lput(t.reloc) + Thearch.Lput(t.nreloc) + Thearch.Lput(t.flag) + Thearch.Lput(t.res1) /* reserved */ + Thearch.Lput(t.res2) /* reserved */ + Thearch.Lput(0) /* reserved */ + } else { + strnput(t.name, 16) + strnput(t.segname, 16) + Thearch.Lput(uint32(t.addr)) + Thearch.Lput(uint32(t.size)) + Thearch.Lput(t.off) + Thearch.Lput(t.align) + Thearch.Lput(t.reloc) + Thearch.Lput(t.nreloc) + Thearch.Lput(t.flag) + Thearch.Lput(t.res1) /* reserved */ + Thearch.Lput(t.res2) /* reserved */ + } + } + } + + for i = 0; i < len(load); i++ { + l = &load[i] + Thearch.Lput(l.type_) + Thearch.Lput(4 * (uint32(len(l.data)) + 2)) + for j = 0; j < len(l.data); j++ { + Thearch.Lput(l.data[j]) + } + } + + return int(Cpos() - o1) +} + +func domacho() { + var s *LSym + + if Debug['d'] != 0 { + return + } + + // empirically, string table must begin with " \x00". + s = Linklookup(Ctxt, ".machosymstr", 0) + + s.Type = SMACHOSYMSTR + s.Reachable = true + Adduint8(Ctxt, s, ' ') + Adduint8(Ctxt, s, '\x00') + + s = Linklookup(Ctxt, ".machosymtab", 0) + s.Type = SMACHOSYMTAB + s.Reachable = true + + if Linkmode != LinkExternal { + s = Linklookup(Ctxt, ".plt", 0) // will be __symbol_stub + s.Type = SMACHOPLT + s.Reachable = true + + s = Linklookup(Ctxt, ".got", 0) // will be __nl_symbol_ptr + s.Type = SMACHOGOT + s.Reachable = true + s.Align = 4 + + s = Linklookup(Ctxt, ".linkedit.plt", 0) // indirect table for .plt + s.Type = SMACHOINDIRECTPLT + s.Reachable = true + + s = Linklookup(Ctxt, ".linkedit.got", 0) // indirect table for .got + s.Type = SMACHOINDIRECTGOT + s.Reachable = true + } +} + +func Machoadddynlib(lib string) { + // Will need to store the library name rounded up + // and 24 bytes of header metadata. If not enough + // space, grab another page of initial space at the + // beginning of the output file. + load_budget -= (len(lib)+7)/8*8 + 24 + + if load_budget < 0 { + HEADR += 4096 + INITTEXT += 4096 + load_budget += 4096 + } + + dylib = append(dylib, lib) +} + +func machoshbits(mseg *MachoSeg, sect *Section, segname string) { + var msect *MachoSect + var buf string + + buf = "__" + strings.Replace(sect.Name[1:], ".", "_", -1) + + msect = newMachoSect(mseg, buf, segname) + if sect.Rellen > 0 { + msect.reloc = uint32(sect.Reloff) + msect.nreloc = uint32(sect.Rellen / 8) + } + + for 1< sect.Seg.Vaddr+sect.Seg.Filelen-sect.Vaddr { + Diag("macho cannot represent section %s crossing data and bss", sect.Name) + } + msect.off = uint32(sect.Seg.Fileoff + sect.Vaddr - sect.Seg.Vaddr) + } else { + // zero fill + msect.off = 0 + + msect.flag |= 1 + } + + if sect.Rwx&1 != 0 { + msect.flag |= 0x400 /* has instructions */ + } + + if sect.Name == ".plt" { + msect.name = "__symbol_stub1" + msect.flag = 0x80000408 /* only instructions, code, symbol stubs */ + msect.res1 = 0 //nkind[SymKindLocal]; + msect.res2 = 6 + } + + if sect.Name == ".got" { + msect.name = "__nl_symbol_ptr" + msect.flag = 6 /* section with nonlazy symbol pointers */ + msect.res1 = uint32(Linklookup(Ctxt, ".linkedit.plt", 0).Size / 4) /* offset into indirect symbol table */ + } +} + +func Asmbmacho() { + var v int64 + var w int64 + var va int64 + var a int + var i int + var mh *MachoHdr + var ms *MachoSeg + var ml *MachoLoad + var sect *Section + + /* apple MACH */ + va = INITTEXT - int64(HEADR) + + mh = getMachoHdr() + switch Thearch.Thechar { + default: + Diag("unknown mach architecture") + Errorexit() + fallthrough + + case '5': + mh.cpu = MACHO_CPU_ARM + mh.subcpu = MACHO_SUBCPU_ARMV7 + + case '6': + mh.cpu = MACHO_CPU_AMD64 + mh.subcpu = MACHO_SUBCPU_X86 + + case '8': + mh.cpu = MACHO_CPU_386 + mh.subcpu = MACHO_SUBCPU_X86 + } + + ms = nil + if Linkmode == LinkExternal { + /* segment for entire file */ + ms = newMachoSeg("", 40) + + ms.fileoffset = Segtext.Fileoff + ms.filesize = Segdata.Fileoff + Segdata.Filelen - Segtext.Fileoff + } + + /* segment for zero page */ + if Linkmode != LinkExternal { + ms = newMachoSeg("__PAGEZERO", 0) + ms.vsize = uint64(va) + } + + /* text */ + v = Rnd(int64(uint64(HEADR)+Segtext.Length), int64(INITRND)) + + if Linkmode != LinkExternal { + ms = newMachoSeg("__TEXT", 20) + ms.vaddr = uint64(va) + ms.vsize = uint64(v) + ms.fileoffset = 0 + ms.filesize = uint64(v) + ms.prot1 = 7 + ms.prot2 = 5 + } + + for sect = Segtext.Sect; sect != nil; sect = sect.Next { + machoshbits(ms, sect, "__TEXT") + } + + /* data */ + if Linkmode != LinkExternal { + w = int64(Segdata.Length) + ms = newMachoSeg("__DATA", 20) + ms.vaddr = uint64(va) + uint64(v) + ms.vsize = uint64(w) + ms.fileoffset = uint64(v) + ms.filesize = Segdata.Filelen + ms.prot1 = 3 + ms.prot2 = 3 + } + + for sect = Segdata.Sect; sect != nil; sect = sect.Next { + machoshbits(ms, sect, "__DATA") + } + + if Linkmode != LinkExternal { + switch Thearch.Thechar { + default: + Diag("unknown macho architecture") + Errorexit() + fallthrough + + case '5': + ml = newMachoLoad(5, 17+2) /* unix thread */ + ml.data[0] = 1 /* thread type */ + ml.data[1] = 17 /* word count */ + ml.data[2+15] = uint32(Entryvalue()) /* start pc */ + + case '6': + ml = newMachoLoad(5, 42+2) /* unix thread */ + ml.data[0] = 4 /* thread type */ + ml.data[1] = 42 /* word count */ + ml.data[2+32] = uint32(Entryvalue()) /* start pc */ + ml.data[2+32+1] = uint32(Entryvalue() >> 16 >> 16) // hide >>32 for 8l + + case '8': + ml = newMachoLoad(5, 16+2) /* unix thread */ + ml.data[0] = 1 /* thread type */ + ml.data[1] = 16 /* word count */ + ml.data[2+10] = uint32(Entryvalue()) /* start pc */ + } + } + + if Debug['d'] == 0 { + var s1 *LSym + var s2 *LSym + var s3 *LSym + var s4 *LSym + + // must match domacholink below + s1 = Linklookup(Ctxt, ".machosymtab", 0) + + s2 = Linklookup(Ctxt, ".linkedit.plt", 0) + s3 = Linklookup(Ctxt, ".linkedit.got", 0) + s4 = Linklookup(Ctxt, ".machosymstr", 0) + + if Linkmode != LinkExternal { + ms = newMachoSeg("__LINKEDIT", 0) + ms.vaddr = uint64(va) + uint64(v) + uint64(Rnd(int64(Segdata.Length), int64(INITRND))) + ms.vsize = uint64(s1.Size) + uint64(s2.Size) + uint64(s3.Size) + uint64(s4.Size) + ms.fileoffset = uint64(linkoff) + ms.filesize = ms.vsize + ms.prot1 = 7 + ms.prot2 = 3 + } + + ml = newMachoLoad(2, 4) /* LC_SYMTAB */ + ml.data[0] = uint32(linkoff) /* symoff */ + ml.data[1] = uint32(nsortsym) /* nsyms */ + ml.data[2] = uint32(linkoff + s1.Size + s2.Size + s3.Size) /* stroff */ + ml.data[3] = uint32(s4.Size) /* strsize */ + + machodysymtab() + + if Linkmode != LinkExternal { + ml = newMachoLoad(14, 6) /* LC_LOAD_DYLINKER */ + ml.data[0] = 12 /* offset to string */ + stringtouint32(ml.data[1:], "/usr/lib/dyld") + + for i = 0; i < len(dylib); i++ { + ml = newMachoLoad(12, 4+(uint32(len(dylib[i]))+1+7)/8*2) /* LC_LOAD_DYLIB */ + ml.data[0] = 24 /* offset of string from beginning of load */ + ml.data[1] = 0 /* time stamp */ + ml.data[2] = 0 /* version */ + ml.data[3] = 0 /* compatibility version */ + stringtouint32(ml.data[4:], dylib[i]) + } + } + } + + // TODO: dwarf headers go in ms too + if Debug['s'] == 0 && Linkmode != LinkExternal { + dwarfaddmachoheaders() + } + + a = machowrite() + if int32(a) > HEADR { + Diag("HEADR too small: %d > %d", a, HEADR) + } +} + +func symkind(s *LSym) int { + if s.Type == SDYNIMPORT { + return SymKindUndef + } + if s.Cgoexport != 0 { + return SymKindExtdef + } + return SymKindLocal +} + +func addsym(s *LSym, name string, type_ int, addr int64, size int64, ver int, gotype *LSym) { + if s == nil { + return + } + + switch type_ { + default: + return + + case 'D', + 'B', + 'T': + break + } + + if sortsym != nil { + sortsym[nsortsym] = s + nkind[symkind(s)]++ + } + + nsortsym++ +} + +type machoscmp []*LSym + +func (x machoscmp) Len() int { + return len(x) +} + +func (x machoscmp) Swap(i, j int) { + x[i], x[j] = x[j], x[i] +} + +func (x machoscmp) Less(i, j int) bool { + var s1 *LSym + var s2 *LSym + var k1 int + var k2 int + + s1 = x[i] + s2 = x[j] + + k1 = symkind(s1) + k2 = symkind(s2) + if k1 != k2 { + return k1-k2 < 0 + } + + return stringsCompare(s1.Extname, s2.Extname) < 0 +} + +func machogenasmsym(put func(*LSym, string, int, int64, int64, int, *LSym)) { + var s *LSym + + genasmsym(put) + for s = Ctxt.Allsym; s != nil; s = s.Allsym { + if s.Type == SDYNIMPORT || s.Type == SHOSTOBJ { + if s.Reachable { + put(s, "", 'D', 0, 0, 0, nil) + } + } + } +} + +func machosymorder() { + var i int + + // On Mac OS X Mountain Lion, we must sort exported symbols + // So we sort them here and pre-allocate dynid for them + // See http://golang.org/issue/4029 + for i = 0; i < len(dynexp); i++ { + dynexp[i].Reachable = true + } + machogenasmsym(addsym) + sortsym = make([]*LSym, nsortsym) + nsortsym = 0 + machogenasmsym(addsym) + sort.Sort(machoscmp(sortsym[:nsortsym])) + for i = 0; i < nsortsym; i++ { + sortsym[i].Dynid = int32(i) + } +} + +func machosymtab() { + var i int + var symtab *LSym + var symstr *LSym + var s *LSym + var o *LSym + var p string + + symtab = Linklookup(Ctxt, ".machosymtab", 0) + symstr = Linklookup(Ctxt, ".machosymstr", 0) + + for i = 0; i < nsortsym; i++ { + s = sortsym[i] + Adduint32(Ctxt, symtab, uint32(symstr.Size)) + + // Only add _ to C symbols. Go symbols have dot in the name. + if !strings.Contains(s.Extname, ".") { + Adduint8(Ctxt, symstr, '_') + } + + // replace "·" as ".", because DTrace cannot handle it. + if !strings.Contains(s.Extname, "·") { + Addstring(symstr, s.Extname) + } else { + for p = s.Extname; p != ""; p = p[1:] { + if uint8(p[0]) == 0xc2 && uint8((p[1:])[0]) == 0xb7 { + Adduint8(Ctxt, symstr, '.') + p = p[1:] + } else { + Adduint8(Ctxt, symstr, uint8(p[0])) + } + } + + Adduint8(Ctxt, symstr, '\x00') + } + + if s.Type == SDYNIMPORT || s.Type == SHOSTOBJ { + Adduint8(Ctxt, symtab, 0x01) // type N_EXT, external symbol + Adduint8(Ctxt, symtab, 0) // no section + Adduint16(Ctxt, symtab, 0) // desc + adduintxx(Ctxt, symtab, 0, Thearch.Ptrsize) // no value + } else { + if s.Cgoexport != 0 { + Adduint8(Ctxt, symtab, 0x0f) + } else { + Adduint8(Ctxt, symtab, 0x0e) + } + o = s + for o.Outer != nil { + o = o.Outer + } + if o.Sect == nil { + Diag("missing section for %s", s.Name) + Adduint8(Ctxt, symtab, 0) + } else { + Adduint8(Ctxt, symtab, uint8((o.Sect.(*Section)).Extnum)) + } + Adduint16(Ctxt, symtab, 0) // desc + adduintxx(Ctxt, symtab, uint64(Symaddr(s)), Thearch.Ptrsize) + } + } +} + +func machodysymtab() { + var n int + var ml *MachoLoad + var s1 *LSym + var s2 *LSym + var s3 *LSym + + ml = newMachoLoad(11, 18) /* LC_DYSYMTAB */ + + n = 0 + ml.data[0] = uint32(n) /* ilocalsym */ + ml.data[1] = uint32(nkind[SymKindLocal]) /* nlocalsym */ + n += nkind[SymKindLocal] + + ml.data[2] = uint32(n) /* iextdefsym */ + ml.data[3] = uint32(nkind[SymKindExtdef]) /* nextdefsym */ + n += nkind[SymKindExtdef] + + ml.data[4] = uint32(n) /* iundefsym */ + ml.data[5] = uint32(nkind[SymKindUndef]) /* nundefsym */ + + ml.data[6] = 0 /* tocoffset */ + ml.data[7] = 0 /* ntoc */ + ml.data[8] = 0 /* modtaboff */ + ml.data[9] = 0 /* nmodtab */ + ml.data[10] = 0 /* extrefsymoff */ + ml.data[11] = 0 /* nextrefsyms */ + + // must match domacholink below + s1 = Linklookup(Ctxt, ".machosymtab", 0) + + s2 = Linklookup(Ctxt, ".linkedit.plt", 0) + s3 = Linklookup(Ctxt, ".linkedit.got", 0) + ml.data[12] = uint32(linkoff + s1.Size) /* indirectsymoff */ + ml.data[13] = uint32((s2.Size + s3.Size) / 4) /* nindirectsyms */ + + ml.data[14] = 0 /* extreloff */ + ml.data[15] = 0 /* nextrel */ + ml.data[16] = 0 /* locreloff */ + ml.data[17] = 0 /* nlocrel */ +} + +func Domacholink() int64 { + var size int + var s1 *LSym + var s2 *LSym + var s3 *LSym + var s4 *LSym + + machosymtab() + + // write data that will be linkedit section + s1 = Linklookup(Ctxt, ".machosymtab", 0) + + s2 = Linklookup(Ctxt, ".linkedit.plt", 0) + s3 = Linklookup(Ctxt, ".linkedit.got", 0) + s4 = Linklookup(Ctxt, ".machosymstr", 0) + + // Force the linkedit section to end on a 16-byte + // boundary. This allows pure (non-cgo) Go binaries + // to be code signed correctly. + // + // Apple's codesign_allocate (a helper utility for + // the codesign utility) can do this fine itself if + // it is run on a dynamic Mach-O binary. However, + // when it is run on a pure (non-cgo) Go binary, where + // the linkedit section is mostly empty, it fails to + // account for the extra padding that it itself adds + // when adding the LC_CODE_SIGNATURE load command + // (which must be aligned on a 16-byte boundary). + // + // By forcing the linkedit section to end on a 16-byte + // boundary, codesign_allocate will not need to apply + // any alignment padding itself, working around the + // issue. + for s4.Size%16 != 0 { + Adduint8(Ctxt, s4, 0) + } + + size = int(s1.Size + s2.Size + s3.Size + s4.Size) + + if size > 0 { + linkoff = Rnd(int64(uint64(HEADR)+Segtext.Length), int64(INITRND)) + Rnd(int64(Segdata.Filelen), int64(INITRND)) + Rnd(int64(Segdwarf.Filelen), int64(INITRND)) + Cseek(linkoff) + + Cwrite(s1.P[:s1.Size]) + Cwrite(s2.P[:s2.Size]) + Cwrite(s3.P[:s3.Size]) + Cwrite(s4.P[:s4.Size]) + } + + return Rnd(int64(size), int64(INITRND)) +} + +func machorelocsect(sect *Section, first *LSym) { + var sym *LSym + var eaddr int32 + var ri int + var r *Reloc + + // If main section has no bits, nothing to relocate. + if sect.Vaddr >= sect.Seg.Vaddr+sect.Seg.Filelen { + return + } + + sect.Reloff = uint64(Cpos()) + for sym = first; sym != nil; sym = sym.Next { + if !sym.Reachable { + continue + } + if uint64(sym.Value) >= sect.Vaddr { + break + } + } + + eaddr = int32(sect.Vaddr + sect.Length) + for ; sym != nil; sym = sym.Next { + if !sym.Reachable { + continue + } + if sym.Value >= int64(eaddr) { + break + } + Ctxt.Cursym = sym + + for ri = 0; ri < len(sym.R); ri++ { + r = &sym.R[ri] + if r.Done != 0 { + continue + } + if Thearch.Machoreloc1(r, int64(uint64(sym.Value+int64(r.Off))-sect.Vaddr)) < 0 { + Diag("unsupported obj reloc %d/%d to %s", r.Type, r.Siz, r.Sym.Name) + } + } + } + + sect.Rellen = uint64(Cpos()) - sect.Reloff +} + +func Machoemitreloc() { + var sect *Section + + for Cpos()&7 != 0 { + Cput(0) + } + + machorelocsect(Segtext.Sect, Ctxt.Textp) + for sect = Segtext.Sect.Next; sect != nil; sect = sect.Next { + machorelocsect(sect, datap) + } + for sect = Segdata.Sect; sect != nil; sect = sect.Next { + machorelocsect(sect, datap) + } +} diff --git a/src/cmd/internal/ld/objfile.go b/src/cmd/internal/ld/objfile.go new file mode 100644 index 0000000000..98b7ba6c89 --- /dev/null +++ b/src/cmd/internal/ld/objfile.go @@ -0,0 +1,366 @@ +// Copyright 2013 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 ld + +import ( + "bytes" + "fmt" + "log" + "strconv" + "strings" +) + +var startmagic string = "\x00\x00go13ld" + +var endmagic string = "\xff\xffgo13ld" + +func ldobjfile(ctxt *Link, f *Biobuf, pkg string, length int64, pn string) { + var c int + var buf [8]uint8 + var start int64 + var lib string + + start = Boffset(f) + ctxt.Version++ + buf = [8]uint8{} + Bread(f, buf[:]) + if string(buf[:]) != startmagic { + log.Fatalf("%s: invalid file start %x %x %x %x %x %x %x %x", pn, buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7]) + } + c = Bgetc(f) + if c != 1 { + log.Fatalf("%s: invalid file version number %d", pn, c) + } + + for { + lib = rdstring(f) + if lib == "" { + break + } + addlib(ctxt, pkg, pn, lib) + } + + for { + c = Bgetc(f) + Bungetc(f) + if c == 0xff { + break + } + readsym(ctxt, f, pkg, pn) + } + + buf = [8]uint8{} + Bread(f, buf[:]) + if string(buf[:]) != endmagic { + log.Fatalf("%s: invalid file end", pn) + } + + if Boffset(f) != start+length { + log.Fatalf("%s: unexpected end at %d, want %d", pn, int64(Boffset(f)), int64(start+length)) + } +} + +var readsym_ndup int + +func readsym(ctxt *Link, f *Biobuf, pkg string, pn string) { + var i int + var j int + var c int + var t int + var v int + var n int + var nreloc int + var size int + var dupok int + var name string + var data []byte + var r *Reloc + var s *LSym + var dup *LSym + var typ *LSym + var pc *Pcln + var a *Auto + + if Bgetc(f) != 0xfe { + log.Fatalf("readsym out of sync") + } + t = int(rdint(f)) + name = expandpkg(rdstring(f), pkg) + v = int(rdint(f)) + if v != 0 && v != 1 { + log.Fatalf("invalid symbol version %d", v) + } + dupok = int(rdint(f)) + dupok &= 1 + size = int(rdint(f)) + typ = rdsym(ctxt, f, pkg) + rddata(f, &data) + nreloc = int(rdint(f)) + + if v != 0 { + v = ctxt.Version + } + s = Linklookup(ctxt, name, v) + dup = nil + if s.Type != 0 && s.Type != SXREF { + if (t == SDATA || t == SBSS || t == SNOPTRBSS) && len(data) == 0 && nreloc == 0 { + if s.Size < int64(size) { + s.Size = int64(size) + } + if typ != nil && s.Gotype == nil { + s.Gotype = typ + } + return + } + + if (s.Type == SDATA || s.Type == SBSS || s.Type == SNOPTRBSS) && len(s.P) == 0 && len(s.R) == 0 { + goto overwrite + } + if s.Type != SBSS && s.Type != SNOPTRBSS && dupok == 0 && s.Dupok == 0 { + log.Fatalf("duplicate symbol %s (types %d and %d) in %s and %s", s.Name, s.Type, t, s.File, pn) + } + if len(s.P) > 0 { + dup = s + s = linknewsym(ctxt, ".dup", readsym_ndup) + readsym_ndup++ // scratch + } + } + +overwrite: + s.File = pkg + s.Dupok = uint8(dupok) + if t == SXREF { + log.Fatalf("bad sxref") + } + if t == 0 { + log.Fatalf("missing type for %s in %s", name, pn) + } + if t == SBSS && (s.Type == SRODATA || s.Type == SNOPTRBSS) { + t = int(s.Type) + } + s.Type = int16(t) + if s.Size < int64(size) { + s.Size = int64(size) + } + if typ != nil { // if bss sym defined multiple times, take type from any one def + s.Gotype = typ + } + if dup != nil && typ != nil { + dup.Gotype = typ + } + s.P = data + s.P = s.P[:len(data)] + if nreloc > 0 { + s.R = make([]Reloc, nreloc) + s.R = s.R[:nreloc] + for i = 0; i < nreloc; i++ { + r = &s.R[i] + r.Off = int32(rdint(f)) + r.Siz = uint8(rdint(f)) + r.Type = int32(rdint(f)) + r.Add = rdint(f) + r.Xadd = rdint(f) + r.Sym = rdsym(ctxt, f, pkg) + r.Xsym = rdsym(ctxt, f, pkg) + } + } + + if len(s.P) > 0 && dup != nil && len(dup.P) > 0 && strings.HasPrefix(s.Name, "gclocals·") { + // content-addressed garbage collection liveness bitmap symbol. + // double check for hash collisions. + if !bytes.Equal(s.P, dup.P) { + log.Fatalf("dupok hash collision for %s in %s and %s", s.Name, s.File, pn) + } + } + + if s.Type == STEXT { + s.Args = int32(rdint(f)) + s.Locals = int32(rdint(f)) + s.Nosplit = uint8(rdint(f)) + v = int(rdint(f)) + s.Leaf = uint8(v & 1) + s.Cfunc = uint8(v & 2) + n = int(rdint(f)) + for i = 0; i < n; i++ { + a = new(Auto) + a.Asym = rdsym(ctxt, f, pkg) + a.Aoffset = int32(rdint(f)) + a.Name = int16(rdint(f)) + a.Gotype = rdsym(ctxt, f, pkg) + a.Link = s.Autom + s.Autom = a + } + + s.Pcln = new(Pcln) + pc = s.Pcln + rddata(f, &pc.Pcsp.P) + rddata(f, &pc.Pcfile.P) + rddata(f, &pc.Pcline.P) + n = int(rdint(f)) + pc.Pcdata = make([]Pcdata, n) + pc.Npcdata = n + for i = 0; i < n; i++ { + rddata(f, &pc.Pcdata[i].P) + } + n = int(rdint(f)) + pc.Funcdata = make([]*LSym, n) + pc.Funcdataoff = make([]int64, n) + pc.Nfuncdata = n + for i = 0; i < n; i++ { + pc.Funcdata[i] = rdsym(ctxt, f, pkg) + } + for i = 0; i < n; i++ { + pc.Funcdataoff[i] = rdint(f) + } + n = int(rdint(f)) + pc.File = make([]*LSym, n) + pc.Nfile = n + for i = 0; i < n; i++ { + pc.File[i] = rdsym(ctxt, f, pkg) + } + + if dup == nil { + if s.Onlist != 0 { + log.Fatalf("symbol %s listed multiple times", s.Name) + } + s.Onlist = 1 + if ctxt.Etextp != nil { + ctxt.Etextp.Next = s + } else { + ctxt.Textp = s + } + ctxt.Etextp = s + } + } + + if ctxt.Debugasm != 0 { + fmt.Fprintf(ctxt.Bso, "%s ", s.Name) + if s.Version != 0 { + fmt.Fprintf(ctxt.Bso, "v=%d ", s.Version) + } + if s.Type != 0 { + fmt.Fprintf(ctxt.Bso, "t=%d ", s.Type) + } + if s.Dupok != 0 { + fmt.Fprintf(ctxt.Bso, "dupok ") + } + if s.Cfunc != 0 { + fmt.Fprintf(ctxt.Bso, "cfunc ") + } + if s.Nosplit != 0 { + fmt.Fprintf(ctxt.Bso, "nosplit ") + } + fmt.Fprintf(ctxt.Bso, "size=%d value=%d", int64(s.Size), int64(s.Value)) + if s.Type == STEXT { + fmt.Fprintf(ctxt.Bso, " args=%#x locals=%#x", uint64(s.Args), uint64(s.Locals)) + } + fmt.Fprintf(ctxt.Bso, "\n") + for i = 0; i < len(s.P); { + fmt.Fprintf(ctxt.Bso, "\t%#04x", uint(i)) + for j = i; j < i+16 && j < len(s.P); j++ { + fmt.Fprintf(ctxt.Bso, " %02x", s.P[j]) + } + for ; j < i+16; j++ { + fmt.Fprintf(ctxt.Bso, " ") + } + fmt.Fprintf(ctxt.Bso, " ") + for j = i; j < i+16 && j < len(s.P); j++ { + c = int(s.P[j]) + if ' ' <= c && c <= 0x7e { + fmt.Fprintf(ctxt.Bso, "%c", c) + } else { + fmt.Fprintf(ctxt.Bso, ".") + } + } + + fmt.Fprintf(ctxt.Bso, "\n") + i += 16 + } + + for i = 0; i < len(s.R); i++ { + r = &s.R[i] + fmt.Fprintf(ctxt.Bso, "\trel %d+%d t=%d %s+%d\n", int(r.Off), r.Siz, r.Type, r.Sym.Name, int64(r.Add)) + } + } +} + +func rdint(f *Biobuf) int64 { + var c int + var uv uint64 + var shift int + + uv = 0 + for shift = 0; ; shift += 7 { + if shift >= 64 { + log.Fatalf("corrupt input") + } + c = Bgetc(f) + uv |= uint64(c&0x7F) << uint(shift) + if c&0x80 == 0 { + break + } + } + + return int64(uv>>1) ^ (int64(uint64(uv)<<63) >> 63) +} + +func rdstring(f *Biobuf) string { + n := rdint(f) + p := make([]byte, n) + Bread(f, p) + return string(p) +} + +func rddata(f *Biobuf, pp *[]byte) { + n := rdint(f) + *pp = make([]byte, n) + Bread(f, *pp) +} + +var symbuf []byte + +func rdsym(ctxt *Link, f *Biobuf, pkg string) *LSym { + var n int + var v int + var p string + var s *LSym + + n = int(rdint(f)) + if n == 0 { + rdint(f) + return nil + } + + if len(symbuf) < n { + symbuf = make([]byte, n) + } + Bread(f, symbuf[:n]) + p = string(symbuf[:n]) + v = int(rdint(f)) + if v != 0 { + v = ctxt.Version + } + s = Linklookup(ctxt, expandpkg(p, pkg), v) + + if v == 0 && s.Name[0] == '$' && s.Type == 0 { + if strings.HasPrefix(s.Name, "$f32.") { + var i32 int32 + x, _ := strconv.ParseUint(s.Name[5:], 16, 32) + i32 = int32(x) + s.Type = SRODATA + Adduint32(ctxt, s, uint32(i32)) + s.Reachable = false + } else if strings.HasPrefix(s.Name, "$f64.") || strings.HasPrefix(s.Name, "$i64.") { + var i64 int64 + x, _ := strconv.ParseUint(s.Name[5:], 16, 64) + i64 = int64(x) + s.Type = SRODATA + Adduint64(ctxt, s, uint64(i64)) + s.Reachable = false + } + } + + return s +} diff --git a/src/cmd/internal/ld/pcln.go b/src/cmd/internal/ld/pcln.go new file mode 100644 index 0000000000..2900664922 --- /dev/null +++ b/src/cmd/internal/ld/pcln.go @@ -0,0 +1,487 @@ +// Copyright 2013 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 ld + +import ( + "cmd/internal/obj" + "fmt" + "log" +) + +// funcpctab writes to dst a pc-value table mapping the code in func to the values +// returned by valfunc parameterized by arg. The invocation of valfunc to update the +// current value is, for each p, +// +// val = valfunc(func, val, p, 0, arg); +// record val as value at p->pc; +// val = valfunc(func, val, p, 1, arg); +// +// where func is the function, val is the current value, p is the instruction being +// considered, and arg can be used to further parameterize valfunc. + +// pctofileline computes either the file number (arg == 0) +// or the line number (arg == 1) to use at p. +// Because p->lineno applies to p, phase == 0 (before p) +// takes care of the update. + +// pctospadj computes the sp adjustment in effect. +// It is oldval plus any adjustment made by p itself. +// The adjustment by p takes effect only after p, so we +// apply the change during phase == 1. + +// pctopcdata computes the pcdata value in effect at p. +// A PCDATA instruction sets the value in effect at future +// non-PCDATA instructions. +// Since PCDATA instructions have no width in the final code, +// it does not matter which phase we use for the update. + +// iteration over encoded pcdata tables. + +func getvarint(pp *[]byte) uint32 { + var p []byte + var shift int + var v uint32 + + v = 0 + p = *pp + for shift = 0; ; shift += 7 { + v |= uint32(p[0]&0x7F) << uint(shift) + tmp4 := p + p = p[1:] + if tmp4[0]&0x80 == 0 { + break + } + } + + *pp = p + return v +} + +func pciternext(it *Pciter) { + var v uint32 + var dv int32 + + it.pc = it.nextpc + if it.done != 0 { + return + } + if -cap(it.p) >= -cap(it.d.P[len(it.d.P):]) { + it.done = 1 + return + } + + // value delta + v = getvarint(&it.p) + + if v == 0 && it.start == 0 { + it.done = 1 + return + } + + it.start = 0 + dv = int32(v>>1) ^ (int32(v<<31) >> 31) + it.value += dv + + // pc delta + v = getvarint(&it.p) + + it.nextpc = it.pc + v*it.pcscale +} + +func pciterinit(ctxt *Link, it *Pciter, d *Pcdata) { + it.d = *d + it.p = it.d.P + it.pc = 0 + it.nextpc = 0 + it.value = -1 + it.start = 1 + it.done = 0 + it.pcscale = uint32(ctxt.Arch.Minlc) + pciternext(it) +} + +// Copyright 2013 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. + +func addvarint(d *Pcdata, val uint32) { + var n int32 + var v uint32 + var p []byte + + n = 0 + for v = val; v >= 0x80; v >>= 7 { + n++ + } + n++ + + old := len(d.P) + for cap(d.P) < len(d.P)+int(n) { + d.P = append(d.P[:cap(d.P)], 0) + } + d.P = d.P[:old+int(n)] + + p = d.P[old:] + for v = val; v >= 0x80; v >>= 7 { + p[0] = byte(v | 0x80) + p = p[1:] + } + p[0] = byte(v) +} + +func addpctab(ftab *LSym, off int32, d *Pcdata) int32 { + var start int32 + + start = int32(len(ftab.P)) + Symgrow(Ctxt, ftab, int64(start)+int64(len(d.P))) + copy(ftab.P[start:], d.P) + + return int32(setuint32(Ctxt, ftab, int64(off), uint32(start))) +} + +func ftabaddstring(ftab *LSym, s string) int32 { + var n int32 + var start int32 + + n = int32(len(s)) + 1 + start = int32(len(ftab.P)) + Symgrow(Ctxt, ftab, int64(start)+int64(n)+1) + copy(ftab.P[start:], s) + return start +} + +func renumberfiles(ctxt *Link, files []*LSym, d *Pcdata) { + var i int + var f *LSym + var out Pcdata + var it Pciter + var v uint32 + var oldval int32 + var newval int32 + var val int32 + var dv int32 + + // Give files numbers. + for i = 0; i < len(files); i++ { + f = files[i] + if f.Type != SFILEPATH { + ctxt.Nhistfile++ + f.Value = int64(ctxt.Nhistfile) + f.Type = SFILEPATH + f.Next = ctxt.Filesyms + ctxt.Filesyms = f + } + } + + newval = -1 + out = Pcdata{} + + for pciterinit(ctxt, &it, d); it.done == 0; pciternext(&it) { + // value delta + oldval = it.value + + if oldval == -1 { + val = -1 + } else { + if oldval < 0 || oldval >= int32(len(files)) { + log.Fatalf("bad pcdata %d", oldval) + } + val = int32(files[oldval].Value) + } + + dv = val - newval + newval = val + v = (uint32(dv) << 1) ^ uint32(int32(dv>>31)) + addvarint(&out, v) + + // pc delta + addvarint(&out, (it.nextpc-it.pc)/it.pcscale) + } + + // terminating value delta + addvarint(&out, 0) + + *d = out +} + +func container(s *LSym) int { + // We want to generate func table entries only for the "lowest level" symbols, + // not containers of subsymbols. + if s != nil && s.Sub != nil { + return 1 + } + return 0 +} + +// pclntab initializes the pclntab symbol with +// runtime function and file name information. + +var pclntab_zpcln Pcln + +func pclntab() { + var i int32 + var nfunc int32 + var start int32 + var funcstart int32 + var ftab *LSym + var s *LSym + var last *LSym + var off int32 + var end int32 + var frameptrsize int32 + var funcdata_bytes int64 + var pcln *Pcln + var it Pciter + + funcdata_bytes = 0 + ftab = Linklookup(Ctxt, "runtime.pclntab", 0) + ftab.Type = SPCLNTAB + ftab.Reachable = true + + // See golang.org/s/go12symtab for the format. Briefly: + // 8-byte header + // nfunc [thearch.ptrsize bytes] + // function table, alternating PC and offset to func struct [each entry thearch.ptrsize bytes] + // end PC [thearch.ptrsize bytes] + // offset to file table [4 bytes] + nfunc = 0 + + for Ctxt.Cursym = Ctxt.Textp; Ctxt.Cursym != nil; Ctxt.Cursym = Ctxt.Cursym.Next { + if container(Ctxt.Cursym) == 0 { + nfunc++ + } + } + + Symgrow(Ctxt, ftab, 8+int64(Thearch.Ptrsize)+int64(nfunc)*2*int64(Thearch.Ptrsize)+int64(Thearch.Ptrsize)+4) + setuint32(Ctxt, ftab, 0, 0xfffffffb) + setuint8(Ctxt, ftab, 6, uint8(Thearch.Minlc)) + setuint8(Ctxt, ftab, 7, uint8(Thearch.Ptrsize)) + setuintxx(Ctxt, ftab, 8, uint64(nfunc), int64(Thearch.Ptrsize)) + + nfunc = 0 + last = nil + for Ctxt.Cursym = Ctxt.Textp; Ctxt.Cursym != nil; Ctxt.Cursym = Ctxt.Cursym.Next { + last = Ctxt.Cursym + if container(Ctxt.Cursym) != 0 { + continue + } + pcln = Ctxt.Cursym.Pcln + if pcln == nil { + pcln = &pclntab_zpcln + } + + funcstart = int32(len(ftab.P)) + funcstart += int32(-len(ftab.P)) & (int32(Thearch.Ptrsize) - 1) + + setaddr(Ctxt, ftab, 8+int64(Thearch.Ptrsize)+int64(nfunc)*2*int64(Thearch.Ptrsize), Ctxt.Cursym) + setuintxx(Ctxt, ftab, 8+int64(Thearch.Ptrsize)+int64(nfunc)*2*int64(Thearch.Ptrsize)+int64(Thearch.Ptrsize), uint64(funcstart), int64(Thearch.Ptrsize)) + + // fixed size of struct, checked below + off = funcstart + + end = funcstart + int32(Thearch.Ptrsize) + 3*4 + 5*4 + int32(pcln.Npcdata)*4 + int32(pcln.Nfuncdata)*int32(Thearch.Ptrsize) + if pcln.Nfuncdata > 0 && (end&int32(Thearch.Ptrsize-1) != 0) { + end += 4 + } + Symgrow(Ctxt, ftab, int64(end)) + + // entry uintptr + off = int32(setaddr(Ctxt, ftab, int64(off), Ctxt.Cursym)) + + // name int32 + off = int32(setuint32(Ctxt, ftab, int64(off), uint32(ftabaddstring(ftab, Ctxt.Cursym.Name)))) + + // args int32 + // TODO: Move into funcinfo. + off = int32(setuint32(Ctxt, ftab, int64(off), uint32(Ctxt.Cursym.Args))) + + // frame int32 + // TODO: Remove entirely. The pcsp table is more precise. + // This is only used by a fallback case during stack walking + // when a called function doesn't have argument information. + // We need to make sure everything has argument information + // and then remove this. + frameptrsize = int32(Thearch.Ptrsize) + + if Ctxt.Cursym.Leaf != 0 { + frameptrsize = 0 + } + off = int32(setuint32(Ctxt, ftab, int64(off), uint32(Ctxt.Cursym.Locals)+uint32(frameptrsize))) + + if pcln != &pclntab_zpcln { + renumberfiles(Ctxt, pcln.File, &pcln.Pcfile) + if false { + // Sanity check the new numbering + for pciterinit(Ctxt, &it, &pcln.Pcfile); it.done == 0; pciternext(&it) { + if it.value < 1 || it.value > Ctxt.Nhistfile { + Diag("bad file number in pcfile: %d not in range [1, %d]\n", it.value, Ctxt.Nhistfile) + Errorexit() + } + } + } + } + + // pcdata + off = addpctab(ftab, off, &pcln.Pcsp) + + off = addpctab(ftab, off, &pcln.Pcfile) + off = addpctab(ftab, off, &pcln.Pcline) + off = int32(setuint32(Ctxt, ftab, int64(off), uint32(pcln.Npcdata))) + off = int32(setuint32(Ctxt, ftab, int64(off), uint32(pcln.Nfuncdata))) + for i = 0; i < int32(pcln.Npcdata); i++ { + off = addpctab(ftab, off, &pcln.Pcdata[i]) + } + + // funcdata, must be pointer-aligned and we're only int32-aligned. + // Missing funcdata will be 0 (nil pointer). + if pcln.Nfuncdata > 0 { + if off&int32(Thearch.Ptrsize-1) != 0 { + off += 4 + } + for i = 0; i < int32(pcln.Nfuncdata); i++ { + if pcln.Funcdata[i] == nil { + setuintxx(Ctxt, ftab, int64(off)+int64(Thearch.Ptrsize)*int64(i), uint64(pcln.Funcdataoff[i]), int64(Thearch.Ptrsize)) + } else { + // TODO: Dedup. + funcdata_bytes += pcln.Funcdata[i].Size + + setaddrplus(Ctxt, ftab, int64(off)+int64(Thearch.Ptrsize)*int64(i), pcln.Funcdata[i], pcln.Funcdataoff[i]) + } + } + + off += int32(pcln.Nfuncdata) * int32(Thearch.Ptrsize) + } + + if off != end { + Diag("bad math in functab: funcstart=%d off=%d but end=%d (npcdata=%d nfuncdata=%d ptrsize=%d)", funcstart, off, end, pcln.Npcdata, pcln.Nfuncdata, Thearch.Ptrsize) + Errorexit() + } + + nfunc++ + } + + // Final entry of table is just end pc. + setaddrplus(Ctxt, ftab, 8+int64(Thearch.Ptrsize)+int64(nfunc)*2*int64(Thearch.Ptrsize), last, last.Size) + + // Start file table. + start = int32(len(ftab.P)) + + start += int32(-len(ftab.P)) & (int32(Thearch.Ptrsize) - 1) + setuint32(Ctxt, ftab, 8+int64(Thearch.Ptrsize)+int64(nfunc)*2*int64(Thearch.Ptrsize)+int64(Thearch.Ptrsize), uint32(start)) + + Symgrow(Ctxt, ftab, int64(start)+(int64(Ctxt.Nhistfile)+1)*4) + setuint32(Ctxt, ftab, int64(start), uint32(Ctxt.Nhistfile)) + for s = Ctxt.Filesyms; s != nil; s = s.Next { + setuint32(Ctxt, ftab, int64(start)+s.Value*4, uint32(ftabaddstring(ftab, s.Name))) + } + + ftab.Size = int64(len(ftab.P)) + + if Debug['v'] != 0 { + fmt.Fprintf(&Bso, "%5.2f pclntab=%d bytes, funcdata total %d bytes\n", obj.Cputime(), int64(ftab.Size), int64(funcdata_bytes)) + } +} + +const ( + BUCKETSIZE = 256 * MINFUNC + SUBBUCKETS = 16 + SUBBUCKETSIZE = BUCKETSIZE / SUBBUCKETS + NOIDX = 0x7fffffff +) + +// findfunctab generates a lookup table to quickly find the containing +// function for a pc. See src/runtime/symtab.go:findfunc for details. +func findfunctab() { + var t *LSym + var s *LSym + var e *LSym + var idx int32 + var i int32 + var j int32 + var nbuckets int32 + var n int32 + var base int32 + var min int64 + var max int64 + var p int64 + var q int64 + var indexes []int32 + + t = Linklookup(Ctxt, "runtime.findfunctab", 0) + t.Type = SRODATA + t.Reachable = true + + // find min and max address + min = Ctxt.Textp.Value + + max = 0 + for s = Ctxt.Textp; s != nil; s = s.Next { + max = s.Value + s.Size + } + + // for each subbucket, compute the minimum of all symbol indexes + // that map to that subbucket. + n = int32((max - min + SUBBUCKETSIZE - 1) / SUBBUCKETSIZE) + + indexes = make([]int32, n) + for i = 0; i < n; i++ { + indexes[i] = NOIDX + } + idx = 0 + for s = Ctxt.Textp; s != nil; s = s.Next { + if container(s) != 0 { + continue + } + p = s.Value + e = s.Next + for container(e) != 0 { + e = e.Next + } + if e != nil { + q = e.Value + } else { + q = max + } + + //print("%d: [%lld %lld] %s\n", idx, p, q, s->name); + for ; p < q; p += SUBBUCKETSIZE { + i = int32((p - min) / SUBBUCKETSIZE) + if indexes[i] > idx { + indexes[i] = idx + } + } + + i = int32((q - 1 - min) / SUBBUCKETSIZE) + if indexes[i] > idx { + indexes[i] = idx + } + idx++ + } + + // allocate table + nbuckets = int32((max - min + BUCKETSIZE - 1) / BUCKETSIZE) + + Symgrow(Ctxt, t, 4*int64(nbuckets)+int64(n)) + + // fill in table + for i = 0; i < nbuckets; i++ { + base = indexes[i*SUBBUCKETS] + if base == NOIDX { + Diag("hole in findfunctab") + } + setuint32(Ctxt, t, int64(i)*(4+SUBBUCKETS), uint32(base)) + for j = 0; j < SUBBUCKETS && i*SUBBUCKETS+j < n; j++ { + idx = indexes[i*SUBBUCKETS+j] + if idx == NOIDX { + Diag("hole in findfunctab") + } + if idx-base >= 256 { + Diag("too many functions in a findfunc bucket! %d/%d %d %d", i, nbuckets, j, idx-base) + } + + setuint8(Ctxt, t, int64(i)*(4+SUBBUCKETS)+4+int64(j), uint8(idx-base)) + } + } +} diff --git a/src/cmd/internal/ld/pe.go b/src/cmd/internal/ld/pe.go new file mode 100644 index 0000000000..c2ef49fbea --- /dev/null +++ b/src/cmd/internal/ld/pe.go @@ -0,0 +1,1060 @@ +// 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 ld + +import ( + "encoding/binary" + "fmt" + "sort" +) + +type IMAGE_FILE_HEADER struct { + Machine uint16 + NumberOfSections uint16 + TimeDateStamp uint32 + PointerToSymbolTable uint32 + NumberOfSymbols uint32 + SizeOfOptionalHeader uint16 + Characteristics uint16 +} + +type IMAGE_DATA_DIRECTORY struct { + VirtualAddress uint32 + Size uint32 +} + +type IMAGE_OPTIONAL_HEADER struct { + Magic uint16 + MajorLinkerVersion uint8 + MinorLinkerVersion uint8 + SizeOfCode uint32 + SizeOfInitializedData uint32 + SizeOfUninitializedData uint32 + AddressOfEntryPoint uint32 + BaseOfCode uint32 + BaseOfData uint32 + ImageBase uint32 + SectionAlignment uint32 + FileAlignment uint32 + MajorOperatingSystemVersion uint16 + MinorOperatingSystemVersion uint16 + MajorImageVersion uint16 + MinorImageVersion uint16 + MajorSubsystemVersion uint16 + MinorSubsystemVersion uint16 + Win32VersionValue uint32 + SizeOfImage uint32 + SizeOfHeaders uint32 + CheckSum uint32 + Subsystem uint16 + DllCharacteristics uint16 + SizeOfStackReserve uint32 + SizeOfStackCommit uint32 + SizeOfHeapReserve uint32 + SizeOfHeapCommit uint32 + LoaderFlags uint32 + NumberOfRvaAndSizes uint32 + DataDirectory [16]IMAGE_DATA_DIRECTORY +} + +type IMAGE_SECTION_HEADER struct { + Name [8]uint8 + VirtualSize uint32 + VirtualAddress uint32 + SizeOfRawData uint32 + PointerToRawData uint32 + PointerToRelocations uint32 + PointerToLineNumbers uint32 + NumberOfRelocations uint16 + NumberOfLineNumbers uint16 + Characteristics uint32 +} + +type IMAGE_IMPORT_DESCRIPTOR struct { + OriginalFirstThunk uint32 + TimeDateStamp uint32 + ForwarderChain uint32 + Name uint32 + FirstThunk uint32 +} + +type IMAGE_EXPORT_DIRECTORY struct { + Characteristics uint32 + TimeDateStamp uint32 + MajorVersion uint16 + MinorVersion uint16 + Name uint32 + Base uint32 + NumberOfFunctions uint32 + NumberOfNames uint32 + AddressOfFunctions uint32 + AddressOfNames uint32 + AddressOfNameOrdinals uint32 +} + +const ( + PEBASE = 0x00400000 + PESECTALIGN = 0x1000 + PEFILEALIGN = 2 << 8 +) + +const ( + IMAGE_FILE_MACHINE_I386 = 0x14c + IMAGE_FILE_MACHINE_AMD64 = 0x8664 + IMAGE_FILE_RELOCS_STRIPPED = 0x0001 + IMAGE_FILE_EXECUTABLE_IMAGE = 0x0002 + IMAGE_FILE_LARGE_ADDRESS_AWARE = 0x0020 + IMAGE_FILE_32BIT_MACHINE = 0x0100 + IMAGE_FILE_DEBUG_STRIPPED = 0x0200 + IMAGE_SCN_CNT_CODE = 0x00000020 + IMAGE_SCN_CNT_INITIALIZED_DATA = 0x00000040 + IMAGE_SCN_CNT_UNINITIALIZED_DATA = 0x00000080 + IMAGE_SCN_MEM_EXECUTE = 0x20000000 + IMAGE_SCN_MEM_READ = 0x40000000 + IMAGE_SCN_MEM_WRITE = 0x80000000 + IMAGE_SCN_MEM_DISCARDABLE = 0x2000000 + IMAGE_DIRECTORY_ENTRY_EXPORT = 0 + IMAGE_DIRECTORY_ENTRY_IMPORT = 1 + IMAGE_DIRECTORY_ENTRY_RESOURCE = 2 + IMAGE_DIRECTORY_ENTRY_EXCEPTION = 3 + IMAGE_DIRECTORY_ENTRY_SECURITY = 4 + IMAGE_DIRECTORY_ENTRY_BASERELOC = 5 + IMAGE_DIRECTORY_ENTRY_DEBUG = 6 + IMAGE_DIRECTORY_ENTRY_COPYRIGHT = 7 + IMAGE_DIRECTORY_ENTRY_ARCHITECTURE = 7 + IMAGE_DIRECTORY_ENTRY_GLOBALPTR = 8 + IMAGE_DIRECTORY_ENTRY_TLS = 9 + IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG = 10 + IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT = 11 + IMAGE_DIRECTORY_ENTRY_IAT = 12 + IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT = 13 + IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR = 14 + IMAGE_SUBSYSTEM_WINDOWS_GUI = 2 + IMAGE_SUBSYSTEM_WINDOWS_CUI = 3 +) + +// X64 +type PE64_IMAGE_OPTIONAL_HEADER struct { + Magic uint16 + MajorLinkerVersion uint8 + MinorLinkerVersion uint8 + SizeOfCode uint32 + SizeOfInitializedData uint32 + SizeOfUninitializedData uint32 + AddressOfEntryPoint uint32 + BaseOfCode uint32 + ImageBase uint64 + SectionAlignment uint32 + FileAlignment uint32 + MajorOperatingSystemVersion uint16 + MinorOperatingSystemVersion uint16 + MajorImageVersion uint16 + MinorImageVersion uint16 + MajorSubsystemVersion uint16 + MinorSubsystemVersion uint16 + Win32VersionValue uint32 + SizeOfImage uint32 + SizeOfHeaders uint32 + CheckSum uint32 + Subsystem uint16 + DllCharacteristics uint16 + SizeOfStackReserve uint64 + SizeOfStackCommit uint64 + SizeOfHeapReserve uint64 + SizeOfHeapCommit uint64 + LoaderFlags uint32 + NumberOfRvaAndSizes uint32 + DataDirectory [16]IMAGE_DATA_DIRECTORY +} + +// 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. + +// PE (Portable Executable) file writing +// http://www.microsoft.com/whdc/system/platform/firmware/PECOFF.mspx + +// DOS stub that prints out +// "This program cannot be run in DOS mode." +var dosstub = []uint8{ + 0x4d, + 0x5a, + 0x90, + 0x00, + 0x03, + 0x00, + 0x04, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0xff, + 0xff, + 0x00, + 0x00, + 0x8b, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x40, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x80, + 0x00, + 0x00, + 0x00, + 0x0e, + 0x1f, + 0xba, + 0x0e, + 0x00, + 0xb4, + 0x09, + 0xcd, + 0x21, + 0xb8, + 0x01, + 0x4c, + 0xcd, + 0x21, + 0x54, + 0x68, + 0x69, + 0x73, + 0x20, + 0x70, + 0x72, + 0x6f, + 0x67, + 0x72, + 0x61, + 0x6d, + 0x20, + 0x63, + 0x61, + 0x6e, + 0x6e, + 0x6f, + 0x74, + 0x20, + 0x62, + 0x65, + 0x20, + 0x72, + 0x75, + 0x6e, + 0x20, + 0x69, + 0x6e, + 0x20, + 0x44, + 0x4f, + 0x53, + 0x20, + 0x6d, + 0x6f, + 0x64, + 0x65, + 0x2e, + 0x0d, + 0x0d, + 0x0a, + 0x24, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, +} + +var rsrcsym *LSym + +var strtbl []byte + +var PESECTHEADR int32 + +var PEFILEHEADR int32 + +var pe64 int + +var pensect int + +var nextsectoff int + +var nextfileoff int + +var textsect int + +var datasect int + +var fh IMAGE_FILE_HEADER + +var oh IMAGE_OPTIONAL_HEADER + +var oh64 PE64_IMAGE_OPTIONAL_HEADER + +var sh [16]IMAGE_SECTION_HEADER + +var dd []IMAGE_DATA_DIRECTORY + +type Imp struct { + s *LSym + off uint64 + next *Imp +} + +type Dll struct { + name string + nameoff uint64 + thunkoff uint64 + ms *Imp + next *Dll +} + +var dr *Dll + +var dexport [1024]*LSym + +var nexport int + +type COFFSym struct { + sym *LSym + strtbloff int + sect int + value int64 +} + +var coffsym []COFFSym + +var ncoffsym int + +func addpesection(name string, sectsize int, filesize int) *IMAGE_SECTION_HEADER { + var h *IMAGE_SECTION_HEADER + + if pensect == 16 { + Diag("too many sections") + Errorexit() + } + + h = &sh[pensect] + pensect++ + copy(h.Name[:], name) + h.VirtualSize = uint32(sectsize) + h.VirtualAddress = uint32(nextsectoff) + nextsectoff = int(Rnd(int64(nextsectoff)+int64(sectsize), PESECTALIGN)) + h.PointerToRawData = uint32(nextfileoff) + if filesize > 0 { + h.SizeOfRawData = uint32(Rnd(int64(filesize), PEFILEALIGN)) + nextfileoff += int(h.SizeOfRawData) + } + + return h +} + +func chksectoff(h *IMAGE_SECTION_HEADER, off int64) { + if off != int64(h.PointerToRawData) { + Diag("%s.PointerToRawData = %#x, want %#x", cstring(h.Name[:]), uint64(int64(h.PointerToRawData)), uint64(off)) + Errorexit() + } +} + +func chksectseg(h *IMAGE_SECTION_HEADER, s *Segment) { + if s.Vaddr-PEBASE != uint64(h.VirtualAddress) { + Diag("%s.VirtualAddress = %#x, want %#x", cstring(h.Name[:]), uint64(int64(h.VirtualAddress)), uint64(int64(s.Vaddr-PEBASE))) + Errorexit() + } + + if s.Fileoff != uint64(h.PointerToRawData) { + Diag("%s.PointerToRawData = %#x, want %#x", cstring(h.Name[:]), uint64(int64(h.PointerToRawData)), uint64(int64(s.Fileoff))) + Errorexit() + } +} + +func Peinit() { + var l int + + switch Thearch.Thechar { + // 64-bit architectures + case '6': + pe64 = 1 + + l = binary.Size(&oh64) + dd = oh64.DataDirectory[:] + + // 32-bit architectures + default: + l = binary.Size(&oh) + + dd = oh.DataDirectory[:] + } + + PEFILEHEADR = int32(Rnd(int64(len(dosstub)+binary.Size(&fh)+l+binary.Size(&sh)), PEFILEALIGN)) + PESECTHEADR = int32(Rnd(int64(PEFILEHEADR), PESECTALIGN)) + nextsectoff = int(PESECTHEADR) + nextfileoff = int(PEFILEHEADR) + + // some mingw libs depend on this symbol, for example, FindPESectionByName + xdefine("__image_base__", SDATA, PEBASE) + + xdefine("_image_base__", SDATA, PEBASE) +} + +func pewrite() { + Cseek(0) + Cwrite(dosstub) + strnput("PE", 4) + + binary.Write(&coutbuf, binary.LittleEndian, &fh) + + if pe64 != 0 { + binary.Write(&coutbuf, binary.LittleEndian, &oh64) + } else { + binary.Write(&coutbuf, binary.LittleEndian, &oh) + } + binary.Write(&coutbuf, binary.LittleEndian, sh[:pensect]) +} + +func strput(s string) { + coutbuf.w.WriteString(s) + Cput(0) + // string must be padded to even size + if (len(s)+1)%2 != 0 { + Cput(0) + } +} + +func initdynimport() *Dll { + var m *Imp + var d *Dll + var s *LSym + var dynamic *LSym + + dr = nil + m = nil + for s = Ctxt.Allsym; s != nil; s = s.Allsym { + if !s.Reachable || s.Type != SDYNIMPORT { + continue + } + for d = dr; d != nil; d = d.next { + if d.name == s.Dynimplib { + m = new(Imp) + break + } + } + + if d == nil { + d = new(Dll) + d.name = s.Dynimplib + d.next = dr + dr = d + m = new(Imp) + } + + m.s = s + m.next = d.ms + d.ms = m + } + + dynamic = Linklookup(Ctxt, ".windynamic", 0) + dynamic.Reachable = true + dynamic.Type = SWINDOWS + for d = dr; d != nil; d = d.next { + for m = d.ms; m != nil; m = m.next { + m.s.Type = SWINDOWS | SSUB + m.s.Sub = dynamic.Sub + dynamic.Sub = m.s + m.s.Value = dynamic.Size + dynamic.Size += int64(Thearch.Ptrsize) + } + + dynamic.Size += int64(Thearch.Ptrsize) + } + + return dr +} + +func addimports(datsect *IMAGE_SECTION_HEADER) { + var isect *IMAGE_SECTION_HEADER + var n uint64 + var oftbase uint64 + var ftbase uint64 + var startoff int64 + var endoff int64 + var m *Imp + var d *Dll + var dynamic *LSym + + startoff = Cpos() + dynamic = Linklookup(Ctxt, ".windynamic", 0) + + // skip import descriptor table (will write it later) + n = 0 + + for d = dr; d != nil; d = d.next { + n++ + } + Cseek(startoff + int64(binary.Size(&IMAGE_IMPORT_DESCRIPTOR{}))*int64(n+1)) + + // write dll names + for d = dr; d != nil; d = d.next { + d.nameoff = uint64(Cpos()) - uint64(startoff) + strput(d.name) + } + + // write function names + for d = dr; d != nil; d = d.next { + for m = d.ms; m != nil; m = m.next { + m.off = uint64(nextsectoff) + uint64(Cpos()) - uint64(startoff) + Wputl(0) // hint + strput(m.s.Extname) + } + } + + // write OriginalFirstThunks + oftbase = uint64(Cpos()) - uint64(startoff) + + n = uint64(Cpos()) + for d = dr; d != nil; d = d.next { + d.thunkoff = uint64(Cpos()) - n + for m = d.ms; m != nil; m = m.next { + if pe64 != 0 { + Vputl(m.off) + } else { + Lputl(uint32(m.off)) + } + } + + if pe64 != 0 { + Vputl(0) + } else { + Lputl(0) + } + } + + // add pe section and pad it at the end + n = uint64(Cpos()) - uint64(startoff) + + isect = addpesection(".idata", int(n), int(n)) + isect.Characteristics = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE + chksectoff(isect, startoff) + strnput("", int(uint64(isect.SizeOfRawData)-n)) + endoff = Cpos() + + // write FirstThunks (allocated in .data section) + ftbase = uint64(dynamic.Value) - uint64(datsect.VirtualAddress) - PEBASE + + Cseek(int64(uint64(datsect.PointerToRawData) + ftbase)) + for d = dr; d != nil; d = d.next { + for m = d.ms; m != nil; m = m.next { + if pe64 != 0 { + Vputl(m.off) + } else { + Lputl(uint32(m.off)) + } + } + + if pe64 != 0 { + Vputl(0) + } else { + Lputl(0) + } + } + + // finally write import descriptor table + Cseek(startoff) + + for d = dr; d != nil; d = d.next { + Lputl(uint32(uint64(isect.VirtualAddress) + oftbase + d.thunkoff)) + Lputl(0) + Lputl(0) + Lputl(uint32(uint64(isect.VirtualAddress) + d.nameoff)) + Lputl(uint32(uint64(datsect.VirtualAddress) + ftbase + d.thunkoff)) + } + + Lputl(0) //end + Lputl(0) + Lputl(0) + Lputl(0) + Lputl(0) + + // update data directory + dd[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress = isect.VirtualAddress + + dd[IMAGE_DIRECTORY_ENTRY_IMPORT].Size = isect.VirtualSize + dd[IMAGE_DIRECTORY_ENTRY_IAT].VirtualAddress = uint32(dynamic.Value - PEBASE) + dd[IMAGE_DIRECTORY_ENTRY_IAT].Size = uint32(dynamic.Size) + + Cseek(endoff) +} + +type pescmp []*LSym + +func (x pescmp) Len() int { + return len(x) +} + +func (x pescmp) Swap(i, j int) { + x[i], x[j] = x[j], x[i] +} + +func (x pescmp) Less(i, j int) bool { + var s1 *LSym + var s2 *LSym + + s1 = x[i] + s2 = x[j] + return stringsCompare(s1.Extname, s2.Extname) < 0 +} + +func initdynexport() { + var s *LSym + + nexport = 0 + for s = Ctxt.Allsym; s != nil; s = s.Allsym { + if !s.Reachable || s.Cgoexport&CgoExportDynamic == 0 { + continue + } + if nexport+1 > len(dexport) { + Diag("pe dynexport table is full") + Errorexit() + } + + dexport[nexport] = s + nexport++ + } + + sort.Sort(pescmp(dexport[:nexport])) +} + +func addexports() { + var sect *IMAGE_SECTION_HEADER + var e IMAGE_EXPORT_DIRECTORY + var size int + var i int + var va int + var va_name int + var va_addr int + var va_na int + var v int + + size = binary.Size(&e) + 10*nexport + len(outfile) + 1 + for i = 0; i < nexport; i++ { + size += len(dexport[i].Extname) + 1 + } + + if nexport == 0 { + return + } + + sect = addpesection(".edata", size, size) + sect.Characteristics = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ + chksectoff(sect, Cpos()) + va = int(sect.VirtualAddress) + dd[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress = uint32(va) + dd[IMAGE_DIRECTORY_ENTRY_EXPORT].Size = sect.VirtualSize + + va_name = va + binary.Size(&e) + nexport*4 + va_addr = va + binary.Size(&e) + va_na = va + binary.Size(&e) + nexport*8 + + e.Characteristics = 0 + e.MajorVersion = 0 + e.MinorVersion = 0 + e.NumberOfFunctions = uint32(nexport) + e.NumberOfNames = uint32(nexport) + e.Name = uint32(va+binary.Size(&e)) + uint32(nexport)*10 // Program names. + e.Base = 1 + e.AddressOfFunctions = uint32(va_addr) + e.AddressOfNames = uint32(va_name) + e.AddressOfNameOrdinals = uint32(va_na) + + // put IMAGE_EXPORT_DIRECTORY + binary.Write(&coutbuf, binary.LittleEndian, &e) + + // put EXPORT Address Table + for i = 0; i < nexport; i++ { + Lputl(uint32(dexport[i].Value - PEBASE)) + } + + // put EXPORT Name Pointer Table + v = int(e.Name + uint32(len(outfile)) + 1) + + for i = 0; i < nexport; i++ { + Lputl(uint32(v)) + v += len(dexport[i].Extname) + 1 + } + + // put EXPORT Ordinal Table + for i = 0; i < nexport; i++ { + Wputl(uint16(i)) + } + + // put Names + strnput(outfile, len(outfile)+1) + + for i = 0; i < nexport; i++ { + strnput(dexport[i].Extname, len(dexport[i].Extname)+1) + } + strnput("", int(sect.SizeOfRawData-uint32(size))) +} + +func dope() { + var rel *LSym + + /* relocation table */ + rel = Linklookup(Ctxt, ".rel", 0) + + rel.Reachable = true + rel.Type = SELFROSECT + + initdynimport() + initdynexport() +} + +func strtbladd(name string) int { + off := len(strtbl) + 4 // offset includes 4-byte length at beginning of table + strtbl = append(strtbl, name...) + strtbl = append(strtbl, 0) + return off +} + +/* + * For more than 8 characters section names, name contains a slash (/) that is + * followed by an ASCII representation of a decimal number that is an offset into + * the string table. + * reference: pecoff_v8.docx Page 24. + * + */ +func newPEDWARFSection(name string, size int64) *IMAGE_SECTION_HEADER { + var h *IMAGE_SECTION_HEADER + var s string + var off int + + if size == 0 { + return nil + } + + off = strtbladd(name) + s = fmt.Sprintf("/%d", off) + h = addpesection(s, int(size), int(size)) + h.Characteristics = IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_DISCARDABLE + + return h +} + +func addpesym(s *LSym, name string, type_ int, addr int64, size int64, ver int, gotype *LSym) { + var cs *COFFSym + + if s == nil { + return + } + + if s.Sect == nil { + return + } + + switch type_ { + default: + return + + case 'D', + 'B', + 'T': + break + } + + if coffsym != nil { + cs = &coffsym[ncoffsym] + cs.sym = s + if len(s.Name) > 8 { + cs.strtbloff = strtbladd(s.Name) + } + if uint64(s.Value) >= Segdata.Vaddr { + cs.value = int64(uint64(s.Value) - Segdata.Vaddr) + cs.sect = datasect + } else if uint64(s.Value) >= Segtext.Vaddr { + cs.value = int64(uint64(s.Value) - Segtext.Vaddr) + cs.sect = textsect + } else { + cs.value = 0 + cs.sect = 0 + Diag("addpesym %#x", addr) + } + } + + ncoffsym++ +} + +func addpesymtable() { + var h *IMAGE_SECTION_HEADER + var i int + var size int + var s *COFFSym + + if Debug['s'] == 0 { + genasmsym(addpesym) + coffsym = make([]COFFSym, ncoffsym) + ncoffsym = 0 + genasmsym(addpesym) + } + + size = len(strtbl) + 4 + 18*ncoffsym + h = addpesection(".symtab", size, size) + h.Characteristics = IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_DISCARDABLE + chksectoff(h, Cpos()) + fh.PointerToSymbolTable = uint32(Cpos()) + fh.NumberOfSymbols = uint32(ncoffsym) + + // put COFF symbol table + for i = 0; i < ncoffsym; i++ { + s = &coffsym[i] + if s.strtbloff == 0 { + strnput(s.sym.Name, 8) + } else { + Lputl(0) + Lputl(uint32(s.strtbloff)) + } + + Lputl(uint32(s.value)) + Wputl(uint16(s.sect)) + Wputl(0x0308) // "array of structs" + Cput(2) // storage class: external + Cput(0) // no aux entries + } + + // put COFF string table + Lputl(uint32(len(strtbl)) + 4) + + for i = 0; i < len(strtbl); i++ { + Cput(uint8(strtbl[i])) + } + strnput("", int(h.SizeOfRawData-uint32(size))) +} + +func setpersrc(sym *LSym) { + if rsrcsym != nil { + Diag("too many .rsrc sections") + } + + rsrcsym = sym +} + +func addpersrc() { + var h *IMAGE_SECTION_HEADER + var p []byte + var val uint32 + var r *Reloc + var ri int + + if rsrcsym == nil { + return + } + + h = addpesection(".rsrc", int(rsrcsym.Size), int(rsrcsym.Size)) + h.Characteristics = IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE | IMAGE_SCN_CNT_INITIALIZED_DATA + chksectoff(h, Cpos()) + + // relocation + for ri = 0; ri < len(rsrcsym.R); ri++ { + r = &rsrcsym.R[ri] + p = rsrcsym.P[r.Off:] + val = uint32(int64(h.VirtualAddress) + r.Add) + + // 32-bit little-endian + p[0] = byte(val) + + p[1] = byte(val >> 8) + p[2] = byte(val >> 16) + p[3] = byte(val >> 24) + } + + Cwrite(rsrcsym.P) + strnput("", int(int64(h.SizeOfRawData)-rsrcsym.Size)) + + // update data directory + dd[IMAGE_DIRECTORY_ENTRY_RESOURCE].VirtualAddress = h.VirtualAddress + + dd[IMAGE_DIRECTORY_ENTRY_RESOURCE].Size = h.VirtualSize +} + +func Asmbpe() { + var t *IMAGE_SECTION_HEADER + var d *IMAGE_SECTION_HEADER + + switch Thearch.Thechar { + default: + Diag("unknown PE architecture") + Errorexit() + fallthrough + + case '6': + fh.Machine = IMAGE_FILE_MACHINE_AMD64 + + case '8': + fh.Machine = IMAGE_FILE_MACHINE_I386 + } + + t = addpesection(".text", int(Segtext.Length), int(Segtext.Length)) + t.Characteristics = IMAGE_SCN_CNT_CODE | IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ + chksectseg(t, &Segtext) + textsect = pensect + + d = addpesection(".data", int(Segdata.Length), int(Segdata.Filelen)) + d.Characteristics = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE + chksectseg(d, &Segdata) + datasect = pensect + + if Debug['s'] == 0 { + dwarfaddpeheaders() + } + + Cseek(int64(nextfileoff)) + addimports(d) + addexports() + addpesymtable() + addpersrc() + + fh.NumberOfSections = uint16(pensect) + + // Being able to produce identical output for identical input is + // much more beneficial than having build timestamp in the header. + fh.TimeDateStamp = 0 + + fh.Characteristics = IMAGE_FILE_RELOCS_STRIPPED | IMAGE_FILE_EXECUTABLE_IMAGE | IMAGE_FILE_DEBUG_STRIPPED + if pe64 != 0 { + fh.SizeOfOptionalHeader = uint16(binary.Size(&oh64)) + fh.Characteristics |= IMAGE_FILE_LARGE_ADDRESS_AWARE + oh64.Magic = 0x20b + } else { + fh.SizeOfOptionalHeader = uint16(binary.Size(&oh)) + fh.Characteristics |= IMAGE_FILE_32BIT_MACHINE + oh.Magic = 0x10b // PE32 + oh.BaseOfData = d.VirtualAddress + } + + // Fill out both oh64 and oh. We only use one. Oh well. + oh64.MajorLinkerVersion = 3 + + oh.MajorLinkerVersion = 3 + oh64.MinorLinkerVersion = 0 + oh.MinorLinkerVersion = 0 + oh64.SizeOfCode = t.SizeOfRawData + oh.SizeOfCode = t.SizeOfRawData + oh64.SizeOfInitializedData = d.SizeOfRawData + oh.SizeOfInitializedData = d.SizeOfRawData + oh64.SizeOfUninitializedData = 0 + oh.SizeOfUninitializedData = 0 + oh64.AddressOfEntryPoint = uint32(Entryvalue() - PEBASE) + oh.AddressOfEntryPoint = uint32(Entryvalue() - PEBASE) + oh64.BaseOfCode = t.VirtualAddress + oh.BaseOfCode = t.VirtualAddress + oh64.ImageBase = PEBASE + oh.ImageBase = PEBASE + oh64.SectionAlignment = PESECTALIGN + oh.SectionAlignment = PESECTALIGN + oh64.FileAlignment = PEFILEALIGN + oh.FileAlignment = PEFILEALIGN + oh64.MajorOperatingSystemVersion = 4 + oh.MajorOperatingSystemVersion = 4 + oh64.MinorOperatingSystemVersion = 0 + oh.MinorOperatingSystemVersion = 0 + oh64.MajorImageVersion = 1 + oh.MajorImageVersion = 1 + oh64.MinorImageVersion = 0 + oh.MinorImageVersion = 0 + oh64.MajorSubsystemVersion = 4 + oh.MajorSubsystemVersion = 4 + oh64.MinorSubsystemVersion = 0 + oh.MinorSubsystemVersion = 0 + oh64.SizeOfImage = uint32(nextsectoff) + oh.SizeOfImage = uint32(nextsectoff) + oh64.SizeOfHeaders = uint32(PEFILEHEADR) + oh.SizeOfHeaders = uint32(PEFILEHEADR) + if headstring == "windowsgui" { + oh64.Subsystem = IMAGE_SUBSYSTEM_WINDOWS_GUI + oh.Subsystem = IMAGE_SUBSYSTEM_WINDOWS_GUI + } else { + oh64.Subsystem = IMAGE_SUBSYSTEM_WINDOWS_CUI + oh.Subsystem = IMAGE_SUBSYSTEM_WINDOWS_CUI + } + + // Disable stack growth as we don't want Windows to + // fiddle with the thread stack limits, which we set + // ourselves to circumvent the stack checks in the + // Windows exception dispatcher. + // Commit size must be strictly less than reserve + // size otherwise reserve will be rounded up to a + // larger size, as verified with VMMap. + + // Go code would be OK with 64k stacks, but we need larger stacks for cgo. + // That default stack reserve size affects only the main thread, + // for other threads we specify stack size in runtime explicitly + // (runtime knows whether cgo is enabled or not). + // If you change stack reserve sizes here, + // change STACKSIZE in runtime/cgo/gcc_windows_{386,amd64}.c as well. + if !iscgo { + oh64.SizeOfStackReserve = 0x00010000 + oh.SizeOfStackReserve = 0x00010000 + oh64.SizeOfStackCommit = 0x0000ffff + oh.SizeOfStackCommit = 0x0000ffff + } else { + oh64.SizeOfStackReserve = 0x00200000 + oh.SizeOfStackReserve = 0x00100000 + + // account for 2 guard pages + oh64.SizeOfStackCommit = 0x00200000 - 0x2000 + + oh.SizeOfStackCommit = 0x00100000 - 0x2000 + } + + oh64.SizeOfHeapReserve = 0x00100000 + oh.SizeOfHeapReserve = 0x00100000 + oh64.SizeOfHeapCommit = 0x00001000 + oh.SizeOfHeapCommit = 0x00001000 + oh64.NumberOfRvaAndSizes = 16 + oh.NumberOfRvaAndSizes = 16 + + pewrite() +} diff --git a/src/cmd/internal/ld/pobj.go b/src/cmd/internal/ld/pobj.go new file mode 100644 index 0000000000..226ccf1c19 --- /dev/null +++ b/src/cmd/internal/ld/pobj.go @@ -0,0 +1,224 @@ +// Inferno utils/6l/obj.c +// http://code.google.com/p/inferno-os/source/browse/utils/6l/obj.c +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package ld + +import ( + "cmd/internal/obj" + "flag" + "fmt" + "os" + "strings" +) + +// Reading object files. + +var noname string = "" + +var paramspace string = "FP" + +func Ldmain() { + Ctxt = linknew(Thelinkarch) + Ctxt.Thechar = int32(Thearch.Thechar) + Ctxt.Thestring = Thestring + Ctxt.Diag = Diag + Ctxt.Bso = &Bso + + Bso = *Binitw(os.Stdout) + Debug = [128]int{} + nerrors = 0 + outfile = "" + HEADTYPE = -1 + INITTEXT = -1 + INITDAT = -1 + INITRND = -1 + INITENTRY = "" + Linkmode = LinkAuto + + // For testing behavior of go command when tools crash. + // Undocumented, not in standard flag parser to avoid + // exposing in usage message. + for _, arg := range os.Args { + if arg == "-crash_for_testing" { + *(*int)(nil) = 0 + } + } + + if Thearch.Thechar == '5' && Ctxt.Goarm == 5 { + Debug['F'] = 1 + } + + obj.Flagcount("1", "use alternate profiling code", &Debug['1']) + if Thearch.Thechar == '6' { + obj.Flagcount("8", "assume 64-bit addresses", &Debug['8']) + } + obj.Flagfn1("B", "info: define ELF NT_GNU_BUILD_ID note", addbuildinfo) + obj.Flagcount("C", "check Go calls to C code", &Debug['C']) + obj.Flagint64("D", "addr: data address", &INITDAT) + obj.Flagstr("E", "sym: entry symbol", &INITENTRY) + if Thearch.Thechar == '5' { + obj.Flagcount("G", "debug pseudo-ops", &Debug['G']) + } + obj.Flagfn1("I", "interp: set ELF interp", setinterp) + obj.Flagfn1("L", "dir: add dir to library path", Lflag) + obj.Flagfn1("H", "head: header type", setheadtype) + obj.Flagcount("K", "add stack underflow checks", &Debug['K']) + if Thearch.Thechar == '5' { + obj.Flagcount("M", "disable software div/mod", &Debug['M']) + } + obj.Flagcount("O", "print pc-line tables", &Debug['O']) + obj.Flagcount("Q", "debug byte-register code gen", &Debug['Q']) + if Thearch.Thechar == '5' { + obj.Flagcount("P", "debug code generation", &Debug['P']) + } + obj.Flagint32("R", "rnd: address rounding", &INITRND) + obj.Flagcount("nil", "check type signatures", &Debug['S']) + obj.Flagint64("T", "addr: text address", &INITTEXT) + obj.Flagfn0("V", "print version and exit", doversion) + obj.Flagcount("W", "disassemble input", &Debug['W']) + obj.Flagfn1("X", "name value: define string data", addstrdata1) + obj.Flagcount("Z", "clear stack frame on entry", &Debug['Z']) + obj.Flagcount("a", "disassemble output", &Debug['a']) + obj.Flagcount("c", "dump call graph", &Debug['c']) + obj.Flagcount("d", "disable dynamic executable", &Debug['d']) + obj.Flagstr("extld", "ld: linker to run in external mode", &extld) + obj.Flagstr("extldflags", "ldflags: flags for external linker", &extldflags) + obj.Flagcount("f", "ignore version mismatch", &Debug['f']) + obj.Flagcount("g", "disable go package data checks", &Debug['g']) + obj.Flagstr("installsuffix", "suffix: pkg directory suffix", &flag_installsuffix) + obj.Flagstr("k", "sym: set field tracking symbol", &tracksym) + obj.Flagfn1("linkmode", "mode: set link mode (internal, external, auto)", setlinkmode) + obj.Flagcount("n", "dump symbol table", &Debug['n']) + obj.Flagstr("o", "outfile: set output file", &outfile) + obj.Flagstr("r", "dir1:dir2:...: set ELF dynamic linker search path", &rpath) + obj.Flagcount("race", "enable race detector", &flag_race) + obj.Flagcount("s", "disable symbol table", &Debug['s']) + if Thearch.Thechar == '5' || Thearch.Thechar == '6' { + obj.Flagcount("shared", "generate shared object (implies -linkmode external)", &Flag_shared) + } + obj.Flagstr("tmpdir", "dir: leave temporary files in this directory", &tmpdir) + obj.Flagcount("u", "reject unsafe packages", &Debug['u']) + obj.Flagcount("v", "print link trace", &Debug['v']) + obj.Flagcount("w", "disable DWARF generation", &Debug['w']) + + // Clumsy hack to preserve old behavior of -X taking two arguments. + for i := 0; i < len(os.Args); i++ { + arg := os.Args[i] + if (arg == "--X" || arg == "-X") && i+2 < len(os.Args) { + os.Args[i+2] = "-X=VALUE:" + os.Args[i+2] + i += 2 + } else if (strings.HasPrefix(arg, "--X=") || strings.HasPrefix(arg, "-X=")) && i+1 < len(os.Args) { + os.Args[i+1] = "-X=VALUE:" + os.Args[i+1] + i++ + } + } + obj.Flagstr("cpuprofile", "file: write cpu profile to file", &cpuprofile) + obj.Flagstr("memprofile", "file: write memory profile to file", &memprofile) + obj.Flagparse(usage) + startProfile() + Ctxt.Bso = &Bso + Ctxt.Debugvlog = int32(Debug['v']) + + if flag.NArg() != 1 { + usage() + } + + if outfile == "" { + if HEADTYPE == Hwindows { + outfile = fmt.Sprintf("%c.out.exe", Thearch.Thechar) + } else { + outfile = fmt.Sprintf("%c.out", Thearch.Thechar) + } + } + + libinit() // creates outfile + + if HEADTYPE == -1 { + HEADTYPE = int32(headtype(goos)) + } + Ctxt.Headtype = int(HEADTYPE) + if headstring == "" { + headstring = Headstr(int(HEADTYPE)) + } + + Thearch.Archinit() + + if Debug['v'] != 0 { + fmt.Fprintf(&Bso, "HEADER = -H%d -T0x%x -D0x%x -R0x%x\n", HEADTYPE, uint64(INITTEXT), uint64(INITDAT), uint32(INITRND)) + } + Bflush(&Bso) + + addlibpath(Ctxt, "command line", "command line", flag.Arg(0), "main") + loadlib() + + if Thearch.Thechar == '5' { + // mark some functions that are only referenced after linker code editing + if Debug['F'] != 0 { + mark(Linkrlookup(Ctxt, "_sfloat", 0)) + } + mark(Linklookup(Ctxt, "runtime.read_tls_fallback", 0)) + } + + checkgo() + deadcode() + callgraph() + paramspace = "SP" /* (FP) now (SP) on output */ + + doelf() + if HEADTYPE == Hdarwin { + domacho() + } + dostkcheck() + if HEADTYPE == Hwindows { + dope() + } + addexport() + Thearch.Gentext() // trampolines, call stubs, etc. + textaddress() + pclntab() + findfunctab() + symtab() + dodata() + address() + doweak() + reloc() + Thearch.Asmb() + undef() + hostlink() + if Debug['v'] != 0 { + fmt.Fprintf(&Bso, "%5.2f cpu time\n", obj.Cputime()) + fmt.Fprintf(&Bso, "%d symbols\n", Ctxt.Nsymbol) + fmt.Fprintf(&Bso, "%d liveness data\n", liveness) + } + + Bflush(&Bso) + + Errorexit() +} diff --git a/src/cmd/internal/ld/sym.go b/src/cmd/internal/ld/sym.go new file mode 100644 index 0000000000..7e275a698b --- /dev/null +++ b/src/cmd/internal/ld/sym.go @@ -0,0 +1,238 @@ +// Derived from Inferno utils/6l/obj.c and utils/6l/span.c +// http://code.google.com/p/inferno-os/source/browse/utils/6l/obj.c +// http://code.google.com/p/inferno-os/source/browse/utils/6l/span.c +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package ld + +import ( + "cmd/internal/obj" + "fmt" + "log" + "os" + "path/filepath" +) + +func yy_isalpha(c int) bool { + return 'A' <= c && c <= 'Z' || 'a' <= c && c <= 'z' +} + +var headers = []struct { + name string + val int +}{ + {"darwin", Hdarwin}, + {"dragonfly", Hdragonfly}, + {"elf", Helf}, + {"freebsd", Hfreebsd}, + {"linux", Hlinux}, + {"android", Hlinux}, // must be after "linux" entry or else headstr(Hlinux) == "android" + {"nacl", Hnacl}, + {"netbsd", Hnetbsd}, + {"openbsd", Hopenbsd}, + {"plan9", Hplan9}, + {"solaris", Hsolaris}, + {"windows", Hwindows}, + {"windowsgui", Hwindows}, +} + +func linknew(arch *LinkArch) *Link { + var ctxt *Link + var p string + var buf string + + ctxt = new(Link) + ctxt.Hash = make(map[symVer]*LSym) + ctxt.Arch = arch + ctxt.Version = HistVersion + ctxt.Goroot = obj.Getgoroot() + + p = obj.Getgoarch() + if p != arch.Name { + log.Fatalf("invalid goarch %s (want %s)", p, arch.Name) + } + + buf, _ = os.Getwd() + if buf == "" { + buf = "/???" + } + buf = filepath.ToSlash(buf) + + ctxt.Headtype = headtype(obj.Getgoos()) + if ctxt.Headtype < 0 { + log.Fatalf("unknown goos %s", obj.Getgoos()) + } + + // Record thread-local storage offset. + // TODO(rsc): Move tlsoffset back into the linker. + switch ctxt.Headtype { + default: + log.Fatalf("unknown thread-local storage offset for %s", Headstr(ctxt.Headtype)) + + case Hplan9, + Hwindows: + break + + /* + * ELF uses TLS offset negative from FS. + * Translate 0(FS) and 8(FS) into -16(FS) and -8(FS). + * Known to low-level assembly in package runtime and runtime/cgo. + */ + case Hlinux, + Hfreebsd, + Hnetbsd, + Hopenbsd, + Hdragonfly, + Hsolaris: + ctxt.Tlsoffset = -2 * ctxt.Arch.Ptrsize + + case Hnacl: + switch ctxt.Arch.Thechar { + default: + log.Fatalf("unknown thread-local storage offset for nacl/%s", ctxt.Arch.Name) + + case '5': + ctxt.Tlsoffset = 0 + + case '6': + ctxt.Tlsoffset = 0 + + case '8': + ctxt.Tlsoffset = -8 + } + + /* + * OS X system constants - offset from 0(GS) to our TLS. + * Explained in ../../runtime/cgo/gcc_darwin_*.c. + */ + case Hdarwin: + switch ctxt.Arch.Thechar { + default: + log.Fatalf("unknown thread-local storage offset for darwin/%s", ctxt.Arch.Name) + + case '6': + ctxt.Tlsoffset = 0x8a0 + + case '8': + ctxt.Tlsoffset = 0x468 + + case '5': + ctxt.Tlsoffset = 0 // dummy value, not needed + } + } + + // On arm, record goarm. + if ctxt.Arch.Thechar == '5' { + p = obj.Getgoarm() + if p != "" { + ctxt.Goarm = int32(obj.Atoi(p)) + } else { + ctxt.Goarm = 6 + } + } + + return ctxt +} + +func linknewsym(ctxt *Link, symb string, v int) *LSym { + var s *LSym + + s = new(LSym) + *s = LSym{} + + s.Dynid = -1 + s.Plt = -1 + s.Got = -1 + s.Name = symb + s.Type = 0 + s.Version = int16(v) + s.Value = 0 + s.Sig = 0 + s.Size = 0 + ctxt.Nsymbol++ + + s.Allsym = ctxt.Allsym + ctxt.Allsym = s + + return s +} + +type symVer struct { + sym string + ver int +} + +func _lookup(ctxt *Link, symb string, v int, creat int) *LSym { + s := ctxt.Hash[symVer{symb, v}] + if s != nil { + return s + } + if creat == 0 { + return nil + } + + s = linknewsym(ctxt, symb, v) + s.Extname = s.Name + ctxt.Hash[symVer{symb, v}] = s + return s +} + +func Linklookup(ctxt *Link, name string, v int) *LSym { + return _lookup(ctxt, name, v, 1) +} + +// read-only lookup +func Linkrlookup(ctxt *Link, name string, v int) *LSym { + return _lookup(ctxt, name, v, 0) +} + +var headstr_buf string + +func Headstr(v int) string { + var i int + + for i = 0; i < len(headers); i++ { + if v == headers[i].val { + return headers[i].name + } + } + headstr_buf = fmt.Sprintf("%d", v) + return headstr_buf +} + +func headtype(name string) int { + var i int + + for i = 0; i < len(headers); i++ { + if name == headers[i].name { + return headers[i].val + } + } + return -1 +} diff --git a/src/cmd/internal/ld/symtab.go b/src/cmd/internal/ld/symtab.go new file mode 100644 index 0000000000..997300c5cb --- /dev/null +++ b/src/cmd/internal/ld/symtab.go @@ -0,0 +1,440 @@ +// Inferno utils/6l/span.c +// http://code.google.com/p/inferno-os/source/browse/utils/6l/span.c +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package ld + +import "strings" + +// Symbol table. + +var maxelfstr int + +func putelfstr(s string) int { + var off int + var n int + + if len(Elfstrdat) == 0 && s != "" { + // first entry must be empty string + putelfstr("") + } + + // Rewrite · to . for ASCII-only tools like DTrace (sigh) + s = strings.Replace(s, "·", ".", -1) + + n = len(s) + 1 + for len(Elfstrdat)+n > cap(Elfstrdat) { + Elfstrdat = append(Elfstrdat[:cap(Elfstrdat)], 0)[:len(Elfstrdat)] + } + + off = len(Elfstrdat) + Elfstrdat = Elfstrdat[:off+n] + copy(Elfstrdat[off:], s) + + return off +} + +func putelfsyment(off int, addr int64, size int64, info int, shndx int, other int) { + switch Thearch.Thechar { + case '6', + '9': + Thearch.Lput(uint32(off)) + Cput(uint8(info)) + Cput(uint8(other)) + Thearch.Wput(uint16(shndx)) + Thearch.Vput(uint64(addr)) + Thearch.Vput(uint64(size)) + Symsize += ELF64SYMSIZE + + default: + Thearch.Lput(uint32(off)) + Thearch.Lput(uint32(addr)) + Thearch.Lput(uint32(size)) + Cput(uint8(info)) + Cput(uint8(other)) + Thearch.Wput(uint16(shndx)) + Symsize += ELF32SYMSIZE + } +} + +var numelfsym int = 1 // 0 is reserved + +var elfbind int + +func putelfsym(x *LSym, s string, t int, addr int64, size int64, ver int, go_ *LSym) { + var bind int + var type_ int + var off int + var other int + var xo *LSym + + switch t { + default: + return + + case 'T': + type_ = STT_FUNC + + case 'D': + type_ = STT_OBJECT + + case 'B': + type_ = STT_OBJECT + } + + xo = x + for xo.Outer != nil { + xo = xo.Outer + } + if xo.Sect == nil { + Ctxt.Cursym = x + Diag("missing section in putelfsym") + return + } + + if (xo.Sect.(*Section)).Elfsect == nil { + Ctxt.Cursym = x + Diag("missing ELF section in putelfsym") + return + } + + // One pass for each binding: STB_LOCAL, STB_GLOBAL, + // maybe one day STB_WEAK. + bind = STB_GLOBAL + + if ver != 0 || (x.Type&SHIDDEN != 0) { + bind = STB_LOCAL + } + + // In external linking mode, we have to invoke gcc with -rdynamic + // to get the exported symbols put into the dynamic symbol table. + // To avoid filling the dynamic table with lots of unnecessary symbols, + // mark all Go symbols local (not global) in the final executable. + if Linkmode == LinkExternal && x.Cgoexport&CgoExportStatic == 0 { + bind = STB_LOCAL + } + + if bind != elfbind { + return + } + + off = putelfstr(s) + if Linkmode == LinkExternal { + addr -= int64((xo.Sect.(*Section)).Vaddr) + } + other = 2 + if x.Type&SHIDDEN != 0 { + other = 0 + } + putelfsyment(off, addr, size, bind<<4|type_&0xf, ((xo.Sect.(*Section)).Elfsect.(*ElfShdr)).shnum, other) + x.Elfsym = int32(numelfsym) + numelfsym++ +} + +func putelfsectionsym(s *LSym, shndx int) { + putelfsyment(0, 0, 0, STB_LOCAL<<4|STT_SECTION, shndx, 0) + s.Elfsym = int32(numelfsym) + numelfsym++ +} + +func putelfsymshndx(sympos int64, shndx int) { + var here int64 + + here = Cpos() + switch Thearch.Thechar { + case '6': + Cseek(sympos + 6) + + default: + Cseek(sympos + 14) + } + + Thearch.Wput(uint16(shndx)) + Cseek(here) +} + +func Asmelfsym() { + var s *LSym + var name string + + // the first symbol entry is reserved + putelfsyment(0, 0, 0, STB_LOCAL<<4|STT_NOTYPE, 0, 0) + + dwarfaddelfsectionsyms() + + elfbind = STB_LOCAL + genasmsym(putelfsym) + + if Linkmode == LinkExternal && HEADTYPE != Hopenbsd { + s = Linklookup(Ctxt, "runtime.tlsg", 0) + if s.Sect == nil { + Ctxt.Cursym = nil + Diag("missing section for %s", s.Name) + Errorexit() + } + + if goos == "android" { + // Android emulates runtime.tlsg as a regular variable. + putelfsyment(putelfstr(s.Name), 0, s.Size, STB_LOCAL<<4|STT_OBJECT, ((s.Sect.(*Section)).Elfsect.(*ElfShdr)).shnum, 0) + } else { + putelfsyment(putelfstr(s.Name), 0, s.Size, STB_LOCAL<<4|STT_TLS, ((s.Sect.(*Section)).Elfsect.(*ElfShdr)).shnum, 0) + } + + s.Elfsym = int32(numelfsym) + numelfsym++ + } + + elfbind = STB_GLOBAL + elfglobalsymndx = numelfsym + genasmsym(putelfsym) + + for s = Ctxt.Allsym; s != nil; s = s.Allsym { + if s.Type != SHOSTOBJ && (s.Type != SDYNIMPORT || !s.Reachable) { + continue + } + if s.Type == SDYNIMPORT { + name = s.Extname + } else { + name = s.Name + } + putelfsyment(putelfstr(name), 0, 0, STB_GLOBAL<<4|STT_NOTYPE, 0, 0) + s.Elfsym = int32(numelfsym) + numelfsym++ + } +} + +func putplan9sym(x *LSym, s string, t int, addr int64, size int64, ver int, go_ *LSym) { + var i int + var l int + + switch t { + case 'T', + 'L', + 'D', + 'B': + if ver != 0 { + t += 'a' - 'A' + } + fallthrough + + case 'a', + 'p', + 'f', + 'z', + 'Z', + 'm': + l = 4 + if HEADTYPE == Hplan9 && Thearch.Thechar == '6' && Debug['8'] == 0 { + Lputb(uint32(addr >> 32)) + l = 8 + } + + Lputb(uint32(addr)) + Cput(uint8(t + 0x80)) /* 0x80 is variable length */ + + if t == 'z' || t == 'Z' { + Cput(uint8(s[0])) + for i = 1; s[i] != 0 || s[i+1] != 0; i += 2 { + Cput(uint8(s[i])) + Cput(uint8(s[i+1])) + } + + Cput(0) + Cput(0) + i++ + } else { + /* skip the '<' in filenames */ + if t == 'f' { + s = s[1:] + } + for i = 0; i < len(s); i++ { + Cput(uint8(s[i])) + } + Cput(0) + } + + Symsize += int32(l) + 1 + int32(i) + 1 + + default: + return + } +} + +func Asmplan9sym() { + genasmsym(putplan9sym) +} + +var symt *LSym + +func Wputl(w uint16) { + Cput(uint8(w)) + Cput(uint8(w >> 8)) +} + +func Wputb(w uint16) { + Cput(uint8(w >> 8)) + Cput(uint8(w)) +} + +func Lputb(l uint32) { + Cput(uint8(l >> 24)) + Cput(uint8(l >> 16)) + Cput(uint8(l >> 8)) + Cput(uint8(l)) +} + +func Lputl(l uint32) { + Cput(uint8(l)) + Cput(uint8(l >> 8)) + Cput(uint8(l >> 16)) + Cput(uint8(l >> 24)) +} + +func Vputb(v uint64) { + Lputb(uint32(v >> 32)) + Lputb(uint32(v)) +} + +func Vputl(v uint64) { + Lputl(uint32(v)) + Lputl(uint32(v >> 32)) +} + +func symtab() { + var s *LSym + var symtype *LSym + var symtypelink *LSym + var symgostring *LSym + var symgofunc *LSym + + dosymtype() + + // Define these so that they'll get put into the symbol table. + // data.c:/^address will provide the actual values. + xdefine("runtime.text", STEXT, 0) + + xdefine("runtime.etext", STEXT, 0) + xdefine("runtime.typelink", SRODATA, 0) + xdefine("runtime.etypelink", SRODATA, 0) + xdefine("runtime.rodata", SRODATA, 0) + xdefine("runtime.erodata", SRODATA, 0) + xdefine("runtime.noptrdata", SNOPTRDATA, 0) + xdefine("runtime.enoptrdata", SNOPTRDATA, 0) + xdefine("runtime.data", SDATA, 0) + xdefine("runtime.edata", SDATA, 0) + xdefine("runtime.bss", SBSS, 0) + xdefine("runtime.ebss", SBSS, 0) + xdefine("runtime.noptrbss", SNOPTRBSS, 0) + xdefine("runtime.enoptrbss", SNOPTRBSS, 0) + xdefine("runtime.end", SBSS, 0) + xdefine("runtime.epclntab", SRODATA, 0) + xdefine("runtime.esymtab", SRODATA, 0) + + // garbage collection symbols + s = Linklookup(Ctxt, "runtime.gcdata", 0) + + s.Type = SRODATA + s.Size = 0 + s.Reachable = true + xdefine("runtime.egcdata", SRODATA, 0) + + s = Linklookup(Ctxt, "runtime.gcbss", 0) + s.Type = SRODATA + s.Size = 0 + s.Reachable = true + xdefine("runtime.egcbss", SRODATA, 0) + + // pseudo-symbols to mark locations of type, string, and go string data. + s = Linklookup(Ctxt, "type.*", 0) + + s.Type = STYPE + s.Size = 0 + s.Reachable = true + symtype = s + + s = Linklookup(Ctxt, "go.string.*", 0) + s.Type = SGOSTRING + s.Size = 0 + s.Reachable = true + symgostring = s + + s = Linklookup(Ctxt, "go.func.*", 0) + s.Type = SGOFUNC + s.Size = 0 + s.Reachable = true + symgofunc = s + + symtypelink = Linklookup(Ctxt, "runtime.typelink", 0) + + symt = Linklookup(Ctxt, "runtime.symtab", 0) + symt.Type = SSYMTAB + symt.Size = 0 + symt.Reachable = true + + // assign specific types so that they sort together. + // within a type they sort by size, so the .* symbols + // just defined above will be first. + // hide the specific symbols. + for s = Ctxt.Allsym; s != nil; s = s.Allsym { + if !s.Reachable || s.Special != 0 || s.Type != SRODATA { + continue + } + if strings.HasPrefix(s.Name, "type.") { + s.Type = STYPE + s.Hide = 1 + s.Outer = symtype + } + + if strings.HasPrefix(s.Name, "go.typelink.") { + s.Type = STYPELINK + s.Hide = 1 + s.Outer = symtypelink + } + + if strings.HasPrefix(s.Name, "go.string.") { + s.Type = SGOSTRING + s.Hide = 1 + s.Outer = symgostring + } + + if strings.HasPrefix(s.Name, "go.func.") { + s.Type = SGOFUNC + s.Hide = 1 + s.Outer = symgofunc + } + + if strings.HasPrefix(s.Name, "gcargs.") || strings.HasPrefix(s.Name, "gclocals.") || strings.HasPrefix(s.Name, "gclocals·") { + s.Type = SGOFUNC + s.Hide = 1 + s.Outer = symgofunc + s.Align = 4 + liveness += (s.Size + int64(s.Align) - 1) &^ (int64(s.Align) - 1) + } + } +} diff --git a/src/cmd/internal/ld/textflag.go b/src/cmd/internal/ld/textflag.go new file mode 100644 index 0000000000..335f20d21d --- /dev/null +++ b/src/cmd/internal/ld/textflag.go @@ -0,0 +1,133 @@ +// Copyright 2013 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 ld + +// Writing and reading of Go object files. +// +// Originally, Go object files were Plan 9 object files, but no longer. +// Now they are more like standard object files, in that each symbol is defined +// by an associated memory image (bytes) and a list of relocations to apply +// during linking. We do not (yet?) use a standard file format, however. +// For now, the format is chosen to be as simple as possible to read and write. +// It may change for reasons of efficiency, or we may even switch to a +// standard file format if there are compelling benefits to doing so. +// See golang.org/s/go13linker for more background. +// +// The file format is: +// +// - magic header: "\x00\x00go13ld" +// - byte 1 - version number +// - sequence of strings giving dependencies (imported packages) +// - empty string (marks end of sequence) +// - sequence of defined symbols +// - byte 0xff (marks end of sequence) +// - magic footer: "\xff\xffgo13ld" +// +// All integers are stored in a zigzag varint format. +// See golang.org/s/go12symtab for a definition. +// +// Data blocks and strings are both stored as an integer +// followed by that many bytes. +// +// A symbol reference is a string name followed by a version. +// An empty name corresponds to a nil LSym* pointer. +// +// Each symbol is laid out as the following fields (taken from LSym*): +// +// - byte 0xfe (sanity check for synchronization) +// - type [int] +// - name [string] +// - version [int] +// - flags [int] +// 1 dupok +// - size [int] +// - gotype [symbol reference] +// - p [data block] +// - nr [int] +// - r [nr relocations, sorted by off] +// +// If type == STEXT, there are a few more fields: +// +// - args [int] +// - locals [int] +// - nosplit [int] +// - flags [int] +// 1 leaf +// 2 C function +// - nlocal [int] +// - local [nlocal automatics] +// - pcln [pcln table] +// +// Each relocation has the encoding: +// +// - off [int] +// - siz [int] +// - type [int] +// - add [int] +// - xadd [int] +// - sym [symbol reference] +// - xsym [symbol reference] +// +// Each local has the encoding: +// +// - asym [symbol reference] +// - offset [int] +// - type [int] +// - gotype [symbol reference] +// +// The pcln table has the encoding: +// +// - pcsp [data block] +// - pcfile [data block] +// - pcline [data block] +// - npcdata [int] +// - pcdata [npcdata data blocks] +// - nfuncdata [int] +// - funcdata [nfuncdata symbol references] +// - funcdatasym [nfuncdata ints] +// - nfile [int] +// - file [nfile symbol references] +// +// The file layout and meaning of type integers are architecture-independent. +// +// TODO(rsc): The file format is good for a first pass but needs work. +// - There are SymID in the object file that should really just be strings. +// - The actual symbol memory images are interlaced with the symbol +// metadata. They should be separated, to reduce the I/O required to +// load just the metadata. +// - The symbol references should be shortened, either with a symbol +// table or by using a simple backward index to an earlier mentioned symbol. + +// Copyright 2013 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. + +// This file defines flags attached to various functions +// and data objects. The compilers, assemblers, and linker must +// all agree on these values. + +// Don't profile the marked routine. This flag is deprecated. + +// It is ok for the linker to get multiple of these symbols. It will +// pick one of the duplicates to use. + +// Don't insert stack check preamble. + +// Put this data in a read-only section. + +// This data contains no pointers. + +// This is a wrapper function and should not count as disabling 'recover'. + +// This function uses its incoming context register. +const ( + NOPROF = 1 + DUPOK = 2 + NOSPLIT = 4 + RODATA = 8 + NOPTR = 16 + WRAPPER = 32 + NEEDCTXT = 64 +) diff --git a/src/cmd/internal/ld/util.go b/src/cmd/internal/ld/util.go new file mode 100644 index 0000000000..9a56f09466 --- /dev/null +++ b/src/cmd/internal/ld/util.go @@ -0,0 +1,369 @@ +// 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 ld + +import ( + "bufio" + "bytes" + "encoding/binary" + "io" + "log" + "os" + "runtime/pprof" + "strings" + "time" +) + +func cstring(x []byte) string { + i := bytes.IndexByte(x, '\x00') + if i >= 0 { + x = x[:i] + } + return string(x) +} + +func plan9quote(s string) string { + if s == "" { + goto needquote + } + for i := 0; i < len(s); i++ { + if s[i] <= ' ' || s[i] == '\'' { + goto needquote + } + } + return s + +needquote: + return "'" + strings.Replace(s, "'", "''", -1) + "'" +} + +func tokenize(s string) []string { + var f []string + for { + s = strings.TrimLeft(s, " \t\r\n") + if s == "" { + break + } + quote := false + i := 0 + for ; i < len(s); i++ { + if s[i] == '\'' { + if quote && i+1 < len(s) && s[i+1] == '\'' { + i++ + continue + } + quote = !quote + } + if !quote && (s[i] == ' ' || s[i] == '\t' || s[i] == '\r' || s[i] == '\n') { + break + } + } + next := s[:i] + s = s[i:] + if strings.Contains(next, "'") { + var buf []byte + quote := false + for i := 0; i < len(next); i++ { + if next[i] == '\'' { + if quote && i+1 < len(next) && next[i+1] == '\'' { + i++ + buf = append(buf, '\'') + } + quote = !quote + continue + } + buf = append(buf, next[i]) + } + next = string(buf) + } + f = append(f, next) + } + return f +} + +func cutStringAtNUL(s string) string { + if i := strings.Index(s, "\x00"); i >= 0 { + s = s[:i] + } + return s +} + +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 *os.File) *Biobuf { + return &Biobuf{w: bufio.NewWriter(w), f: 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.Panicf("seeking in output [%d %d %p]: %v", offset, whence, b.f, err) + } + if b.r != nil { + b.r.Reset(b.f) + } + return off +} + +func Boffset(b *Biobuf) int64 { + if b.w != nil { + 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 [0, 1]: %v", err) + } + if b.r != nil { + off -= int64(b.r.Buffered()) + } + 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 { + if b.numUnget > 0 { + Bseek(b, -int64(b.numUnget), 1) + b.numUnget = 0 + } + 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 { + if b.numUnget > 0 { + Bseek(b, -int64(b.numUnget), 1) + b.numUnget = 0 + } + 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 { + if b.numUnget > 0 { + Bseek(b, -int64(b.numUnget), 1) + b.numUnget = 0 + } + 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 { + if b.numUnget > 0 { + Bseek(b, -int64(b.numUnget), 1) + b.numUnget = 0 + } + 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 +} + +// strings.Compare, introduced in Go 1.5. +func stringsCompare(a, b string) int { + if a == b { + return 0 + } + if a < b { + return -1 + } + return +1 +} + +var atExitFuncs []func() + +func AtExit(f func()) { + atExitFuncs = append(atExitFuncs, f) +} + +func Exit(code int) { + for i := len(atExitFuncs) - 1; i >= 0; i-- { + f := atExitFuncs[i] + atExitFuncs = atExitFuncs[:i] + f() + } + os.Exit(code) +} + +var cpuprofile string +var memprofile string + +func startProfile() { + if cpuprofile != "" { + f, err := os.Create(cpuprofile) + if err != nil { + log.Fatalf("%v", err) + } + if err := pprof.StartCPUProfile(f); err != nil { + log.Fatalf("%v", err) + } + AtExit(pprof.StopCPUProfile) + } + if memprofile != "" { + f, err := os.Create(memprofile) + if err != nil { + log.Fatalf("%v", err) + } + AtExit(func() { + if err := pprof.WriteHeapProfile(f); err != nil { + log.Fatalf("%v", err) + } + }) + } +} + +func artrim(x []byte) string { + i := 0 + j := len(x) + for i < len(x) && x[i] == ' ' { + i++ + } + for j > i && x[j-1] == ' ' { + j-- + } + return string(x[i:j]) +} + +func stringtouint32(x []uint32, s string) { + for i := 0; len(s) > 0; i++ { + var buf [4]byte + s = s[copy(buf[:], s):] + x[i] = binary.LittleEndian.Uint32(buf[:]) + } +} + +var start = time.Now() + +func elapsed() float64 { + return time.Since(start).Seconds() +} diff --git a/src/cmd/internal/ld/z.go b/src/cmd/internal/ld/z.go new file mode 100644 index 0000000000..7330ae2981 --- /dev/null +++ b/src/cmd/internal/ld/z.go @@ -0,0 +1 @@ +package ld diff --git a/src/cmd/new5l/asm.go b/src/cmd/new5l/asm.go new file mode 100644 index 0000000000..909f682c98 --- /dev/null +++ b/src/cmd/new5l/asm.go @@ -0,0 +1,777 @@ +// Inferno utils/5l/asm.c +// http://code.google.com/p/inferno-os/source/browse/utils/5l/asm.c +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package main + +import ( + "cmd/internal/obj" + "fmt" + "log" +) +import "cmd/internal/ld" + +func needlib(name string) int { + var p string + var s *ld.LSym + + if name[0] == '\x00' { + return 0 + } + + /* reuse hash code in symbol table */ + p = fmt.Sprintf(".dynlib.%s", name) + + s = ld.Linklookup(ld.Ctxt, p, 0) + + if s.Type == 0 { + s.Type = 100 // avoid SDATA, etc. + return 1 + } + + return 0 +} + +func gentext() { +} + +// Preserve highest 8 bits of a, and do addition to lower 24-bit +// of a and b; used to adjust ARM branch intruction's target +func braddoff(a int32, b int32) int32 { + return int32((uint32(a))&0xff000000 | 0x00ffffff&uint32(a+b)) +} + +func adddynrela(rel *ld.LSym, s *ld.LSym, r *ld.Reloc) { + ld.Addaddrplus(ld.Ctxt, rel, s, int64(r.Off)) + ld.Adduint32(ld.Ctxt, rel, ld.R_ARM_RELATIVE) +} + +func adddynrel(s *ld.LSym, r *ld.Reloc) { + var targ *ld.LSym + var rel *ld.LSym + + targ = r.Sym + ld.Ctxt.Cursym = s + + switch r.Type { + default: + if r.Type >= 256 { + ld.Diag("unexpected relocation type %d", r.Type) + return + } + + // Handle relocations found in ELF object files. + case 256 + ld.R_ARM_PLT32: + r.Type = ld.R_CALLARM + + if targ.Type == ld.SDYNIMPORT { + addpltsym(ld.Ctxt, targ) + r.Sym = ld.Linklookup(ld.Ctxt, ".plt", 0) + r.Add = int64(braddoff(int32(r.Add), targ.Plt/4)) + } + + return + + case 256 + ld.R_ARM_THM_PC22: // R_ARM_THM_CALL + ld.Diag("R_ARM_THM_CALL, are you using -marm?") + + ld.Errorexit() + return + + case 256 + ld.R_ARM_GOT32: // R_ARM_GOT_BREL + if targ.Type != ld.SDYNIMPORT { + addgotsyminternal(ld.Ctxt, targ) + } else { + addgotsym(ld.Ctxt, targ) + } + + r.Type = ld.R_CONST // write r->add during relocsym + r.Sym = nil + r.Add += int64(targ.Got) + return + + case 256 + ld.R_ARM_GOT_PREL: // GOT(nil) + A - nil + if targ.Type != ld.SDYNIMPORT { + addgotsyminternal(ld.Ctxt, targ) + } else { + addgotsym(ld.Ctxt, targ) + } + + r.Type = ld.R_PCREL + r.Sym = ld.Linklookup(ld.Ctxt, ".got", 0) + r.Add += int64(targ.Got) + 4 + return + + case 256 + ld.R_ARM_GOTOFF: // R_ARM_GOTOFF32 + r.Type = ld.R_GOTOFF + + return + + case 256 + ld.R_ARM_GOTPC: // R_ARM_BASE_PREL + r.Type = ld.R_PCREL + + r.Sym = ld.Linklookup(ld.Ctxt, ".got", 0) + r.Add += 4 + return + + case 256 + ld.R_ARM_CALL: + r.Type = ld.R_CALLARM + if targ.Type == ld.SDYNIMPORT { + addpltsym(ld.Ctxt, targ) + r.Sym = ld.Linklookup(ld.Ctxt, ".plt", 0) + r.Add = int64(braddoff(int32(r.Add), targ.Plt/4)) + } + + return + + case 256 + ld.R_ARM_REL32: // R_ARM_REL32 + r.Type = ld.R_PCREL + + r.Add += 4 + return + + case 256 + ld.R_ARM_ABS32: + if targ.Type == ld.SDYNIMPORT { + ld.Diag("unexpected R_ARM_ABS32 relocation for dynamic symbol %s", targ.Name) + } + r.Type = ld.R_ADDR + return + + // we can just ignore this, because we are targeting ARM V5+ anyway + case 256 + ld.R_ARM_V4BX: + if r.Sym != nil { + // R_ARM_V4BX is ABS relocation, so this symbol is a dummy symbol, ignore it + r.Sym.Type = 0 + } + + r.Sym = nil + return + + case 256 + ld.R_ARM_PC24, + 256 + ld.R_ARM_JUMP24: + r.Type = ld.R_CALLARM + if targ.Type == ld.SDYNIMPORT { + addpltsym(ld.Ctxt, targ) + r.Sym = ld.Linklookup(ld.Ctxt, ".plt", 0) + r.Add = int64(braddoff(int32(r.Add), targ.Plt/4)) + } + + return + } + + // Handle references to ELF symbols from our own object files. + if targ.Type != ld.SDYNIMPORT { + return + } + + switch r.Type { + case ld.R_CALLARM: + addpltsym(ld.Ctxt, targ) + r.Sym = ld.Linklookup(ld.Ctxt, ".plt", 0) + r.Add = int64(targ.Plt) + return + + case ld.R_ADDR: + if s.Type != ld.SDATA { + break + } + if ld.Iself { + adddynsym(ld.Ctxt, targ) + rel = ld.Linklookup(ld.Ctxt, ".rel", 0) + ld.Addaddrplus(ld.Ctxt, rel, s, int64(r.Off)) + ld.Adduint32(ld.Ctxt, rel, ld.ELF32_R_INFO(uint32(targ.Dynid), ld.R_ARM_GLOB_DAT)) // we need a nil + A dynmic reloc + r.Type = ld.R_CONST // write r->add during relocsym + r.Sym = nil + return + } + } + + ld.Ctxt.Cursym = s + ld.Diag("unsupported relocation for dynamic symbol %s (type=%d stype=%d)", targ.Name, r.Type, targ.Type) +} + +func elfreloc1(r *ld.Reloc, sectoff int64) int { + var elfsym int32 + + ld.Thearch.Lput(uint32(sectoff)) + + elfsym = r.Xsym.Elfsym + switch r.Type { + default: + return -1 + + case ld.R_ADDR: + if r.Siz == 4 { + ld.Thearch.Lput(ld.R_ARM_ABS32 | uint32(elfsym)<<8) + } else { + return -1 + } + + case ld.R_PCREL: + if r.Siz == 4 { + ld.Thearch.Lput(ld.R_ARM_REL32 | uint32(elfsym)<<8) + } else { + return -1 + } + + case ld.R_CALLARM: + if r.Siz == 4 { + if r.Add&0xff000000 == 0xeb000000 { // BL + ld.Thearch.Lput(ld.R_ARM_CALL | uint32(elfsym)<<8) + } else { + ld.Thearch.Lput(ld.R_ARM_JUMP24 | uint32(elfsym)<<8) + } + } else { + return -1 + } + + case ld.R_TLS: + if r.Siz == 4 { + if ld.Flag_shared != 0 { + ld.Thearch.Lput(ld.R_ARM_TLS_IE32 | uint32(elfsym)<<8) + } else { + ld.Thearch.Lput(ld.R_ARM_TLS_LE32 | uint32(elfsym)<<8) + } + } else { + return -1 + } + } + + return 0 +} + +func elfsetupplt() { + var plt *ld.LSym + var got *ld.LSym + + plt = ld.Linklookup(ld.Ctxt, ".plt", 0) + got = ld.Linklookup(ld.Ctxt, ".got.plt", 0) + if plt.Size == 0 { + // str lr, [sp, #-4]! + ld.Adduint32(ld.Ctxt, plt, 0xe52de004) + + // ldr lr, [pc, #4] + ld.Adduint32(ld.Ctxt, plt, 0xe59fe004) + + // add lr, pc, lr + ld.Adduint32(ld.Ctxt, plt, 0xe08fe00e) + + // ldr pc, [lr, #8]! + ld.Adduint32(ld.Ctxt, plt, 0xe5bef008) + + // .word &GLOBAL_OFFSET_TABLE[0] - . + ld.Addpcrelplus(ld.Ctxt, plt, got, 4) + + // the first .plt entry requires 3 .plt.got entries + ld.Adduint32(ld.Ctxt, got, 0) + + ld.Adduint32(ld.Ctxt, got, 0) + ld.Adduint32(ld.Ctxt, got, 0) + } +} + +func machoreloc1(r *ld.Reloc, sectoff int64) int { + var v uint32 + var rs *ld.LSym + + rs = r.Xsym + + if rs.Type == ld.SHOSTOBJ || r.Type == ld.R_CALLARM { + if rs.Dynid < 0 { + ld.Diag("reloc %d to non-macho symbol %s type=%d", r.Type, rs.Name, rs.Type) + return -1 + } + + v = uint32(rs.Dynid) + v |= 1 << 27 // external relocation + } else { + v = uint32((rs.Sect.(*ld.Section)).Extnum) + if v == 0 { + ld.Diag("reloc %d to symbol %s in non-macho section %s type=%d", r.Type, rs.Name, (rs.Sect.(*ld.Section)).Name, rs.Type) + return -1 + } + } + + switch r.Type { + default: + return -1 + + case ld.R_ADDR: + v |= ld.MACHO_GENERIC_RELOC_VANILLA << 28 + + case ld.R_CALLARM: + v |= 1 << 24 // pc-relative bit + v |= ld.MACHO_ARM_RELOC_BR24 << 28 + } + + switch r.Siz { + default: + return -1 + + case 1: + v |= 0 << 25 + + case 2: + v |= 1 << 25 + + case 4: + v |= 2 << 25 + + case 8: + v |= 3 << 25 + } + + ld.Thearch.Lput(uint32(sectoff)) + ld.Thearch.Lput(v) + return 0 +} + +func archreloc(r *ld.Reloc, s *ld.LSym, val *int64) int { + var rs *ld.LSym + + if ld.Linkmode == ld.LinkExternal { + switch r.Type { + case ld.R_CALLARM: + r.Done = 0 + + // set up addend for eventual relocation via outer symbol. + rs = r.Sym + + r.Xadd = r.Add + if r.Xadd&0x800000 != 0 { + r.Xadd |= ^0xffffff + } + r.Xadd *= 4 + for rs.Outer != nil { + r.Xadd += ld.Symaddr(rs) - ld.Symaddr(rs.Outer) + rs = rs.Outer + } + + if rs.Type != ld.SHOSTOBJ && rs.Sect == nil { + ld.Diag("missing section for %s", rs.Name) + } + r.Xsym = rs + + // ld64 for arm seems to want the symbol table to contain offset + // into the section rather than pseudo virtual address that contains + // the section load address. + // we need to compensate that by removing the instruction's address + // from addend. + if ld.HEADTYPE == ld.Hdarwin { + r.Xadd -= ld.Symaddr(s) + int64(r.Off) + } + + *val = int64(braddoff(int32(0xff000000&uint32(r.Add)), int32(0xffffff&uint32(r.Xadd/4)))) + return 0 + } + + return -1 + } + + switch r.Type { + case ld.R_CONST: + *val = r.Add + return 0 + + case ld.R_GOTOFF: + *val = ld.Symaddr(r.Sym) + r.Add - ld.Symaddr(ld.Linklookup(ld.Ctxt, ".got", 0)) + return 0 + + // The following three arch specific relocations are only for generation of + // Linux/ARM ELF's PLT entry (3 assembler instruction) + case ld.R_PLT0: // add ip, pc, #0xXX00000 + if ld.Symaddr(ld.Linklookup(ld.Ctxt, ".got.plt", 0)) < ld.Symaddr(ld.Linklookup(ld.Ctxt, ".plt", 0)) { + ld.Diag(".got.plt should be placed after .plt section.") + } + *val = 0xe28fc600 + (0xff & (int64(uint32(ld.Symaddr(r.Sym)-(ld.Symaddr(ld.Linklookup(ld.Ctxt, ".plt", 0))+int64(r.Off))+r.Add)) >> 20)) + return 0 + + case ld.R_PLT1: // add ip, ip, #0xYY000 + *val = 0xe28cca00 + (0xff & (int64(uint32(ld.Symaddr(r.Sym)-(ld.Symaddr(ld.Linklookup(ld.Ctxt, ".plt", 0))+int64(r.Off))+r.Add+4)) >> 12)) + + return 0 + + case ld.R_PLT2: // ldr pc, [ip, #0xZZZ]! + *val = 0xe5bcf000 + (0xfff & int64(uint32(ld.Symaddr(r.Sym)-(ld.Symaddr(ld.Linklookup(ld.Ctxt, ".plt", 0))+int64(r.Off))+r.Add+8))) + + return 0 + + case ld.R_CALLARM: // bl XXXXXX or b YYYYYY + *val = int64(braddoff(int32(0xff000000&uint32(r.Add)), int32(0xffffff&uint32((ld.Symaddr(r.Sym)+int64((uint32(r.Add))*4)-(s.Value+int64(r.Off)))/4)))) + + return 0 + } + + return -1 +} + +func archrelocvariant(r *ld.Reloc, s *ld.LSym, t int64) int64 { + log.Fatalf("unexpected relocation variant") + return t +} + +func addpltreloc(ctxt *ld.Link, plt *ld.LSym, got *ld.LSym, sym *ld.LSym, typ int) *ld.Reloc { + var r *ld.Reloc + + r = ld.Addrel(plt) + r.Sym = got + r.Off = int32(plt.Size) + r.Siz = 4 + r.Type = int32(typ) + r.Add = int64(sym.Got) - 8 + + plt.Reachable = true + plt.Size += 4 + ld.Symgrow(ctxt, plt, plt.Size) + + return r +} + +func addpltsym(ctxt *ld.Link, s *ld.LSym) { + var plt *ld.LSym + var got *ld.LSym + var rel *ld.LSym + + if s.Plt >= 0 { + return + } + + adddynsym(ctxt, s) + + if ld.Iself { + plt = ld.Linklookup(ctxt, ".plt", 0) + got = ld.Linklookup(ctxt, ".got.plt", 0) + rel = ld.Linklookup(ctxt, ".rel.plt", 0) + if plt.Size == 0 { + elfsetupplt() + } + + // .got entry + s.Got = int32(got.Size) + + // In theory, all GOT should point to the first PLT entry, + // Linux/ARM's dynamic linker will do that for us, but FreeBSD/ARM's + // dynamic linker won't, so we'd better do it ourselves. + ld.Addaddrplus(ctxt, got, plt, 0) + + // .plt entry, this depends on the .got entry + s.Plt = int32(plt.Size) + + addpltreloc(ctxt, plt, got, s, ld.R_PLT0) // add lr, pc, #0xXX00000 + addpltreloc(ctxt, plt, got, s, ld.R_PLT1) // add lr, lr, #0xYY000 + addpltreloc(ctxt, plt, got, s, ld.R_PLT2) // ldr pc, [lr, #0xZZZ]! + + // rel + ld.Addaddrplus(ctxt, rel, got, int64(s.Got)) + + ld.Adduint32(ctxt, rel, ld.ELF32_R_INFO(uint32(s.Dynid), ld.R_ARM_JUMP_SLOT)) + } else { + ld.Diag("addpltsym: unsupported binary format") + } +} + +func addgotsyminternal(ctxt *ld.Link, s *ld.LSym) { + var got *ld.LSym + + if s.Got >= 0 { + return + } + + got = ld.Linklookup(ctxt, ".got", 0) + s.Got = int32(got.Size) + + ld.Addaddrplus(ctxt, got, s, 0) + + if ld.Iself { + } else { + ld.Diag("addgotsyminternal: unsupported binary format") + } +} + +func addgotsym(ctxt *ld.Link, s *ld.LSym) { + var got *ld.LSym + var rel *ld.LSym + + if s.Got >= 0 { + return + } + + adddynsym(ctxt, s) + got = ld.Linklookup(ctxt, ".got", 0) + s.Got = int32(got.Size) + ld.Adduint32(ctxt, got, 0) + + if ld.Iself { + rel = ld.Linklookup(ctxt, ".rel", 0) + ld.Addaddrplus(ctxt, rel, got, int64(s.Got)) + ld.Adduint32(ctxt, rel, ld.ELF32_R_INFO(uint32(s.Dynid), ld.R_ARM_GLOB_DAT)) + } else { + ld.Diag("addgotsym: unsupported binary format") + } +} + +func adddynsym(ctxt *ld.Link, s *ld.LSym) { + var d *ld.LSym + var t int + var name string + + if s.Dynid >= 0 { + return + } + + if ld.Iself { + s.Dynid = int32(ld.Nelfsym) + ld.Nelfsym++ + + d = ld.Linklookup(ctxt, ".dynsym", 0) + + /* name */ + name = s.Extname + + ld.Adduint32(ctxt, d, uint32(ld.Addstring(ld.Linklookup(ctxt, ".dynstr", 0), name))) + + /* value */ + if s.Type == ld.SDYNIMPORT { + ld.Adduint32(ctxt, d, 0) + } else { + ld.Addaddr(ctxt, d, s) + } + + /* size */ + ld.Adduint32(ctxt, d, 0) + + /* type */ + t = ld.STB_GLOBAL << 4 + + if (s.Cgoexport&ld.CgoExportDynamic != 0) && s.Type&ld.SMASK == ld.STEXT { + t |= ld.STT_FUNC + } else { + t |= ld.STT_OBJECT + } + ld.Adduint8(ctxt, d, uint8(t)) + ld.Adduint8(ctxt, d, 0) + + /* shndx */ + if s.Type == ld.SDYNIMPORT { + ld.Adduint16(ctxt, d, ld.SHN_UNDEF) + } else { + ld.Adduint16(ctxt, d, 1) + } + } else { + ld.Diag("adddynsym: unsupported binary format") + } +} + +func adddynlib(lib string) { + var s *ld.LSym + + if needlib(lib) == 0 { + return + } + + if ld.Iself { + s = ld.Linklookup(ld.Ctxt, ".dynstr", 0) + if s.Size == 0 { + ld.Addstring(s, "") + } + ld.Elfwritedynent(ld.Linklookup(ld.Ctxt, ".dynamic", 0), ld.DT_NEEDED, uint64(ld.Addstring(s, lib))) + } else if ld.HEADTYPE == ld.Hdarwin { + ld.Machoadddynlib(lib) + } else { + ld.Diag("adddynlib: unsupported binary format") + } +} + +func asmb() { + var symo uint32 + var dwarfoff uint32 + var machlink uint32 + var sect *ld.Section + var sym *ld.LSym + var i int + + if ld.Debug['v'] != 0 { + fmt.Fprintf(&ld.Bso, "%5.2f asmb\n", obj.Cputime()) + } + ld.Bflush(&ld.Bso) + + if ld.Iself { + ld.Asmbelfsetup() + } + + sect = ld.Segtext.Sect + ld.Cseek(int64(sect.Vaddr - ld.Segtext.Vaddr + ld.Segtext.Fileoff)) + ld.Codeblk(int64(sect.Vaddr), int64(sect.Length)) + for sect = sect.Next; sect != nil; sect = sect.Next { + ld.Cseek(int64(sect.Vaddr - ld.Segtext.Vaddr + ld.Segtext.Fileoff)) + ld.Datblk(int64(sect.Vaddr), int64(sect.Length)) + } + + if ld.Segrodata.Filelen > 0 { + if ld.Debug['v'] != 0 { + fmt.Fprintf(&ld.Bso, "%5.2f rodatblk\n", obj.Cputime()) + } + ld.Bflush(&ld.Bso) + + ld.Cseek(int64(ld.Segrodata.Fileoff)) + ld.Datblk(int64(ld.Segrodata.Vaddr), int64(ld.Segrodata.Filelen)) + } + + if ld.Debug['v'] != 0 { + fmt.Fprintf(&ld.Bso, "%5.2f datblk\n", obj.Cputime()) + } + ld.Bflush(&ld.Bso) + + ld.Cseek(int64(ld.Segdata.Fileoff)) + ld.Datblk(int64(ld.Segdata.Vaddr), int64(ld.Segdata.Filelen)) + + machlink = 0 + if ld.HEADTYPE == ld.Hdarwin { + if ld.Debug['v'] != 0 { + fmt.Fprintf(&ld.Bso, "%5.2f dwarf\n", obj.Cputime()) + } + + if ld.Debug['w'] == 0 { + dwarfoff = uint32(ld.Rnd(int64(uint64(ld.HEADR)+ld.Segtext.Length), int64(ld.INITRND)) + ld.Rnd(int64(ld.Segdata.Filelen), int64(ld.INITRND))) + ld.Cseek(int64(dwarfoff)) + + ld.Segdwarf.Fileoff = uint64(ld.Cpos()) + ld.Dwarfemitdebugsections() + ld.Segdwarf.Filelen = uint64(ld.Cpos()) - ld.Segdwarf.Fileoff + } + + machlink = uint32(ld.Domacholink()) + } + + /* output symbol table */ + ld.Symsize = 0 + + ld.Lcsize = 0 + symo = 0 + if ld.Debug['s'] == 0 { + // TODO: rationalize + if ld.Debug['v'] != 0 { + fmt.Fprintf(&ld.Bso, "%5.2f sym\n", obj.Cputime()) + } + ld.Bflush(&ld.Bso) + switch ld.HEADTYPE { + default: + if ld.Iself { + symo = uint32(ld.Segdata.Fileoff + ld.Segdata.Filelen) + symo = uint32(ld.Rnd(int64(symo), int64(ld.INITRND))) + } + + case ld.Hplan9: + symo = uint32(ld.Segdata.Fileoff + ld.Segdata.Filelen) + + case ld.Hdarwin: + symo = uint32(ld.Rnd(int64(uint64(ld.HEADR)+ld.Segtext.Filelen), int64(ld.INITRND)) + ld.Rnd(int64(ld.Segdata.Filelen), int64(ld.INITRND)) + int64(machlink)) + } + + ld.Cseek(int64(symo)) + switch ld.HEADTYPE { + default: + if ld.Iself { + if ld.Debug['v'] != 0 { + fmt.Fprintf(&ld.Bso, "%5.2f elfsym\n", obj.Cputime()) + } + ld.Asmelfsym() + ld.Cflush() + ld.Cwrite(ld.Elfstrdat) + + if ld.Debug['v'] != 0 { + fmt.Fprintf(&ld.Bso, "%5.2f dwarf\n", obj.Cputime()) + } + ld.Dwarfemitdebugsections() + + if ld.Linkmode == ld.LinkExternal { + ld.Elfemitreloc() + } + } + + case ld.Hplan9: + ld.Asmplan9sym() + ld.Cflush() + + sym = ld.Linklookup(ld.Ctxt, "pclntab", 0) + if sym != nil { + ld.Lcsize = int32(len(sym.P)) + for i = 0; int32(i) < ld.Lcsize; i++ { + ld.Cput(uint8(sym.P[i])) + } + + ld.Cflush() + } + + case ld.Hdarwin: + if ld.Linkmode == ld.LinkExternal { + ld.Machoemitreloc() + } + } + } + + ld.Ctxt.Cursym = nil + if ld.Debug['v'] != 0 { + fmt.Fprintf(&ld.Bso, "%5.2f header\n", obj.Cputime()) + } + ld.Bflush(&ld.Bso) + ld.Cseek(0) + switch ld.HEADTYPE { + default: + case ld.Hplan9: /* plan 9 */ + ld.Thearch.Lput(0x647) /* magic */ + ld.Thearch.Lput(uint32(ld.Segtext.Filelen)) /* sizes */ + ld.Thearch.Lput(uint32(ld.Segdata.Filelen)) + ld.Thearch.Lput(uint32(ld.Segdata.Length - ld.Segdata.Filelen)) + ld.Thearch.Lput(uint32(ld.Symsize)) /* nsyms */ + ld.Thearch.Lput(uint32(ld.Entryvalue())) /* va of entry */ + ld.Thearch.Lput(0) + ld.Thearch.Lput(uint32(ld.Lcsize)) + + case ld.Hlinux, + ld.Hfreebsd, + ld.Hnetbsd, + ld.Hopenbsd, + ld.Hnacl: + ld.Asmbelf(int64(symo)) + + case ld.Hdarwin: + ld.Asmbmacho() + } + + ld.Cflush() + if ld.Debug['c'] != 0 { + fmt.Printf("textsize=%d\n", ld.Segtext.Filelen) + fmt.Printf("datsize=%d\n", ld.Segdata.Filelen) + fmt.Printf("bsssize=%d\n", ld.Segdata.Length-ld.Segdata.Filelen) + fmt.Printf("symsize=%d\n", ld.Symsize) + fmt.Printf("lcsize=%d\n", ld.Lcsize) + fmt.Printf("total=%d\n", ld.Segtext.Filelen+ld.Segdata.Length+uint64(ld.Symsize)+uint64(ld.Lcsize)) + } +} diff --git a/src/cmd/new5l/l.go b/src/cmd/new5l/l.go new file mode 100644 index 0000000000..9eb078dcc5 --- /dev/null +++ b/src/cmd/new5l/l.go @@ -0,0 +1,78 @@ +// Inferno utils/5l/asm.c +// http://code.google.com/p/inferno-os/source/browse/utils/5l/asm.c +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package main + +// Writing object files. + +// Inferno utils/5l/l.h +// http://code.google.com/p/inferno-os/source/browse/utils/5l/l.h +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +const ( + thechar = '5' + PtrSize = 4 + IntSize = 4 + RegSize = 4 + MaxAlign = 8 + FuncAlign = 4 + MINLC = 4 +) + +/* Used by ../ld/dwarf.c */ +const ( + DWARFREGSP = 13 +) diff --git a/src/cmd/new5l/obj.go b/src/cmd/new5l/obj.go new file mode 100644 index 0000000000..98ebbc23cc --- /dev/null +++ b/src/cmd/new5l/obj.go @@ -0,0 +1,182 @@ +// Inferno utils/5l/obj.c +// http://code.google.com/p/inferno-os/source/browse/utils/5l/obj.c +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package main + +import ( + "cmd/internal/obj" + "fmt" + "log" +) +import "cmd/internal/ld" + +// Reading object files. + +func main() { + linkarchinit() + ld.Ldmain() +} + +func linkarchinit() { + ld.Thestring = "arm" + ld.Thelinkarch = &ld.Linkarm + + ld.Thearch.Thechar = thechar + ld.Thearch.Ptrsize = ld.Thelinkarch.Ptrsize + ld.Thearch.Intsize = ld.Thelinkarch.Ptrsize + ld.Thearch.Regsize = ld.Thelinkarch.Regsize + ld.Thearch.Funcalign = FuncAlign + ld.Thearch.Maxalign = MaxAlign + ld.Thearch.Minlc = MINLC + ld.Thearch.Dwarfregsp = DWARFREGSP + + ld.Thearch.Adddynlib = adddynlib + ld.Thearch.Adddynrel = adddynrel + ld.Thearch.Adddynsym = adddynsym + ld.Thearch.Archinit = archinit + ld.Thearch.Archreloc = archreloc + ld.Thearch.Archrelocvariant = archrelocvariant + ld.Thearch.Asmb = asmb + ld.Thearch.Elfreloc1 = elfreloc1 + ld.Thearch.Elfsetupplt = elfsetupplt + ld.Thearch.Gentext = gentext + ld.Thearch.Machoreloc1 = machoreloc1 + ld.Thearch.Lput = ld.Lputl + ld.Thearch.Wput = ld.Wputl + ld.Thearch.Vput = ld.Vputl + + ld.Thearch.Linuxdynld = "/lib/ld-linux.so.3" // 2 for OABI, 3 for EABI + ld.Thearch.Freebsddynld = "/usr/libexec/ld-elf.so.1" + ld.Thearch.Openbsddynld = "XXX" + ld.Thearch.Netbsddynld = "/libexec/ld.elf_so" + ld.Thearch.Dragonflydynld = "XXX" + ld.Thearch.Solarisdynld = "XXX" +} + +func archinit() { + var s *ld.LSym + + // getgoextlinkenabled is based on GO_EXTLINK_ENABLED when + // Go was built; see ../../make.bash. + if ld.Linkmode == ld.LinkAuto && obj.Getgoextlinkenabled() == "0" { + ld.Linkmode = ld.LinkInternal + } + + switch ld.HEADTYPE { + default: + if ld.Linkmode == ld.LinkAuto { + ld.Linkmode = ld.LinkInternal + } + if ld.Linkmode == ld.LinkExternal && obj.Getgoextlinkenabled() != "1" { + log.Fatalf("cannot use -linkmode=external with -H %s", ld.Headstr(int(ld.HEADTYPE))) + } + + case ld.Hlinux, + ld.Hfreebsd, + ld.Hnacl, + ld.Hdarwin: + break + } + + switch ld.HEADTYPE { + default: + ld.Diag("unknown -H option") + ld.Errorexit() + fallthrough + + case ld.Hplan9: /* plan 9 */ + ld.HEADR = 32 + + if ld.INITTEXT == -1 { + ld.INITTEXT = 4128 + } + if ld.INITDAT == -1 { + ld.INITDAT = 0 + } + if ld.INITRND == -1 { + ld.INITRND = 4096 + } + + case ld.Hlinux, /* arm elf */ + ld.Hfreebsd, + ld.Hnetbsd: + ld.Debug['d'] = 0 + // with dynamic linking + ld.Elfinit() + ld.HEADR = ld.ELFRESERVE + if ld.INITTEXT == -1 { + ld.INITTEXT = 0x10000 + int64(ld.HEADR) + } + if ld.INITDAT == -1 { + ld.INITDAT = 0 + } + if ld.INITRND == -1 { + ld.INITRND = 4096 + } + + case ld.Hnacl: + ld.Elfinit() + ld.HEADR = 0x10000 + ld.Funcalign = 16 + if ld.INITTEXT == -1 { + ld.INITTEXT = 0x20000 + } + if ld.INITDAT == -1 { + ld.INITDAT = 0 + } + if ld.INITRND == -1 { + ld.INITRND = 0x10000 + } + + case ld.Hdarwin: /* apple MACH */ + ld.Debug['w'] = 1 // disable DWARF generataion + ld.Machoinit() + ld.HEADR = ld.INITIAL_MACHO_HEADR + if ld.INITTEXT == -1 { + ld.INITTEXT = 4096 + int64(ld.HEADR) + } + if ld.INITDAT == -1 { + ld.INITDAT = 0 + } + if ld.INITRND == -1 { + ld.INITRND = 4096 + } + } + + if ld.INITDAT != 0 && ld.INITRND != 0 { + fmt.Printf("warning: -D0x%x is ignored because of -R0x%x\n", uint64(ld.INITDAT), uint32(ld.INITRND)) + } + + // embed goarm to runtime.goarm + s = ld.Linklookup(ld.Ctxt, "runtime.goarm", 0) + + s.Type = ld.SRODATA + ld.Adduint8(ld.Ctxt, s, uint8(ld.Ctxt.Goarm)) +} diff --git a/src/cmd/new6l/asm.go b/src/cmd/new6l/asm.go new file mode 100644 index 0000000000..72e9dbf26d --- /dev/null +++ b/src/cmd/new6l/asm.go @@ -0,0 +1,829 @@ +// Inferno utils/6l/asm.c +// http://code.google.com/p/inferno-os/source/browse/utils/6l/asm.c +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package main + +import ( + "cmd/internal/obj" + "fmt" + "log" +) +import "cmd/internal/ld" + +func PADDR(x uint32) uint32 { + return x &^ 0x80000000 +} + +var zeroes string + +func needlib(name string) int { + var p string + var s *ld.LSym + + if name[0] == '\x00' { + return 0 + } + + /* reuse hash code in symbol table */ + p = fmt.Sprintf(".elfload.%s", name) + + s = ld.Linklookup(ld.Ctxt, p, 0) + + if s.Type == 0 { + s.Type = 100 // avoid SDATA, etc. + return 1 + } + + return 0 +} + +func gentext() { +} + +func adddynrela(rela *ld.LSym, s *ld.LSym, r *ld.Reloc) { + ld.Addaddrplus(ld.Ctxt, rela, s, int64(r.Off)) + ld.Adduint64(ld.Ctxt, rela, ld.R_X86_64_RELATIVE) + ld.Addaddrplus(ld.Ctxt, rela, r.Sym, r.Add) // Addend +} + +func adddynrel(s *ld.LSym, r *ld.Reloc) { + var targ *ld.LSym + var rela *ld.LSym + var got *ld.LSym + + targ = r.Sym + ld.Ctxt.Cursym = s + + switch r.Type { + default: + if r.Type >= 256 { + ld.Diag("unexpected relocation type %d", r.Type) + return + } + + // Handle relocations found in ELF object files. + case 256 + ld.R_X86_64_PC32: + if targ.Type == ld.SDYNIMPORT { + ld.Diag("unexpected R_X86_64_PC32 relocation for dynamic symbol %s", targ.Name) + } + if targ.Type == 0 || targ.Type == ld.SXREF { + ld.Diag("unknown symbol %s in pcrel", targ.Name) + } + r.Type = ld.R_PCREL + r.Add += 4 + return + + case 256 + ld.R_X86_64_PLT32: + r.Type = ld.R_PCREL + r.Add += 4 + if targ.Type == ld.SDYNIMPORT { + addpltsym(targ) + r.Sym = ld.Linklookup(ld.Ctxt, ".plt", 0) + r.Add += int64(targ.Plt) + } + + return + + case 256 + ld.R_X86_64_GOTPCREL: + if targ.Type != ld.SDYNIMPORT { + // have symbol + if r.Off >= 2 && s.P[r.Off-2] == 0x8b { + // turn MOVQ of GOT entry into LEAQ of symbol itself + s.P[r.Off-2] = 0x8d + + r.Type = ld.R_PCREL + r.Add += 4 + return + } + } + + // fall back to using GOT and hope for the best (CMOV*) + // TODO: just needs relocation, no need to put in .dynsym + addgotsym(targ) + + r.Type = ld.R_PCREL + r.Sym = ld.Linklookup(ld.Ctxt, ".got", 0) + r.Add += 4 + r.Add += int64(targ.Got) + return + + case 256 + ld.R_X86_64_64: + if targ.Type == ld.SDYNIMPORT { + ld.Diag("unexpected R_X86_64_64 relocation for dynamic symbol %s", targ.Name) + } + r.Type = ld.R_ADDR + return + + // TODO: What is the difference between all these? + // Handle relocations found in Mach-O object files. + case 512 + ld.MACHO_X86_64_RELOC_UNSIGNED*2 + 0, + 512 + ld.MACHO_X86_64_RELOC_SIGNED*2 + 0, + 512 + ld.MACHO_X86_64_RELOC_BRANCH*2 + 0: + r.Type = ld.R_ADDR + + if targ.Type == ld.SDYNIMPORT { + ld.Diag("unexpected reloc for dynamic symbol %s", targ.Name) + } + return + + case 512 + ld.MACHO_X86_64_RELOC_BRANCH*2 + 1: + if targ.Type == ld.SDYNIMPORT { + addpltsym(targ) + r.Sym = ld.Linklookup(ld.Ctxt, ".plt", 0) + r.Add = int64(targ.Plt) + r.Type = ld.R_PCREL + return + } + fallthrough + + // fall through + case 512 + ld.MACHO_X86_64_RELOC_UNSIGNED*2 + 1, + 512 + ld.MACHO_X86_64_RELOC_SIGNED*2 + 1, + 512 + ld.MACHO_X86_64_RELOC_SIGNED_1*2 + 1, + 512 + ld.MACHO_X86_64_RELOC_SIGNED_2*2 + 1, + 512 + ld.MACHO_X86_64_RELOC_SIGNED_4*2 + 1: + r.Type = ld.R_PCREL + + if targ.Type == ld.SDYNIMPORT { + ld.Diag("unexpected pc-relative reloc for dynamic symbol %s", targ.Name) + } + return + + case 512 + ld.MACHO_X86_64_RELOC_GOT_LOAD*2 + 1: + if targ.Type != ld.SDYNIMPORT { + // have symbol + // turn MOVQ of GOT entry into LEAQ of symbol itself + if r.Off < 2 || s.P[r.Off-2] != 0x8b { + ld.Diag("unexpected GOT_LOAD reloc for non-dynamic symbol %s", targ.Name) + return + } + + s.P[r.Off-2] = 0x8d + r.Type = ld.R_PCREL + return + } + fallthrough + + // fall through + case 512 + ld.MACHO_X86_64_RELOC_GOT*2 + 1: + if targ.Type != ld.SDYNIMPORT { + ld.Diag("unexpected GOT reloc for non-dynamic symbol %s", targ.Name) + } + addgotsym(targ) + r.Type = ld.R_PCREL + r.Sym = ld.Linklookup(ld.Ctxt, ".got", 0) + r.Add += int64(targ.Got) + return + } + + // Handle references to ELF symbols from our own object files. + if targ.Type != ld.SDYNIMPORT { + return + } + + switch r.Type { + case ld.R_CALL, + ld.R_PCREL: + addpltsym(targ) + r.Sym = ld.Linklookup(ld.Ctxt, ".plt", 0) + r.Add = int64(targ.Plt) + return + + case ld.R_ADDR: + if s.Type == ld.STEXT && ld.Iself { + // The code is asking for the address of an external + // function. We provide it with the address of the + // correspondent GOT symbol. + addgotsym(targ) + + r.Sym = ld.Linklookup(ld.Ctxt, ".got", 0) + r.Add += int64(targ.Got) + return + } + + if s.Type != ld.SDATA { + break + } + if ld.Iself { + adddynsym(ld.Ctxt, targ) + rela = ld.Linklookup(ld.Ctxt, ".rela", 0) + ld.Addaddrplus(ld.Ctxt, rela, s, int64(r.Off)) + if r.Siz == 8 { + ld.Adduint64(ld.Ctxt, rela, ld.ELF64_R_INFO(uint32(targ.Dynid), ld.R_X86_64_64)) + } else { + ld.Adduint64(ld.Ctxt, rela, ld.ELF64_R_INFO(uint32(targ.Dynid), ld.R_X86_64_32)) + } + ld.Adduint64(ld.Ctxt, rela, uint64(r.Add)) + r.Type = 256 // ignore during relocsym + return + } + + if ld.HEADTYPE == ld.Hdarwin && s.Size == int64(ld.Thearch.Ptrsize) && r.Off == 0 { + // Mach-O relocations are a royal pain to lay out. + // They use a compact stateful bytecode representation + // that is too much bother to deal with. + // Instead, interpret the C declaration + // void *_Cvar_stderr = &stderr; + // as making _Cvar_stderr the name of a GOT entry + // for stderr. This is separate from the usual GOT entry, + // just in case the C code assigns to the variable, + // and of course it only works for single pointers, + // but we only need to support cgo and that's all it needs. + adddynsym(ld.Ctxt, targ) + + got = ld.Linklookup(ld.Ctxt, ".got", 0) + s.Type = got.Type | ld.SSUB + s.Outer = got + s.Sub = got.Sub + got.Sub = s + s.Value = got.Size + ld.Adduint64(ld.Ctxt, got, 0) + ld.Adduint32(ld.Ctxt, ld.Linklookup(ld.Ctxt, ".linkedit.got", 0), uint32(targ.Dynid)) + r.Type = 256 // ignore during relocsym + return + } + } + + ld.Ctxt.Cursym = s + ld.Diag("unsupported relocation for dynamic symbol %s (type=%d stype=%d)", targ.Name, r.Type, targ.Type) +} + +func elfreloc1(r *ld.Reloc, sectoff int64) int { + var elfsym int32 + + ld.Thearch.Vput(uint64(sectoff)) + + elfsym = r.Xsym.Elfsym + switch r.Type { + default: + return -1 + + case ld.R_ADDR: + if r.Siz == 4 { + ld.Thearch.Vput(ld.R_X86_64_32 | uint64(elfsym)<<32) + } else if r.Siz == 8 { + ld.Thearch.Vput(ld.R_X86_64_64 | uint64(elfsym)<<32) + } else { + return -1 + } + + case ld.R_TLS_LE: + if r.Siz == 4 { + ld.Thearch.Vput(ld.R_X86_64_TPOFF32 | uint64(elfsym)<<32) + } else { + return -1 + } + + case ld.R_CALL: + if r.Siz == 4 { + if r.Xsym.Type == ld.SDYNIMPORT { + ld.Thearch.Vput(ld.R_X86_64_GOTPCREL | uint64(elfsym)<<32) + } else { + ld.Thearch.Vput(ld.R_X86_64_PC32 | uint64(elfsym)<<32) + } + } else { + return -1 + } + + case ld.R_PCREL: + if r.Siz == 4 { + ld.Thearch.Vput(ld.R_X86_64_PC32 | uint64(elfsym)<<32) + } else { + return -1 + } + + case ld.R_TLS: + if r.Siz == 4 { + if ld.Flag_shared != 0 { + ld.Thearch.Vput(ld.R_X86_64_GOTTPOFF | uint64(elfsym)<<32) + } else { + ld.Thearch.Vput(ld.R_X86_64_TPOFF32 | uint64(elfsym)<<32) + } + } else { + return -1 + } + } + + ld.Thearch.Vput(uint64(r.Xadd)) + return 0 +} + +func machoreloc1(r *ld.Reloc, sectoff int64) int { + var v uint32 + var rs *ld.LSym + + rs = r.Xsym + + if rs.Type == ld.SHOSTOBJ || r.Type == ld.R_PCREL { + if rs.Dynid < 0 { + ld.Diag("reloc %d to non-macho symbol %s type=%d", r.Type, rs.Name, rs.Type) + return -1 + } + + v = uint32(rs.Dynid) + v |= 1 << 27 // external relocation + } else { + v = uint32((rs.Sect.(*ld.Section)).Extnum) + if v == 0 { + ld.Diag("reloc %d to symbol %s in non-macho section %s type=%d", r.Type, rs.Name, (rs.Sect.(*ld.Section)).Name, rs.Type) + return -1 + } + } + + switch r.Type { + default: + return -1 + + case ld.R_ADDR: + v |= ld.MACHO_X86_64_RELOC_UNSIGNED << 28 + + case ld.R_CALL: + v |= 1 << 24 // pc-relative bit + v |= ld.MACHO_X86_64_RELOC_BRANCH << 28 + + // NOTE: Only works with 'external' relocation. Forced above. + case ld.R_PCREL: + v |= 1 << 24 // pc-relative bit + v |= ld.MACHO_X86_64_RELOC_SIGNED << 28 + } + + switch r.Siz { + default: + return -1 + + case 1: + v |= 0 << 25 + + case 2: + v |= 1 << 25 + + case 4: + v |= 2 << 25 + + case 8: + v |= 3 << 25 + } + + ld.Thearch.Lput(uint32(sectoff)) + ld.Thearch.Lput(v) + return 0 +} + +func archreloc(r *ld.Reloc, s *ld.LSym, val *int64) int { + return -1 +} + +func archrelocvariant(r *ld.Reloc, s *ld.LSym, t int64) int64 { + log.Fatalf("unexpected relocation variant") + return t +} + +func elfsetupplt() { + var plt *ld.LSym + var got *ld.LSym + + plt = ld.Linklookup(ld.Ctxt, ".plt", 0) + got = ld.Linklookup(ld.Ctxt, ".got.plt", 0) + if plt.Size == 0 { + // pushq got+8(IP) + ld.Adduint8(ld.Ctxt, plt, 0xff) + + ld.Adduint8(ld.Ctxt, plt, 0x35) + ld.Addpcrelplus(ld.Ctxt, plt, got, 8) + + // jmpq got+16(IP) + ld.Adduint8(ld.Ctxt, plt, 0xff) + + ld.Adduint8(ld.Ctxt, plt, 0x25) + ld.Addpcrelplus(ld.Ctxt, plt, got, 16) + + // nopl 0(AX) + ld.Adduint32(ld.Ctxt, plt, 0x00401f0f) + + // assume got->size == 0 too + ld.Addaddrplus(ld.Ctxt, got, ld.Linklookup(ld.Ctxt, ".dynamic", 0), 0) + + ld.Adduint64(ld.Ctxt, got, 0) + ld.Adduint64(ld.Ctxt, got, 0) + } +} + +func addpltsym(s *ld.LSym) { + if s.Plt >= 0 { + return + } + + adddynsym(ld.Ctxt, s) + + if ld.Iself { + var plt *ld.LSym + var got *ld.LSym + var rela *ld.LSym + + plt = ld.Linklookup(ld.Ctxt, ".plt", 0) + got = ld.Linklookup(ld.Ctxt, ".got.plt", 0) + rela = ld.Linklookup(ld.Ctxt, ".rela.plt", 0) + if plt.Size == 0 { + elfsetupplt() + } + + // jmpq *got+size(IP) + ld.Adduint8(ld.Ctxt, plt, 0xff) + + ld.Adduint8(ld.Ctxt, plt, 0x25) + ld.Addpcrelplus(ld.Ctxt, plt, got, got.Size) + + // add to got: pointer to current pos in plt + ld.Addaddrplus(ld.Ctxt, got, plt, plt.Size) + + // pushq $x + ld.Adduint8(ld.Ctxt, plt, 0x68) + + ld.Adduint32(ld.Ctxt, plt, uint32((got.Size-24-8)/8)) + + // jmpq .plt + ld.Adduint8(ld.Ctxt, plt, 0xe9) + + ld.Adduint32(ld.Ctxt, plt, uint32(-(plt.Size + 4))) + + // rela + ld.Addaddrplus(ld.Ctxt, rela, got, got.Size-8) + + ld.Adduint64(ld.Ctxt, rela, ld.ELF64_R_INFO(uint32(s.Dynid), ld.R_X86_64_JMP_SLOT)) + ld.Adduint64(ld.Ctxt, rela, 0) + + s.Plt = int32(plt.Size - 16) + } else if ld.HEADTYPE == ld.Hdarwin { + // To do lazy symbol lookup right, we're supposed + // to tell the dynamic loader which library each + // symbol comes from and format the link info + // section just so. I'm too lazy (ha!) to do that + // so for now we'll just use non-lazy pointers, + // which don't need to be told which library to use. + // + // http://networkpx.blogspot.com/2009/09/about-lcdyldinfoonly-command.html + // has details about what we're avoiding. + + var plt *ld.LSym + + addgotsym(s) + plt = ld.Linklookup(ld.Ctxt, ".plt", 0) + + ld.Adduint32(ld.Ctxt, ld.Linklookup(ld.Ctxt, ".linkedit.plt", 0), uint32(s.Dynid)) + + // jmpq *got+size(IP) + s.Plt = int32(plt.Size) + + ld.Adduint8(ld.Ctxt, plt, 0xff) + ld.Adduint8(ld.Ctxt, plt, 0x25) + ld.Addpcrelplus(ld.Ctxt, plt, ld.Linklookup(ld.Ctxt, ".got", 0), int64(s.Got)) + } else { + ld.Diag("addpltsym: unsupported binary format") + } +} + +func addgotsym(s *ld.LSym) { + var got *ld.LSym + var rela *ld.LSym + + if s.Got >= 0 { + return + } + + adddynsym(ld.Ctxt, s) + got = ld.Linklookup(ld.Ctxt, ".got", 0) + s.Got = int32(got.Size) + ld.Adduint64(ld.Ctxt, got, 0) + + if ld.Iself { + rela = ld.Linklookup(ld.Ctxt, ".rela", 0) + ld.Addaddrplus(ld.Ctxt, rela, got, int64(s.Got)) + ld.Adduint64(ld.Ctxt, rela, ld.ELF64_R_INFO(uint32(s.Dynid), ld.R_X86_64_GLOB_DAT)) + ld.Adduint64(ld.Ctxt, rela, 0) + } else if ld.HEADTYPE == ld.Hdarwin { + ld.Adduint32(ld.Ctxt, ld.Linklookup(ld.Ctxt, ".linkedit.got", 0), uint32(s.Dynid)) + } else { + ld.Diag("addgotsym: unsupported binary format") + } +} + +func adddynsym(ctxt *ld.Link, s *ld.LSym) { + var d *ld.LSym + var t int + var name string + + if s.Dynid >= 0 { + return + } + + if ld.Iself { + s.Dynid = int32(ld.Nelfsym) + ld.Nelfsym++ + + d = ld.Linklookup(ctxt, ".dynsym", 0) + + name = s.Extname + ld.Adduint32(ctxt, d, uint32(ld.Addstring(ld.Linklookup(ctxt, ".dynstr", 0), name))) + + /* type */ + t = ld.STB_GLOBAL << 4 + + if s.Cgoexport != 0 && s.Type&ld.SMASK == ld.STEXT { + t |= ld.STT_FUNC + } else { + t |= ld.STT_OBJECT + } + ld.Adduint8(ctxt, d, uint8(t)) + + /* reserved */ + ld.Adduint8(ctxt, d, 0) + + /* section where symbol is defined */ + if s.Type == ld.SDYNIMPORT { + ld.Adduint16(ctxt, d, ld.SHN_UNDEF) + } else { + ld.Adduint16(ctxt, d, 1) + } + + /* value */ + if s.Type == ld.SDYNIMPORT { + ld.Adduint64(ctxt, d, 0) + } else { + ld.Addaddr(ctxt, d, s) + } + + /* size of object */ + ld.Adduint64(ctxt, d, uint64(s.Size)) + + if s.Cgoexport&ld.CgoExportDynamic == 0 && s.Dynimplib != "" && needlib(s.Dynimplib) != 0 { + ld.Elfwritedynent(ld.Linklookup(ctxt, ".dynamic", 0), ld.DT_NEEDED, uint64(ld.Addstring(ld.Linklookup(ctxt, ".dynstr", 0), s.Dynimplib))) + } + } else if ld.HEADTYPE == ld.Hdarwin { + ld.Diag("adddynsym: missed symbol %s (%s)", s.Name, s.Extname) + } else if ld.HEADTYPE == ld.Hwindows { + } else // already taken care of + { + ld.Diag("adddynsym: unsupported binary format") + } +} + +func adddynlib(lib string) { + var s *ld.LSym + + if needlib(lib) == 0 { + return + } + + if ld.Iself { + s = ld.Linklookup(ld.Ctxt, ".dynstr", 0) + if s.Size == 0 { + ld.Addstring(s, "") + } + ld.Elfwritedynent(ld.Linklookup(ld.Ctxt, ".dynamic", 0), ld.DT_NEEDED, uint64(ld.Addstring(s, lib))) + } else if ld.HEADTYPE == ld.Hdarwin { + ld.Machoadddynlib(lib) + } else { + ld.Diag("adddynlib: unsupported binary format") + } +} + +func asmb() { + var magic int32 + var i int + var vl int64 + var symo int64 + var dwarfoff int64 + var machlink int64 + var sect *ld.Section + var sym *ld.LSym + + if ld.Debug['v'] != 0 { + fmt.Fprintf(&ld.Bso, "%5.2f asmb\n", obj.Cputime()) + } + ld.Bflush(&ld.Bso) + + if ld.Debug['v'] != 0 { + fmt.Fprintf(&ld.Bso, "%5.2f codeblk\n", obj.Cputime()) + } + ld.Bflush(&ld.Bso) + + if ld.Iself { + ld.Asmbelfsetup() + } + + sect = ld.Segtext.Sect + ld.Cseek(int64(sect.Vaddr - ld.Segtext.Vaddr + ld.Segtext.Fileoff)) + ld.Codeblk(int64(sect.Vaddr), int64(sect.Length)) + for sect = sect.Next; sect != nil; sect = sect.Next { + ld.Cseek(int64(sect.Vaddr - ld.Segtext.Vaddr + ld.Segtext.Fileoff)) + ld.Datblk(int64(sect.Vaddr), int64(sect.Length)) + } + + if ld.Segrodata.Filelen > 0 { + if ld.Debug['v'] != 0 { + fmt.Fprintf(&ld.Bso, "%5.2f rodatblk\n", obj.Cputime()) + } + ld.Bflush(&ld.Bso) + + ld.Cseek(int64(ld.Segrodata.Fileoff)) + ld.Datblk(int64(ld.Segrodata.Vaddr), int64(ld.Segrodata.Filelen)) + } + + if ld.Debug['v'] != 0 { + fmt.Fprintf(&ld.Bso, "%5.2f datblk\n", obj.Cputime()) + } + ld.Bflush(&ld.Bso) + + ld.Cseek(int64(ld.Segdata.Fileoff)) + ld.Datblk(int64(ld.Segdata.Vaddr), int64(ld.Segdata.Filelen)) + + machlink = 0 + if ld.HEADTYPE == ld.Hdarwin { + if ld.Debug['v'] != 0 { + fmt.Fprintf(&ld.Bso, "%5.2f dwarf\n", obj.Cputime()) + } + + dwarfoff = ld.Rnd(int64(uint64(ld.HEADR)+ld.Segtext.Length), int64(ld.INITRND)) + ld.Rnd(int64(ld.Segdata.Filelen), int64(ld.INITRND)) + ld.Cseek(dwarfoff) + + ld.Segdwarf.Fileoff = uint64(ld.Cpos()) + ld.Dwarfemitdebugsections() + ld.Segdwarf.Filelen = uint64(ld.Cpos()) - ld.Segdwarf.Fileoff + + machlink = ld.Domacholink() + } + + switch ld.HEADTYPE { + default: + ld.Diag("unknown header type %d", ld.HEADTYPE) + fallthrough + + case ld.Hplan9, + ld.Helf: + break + + case ld.Hdarwin: + ld.Debug['8'] = 1 /* 64-bit addresses */ + + case ld.Hlinux, + ld.Hfreebsd, + ld.Hnetbsd, + ld.Hopenbsd, + ld.Hdragonfly, + ld.Hsolaris: + ld.Debug['8'] = 1 /* 64-bit addresses */ + + case ld.Hnacl, + ld.Hwindows: + break + } + + ld.Symsize = 0 + ld.Spsize = 0 + ld.Lcsize = 0 + symo = 0 + if ld.Debug['s'] == 0 { + if ld.Debug['v'] != 0 { + fmt.Fprintf(&ld.Bso, "%5.2f sym\n", obj.Cputime()) + } + ld.Bflush(&ld.Bso) + switch ld.HEADTYPE { + default: + case ld.Hplan9, + ld.Helf: + ld.Debug['s'] = 1 + symo = int64(ld.Segdata.Fileoff + ld.Segdata.Filelen) + + case ld.Hdarwin: + symo = int64(ld.Segdata.Fileoff + uint64(ld.Rnd(int64(ld.Segdata.Filelen), int64(ld.INITRND))) + uint64(machlink)) + + case ld.Hlinux, + ld.Hfreebsd, + ld.Hnetbsd, + ld.Hopenbsd, + ld.Hdragonfly, + ld.Hsolaris, + ld.Hnacl: + symo = int64(ld.Segdata.Fileoff + ld.Segdata.Filelen) + symo = ld.Rnd(symo, int64(ld.INITRND)) + + case ld.Hwindows: + symo = int64(ld.Segdata.Fileoff + ld.Segdata.Filelen) + symo = ld.Rnd(symo, ld.PEFILEALIGN) + } + + ld.Cseek(symo) + switch ld.HEADTYPE { + default: + if ld.Iself { + ld.Cseek(symo) + ld.Asmelfsym() + ld.Cflush() + ld.Cwrite(ld.Elfstrdat) + + if ld.Debug['v'] != 0 { + fmt.Fprintf(&ld.Bso, "%5.2f dwarf\n", obj.Cputime()) + } + + ld.Dwarfemitdebugsections() + + if ld.Linkmode == ld.LinkExternal { + ld.Elfemitreloc() + } + } + + case ld.Hplan9: + ld.Asmplan9sym() + ld.Cflush() + + sym = ld.Linklookup(ld.Ctxt, "pclntab", 0) + if sym != nil { + ld.Lcsize = int32(len(sym.P)) + for i = 0; int32(i) < ld.Lcsize; i++ { + ld.Cput(uint8(sym.P[i])) + } + + ld.Cflush() + } + + case ld.Hwindows: + if ld.Debug['v'] != 0 { + fmt.Fprintf(&ld.Bso, "%5.2f dwarf\n", obj.Cputime()) + } + + ld.Dwarfemitdebugsections() + + case ld.Hdarwin: + if ld.Linkmode == ld.LinkExternal { + ld.Machoemitreloc() + } + } + } + + if ld.Debug['v'] != 0 { + fmt.Fprintf(&ld.Bso, "%5.2f headr\n", obj.Cputime()) + } + ld.Bflush(&ld.Bso) + ld.Cseek(0) + switch ld.HEADTYPE { + default: + case ld.Hplan9: /* plan9 */ + magic = 4*26*26 + 7 + + magic |= 0x00008000 /* fat header */ + ld.Lputb(uint32(magic)) /* magic */ + ld.Lputb(uint32(ld.Segtext.Filelen)) /* sizes */ + ld.Lputb(uint32(ld.Segdata.Filelen)) + ld.Lputb(uint32(ld.Segdata.Length - ld.Segdata.Filelen)) + ld.Lputb(uint32(ld.Symsize)) /* nsyms */ + vl = ld.Entryvalue() + ld.Lputb(PADDR(uint32(vl))) /* va of entry */ + ld.Lputb(uint32(ld.Spsize)) /* sp offsets */ + ld.Lputb(uint32(ld.Lcsize)) /* line offsets */ + ld.Vputb(uint64(vl)) /* va of entry */ + + case ld.Hdarwin: + ld.Asmbmacho() + + case ld.Hlinux, + ld.Hfreebsd, + ld.Hnetbsd, + ld.Hopenbsd, + ld.Hdragonfly, + ld.Hsolaris, + ld.Hnacl: + ld.Asmbelf(symo) + + case ld.Hwindows: + ld.Asmbpe() + } + + ld.Cflush() +} diff --git a/src/cmd/new6l/l.go b/src/cmd/new6l/l.go new file mode 100644 index 0000000000..f698679aac --- /dev/null +++ b/src/cmd/new6l/l.go @@ -0,0 +1,78 @@ +// Inferno utils/6l/asm.c +// http://code.google.com/p/inferno-os/source/browse/utils/6l/asm.c +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package main + +// Writing object files. + +// Inferno utils/6l/l.h +// http://code.google.com/p/inferno-os/source/browse/utils/6l/l.h +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +const ( + thechar = '6' + MaxAlign = 32 + FuncAlign = 16 +) + +const ( + MINLC = 1 +) + +/* Used by ../ld/dwarf.c */ +const ( + DWARFREGSP = 7 +) diff --git a/src/cmd/new6l/obj.go b/src/cmd/new6l/obj.go new file mode 100644 index 0000000000..a34e30d447 --- /dev/null +++ b/src/cmd/new6l/obj.go @@ -0,0 +1,215 @@ +// Inferno utils/6l/obj.c +// http://code.google.com/p/inferno-os/source/browse/utils/6l/obj.c +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package main + +import ( + "cmd/internal/obj" + "fmt" + "log" +) +import "cmd/internal/ld" + +// Reading object files. + +func main() { + linkarchinit() + ld.Ldmain() +} + +func linkarchinit() { + ld.Thestring = "amd64" + ld.Thelinkarch = &ld.Linkamd64 + if obj.Getgoarch() == "amd64p32" { + ld.Thelinkarch = &ld.Linkamd64p32 + } + + ld.Thearch.Thechar = thechar + ld.Thearch.Ptrsize = ld.Thelinkarch.Ptrsize + ld.Thearch.Intsize = ld.Thelinkarch.Ptrsize + ld.Thearch.Regsize = ld.Thelinkarch.Regsize + ld.Thearch.Funcalign = FuncAlign + ld.Thearch.Maxalign = MaxAlign + ld.Thearch.Minlc = MINLC + ld.Thearch.Dwarfregsp = DWARFREGSP + + ld.Thearch.Adddynlib = adddynlib + ld.Thearch.Adddynrel = adddynrel + ld.Thearch.Adddynsym = adddynsym + ld.Thearch.Archinit = archinit + ld.Thearch.Archreloc = archreloc + ld.Thearch.Archrelocvariant = archrelocvariant + ld.Thearch.Asmb = asmb + ld.Thearch.Elfreloc1 = elfreloc1 + ld.Thearch.Elfsetupplt = elfsetupplt + ld.Thearch.Gentext = gentext + ld.Thearch.Machoreloc1 = machoreloc1 + ld.Thearch.Lput = ld.Lputl + ld.Thearch.Wput = ld.Wputl + ld.Thearch.Vput = ld.Vputl + + ld.Thearch.Linuxdynld = "/lib64/ld-linux-x86-64.so.2" + ld.Thearch.Freebsddynld = "/libexec/ld-elf.so.1" + ld.Thearch.Openbsddynld = "/usr/libexec/ld.so" + ld.Thearch.Netbsddynld = "/libexec/ld.elf_so" + ld.Thearch.Dragonflydynld = "/usr/libexec/ld-elf.so.2" + ld.Thearch.Solarisdynld = "/lib/amd64/ld.so.1" +} + +func archinit() { + // getgoextlinkenabled is based on GO_EXTLINK_ENABLED when + // Go was built; see ../../make.bash. + if ld.Linkmode == ld.LinkAuto && obj.Getgoextlinkenabled() == "0" { + ld.Linkmode = ld.LinkInternal + } + + if ld.Flag_shared != 0 { + ld.Linkmode = ld.LinkExternal + } + + switch ld.HEADTYPE { + default: + if ld.Linkmode == ld.LinkAuto { + ld.Linkmode = ld.LinkInternal + } + if ld.Linkmode == ld.LinkExternal && obj.Getgoextlinkenabled() != "1" { + log.Fatalf("cannot use -linkmode=external with -H %s", ld.Headstr(int(ld.HEADTYPE))) + } + + case ld.Hdarwin, + ld.Hdragonfly, + ld.Hfreebsd, + ld.Hlinux, + ld.Hnacl, + ld.Hnetbsd, + ld.Hopenbsd, + ld.Hsolaris: + break + } + + switch ld.HEADTYPE { + default: + ld.Diag("unknown -H option") + ld.Errorexit() + fallthrough + + case ld.Hplan9: /* plan 9 */ + ld.HEADR = 32 + 8 + + if ld.INITTEXT == -1 { + ld.INITTEXT = 0x200000 + int64(ld.HEADR) + } + if ld.INITDAT == -1 { + ld.INITDAT = 0 + } + if ld.INITRND == -1 { + ld.INITRND = 0x200000 + } + + case ld.Helf: /* elf32 executable */ + ld.HEADR = int32(ld.Rnd(52+3*32, 16)) + + if ld.INITTEXT == -1 { + ld.INITTEXT = 0x80110000 + } + if ld.INITDAT == -1 { + ld.INITDAT = 0 + } + if ld.INITRND == -1 { + ld.INITRND = 4096 + } + + case ld.Hdarwin: /* apple MACH */ + ld.Machoinit() + + ld.HEADR = ld.INITIAL_MACHO_HEADR + if ld.INITRND == -1 { + ld.INITRND = 4096 + } + if ld.INITTEXT == -1 { + ld.INITTEXT = 4096 + int64(ld.HEADR) + } + if ld.INITDAT == -1 { + ld.INITDAT = 0 + } + + case ld.Hlinux, /* elf64 executable */ + ld.Hfreebsd, /* freebsd */ + ld.Hnetbsd, /* netbsd */ + ld.Hopenbsd, /* openbsd */ + ld.Hdragonfly, /* dragonfly */ + ld.Hsolaris: /* solaris */ + ld.Elfinit() + + ld.HEADR = ld.ELFRESERVE + if ld.INITTEXT == -1 { + ld.INITTEXT = (1 << 22) + int64(ld.HEADR) + } + if ld.INITDAT == -1 { + ld.INITDAT = 0 + } + if ld.INITRND == -1 { + ld.INITRND = 4096 + } + + case ld.Hnacl: + ld.Elfinit() + ld.Debug['w']++ // disable dwarf, which gets confused and is useless anyway + ld.HEADR = 0x10000 + ld.Funcalign = 32 + if ld.INITTEXT == -1 { + ld.INITTEXT = 0x20000 + } + if ld.INITDAT == -1 { + ld.INITDAT = 0 + } + if ld.INITRND == -1 { + ld.INITRND = 0x10000 + } + + case ld.Hwindows: /* PE executable */ + ld.Peinit() + + ld.HEADR = ld.PEFILEHEADR + if ld.INITTEXT == -1 { + ld.INITTEXT = ld.PEBASE + int64(ld.PESECTHEADR) + } + if ld.INITDAT == -1 { + ld.INITDAT = 0 + } + if ld.INITRND == -1 { + ld.INITRND = ld.PESECTALIGN + } + } + + if ld.INITDAT != 0 && ld.INITRND != 0 { + fmt.Printf("warning: -D0x%x is ignored because of -R0x%x\n", uint64(ld.INITDAT), uint32(ld.INITRND)) + } +} diff --git a/src/cmd/new6l/z.go b/src/cmd/new6l/z.go new file mode 100644 index 0000000000..06ab7d0f9a --- /dev/null +++ b/src/cmd/new6l/z.go @@ -0,0 +1 @@ +package main diff --git a/src/cmd/new8l/asm.go b/src/cmd/new8l/asm.go new file mode 100644 index 0000000000..295c62ebf7 --- /dev/null +++ b/src/cmd/new8l/asm.go @@ -0,0 +1,734 @@ +// Inferno utils/8l/asm.c +// http://code.google.com/p/inferno-os/source/browse/utils/8l/asm.c +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package main + +import ( + "cmd/internal/obj" + "fmt" + "log" +) +import "cmd/internal/ld" + +func needlib(name string) int { + var p string + var s *ld.LSym + + if name[0] == '\x00' { + return 0 + } + + /* reuse hash code in symbol table */ + p = fmt.Sprintf(".dynlib.%s", name) + + s = ld.Linklookup(ld.Ctxt, p, 0) + + if s.Type == 0 { + s.Type = 100 // avoid SDATA, etc. + return 1 + } + + return 0 +} + +func gentext() { +} + +func adddynrela(rela *ld.LSym, s *ld.LSym, r *ld.Reloc) { + log.Fatalf("adddynrela not implemented") +} + +func adddynrel(s *ld.LSym, r *ld.Reloc) { + var targ *ld.LSym + var rel *ld.LSym + var got *ld.LSym + + targ = r.Sym + ld.Ctxt.Cursym = s + + switch r.Type { + default: + if r.Type >= 256 { + ld.Diag("unexpected relocation type %d", r.Type) + return + } + + // Handle relocations found in ELF object files. + case 256 + ld.R_386_PC32: + if targ.Type == ld.SDYNIMPORT { + ld.Diag("unexpected R_386_PC32 relocation for dynamic symbol %s", targ.Name) + } + if targ.Type == 0 || targ.Type == ld.SXREF { + ld.Diag("unknown symbol %s in pcrel", targ.Name) + } + r.Type = ld.R_PCREL + r.Add += 4 + return + + case 256 + ld.R_386_PLT32: + r.Type = ld.R_PCREL + r.Add += 4 + if targ.Type == ld.SDYNIMPORT { + addpltsym(ld.Ctxt, targ) + r.Sym = ld.Linklookup(ld.Ctxt, ".plt", 0) + r.Add += int64(targ.Plt) + } + + return + + case 256 + ld.R_386_GOT32: + if targ.Type != ld.SDYNIMPORT { + // have symbol + if r.Off >= 2 && s.P[r.Off-2] == 0x8b { + // turn MOVL of GOT entry into LEAL of symbol address, relative to GOT. + s.P[r.Off-2] = 0x8d + + r.Type = ld.R_GOTOFF + return + } + + if r.Off >= 2 && s.P[r.Off-2] == 0xff && s.P[r.Off-1] == 0xb3 { + // turn PUSHL of GOT entry into PUSHL of symbol itself. + // use unnecessary SS prefix to keep instruction same length. + s.P[r.Off-2] = 0x36 + + s.P[r.Off-1] = 0x68 + r.Type = ld.R_ADDR + return + } + + ld.Diag("unexpected GOT reloc for non-dynamic symbol %s", targ.Name) + return + } + + addgotsym(ld.Ctxt, targ) + r.Type = ld.R_CONST // write r->add during relocsym + r.Sym = nil + r.Add += int64(targ.Got) + return + + case 256 + ld.R_386_GOTOFF: + r.Type = ld.R_GOTOFF + return + + case 256 + ld.R_386_GOTPC: + r.Type = ld.R_PCREL + r.Sym = ld.Linklookup(ld.Ctxt, ".got", 0) + r.Add += 4 + return + + case 256 + ld.R_386_32: + if targ.Type == ld.SDYNIMPORT { + ld.Diag("unexpected R_386_32 relocation for dynamic symbol %s", targ.Name) + } + r.Type = ld.R_ADDR + return + + case 512 + ld.MACHO_GENERIC_RELOC_VANILLA*2 + 0: + r.Type = ld.R_ADDR + if targ.Type == ld.SDYNIMPORT { + ld.Diag("unexpected reloc for dynamic symbol %s", targ.Name) + } + return + + case 512 + ld.MACHO_GENERIC_RELOC_VANILLA*2 + 1: + if targ.Type == ld.SDYNIMPORT { + addpltsym(ld.Ctxt, targ) + r.Sym = ld.Linklookup(ld.Ctxt, ".plt", 0) + r.Add = int64(targ.Plt) + r.Type = ld.R_PCREL + return + } + + r.Type = ld.R_PCREL + return + + case 512 + ld.MACHO_FAKE_GOTPCREL: + if targ.Type != ld.SDYNIMPORT { + // have symbol + // turn MOVL of GOT entry into LEAL of symbol itself + if r.Off < 2 || s.P[r.Off-2] != 0x8b { + ld.Diag("unexpected GOT reloc for non-dynamic symbol %s", targ.Name) + return + } + + s.P[r.Off-2] = 0x8d + r.Type = ld.R_PCREL + return + } + + addgotsym(ld.Ctxt, targ) + r.Sym = ld.Linklookup(ld.Ctxt, ".got", 0) + r.Add += int64(targ.Got) + r.Type = ld.R_PCREL + return + } + + // Handle references to ELF symbols from our own object files. + if targ.Type != ld.SDYNIMPORT { + return + } + + switch r.Type { + case ld.R_CALL, + ld.R_PCREL: + addpltsym(ld.Ctxt, targ) + r.Sym = ld.Linklookup(ld.Ctxt, ".plt", 0) + r.Add = int64(targ.Plt) + return + + case ld.R_ADDR: + if s.Type != ld.SDATA { + break + } + if ld.Iself { + adddynsym(ld.Ctxt, targ) + rel = ld.Linklookup(ld.Ctxt, ".rel", 0) + ld.Addaddrplus(ld.Ctxt, rel, s, int64(r.Off)) + ld.Adduint32(ld.Ctxt, rel, ld.ELF32_R_INFO(uint32(targ.Dynid), ld.R_386_32)) + r.Type = ld.R_CONST // write r->add during relocsym + r.Sym = nil + return + } + + if ld.HEADTYPE == ld.Hdarwin && s.Size == PtrSize && r.Off == 0 { + // Mach-O relocations are a royal pain to lay out. + // They use a compact stateful bytecode representation + // that is too much bother to deal with. + // Instead, interpret the C declaration + // void *_Cvar_stderr = &stderr; + // as making _Cvar_stderr the name of a GOT entry + // for stderr. This is separate from the usual GOT entry, + // just in case the C code assigns to the variable, + // and of course it only works for single pointers, + // but we only need to support cgo and that's all it needs. + adddynsym(ld.Ctxt, targ) + + got = ld.Linklookup(ld.Ctxt, ".got", 0) + s.Type = got.Type | ld.SSUB + s.Outer = got + s.Sub = got.Sub + got.Sub = s + s.Value = got.Size + ld.Adduint32(ld.Ctxt, got, 0) + ld.Adduint32(ld.Ctxt, ld.Linklookup(ld.Ctxt, ".linkedit.got", 0), uint32(targ.Dynid)) + r.Type = 256 // ignore during relocsym + return + } + } + + ld.Ctxt.Cursym = s + ld.Diag("unsupported relocation for dynamic symbol %s (type=%d stype=%d)", targ.Name, r.Type, targ.Type) +} + +func elfreloc1(r *ld.Reloc, sectoff int64) int { + var elfsym int32 + + ld.Thearch.Lput(uint32(sectoff)) + + elfsym = r.Xsym.Elfsym + switch r.Type { + default: + return -1 + + case ld.R_ADDR: + if r.Siz == 4 { + ld.Thearch.Lput(ld.R_386_32 | uint32(elfsym)<<8) + } else { + return -1 + } + + case ld.R_CALL, + ld.R_PCREL: + if r.Siz == 4 { + ld.Thearch.Lput(ld.R_386_PC32 | uint32(elfsym)<<8) + } else { + return -1 + } + + case ld.R_TLS_LE, + ld.R_TLS_IE: + if r.Siz == 4 { + ld.Thearch.Lput(ld.R_386_TLS_LE | uint32(elfsym)<<8) + } else { + return -1 + } + } + + return 0 +} + +func machoreloc1(r *ld.Reloc, sectoff int64) int { + var v uint32 + var rs *ld.LSym + + rs = r.Xsym + + if rs.Type == ld.SHOSTOBJ { + if rs.Dynid < 0 { + ld.Diag("reloc %d to non-macho symbol %s type=%d", r.Type, rs.Name, rs.Type) + return -1 + } + + v = uint32(rs.Dynid) + v |= 1 << 27 // external relocation + } else { + v = uint32((rs.Sect.(*ld.Section)).Extnum) + if v == 0 { + ld.Diag("reloc %d to symbol %s in non-macho section %s type=%d", r.Type, rs.Name, (rs.Sect.(*ld.Section)).Name, rs.Type) + return -1 + } + } + + switch r.Type { + default: + return -1 + + case ld.R_ADDR: + v |= ld.MACHO_GENERIC_RELOC_VANILLA << 28 + + case ld.R_CALL, + ld.R_PCREL: + v |= 1 << 24 // pc-relative bit + v |= ld.MACHO_GENERIC_RELOC_VANILLA << 28 + } + + switch r.Siz { + default: + return -1 + + case 1: + v |= 0 << 25 + + case 2: + v |= 1 << 25 + + case 4: + v |= 2 << 25 + + case 8: + v |= 3 << 25 + } + + ld.Thearch.Lput(uint32(sectoff)) + ld.Thearch.Lput(v) + return 0 +} + +func archreloc(r *ld.Reloc, s *ld.LSym, val *int64) int { + if ld.Linkmode == ld.LinkExternal { + return -1 + } + switch r.Type { + case ld.R_CONST: + *val = r.Add + return 0 + + case ld.R_GOTOFF: + *val = ld.Symaddr(r.Sym) + r.Add - ld.Symaddr(ld.Linklookup(ld.Ctxt, ".got", 0)) + return 0 + } + + return -1 +} + +func archrelocvariant(r *ld.Reloc, s *ld.LSym, t int64) int64 { + log.Fatalf("unexpected relocation variant") + return t +} + +func elfsetupplt() { + var plt *ld.LSym + var got *ld.LSym + + plt = ld.Linklookup(ld.Ctxt, ".plt", 0) + got = ld.Linklookup(ld.Ctxt, ".got.plt", 0) + if plt.Size == 0 { + // pushl got+4 + ld.Adduint8(ld.Ctxt, plt, 0xff) + + ld.Adduint8(ld.Ctxt, plt, 0x35) + ld.Addaddrplus(ld.Ctxt, plt, got, 4) + + // jmp *got+8 + ld.Adduint8(ld.Ctxt, plt, 0xff) + + ld.Adduint8(ld.Ctxt, plt, 0x25) + ld.Addaddrplus(ld.Ctxt, plt, got, 8) + + // zero pad + ld.Adduint32(ld.Ctxt, plt, 0) + + // assume got->size == 0 too + ld.Addaddrplus(ld.Ctxt, got, ld.Linklookup(ld.Ctxt, ".dynamic", 0), 0) + + ld.Adduint32(ld.Ctxt, got, 0) + ld.Adduint32(ld.Ctxt, got, 0) + } +} + +func addpltsym(ctxt *ld.Link, s *ld.LSym) { + var plt *ld.LSym + var got *ld.LSym + var rel *ld.LSym + + if s.Plt >= 0 { + return + } + + adddynsym(ctxt, s) + + if ld.Iself { + plt = ld.Linklookup(ctxt, ".plt", 0) + got = ld.Linklookup(ctxt, ".got.plt", 0) + rel = ld.Linklookup(ctxt, ".rel.plt", 0) + if plt.Size == 0 { + elfsetupplt() + } + + // jmpq *got+size + ld.Adduint8(ctxt, plt, 0xff) + + ld.Adduint8(ctxt, plt, 0x25) + ld.Addaddrplus(ctxt, plt, got, got.Size) + + // add to got: pointer to current pos in plt + ld.Addaddrplus(ctxt, got, plt, plt.Size) + + // pushl $x + ld.Adduint8(ctxt, plt, 0x68) + + ld.Adduint32(ctxt, plt, uint32(rel.Size)) + + // jmp .plt + ld.Adduint8(ctxt, plt, 0xe9) + + ld.Adduint32(ctxt, plt, uint32(-(plt.Size + 4))) + + // rel + ld.Addaddrplus(ctxt, rel, got, got.Size-4) + + ld.Adduint32(ctxt, rel, ld.ELF32_R_INFO(uint32(s.Dynid), ld.R_386_JMP_SLOT)) + + s.Plt = int32(plt.Size - 16) + } else if ld.HEADTYPE == ld.Hdarwin { + // Same laziness as in 6l. + + var plt *ld.LSym + + plt = ld.Linklookup(ctxt, ".plt", 0) + + addgotsym(ctxt, s) + + ld.Adduint32(ctxt, ld.Linklookup(ctxt, ".linkedit.plt", 0), uint32(s.Dynid)) + + // jmpq *got+size(IP) + s.Plt = int32(plt.Size) + + ld.Adduint8(ctxt, plt, 0xff) + ld.Adduint8(ctxt, plt, 0x25) + ld.Addaddrplus(ctxt, plt, ld.Linklookup(ctxt, ".got", 0), int64(s.Got)) + } else { + ld.Diag("addpltsym: unsupported binary format") + } +} + +func addgotsym(ctxt *ld.Link, s *ld.LSym) { + var got *ld.LSym + var rel *ld.LSym + + if s.Got >= 0 { + return + } + + adddynsym(ctxt, s) + got = ld.Linklookup(ctxt, ".got", 0) + s.Got = int32(got.Size) + ld.Adduint32(ctxt, got, 0) + + if ld.Iself { + rel = ld.Linklookup(ctxt, ".rel", 0) + ld.Addaddrplus(ctxt, rel, got, int64(s.Got)) + ld.Adduint32(ctxt, rel, ld.ELF32_R_INFO(uint32(s.Dynid), ld.R_386_GLOB_DAT)) + } else if ld.HEADTYPE == ld.Hdarwin { + ld.Adduint32(ctxt, ld.Linklookup(ctxt, ".linkedit.got", 0), uint32(s.Dynid)) + } else { + ld.Diag("addgotsym: unsupported binary format") + } +} + +func adddynsym(ctxt *ld.Link, s *ld.LSym) { + var d *ld.LSym + var t int + var name string + + if s.Dynid >= 0 { + return + } + + if ld.Iself { + s.Dynid = int32(ld.Nelfsym) + ld.Nelfsym++ + + d = ld.Linklookup(ctxt, ".dynsym", 0) + + /* name */ + name = s.Extname + + ld.Adduint32(ctxt, d, uint32(ld.Addstring(ld.Linklookup(ctxt, ".dynstr", 0), name))) + + /* value */ + if s.Type == ld.SDYNIMPORT { + ld.Adduint32(ctxt, d, 0) + } else { + ld.Addaddr(ctxt, d, s) + } + + /* size */ + ld.Adduint32(ctxt, d, 0) + + /* type */ + t = ld.STB_GLOBAL << 4 + + if s.Cgoexport != 0 && s.Type&ld.SMASK == ld.STEXT { + t |= ld.STT_FUNC + } else { + t |= ld.STT_OBJECT + } + ld.Adduint8(ctxt, d, uint8(t)) + ld.Adduint8(ctxt, d, 0) + + /* shndx */ + if s.Type == ld.SDYNIMPORT { + ld.Adduint16(ctxt, d, ld.SHN_UNDEF) + } else { + ld.Adduint16(ctxt, d, 1) + } + } else if ld.HEADTYPE == ld.Hdarwin { + ld.Diag("adddynsym: missed symbol %s (%s)", s.Name, s.Extname) + } else if ld.HEADTYPE == ld.Hwindows { + } else // already taken care of + { + ld.Diag("adddynsym: unsupported binary format") + } +} + +func adddynlib(lib string) { + var s *ld.LSym + + if needlib(lib) == 0 { + return + } + + if ld.Iself { + s = ld.Linklookup(ld.Ctxt, ".dynstr", 0) + if s.Size == 0 { + ld.Addstring(s, "") + } + ld.Elfwritedynent(ld.Linklookup(ld.Ctxt, ".dynamic", 0), ld.DT_NEEDED, uint64(ld.Addstring(s, lib))) + } else if ld.HEADTYPE == ld.Hdarwin { + ld.Machoadddynlib(lib) + } else if ld.HEADTYPE != ld.Hwindows { + ld.Diag("adddynlib: unsupported binary format") + } +} + +func asmb() { + var magic int32 + var symo uint32 + var dwarfoff uint32 + var machlink uint32 + var sect *ld.Section + var sym *ld.LSym + var i int + + if ld.Debug['v'] != 0 { + fmt.Fprintf(&ld.Bso, "%5.2f asmb\n", obj.Cputime()) + } + ld.Bflush(&ld.Bso) + + if ld.Iself { + ld.Asmbelfsetup() + } + + sect = ld.Segtext.Sect + ld.Cseek(int64(sect.Vaddr - ld.Segtext.Vaddr + ld.Segtext.Fileoff)) + ld.Codeblk(int64(sect.Vaddr), int64(sect.Length)) + for sect = sect.Next; sect != nil; sect = sect.Next { + ld.Cseek(int64(sect.Vaddr - ld.Segtext.Vaddr + ld.Segtext.Fileoff)) + ld.Datblk(int64(sect.Vaddr), int64(sect.Length)) + } + + if ld.Segrodata.Filelen > 0 { + if ld.Debug['v'] != 0 { + fmt.Fprintf(&ld.Bso, "%5.2f rodatblk\n", obj.Cputime()) + } + ld.Bflush(&ld.Bso) + + ld.Cseek(int64(ld.Segrodata.Fileoff)) + ld.Datblk(int64(ld.Segrodata.Vaddr), int64(ld.Segrodata.Filelen)) + } + + if ld.Debug['v'] != 0 { + fmt.Fprintf(&ld.Bso, "%5.2f datblk\n", obj.Cputime()) + } + ld.Bflush(&ld.Bso) + + ld.Cseek(int64(ld.Segdata.Fileoff)) + ld.Datblk(int64(ld.Segdata.Vaddr), int64(ld.Segdata.Filelen)) + + machlink = 0 + if ld.HEADTYPE == ld.Hdarwin { + if ld.Debug['v'] != 0 { + fmt.Fprintf(&ld.Bso, "%5.2f dwarf\n", obj.Cputime()) + } + + dwarfoff = uint32(ld.Rnd(int64(uint64(ld.HEADR)+ld.Segtext.Length), int64(ld.INITRND)) + ld.Rnd(int64(ld.Segdata.Filelen), int64(ld.INITRND))) + ld.Cseek(int64(dwarfoff)) + + ld.Segdwarf.Fileoff = uint64(ld.Cpos()) + ld.Dwarfemitdebugsections() + ld.Segdwarf.Filelen = uint64(ld.Cpos()) - ld.Segdwarf.Fileoff + + machlink = uint32(ld.Domacholink()) + } + + ld.Symsize = 0 + ld.Spsize = 0 + ld.Lcsize = 0 + symo = 0 + if ld.Debug['s'] == 0 { + // TODO: rationalize + if ld.Debug['v'] != 0 { + fmt.Fprintf(&ld.Bso, "%5.2f sym\n", obj.Cputime()) + } + ld.Bflush(&ld.Bso) + switch ld.HEADTYPE { + default: + if ld.Iself { + symo = uint32(ld.Segdata.Fileoff + ld.Segdata.Filelen) + symo = uint32(ld.Rnd(int64(symo), int64(ld.INITRND))) + } + + case ld.Hplan9: + symo = uint32(ld.Segdata.Fileoff + ld.Segdata.Filelen) + + case ld.Hdarwin: + symo = uint32(ld.Segdata.Fileoff + uint64(ld.Rnd(int64(ld.Segdata.Filelen), int64(ld.INITRND))) + uint64(machlink)) + + case ld.Hwindows: + symo = uint32(ld.Segdata.Fileoff + ld.Segdata.Filelen) + symo = uint32(ld.Rnd(int64(symo), ld.PEFILEALIGN)) + } + + ld.Cseek(int64(symo)) + switch ld.HEADTYPE { + default: + if ld.Iself { + if ld.Debug['v'] != 0 { + fmt.Fprintf(&ld.Bso, "%5.2f elfsym\n", obj.Cputime()) + } + ld.Asmelfsym() + ld.Cflush() + ld.Cwrite(ld.Elfstrdat) + + if ld.Debug['v'] != 0 { + fmt.Fprintf(&ld.Bso, "%5.2f dwarf\n", obj.Cputime()) + } + ld.Dwarfemitdebugsections() + + if ld.Linkmode == ld.LinkExternal { + ld.Elfemitreloc() + } + } + + case ld.Hplan9: + ld.Asmplan9sym() + ld.Cflush() + + sym = ld.Linklookup(ld.Ctxt, "pclntab", 0) + if sym != nil { + ld.Lcsize = int32(len(sym.P)) + for i = 0; int32(i) < ld.Lcsize; i++ { + ld.Cput(uint8(sym.P[i])) + } + + ld.Cflush() + } + + case ld.Hwindows: + if ld.Debug['v'] != 0 { + fmt.Fprintf(&ld.Bso, "%5.2f dwarf\n", obj.Cputime()) + } + ld.Dwarfemitdebugsections() + + case ld.Hdarwin: + if ld.Linkmode == ld.LinkExternal { + ld.Machoemitreloc() + } + } + } + + if ld.Debug['v'] != 0 { + fmt.Fprintf(&ld.Bso, "%5.2f headr\n", obj.Cputime()) + } + ld.Bflush(&ld.Bso) + ld.Cseek(0) + switch ld.HEADTYPE { + default: + case ld.Hplan9: /* plan9 */ + magic = 4*11*11 + 7 + + ld.Lputb(uint32(magic)) /* magic */ + ld.Lputb(uint32(ld.Segtext.Filelen)) /* sizes */ + ld.Lputb(uint32(ld.Segdata.Filelen)) + ld.Lputb(uint32(ld.Segdata.Length - ld.Segdata.Filelen)) + ld.Lputb(uint32(ld.Symsize)) /* nsyms */ + ld.Lputb(uint32(ld.Entryvalue())) /* va of entry */ + ld.Lputb(uint32(ld.Spsize)) /* sp offsets */ + ld.Lputb(uint32(ld.Lcsize)) /* line offsets */ + + case ld.Hdarwin: + ld.Asmbmacho() + + case ld.Hlinux, + ld.Hfreebsd, + ld.Hnetbsd, + ld.Hopenbsd, + ld.Hdragonfly, + ld.Hnacl: + ld.Asmbelf(int64(symo)) + + case ld.Hwindows: + ld.Asmbpe() + } + + ld.Cflush() +} diff --git a/src/cmd/new8l/l.go b/src/cmd/new8l/l.go new file mode 100644 index 0000000000..fbeee984a3 --- /dev/null +++ b/src/cmd/new8l/l.go @@ -0,0 +1,78 @@ +// Inferno utils/8l/asm.c +// http://code.google.com/p/inferno-os/source/browse/utils/8l/asm.c +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package main + +// Writing object files. + +// Inferno utils/8l/l.h +// http://code.google.com/p/inferno-os/source/browse/utils/8l/l.h +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +const ( + thechar = '8' + PtrSize = 4 + IntSize = 4 + RegSize = 4 + MaxAlign = 32 + FuncAlign = 16 + MINLC = 1 +) + +/* Used by ../ld/dwarf.c */ +const ( + DWARFREGSP = 4 +) diff --git a/src/cmd/new8l/obj.go b/src/cmd/new8l/obj.go new file mode 100644 index 0000000000..5d44721074 --- /dev/null +++ b/src/cmd/new8l/obj.go @@ -0,0 +1,191 @@ +// Inferno utils/8l/obj.c +// http://code.google.com/p/inferno-os/source/browse/utils/8l/obj.c +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package main + +import ( + "cmd/internal/obj" + "fmt" + "log" +) +import "cmd/internal/ld" + +// Reading object files. + +func main() { + linkarchinit() + ld.Ldmain() +} + +func linkarchinit() { + ld.Thestring = "386" + ld.Thelinkarch = &ld.Link386 + + ld.Thearch.Thechar = thechar + ld.Thearch.Ptrsize = ld.Thelinkarch.Ptrsize + ld.Thearch.Intsize = ld.Thelinkarch.Ptrsize + ld.Thearch.Regsize = ld.Thelinkarch.Regsize + ld.Thearch.Funcalign = FuncAlign + ld.Thearch.Maxalign = MaxAlign + ld.Thearch.Minlc = MINLC + ld.Thearch.Dwarfregsp = DWARFREGSP + + ld.Thearch.Adddynlib = adddynlib + ld.Thearch.Adddynrel = adddynrel + ld.Thearch.Adddynsym = adddynsym + ld.Thearch.Archinit = archinit + ld.Thearch.Archreloc = archreloc + ld.Thearch.Archrelocvariant = archrelocvariant + ld.Thearch.Asmb = asmb + ld.Thearch.Elfreloc1 = elfreloc1 + ld.Thearch.Elfsetupplt = elfsetupplt + ld.Thearch.Gentext = gentext + ld.Thearch.Machoreloc1 = machoreloc1 + ld.Thearch.Lput = ld.Lputl + ld.Thearch.Wput = ld.Wputl + ld.Thearch.Vput = ld.Vputl + + ld.Thearch.Linuxdynld = "/lib/ld-linux.so.2" + ld.Thearch.Freebsddynld = "/usr/libexec/ld-elf.so.1" + ld.Thearch.Openbsddynld = "/usr/libexec/ld.so" + ld.Thearch.Netbsddynld = "/usr/libexec/ld.elf_so" + ld.Thearch.Dragonflydynld = "/usr/libexec/ld-elf.so.2" + ld.Thearch.Solarisdynld = "/lib/ld.so.1" +} + +func archinit() { + // getgoextlinkenabled is based on GO_EXTLINK_ENABLED when + // Go was built; see ../../make.bash. + if ld.Linkmode == ld.LinkAuto && obj.Getgoextlinkenabled() == "0" { + ld.Linkmode = ld.LinkInternal + } + + switch ld.HEADTYPE { + default: + if ld.Linkmode == ld.LinkAuto { + ld.Linkmode = ld.LinkInternal + } + if ld.Linkmode == ld.LinkExternal && obj.Getgoextlinkenabled() != "1" { + log.Fatalf("cannot use -linkmode=external with -H %s", ld.Headstr(int(ld.HEADTYPE))) + } + + case ld.Hdarwin, + ld.Hdragonfly, + ld.Hfreebsd, + ld.Hlinux, + ld.Hnetbsd, + ld.Hopenbsd: + break + } + + switch ld.HEADTYPE { + default: + ld.Diag("unknown -H option") + ld.Errorexit() + fallthrough + + case ld.Hplan9: /* plan 9 */ + ld.HEADR = 32 + + if ld.INITTEXT == -1 { + ld.INITTEXT = 4096 + 32 + } + if ld.INITDAT == -1 { + ld.INITDAT = 0 + } + if ld.INITRND == -1 { + ld.INITRND = 4096 + } + + case ld.Hdarwin: /* apple MACH */ + ld.Machoinit() + + ld.HEADR = ld.INITIAL_MACHO_HEADR + if ld.INITTEXT == -1 { + ld.INITTEXT = 4096 + int64(ld.HEADR) + } + if ld.INITDAT == -1 { + ld.INITDAT = 0 + } + if ld.INITRND == -1 { + ld.INITRND = 4096 + } + + case ld.Hlinux, /* elf32 executable */ + ld.Hfreebsd, + ld.Hnetbsd, + ld.Hopenbsd, + ld.Hdragonfly: + ld.Elfinit() + + ld.HEADR = ld.ELFRESERVE + if ld.INITTEXT == -1 { + ld.INITTEXT = 0x08048000 + int64(ld.HEADR) + } + if ld.INITDAT == -1 { + ld.INITDAT = 0 + } + if ld.INITRND == -1 { + ld.INITRND = 4096 + } + + case ld.Hnacl: + ld.Elfinit() + ld.HEADR = 0x10000 + ld.Funcalign = 32 + if ld.INITTEXT == -1 { + ld.INITTEXT = 0x20000 + } + if ld.INITDAT == -1 { + ld.INITDAT = 0 + } + if ld.INITRND == -1 { + ld.INITRND = 0x10000 + } + + case ld.Hwindows: /* PE executable */ + ld.Peinit() + + ld.HEADR = ld.PEFILEHEADR + if ld.INITTEXT == -1 { + ld.INITTEXT = ld.PEBASE + int64(ld.PESECTHEADR) + } + if ld.INITDAT == -1 { + ld.INITDAT = 0 + } + if ld.INITRND == -1 { + ld.INITRND = ld.PESECTALIGN + } + } + + if ld.INITDAT != 0 && ld.INITRND != 0 { + fmt.Printf("warning: -D0x%x is ignored because of -R0x%x\n", uint64(ld.INITDAT), uint32(ld.INITRND)) + } +} diff --git a/src/cmd/new9l/asm.go b/src/cmd/new9l/asm.go new file mode 100644 index 0000000000..eab0ef2af1 --- /dev/null +++ b/src/cmd/new9l/asm.go @@ -0,0 +1,851 @@ +// Inferno utils/5l/asm.c +// http://code.google.com/p/inferno-os/source/browse/utils/5l/asm.c +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package main + +import ( + "cmd/internal/obj" + "fmt" + "log" +) +import "cmd/internal/ld" + +func needlib(name string) int { + var p string + var s *ld.LSym + + if name[0] == '\x00' { + return 0 + } + + /* reuse hash code in symbol table */ + p = fmt.Sprintf(".dynlib.%s", name) + + s = ld.Linklookup(ld.Ctxt, p, 0) + + if s.Type == 0 { + s.Type = 100 // avoid SDATA, etc. + return 1 + } + + return 0 +} + +func gentext() { + var s *ld.LSym + var stub *ld.LSym + var pprevtextp **ld.LSym + var r *ld.Reloc + var n string + var o1 uint32 + var i int + + // The ppc64 ABI PLT has similar concepts to other + // architectures, but is laid out quite differently. When we + // see an R_PPC64_REL24 relocation to a dynamic symbol + // (indicating that the call needs to go through the PLT), we + // generate up to three stubs and reserve a PLT slot. + // + // 1) The call site will be bl x; nop (where the relocation + // applies to the bl). We rewrite this to bl x_stub; ld + // r2,24(r1). The ld is necessary because x_stub will save + // r2 (the TOC pointer) at 24(r1) (the "TOC save slot"). + // + // 2) We reserve space for a pointer in the .plt section (once + // per referenced dynamic function). .plt is a data + // section filled solely by the dynamic linker (more like + // .plt.got on other architectures). Initially, the + // dynamic linker will fill each slot with a pointer to the + // corresponding x@plt entry point. + // + // 3) We generate the "call stub" x_stub (once per dynamic + // function/object file pair). This saves the TOC in the + // TOC save slot, reads the function pointer from x's .plt + // slot and calls it like any other global entry point + // (including setting r12 to the function address). + // + // 4) We generate the "symbol resolver stub" x@plt (once per + // dynamic function). This is solely a branch to the glink + // resolver stub. + // + // 5) We generate the glink resolver stub (only once). This + // computes which symbol resolver stub we came through and + // invokes the dynamic resolver via a pointer provided by + // the dynamic linker. This will patch up the .plt slot to + // point directly at the function so future calls go + // straight from the call stub to the real function, and + // then call the function. + + // NOTE: It's possible we could make ppc64 closer to other + // architectures: ppc64's .plt is like .plt.got on other + // platforms and ppc64's .glink is like .plt on other + // platforms. + + // Find all R_PPC64_REL24 relocations that reference dynamic + // imports. Reserve PLT entries for these symbols and + // generate call stubs. The call stubs need to live in .text, + // which is why we need to do this pass this early. + // + // This assumes "case 1" from the ABI, where the caller needs + // us to save and restore the TOC pointer. + pprevtextp = &ld.Ctxt.Textp + + for s = *pprevtextp; s != nil; (func() { pprevtextp = &s.Next; s = *pprevtextp })() { + for i = range s.R { + r = &s.R[i] + if r.Type != 256+ld.R_PPC64_REL24 || r.Sym.Type != ld.SDYNIMPORT { + continue + } + + // Reserve PLT entry and generate symbol + // resolver + addpltsym(ld.Ctxt, r.Sym) + + // Generate call stub + n = fmt.Sprintf("%s.%s", s.Name, r.Sym.Name) + + stub = ld.Linklookup(ld.Ctxt, n, 0) + stub.Reachable = stub.Reachable || s.Reachable + if stub.Size == 0 { + // Need outer to resolve .TOC. + stub.Outer = s + + // Link in to textp before s (we could + // do it after, but would have to skip + // the subsymbols) + *pprevtextp = stub + + stub.Next = s + pprevtextp = &stub.Next + + gencallstub(1, stub, r.Sym) + } + + // Update the relocation to use the call stub + r.Sym = stub + + // Restore TOC after bl. The compiler put a + // nop here for us to overwrite. + o1 = 0xe8410018 // ld r2,24(r1) + ld.Ctxt.Arch.ByteOrder.PutUint32(s.P[r.Off:], o1) + } + } +} + +// Construct a call stub in stub that calls symbol targ via its PLT +// entry. +func gencallstub(abicase int, stub *ld.LSym, targ *ld.LSym) { + var plt *ld.LSym + var r *ld.Reloc + + if abicase != 1 { + // If we see R_PPC64_TOCSAVE or R_PPC64_REL24_NOTOC + // relocations, we'll need to implement cases 2 and 3. + log.Fatalf("gencallstub only implements case 1 calls") + } + + plt = ld.Linklookup(ld.Ctxt, ".plt", 0) + + stub.Type = ld.STEXT + + // Save TOC pointer in TOC save slot + ld.Adduint32(ld.Ctxt, stub, 0xf8410018) // std r2,24(r1) + + // Load the function pointer from the PLT. + r = ld.Addrel(stub) + + r.Off = int32(stub.Size) + r.Sym = plt + r.Add = int64(targ.Plt) + r.Siz = 2 + if ld.Ctxt.Arch.Endian == ld.BigEndian { + r.Off += int32(r.Siz) + } + r.Type = ld.R_POWER_TOC + r.Variant = ld.RV_POWER_HA + ld.Adduint32(ld.Ctxt, stub, 0x3d820000) // addis r12,r2,targ@plt@toc@ha + r = ld.Addrel(stub) + r.Off = int32(stub.Size) + r.Sym = plt + r.Add = int64(targ.Plt) + r.Siz = 2 + if ld.Ctxt.Arch.Endian == ld.BigEndian { + r.Off += int32(r.Siz) + } + r.Type = ld.R_POWER_TOC + r.Variant = ld.RV_POWER_LO + ld.Adduint32(ld.Ctxt, stub, 0xe98c0000) // ld r12,targ@plt@toc@l(r12) + + // Jump to the loaded pointer + ld.Adduint32(ld.Ctxt, stub, 0x7d8903a6) // mtctr r12 + ld.Adduint32(ld.Ctxt, stub, 0x4e800420) // bctr +} + +func adddynrela(rel *ld.LSym, s *ld.LSym, r *ld.Reloc) { + log.Fatalf("adddynrela not implemented") +} + +func adddynrel(s *ld.LSym, r *ld.Reloc) { + var targ *ld.LSym + var rela *ld.LSym + + targ = r.Sym + ld.Ctxt.Cursym = s + + switch r.Type { + default: + if r.Type >= 256 { + ld.Diag("unexpected relocation type %d", r.Type) + return + } + + // Handle relocations found in ELF object files. + case 256 + ld.R_PPC64_REL24: + r.Type = ld.R_CALLPOWER + + // This is a local call, so the caller isn't setting + // up r12 and r2 is the same for the caller and + // callee. Hence, we need to go to the local entry + // point. (If we don't do this, the callee will try + // to use r12 to compute r2.) + r.Add += int64(r.Sym.Localentry) * 4 + + if targ.Type == ld.SDYNIMPORT { + // Should have been handled in elfsetupplt + ld.Diag("unexpected R_PPC64_REL24 for dyn import") + } + + return + + case 256 + ld.R_PPC64_ADDR64: + r.Type = ld.R_ADDR + if targ.Type == ld.SDYNIMPORT { + // These happen in .toc sections + adddynsym(ld.Ctxt, targ) + + rela = ld.Linklookup(ld.Ctxt, ".rela", 0) + ld.Addaddrplus(ld.Ctxt, rela, s, int64(r.Off)) + ld.Adduint64(ld.Ctxt, rela, ld.ELF64_R_INFO(uint32(targ.Dynid), ld.R_PPC64_ADDR64)) + ld.Adduint64(ld.Ctxt, rela, uint64(r.Add)) + r.Type = 256 // ignore during relocsym + } + + return + + case 256 + ld.R_PPC64_TOC16: + r.Type = ld.R_POWER_TOC + r.Variant = ld.RV_POWER_LO | ld.RV_CHECK_OVERFLOW + return + + case 256 + ld.R_PPC64_TOC16_LO: + r.Type = ld.R_POWER_TOC + r.Variant = ld.RV_POWER_LO + return + + case 256 + ld.R_PPC64_TOC16_HA: + r.Type = ld.R_POWER_TOC + r.Variant = ld.RV_POWER_HA | ld.RV_CHECK_OVERFLOW + return + + case 256 + ld.R_PPC64_TOC16_HI: + r.Type = ld.R_POWER_TOC + r.Variant = ld.RV_POWER_HI | ld.RV_CHECK_OVERFLOW + return + + case 256 + ld.R_PPC64_TOC16_DS: + r.Type = ld.R_POWER_TOC + r.Variant = ld.RV_POWER_DS | ld.RV_CHECK_OVERFLOW + return + + case 256 + ld.R_PPC64_TOC16_LO_DS: + r.Type = ld.R_POWER_TOC + r.Variant = ld.RV_POWER_DS + return + + case 256 + ld.R_PPC64_REL16_LO: + r.Type = ld.R_PCREL + r.Variant = ld.RV_POWER_LO + r.Add += 2 // Compensate for relocation size of 2 + return + + case 256 + ld.R_PPC64_REL16_HI: + r.Type = ld.R_PCREL + r.Variant = ld.RV_POWER_HI | ld.RV_CHECK_OVERFLOW + r.Add += 2 + return + + case 256 + ld.R_PPC64_REL16_HA: + r.Type = ld.R_PCREL + r.Variant = ld.RV_POWER_HA | ld.RV_CHECK_OVERFLOW + r.Add += 2 + return + } + + // Handle references to ELF symbols from our own object files. + if targ.Type != ld.SDYNIMPORT { + return + } + + // TODO(austin): Translate our relocations to ELF + + ld.Diag("unsupported relocation for dynamic symbol %s (type=%d stype=%d)", targ.Name, r.Type, targ.Type) +} + +func elfreloc1(r *ld.Reloc, sectoff int64) int { + // TODO(minux) + return -1 +} + +func elfsetupplt() { + var plt *ld.LSym + + plt = ld.Linklookup(ld.Ctxt, ".plt", 0) + if plt.Size == 0 { + // The dynamic linker stores the address of the + // dynamic resolver and the DSO identifier in the two + // doublewords at the beginning of the .plt section + // before the PLT array. Reserve space for these. + plt.Size = 16 + } +} + +func machoreloc1(r *ld.Reloc, sectoff int64) int { + return -1 +} + +// Return the value of .TOC. for symbol s +func symtoc(s *ld.LSym) int64 { + var toc *ld.LSym + + if s.Outer != nil { + toc = ld.Linkrlookup(ld.Ctxt, ".TOC.", int(s.Outer.Version)) + } else { + toc = ld.Linkrlookup(ld.Ctxt, ".TOC.", int(s.Version)) + } + + if toc == nil { + ld.Diag("TOC-relative relocation in object without .TOC.") + return 0 + } + + return toc.Value +} + +func archreloc(r *ld.Reloc, s *ld.LSym, val *int64) int { + var o1 uint32 + var o2 uint32 + var t int64 + + if ld.Linkmode == ld.LinkExternal { + // TODO(minux): translate R_ADDRPOWER and R_CALLPOWER into standard ELF relocations. + // R_ADDRPOWER corresponds to R_PPC_ADDR16_HA and R_PPC_ADDR16_LO. + // R_CALLPOWER corresponds to R_PPC_REL24. + return -1 + } + + switch r.Type { + case ld.R_CONST: + *val = r.Add + return 0 + + case ld.R_GOTOFF: + *val = ld.Symaddr(r.Sym) + r.Add - ld.Symaddr(ld.Linklookup(ld.Ctxt, ".got", 0)) + return 0 + + // r->add is two ppc64 instructions holding an immediate 32-bit constant. + // We want to add r->sym's address to that constant. + // The encoding of the immediate x<<16 + y, + // where x is the low 16 bits of the first instruction and y is the low 16 + // bits of the second. Both x and y are signed (int16, not uint16). + case ld.R_ADDRPOWER: + o1 = uint32(r.Add >> 32) + + o2 = uint32(r.Add) + t = ld.Symaddr(r.Sym) + if t < 0 { + ld.Ctxt.Diag("relocation for %s is too big (>=2G): %d", s.Name, ld.Symaddr(r.Sym)) + } + + t += ((int64(o1) & 0xffff) << 16) + (int64(int32(o2)) << 16 >> 16) + if t&0x8000 != 0 { + t += 0x10000 + } + o1 = uint32(int64(o1&0xffff0000) | (t>>16)&0xffff) + o2 = uint32(int64(o2&0xffff0000) | t&0xffff) + + // when laid out, the instruction order must always be o1, o2. + if ld.Ctxt.Arch.Endian == ld.BigEndian { + *val = int64(o1)<<32 | int64(o2) + } else { + *val = int64(o2)<<32 | int64(o1) + } + return 0 + + // Bits 6 through 29 = (S + A - P) >> 2 + case ld.R_CALLPOWER: + if ld.Ctxt.Arch.Endian == ld.BigEndian { + o1 = ld.Be32(s.P[r.Off:]) + } else { + o1 = ld.Le32(s.P[r.Off:]) + } + + t = ld.Symaddr(r.Sym) + r.Add - (s.Value + int64(r.Off)) + if t&3 != 0 { + ld.Ctxt.Diag("relocation for %s+%d is not aligned: %d", r.Sym.Name, r.Off, t) + } + if int64(int32(t<<6)>>6) != t { + // TODO(austin) This can happen if text > 32M. + // Add a call trampoline to .text in that case. + ld.Ctxt.Diag("relocation for %s+%d is too big: %d", r.Sym.Name, r.Off, t) + } + + *val = int64(o1)&0xfc000003 | t&^0xfc000003 + return 0 + + case ld.R_POWER_TOC: // S + A - .TOC. + *val = ld.Symaddr(r.Sym) + r.Add - symtoc(s) + + return 0 + } + + return -1 +} + +func archrelocvariant(r *ld.Reloc, s *ld.LSym, t int64) int64 { + var o1 uint32 + switch r.Variant & ld.RV_TYPE_MASK { + default: + ld.Diag("unexpected relocation variant %d", r.Variant) + fallthrough + + case ld.RV_NONE: + return t + + case ld.RV_POWER_LO: + if r.Variant&ld.RV_CHECK_OVERFLOW != 0 { + // Whether to check for signed or unsigned + // overflow depends on the instruction + if ld.Ctxt.Arch.Endian == ld.BigEndian { + o1 = ld.Be32(s.P[r.Off-2:]) + } else { + o1 = ld.Le32(s.P[r.Off:]) + } + switch o1 >> 26 { + case 24, // ori + 26, // xori + 28: // andi + if t>>16 != 0 { + goto overflow + } + + default: + if int64(int16(t)) != t { + goto overflow + } + } + } + + return int64(int16(t)) + + case ld.RV_POWER_HA: + t += 0x8000 + fallthrough + + // Fallthrough + case ld.RV_POWER_HI: + t >>= 16 + + if r.Variant&ld.RV_CHECK_OVERFLOW != 0 { + // Whether to check for signed or unsigned + // overflow depends on the instruction + if ld.Ctxt.Arch.Endian == ld.BigEndian { + o1 = ld.Be32(s.P[r.Off-2:]) + } else { + o1 = ld.Le32(s.P[r.Off:]) + } + switch o1 >> 26 { + case 25, // oris + 27, // xoris + 29: // andis + if t>>16 != 0 { + goto overflow + } + + default: + if int64(int16(t)) != t { + goto overflow + } + } + } + + return int64(int16(t)) + + case ld.RV_POWER_DS: + if ld.Ctxt.Arch.Endian == ld.BigEndian { + o1 = uint32(ld.Be16(s.P[r.Off:])) + } else { + o1 = uint32(ld.Le16(s.P[r.Off:])) + } + if t&3 != 0 { + ld.Diag("relocation for %s+%d is not aligned: %d", r.Sym.Name, r.Off, t) + } + if (r.Variant&ld.RV_CHECK_OVERFLOW != 0) && int64(int16(t)) != t { + goto overflow + } + return int64(o1)&0x3 | int64(int16(t)) + } + +overflow: + ld.Diag("relocation for %s+%d is too big: %d", r.Sym.Name, r.Off, t) + return t +} + +func addpltsym(ctxt *ld.Link, s *ld.LSym) { + if s.Plt >= 0 { + return + } + + adddynsym(ctxt, s) + + if ld.Iself { + var plt *ld.LSym + var rela *ld.LSym + var glink *ld.LSym + var r *ld.Reloc + + plt = ld.Linklookup(ctxt, ".plt", 0) + rela = ld.Linklookup(ctxt, ".rela.plt", 0) + if plt.Size == 0 { + elfsetupplt() + } + + // Create the glink resolver if necessary + glink = ensureglinkresolver() + + // Write symbol resolver stub (just a branch to the + // glink resolver stub) + r = ld.Addrel(glink) + + r.Sym = glink + r.Off = int32(glink.Size) + r.Siz = 4 + r.Type = ld.R_CALLPOWER + ld.Adduint32(ctxt, glink, 0x48000000) // b .glink + + // In the ppc64 ABI, the dynamic linker is responsible + // for writing the entire PLT. We just need to + // reserve 8 bytes for each PLT entry and generate a + // JMP_SLOT dynamic relocation for it. + // + // TODO(austin): ABI v1 is different + s.Plt = int32(plt.Size) + + plt.Size += 8 + + ld.Addaddrplus(ctxt, rela, plt, int64(s.Plt)) + ld.Adduint64(ctxt, rela, ld.ELF64_R_INFO(uint32(s.Dynid), ld.R_PPC64_JMP_SLOT)) + ld.Adduint64(ctxt, rela, 0) + } else { + ld.Diag("addpltsym: unsupported binary format") + } +} + +// Generate the glink resolver stub if necessary and return the .glink section +func ensureglinkresolver() *ld.LSym { + var glink *ld.LSym + var s *ld.LSym + var r *ld.Reloc + + glink = ld.Linklookup(ld.Ctxt, ".glink", 0) + if glink.Size != 0 { + return glink + } + + // This is essentially the resolver from the ppc64 ELF ABI. + // At entry, r12 holds the address of the symbol resolver stub + // for the target routine and the argument registers hold the + // arguments for the target routine. + // + // This stub is PIC, so first get the PC of label 1 into r11. + // Other things will be relative to this. + ld.Adduint32(ld.Ctxt, glink, 0x7c0802a6) // mflr r0 + ld.Adduint32(ld.Ctxt, glink, 0x429f0005) // bcl 20,31,1f + ld.Adduint32(ld.Ctxt, glink, 0x7d6802a6) // 1: mflr r11 + ld.Adduint32(ld.Ctxt, glink, 0x7c0803a6) // mtlf r0 + + // Compute the .plt array index from the entry point address. + // Because this is PIC, everything is relative to label 1b (in + // r11): + // r0 = ((r12 - r11) - (res_0 - r11)) / 4 = (r12 - res_0) / 4 + ld.Adduint32(ld.Ctxt, glink, 0x3800ffd0) // li r0,-(res_0-1b)=-48 + ld.Adduint32(ld.Ctxt, glink, 0x7c006214) // add r0,r0,r12 + ld.Adduint32(ld.Ctxt, glink, 0x7c0b0050) // sub r0,r0,r11 + ld.Adduint32(ld.Ctxt, glink, 0x7800f082) // srdi r0,r0,2 + + // r11 = address of the first byte of the PLT + r = ld.Addrel(glink) + + r.Off = int32(glink.Size) + r.Sym = ld.Linklookup(ld.Ctxt, ".plt", 0) + r.Siz = 8 + r.Type = ld.R_ADDRPOWER + + // addis r11,0,.plt@ha; addi r11,r11,.plt@l + r.Add = 0x3d600000<<32 | 0x396b0000 + + glink.Size += 8 + + // Load r12 = dynamic resolver address and r11 = DSO + // identifier from the first two doublewords of the PLT. + ld.Adduint32(ld.Ctxt, glink, 0xe98b0000) // ld r12,0(r11) + ld.Adduint32(ld.Ctxt, glink, 0xe96b0008) // ld r11,8(r11) + + // Jump to the dynamic resolver + ld.Adduint32(ld.Ctxt, glink, 0x7d8903a6) // mtctr r12 + ld.Adduint32(ld.Ctxt, glink, 0x4e800420) // bctr + + // The symbol resolvers must immediately follow. + // res_0: + + // Add DT_PPC64_GLINK .dynamic entry, which points to 32 bytes + // before the first symbol resolver stub. + s = ld.Linklookup(ld.Ctxt, ".dynamic", 0) + + ld.Elfwritedynentsymplus(s, ld.DT_PPC64_GLINK, glink, glink.Size-32) + + return glink +} + +func adddynsym(ctxt *ld.Link, s *ld.LSym) { + var d *ld.LSym + var t int + var name string + + if s.Dynid >= 0 { + return + } + + if ld.Iself { + s.Dynid = int32(ld.Nelfsym) + ld.Nelfsym++ + + d = ld.Linklookup(ctxt, ".dynsym", 0) + + name = s.Extname + ld.Adduint32(ctxt, d, uint32(ld.Addstring(ld.Linklookup(ctxt, ".dynstr", 0), name))) + + /* type */ + t = ld.STB_GLOBAL << 4 + + if s.Cgoexport != 0 && s.Type&ld.SMASK == ld.STEXT { + t |= ld.STT_FUNC + } else { + t |= ld.STT_OBJECT + } + ld.Adduint8(ctxt, d, uint8(t)) + + /* reserved */ + ld.Adduint8(ctxt, d, 0) + + /* section where symbol is defined */ + if s.Type == ld.SDYNIMPORT { + ld.Adduint16(ctxt, d, ld.SHN_UNDEF) + } else { + ld.Adduint16(ctxt, d, 1) + } + + /* value */ + if s.Type == ld.SDYNIMPORT { + ld.Adduint64(ctxt, d, 0) + } else { + ld.Addaddr(ctxt, d, s) + } + + /* size of object */ + ld.Adduint64(ctxt, d, uint64(s.Size)) + } else { + ld.Diag("adddynsym: unsupported binary format") + } +} + +func adddynlib(lib string) { + var s *ld.LSym + + if needlib(lib) == 0 { + return + } + + if ld.Iself { + s = ld.Linklookup(ld.Ctxt, ".dynstr", 0) + if s.Size == 0 { + ld.Addstring(s, "") + } + ld.Elfwritedynent(ld.Linklookup(ld.Ctxt, ".dynamic", 0), ld.DT_NEEDED, uint64(ld.Addstring(s, lib))) + } else { + ld.Diag("adddynlib: unsupported binary format") + } +} + +func asmb() { + var symo uint32 + var sect *ld.Section + var sym *ld.LSym + var i int + + if ld.Debug['v'] != 0 { + fmt.Fprintf(&ld.Bso, "%5.2f asmb\n", obj.Cputime()) + } + ld.Bflush(&ld.Bso) + + if ld.Iself { + ld.Asmbelfsetup() + } + + sect = ld.Segtext.Sect + ld.Cseek(int64(sect.Vaddr - ld.Segtext.Vaddr + ld.Segtext.Fileoff)) + ld.Codeblk(int64(sect.Vaddr), int64(sect.Length)) + for sect = sect.Next; sect != nil; sect = sect.Next { + ld.Cseek(int64(sect.Vaddr - ld.Segtext.Vaddr + ld.Segtext.Fileoff)) + ld.Datblk(int64(sect.Vaddr), int64(sect.Length)) + } + + if ld.Segrodata.Filelen > 0 { + if ld.Debug['v'] != 0 { + fmt.Fprintf(&ld.Bso, "%5.2f rodatblk\n", obj.Cputime()) + } + ld.Bflush(&ld.Bso) + + ld.Cseek(int64(ld.Segrodata.Fileoff)) + ld.Datblk(int64(ld.Segrodata.Vaddr), int64(ld.Segrodata.Filelen)) + } + + if ld.Debug['v'] != 0 { + fmt.Fprintf(&ld.Bso, "%5.2f datblk\n", obj.Cputime()) + } + ld.Bflush(&ld.Bso) + + ld.Cseek(int64(ld.Segdata.Fileoff)) + ld.Datblk(int64(ld.Segdata.Vaddr), int64(ld.Segdata.Filelen)) + + /* output symbol table */ + ld.Symsize = 0 + + ld.Lcsize = 0 + symo = 0 + if ld.Debug['s'] == 0 { + // TODO: rationalize + if ld.Debug['v'] != 0 { + fmt.Fprintf(&ld.Bso, "%5.2f sym\n", obj.Cputime()) + } + ld.Bflush(&ld.Bso) + switch ld.HEADTYPE { + default: + if ld.Iself { + symo = uint32(ld.Segdata.Fileoff + ld.Segdata.Filelen) + symo = uint32(ld.Rnd(int64(symo), int64(ld.INITRND))) + } + + case ld.Hplan9: + symo = uint32(ld.Segdata.Fileoff + ld.Segdata.Filelen) + } + + ld.Cseek(int64(symo)) + switch ld.HEADTYPE { + default: + if ld.Iself { + if ld.Debug['v'] != 0 { + fmt.Fprintf(&ld.Bso, "%5.2f elfsym\n", obj.Cputime()) + } + ld.Asmelfsym() + ld.Cflush() + ld.Cwrite(ld.Elfstrdat) + + if ld.Debug['v'] != 0 { + fmt.Fprintf(&ld.Bso, "%5.2f dwarf\n", obj.Cputime()) + } + ld.Dwarfemitdebugsections() + + if ld.Linkmode == ld.LinkExternal { + ld.Elfemitreloc() + } + } + + case ld.Hplan9: + ld.Asmplan9sym() + ld.Cflush() + + sym = ld.Linklookup(ld.Ctxt, "pclntab", 0) + if sym != nil { + ld.Lcsize = int32(len(sym.P)) + for i = 0; int32(i) < ld.Lcsize; i++ { + ld.Cput(uint8(sym.P[i])) + } + + ld.Cflush() + } + } + } + + ld.Ctxt.Cursym = nil + if ld.Debug['v'] != 0 { + fmt.Fprintf(&ld.Bso, "%5.2f header\n", obj.Cputime()) + } + ld.Bflush(&ld.Bso) + ld.Cseek(0) + switch ld.HEADTYPE { + default: + case ld.Hplan9: /* plan 9 */ + ld.Thearch.Lput(0x647) /* magic */ + ld.Thearch.Lput(uint32(ld.Segtext.Filelen)) /* sizes */ + ld.Thearch.Lput(uint32(ld.Segdata.Filelen)) + ld.Thearch.Lput(uint32(ld.Segdata.Length - ld.Segdata.Filelen)) + ld.Thearch.Lput(uint32(ld.Symsize)) /* nsyms */ + ld.Thearch.Lput(uint32(ld.Entryvalue())) /* va of entry */ + ld.Thearch.Lput(0) + ld.Thearch.Lput(uint32(ld.Lcsize)) + + case ld.Hlinux, + ld.Hfreebsd, + ld.Hnetbsd, + ld.Hopenbsd, + ld.Hnacl: + ld.Asmbelf(int64(symo)) + } + + ld.Cflush() + if ld.Debug['c'] != 0 { + fmt.Printf("textsize=%d\n", ld.Segtext.Filelen) + fmt.Printf("datsize=%d\n", ld.Segdata.Filelen) + fmt.Printf("bsssize=%d\n", ld.Segdata.Length-ld.Segdata.Filelen) + fmt.Printf("symsize=%d\n", ld.Symsize) + fmt.Printf("lcsize=%d\n", ld.Lcsize) + fmt.Printf("total=%d\n", ld.Segtext.Filelen+ld.Segdata.Length+uint64(ld.Symsize)+uint64(ld.Lcsize)) + } +} diff --git a/src/cmd/new9l/l.go b/src/cmd/new9l/l.go new file mode 100644 index 0000000000..7ddac3f34f --- /dev/null +++ b/src/cmd/new9l/l.go @@ -0,0 +1,77 @@ +// Inferno utils/5l/asm.c +// http://code.google.com/p/inferno-os/source/browse/utils/5l/asm.c +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package main + +// Writing object files. + +// cmd/9l/l.h from Vita Nuova. +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2008 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2008 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +const ( + thechar = '9' + PtrSize = 8 + IntSize = 8 + RegSize = 8 + MaxAlign = 32 + FuncAlign = 8 + MINLC = 4 +) + +/* Used by ../ld/dwarf.c */ +const ( + DWARFREGSP = 1 +) diff --git a/src/cmd/new9l/obj.go b/src/cmd/new9l/obj.go new file mode 100644 index 0000000000..8bba6be70d --- /dev/null +++ b/src/cmd/new9l/obj.go @@ -0,0 +1,165 @@ +// Inferno utils/5l/obj.c +// http://code.google.com/p/inferno-os/source/browse/utils/5l/obj.c +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package main + +import ( + "cmd/internal/obj" + "fmt" + "log" +) +import "cmd/internal/ld" + +// Reading object files. + +func main() { + linkarchinit() + ld.Ldmain() +} + +func linkarchinit() { + ld.Thestring = obj.Getgoarch() + if ld.Thestring == "ppc64le" { + ld.Thelinkarch = &ld.Linkppc64le + } else { + ld.Thelinkarch = &ld.Linkppc64 + } + + ld.Thearch.Thechar = thechar + ld.Thearch.Ptrsize = ld.Thelinkarch.Ptrsize + ld.Thearch.Intsize = ld.Thelinkarch.Ptrsize + ld.Thearch.Regsize = ld.Thelinkarch.Regsize + ld.Thearch.Funcalign = FuncAlign + ld.Thearch.Maxalign = MaxAlign + ld.Thearch.Minlc = MINLC + ld.Thearch.Dwarfregsp = DWARFREGSP + + ld.Thearch.Adddynlib = adddynlib + ld.Thearch.Adddynrel = adddynrel + ld.Thearch.Adddynsym = adddynsym + ld.Thearch.Archinit = archinit + ld.Thearch.Archreloc = archreloc + ld.Thearch.Archrelocvariant = archrelocvariant + ld.Thearch.Asmb = asmb + ld.Thearch.Elfreloc1 = elfreloc1 + ld.Thearch.Elfsetupplt = elfsetupplt + ld.Thearch.Gentext = gentext + ld.Thearch.Machoreloc1 = machoreloc1 + if ld.Thelinkarch == &ld.Linkppc64le { + ld.Thearch.Lput = ld.Lputl + ld.Thearch.Wput = ld.Wputl + ld.Thearch.Vput = ld.Vputl + } else { + ld.Thearch.Lput = ld.Lputb + ld.Thearch.Wput = ld.Wputb + ld.Thearch.Vput = ld.Vputb + } + + // TODO(austin): ABI v1 uses /usr/lib/ld.so.1 + ld.Thearch.Linuxdynld = "/lib64/ld64.so.1" + + ld.Thearch.Freebsddynld = "XXX" + ld.Thearch.Openbsddynld = "XXX" + ld.Thearch.Netbsddynld = "XXX" + ld.Thearch.Dragonflydynld = "XXX" + ld.Thearch.Solarisdynld = "XXX" +} + +func archinit() { + // getgoextlinkenabled is based on GO_EXTLINK_ENABLED when + // Go was built; see ../../make.bash. + if ld.Linkmode == ld.LinkAuto && obj.Getgoextlinkenabled() == "0" { + ld.Linkmode = ld.LinkInternal + } + + switch ld.HEADTYPE { + default: + if ld.Linkmode == ld.LinkAuto { + ld.Linkmode = ld.LinkInternal + } + if ld.Linkmode == ld.LinkExternal && obj.Getgoextlinkenabled() != "1" { + log.Fatalf("cannot use -linkmode=external with -H %s", ld.Headstr(int(ld.HEADTYPE))) + } + } + + switch ld.HEADTYPE { + default: + ld.Diag("unknown -H option") + ld.Errorexit() + fallthrough + + case ld.Hplan9: /* plan 9 */ + ld.HEADR = 32 + + if ld.INITTEXT == -1 { + ld.INITTEXT = 4128 + } + if ld.INITDAT == -1 { + ld.INITDAT = 0 + } + if ld.INITRND == -1 { + ld.INITRND = 4096 + } + + case ld.Hlinux: /* ppc64 elf */ + if ld.Thestring == "ppc64" { + ld.Debug['d'] = 1 // TODO(austin): ELF ABI v1 not supported yet + } + ld.Elfinit() + ld.HEADR = ld.ELFRESERVE + if ld.INITTEXT == -1 { + ld.INITTEXT = 0x10000 + int64(ld.HEADR) + } + if ld.INITDAT == -1 { + ld.INITDAT = 0 + } + if ld.INITRND == -1 { + ld.INITRND = 0x10000 + } + + case ld.Hnacl: + ld.Elfinit() + ld.HEADR = 0x10000 + ld.Funcalign = 16 + if ld.INITTEXT == -1 { + ld.INITTEXT = 0x20000 + } + if ld.INITDAT == -1 { + ld.INITDAT = 0 + } + if ld.INITRND == -1 { + ld.INITRND = 0x10000 + } + } + + if ld.INITDAT != 0 && ld.INITRND != 0 { + fmt.Printf("warning: -D0x%x is ignored because of -R0x%x\n", uint64(ld.INITDAT), uint32(ld.INITRND)) + } +}