mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
The long comment block in obj6.go:progedit talked about the two code sequences for accessing g as "local exec" and "initial exec", but really they are both forms of local exec. This stuff is confusing enough without using the wrong words for things, so rewrite it to talk about 2-instruction and 1-instruction sequences. Unfortunately the confusion has made it into code, with the R_TLS_IE relocation now doing double duty as meaning actual initial exec when externally linking and boring old local exec when linking internally (half of this is my fault). So this stops using R_TLS_IE in the local exec case. There is a chance this might break plan9 or windows, but I don't think so. Next step is working out what the heck is going on on ARM... Change-Id: I09da4388210cf49dbc99fd25f5172bbe517cee57 Reviewed-on: https://go-review.googlesource.com/9273 Reviewed-by: Ian Lance Taylor <iant@golang.org> Run-TryBot: Ian Lance Taylor <iant@golang.org>
1787 lines
44 KiB
Go
1787 lines
44 KiB
Go
// 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 = obj.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 {
|
|
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 adduint(ctxt *Link, s *LSym, v uint64) int64 {
|
|
return adduintxx(ctxt, s, v, Thearch.Intsize)
|
|
}
|
|
|
|
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 {
|
|
if s.Type == 0 {
|
|
s.Type = obj.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 = obj.R_ADDR
|
|
r.Add = add
|
|
return i + int64(r.Siz)
|
|
}
|
|
|
|
func Addpcrelplus(ctxt *Link, s *LSym, t *LSym, add int64) int64 {
|
|
if s.Type == 0 {
|
|
s.Type = obj.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 = obj.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 {
|
|
if s.Type == 0 {
|
|
s.Type = obj.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 = obj.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 {
|
|
if s.Type == 0 {
|
|
s.Type = obj.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 = obj.R_SIZE
|
|
return i + int64(r.Siz)
|
|
}
|
|
|
|
func addaddrplus4(ctxt *Link, s *LSym, t *LSym, add int64) int64 {
|
|
if s.Type == 0 {
|
|
s.Type = obj.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 = obj.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 != obj.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 {
|
|
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 off int32
|
|
var siz int32
|
|
var fl int32
|
|
var o int64
|
|
|
|
Ctxt.Cursym = s
|
|
for ri := int32(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&(obj.SMASK|obj.SHIDDEN) == 0 || r.Sym.Type&obj.SMASK == obj.SXREF) {
|
|
// When putting the runtime but not main into a shared library
|
|
// these symbols are undefined and that's OK.
|
|
if Buildmode == BuildmodeShared && (r.Sym.Name == "main.main" || r.Sym.Name == "main.init") {
|
|
r.Sym.Type = obj.SDYNIMPORT
|
|
} else {
|
|
Diag("%s: not defined", r.Sym.Name)
|
|
continue
|
|
}
|
|
}
|
|
|
|
if r.Type >= 256 {
|
|
continue
|
|
}
|
|
if r.Siz == 0 { // informational relocation - no work to do
|
|
continue
|
|
}
|
|
|
|
// We need to be able to reference dynimport symbols when linking against
|
|
// shared libraries, and Solaris needs it always
|
|
if HEADTYPE != obj.Hsolaris && r.Sym != nil && r.Sym.Type == obj.SDYNIMPORT && !DynlinkingGo() {
|
|
Diag("unhandled relocation for %s (type %d rtype %d)", r.Sym.Name, r.Sym.Type, r.Type)
|
|
}
|
|
if r.Sym != nil && r.Sym.Type != obj.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 == obj.R_TLS && goos == "android" {
|
|
r.Type = obj.R_ADDR
|
|
}
|
|
|
|
switch r.Type {
|
|
default:
|
|
o = 0
|
|
if Thearch.Archreloc(r, s, &o) < 0 {
|
|
Diag("unknown reloc %d", r.Type)
|
|
}
|
|
|
|
case obj.R_TLS:
|
|
if Linkmode == LinkExternal && Iself && HEADTYPE != obj.Hopenbsd {
|
|
r.Done = 0
|
|
r.Sym = Ctxt.Tlsg
|
|
r.Xsym = Ctxt.Tlsg
|
|
r.Xadd = r.Add
|
|
o = r.Add
|
|
break
|
|
}
|
|
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 obj.R_TLS_LE:
|
|
if Linkmode == LinkExternal && Iself && HEADTYPE != obj.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 == obj.Hplan9 {
|
|
o = int64(Ctxt.Tlsoffset) + r.Add
|
|
} else if Ctxt.Headtype == obj.Hwindows {
|
|
o = r.Add
|
|
} else {
|
|
log.Fatalf("unexpected R_TLS_LE relocation for %s", Headstr(Ctxt.Headtype))
|
|
}
|
|
|
|
case obj.R_TLS_IE:
|
|
if Linkmode == LinkExternal && Iself && HEADTYPE != obj.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
|
|
}
|
|
log.Fatalf("cannot handle R_TLS_IE when linking internally")
|
|
|
|
case obj.R_ADDR:
|
|
if Linkmode == LinkExternal && r.Sym.Type != obj.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 != obj.SHOSTOBJ && rs.Type != obj.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 == obj.Hdarwin {
|
|
// ld64 for arm64 has a bug where if the address pointed to by o exists in the
|
|
// symbol table (dynid >= 0), or is inside a symbol that exists in the symbol
|
|
// table, then it will add o twice into the relocated value.
|
|
// The workaround is that on arm64 don't ever add symaddr to o and always use
|
|
// extern relocation by requiring rs->dynid >= 0.
|
|
if rs.Type != obj.SHOSTOBJ {
|
|
if Thearch.Thechar == '7' && rs.Dynid < 0 {
|
|
Diag("R_ADDR reloc to %s+%d is not supported on darwin/arm64", rs.Name, o)
|
|
}
|
|
if Thearch.Thechar != '7' {
|
|
o += Symaddr(rs)
|
|
}
|
|
}
|
|
} else if HEADTYPE == obj.Hwindows {
|
|
// nothing to do
|
|
} 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 (%#x + %#x)", uint64(o), Symaddr(r.Sym), r.Add)
|
|
errorexit()
|
|
}
|
|
|
|
// r->sym can be null when CALL $(constant) is transformed from absolute PC to relative PC call.
|
|
case obj.R_CALL, obj.R_GOTPCREL, obj.R_PCREL:
|
|
if Linkmode == LinkExternal && r.Sym != nil && r.Sym.Type != obj.SCONST && (r.Sym.Sect != Ctxt.Cursym.Sect || r.Type == obj.R_GOTPCREL) {
|
|
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 != obj.SHOSTOBJ && rs.Type != obj.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 == obj.Hdarwin {
|
|
if r.Type == obj.R_CALL {
|
|
if rs.Type != obj.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 if HEADTYPE == obj.Hwindows && Thearch.Thechar == '6' { // only amd64 needs PCREL
|
|
// PE/COFF's PC32 relocation uses the address after the relocated
|
|
// bytes as the base. Compensate by skewing the addend.
|
|
o += int64(r.Siz)
|
|
// GNU ld always add VirtualAddress of the .text section to the
|
|
// relocated address, compensate that.
|
|
o -= int64(s.Sect.(*Section).Vaddr - PEBASE)
|
|
} 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 obj.R_SIZE:
|
|
o = r.Sym.Size + r.Add
|
|
}
|
|
|
|
if r.Variant != RV_NONE {
|
|
o = Thearch.Archrelocvariant(r, s, o)
|
|
}
|
|
|
|
if false {
|
|
nam := "<nil>"
|
|
if r.Sym != nil {
|
|
nam = r.Sym.Name
|
|
}
|
|
fmt.Printf("relocate %s %#x (%#x+%#x, size %d) => %s %#x +%#x [type %d/%d, %x]\n", s.Name, s.Value+int64(off), s.Value, r.Off, r.Siz, nam, Symaddr(r.Sym), r.Add, r.Type, r.Variant, 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 == obj.R_PCREL || r.Type == obj.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() {
|
|
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) {
|
|
if HEADTYPE == obj.Hwindows && Linkmode != LinkExternal {
|
|
rel := Linklookup(Ctxt, ".rel", 0)
|
|
if s == rel {
|
|
return
|
|
}
|
|
var r *Reloc
|
|
var targ *LSym
|
|
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
|
|
}
|
|
|
|
var r *Reloc
|
|
for ri := 0; ri < len(s.R); ri++ {
|
|
r = &s.R[ri]
|
|
if r.Sym != nil && r.Sym.Type == obj.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() {
|
|
// -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 != obj.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
|
|
|
|
for sym = start; sym != nil; sym = sym.Next {
|
|
if sym.Type&obj.SSUB == 0 && sym.Value >= addr {
|
|
break
|
|
}
|
|
}
|
|
|
|
eaddr := addr + size
|
|
var ep []byte
|
|
var p []byte
|
|
for ; sym != nil; sym = sym.Next {
|
|
if sym.Type&obj.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) {
|
|
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
|
|
}
|
|
|
|
var sym *LSym
|
|
for sym = Ctxt.Textp; sym != nil; sym = sym.Next {
|
|
if !sym.Reachable {
|
|
continue
|
|
}
|
|
if sym.Value >= addr {
|
|
break
|
|
}
|
|
}
|
|
|
|
eaddr := addr + size
|
|
var n int64
|
|
var q []byte
|
|
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) {
|
|
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
|
|
}
|
|
|
|
var sym *LSym
|
|
for sym = datap; sym != nil; sym = sym.Next {
|
|
if sym.Value >= addr {
|
|
break
|
|
}
|
|
}
|
|
|
|
eaddr := addr + size
|
|
var ep []byte
|
|
var i int64
|
|
var p []byte
|
|
var r *Reloc
|
|
var rsname string
|
|
var typ string
|
|
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 obj.R_ADDR:
|
|
typ = "addr"
|
|
|
|
case obj.R_PCREL:
|
|
typ = "pcrel"
|
|
|
|
case obj.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) {
|
|
p := fmt.Sprintf("%s.str", name)
|
|
sp := Linklookup(Ctxt, p, 0)
|
|
|
|
Addstring(sp, value)
|
|
sp.Type = obj.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 {
|
|
if s.Type == 0 {
|
|
s.Type = obj.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 addinitarrdata(s *LSym) {
|
|
p := s.Name + ".ptr"
|
|
sp := Linklookup(Ctxt, p, 0)
|
|
sp.Type = obj.SINITARR
|
|
sp.Size = 0
|
|
sp.Dupok = 1
|
|
Addaddr(Ctxt, sp, s)
|
|
}
|
|
|
|
func dosymtype() {
|
|
for s := Ctxt.Allsym; s != nil; s = s.Allsym {
|
|
if len(s.P) > 0 {
|
|
if s.Type == obj.SBSS {
|
|
s.Type = obj.SDATA
|
|
}
|
|
if s.Type == obj.SNOPTRBSS {
|
|
s.Type = obj.SNOPTRDATA
|
|
}
|
|
}
|
|
// Create a new entry in the .init_array section that points to the
|
|
// library initializer function.
|
|
switch Buildmode {
|
|
case BuildmodeCArchive, BuildmodeCShared:
|
|
if s.Name == INITENTRY {
|
|
addinitarrdata(s)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func symalign(s *LSym) 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
|
|
|
|
max := int32(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) {
|
|
if g.datasize == 0 {
|
|
return
|
|
}
|
|
proggenemit(g, obj.InsData)
|
|
proggenemit(g, uint8(g.datasize))
|
|
s := (g.datasize + obj.PointersPerByte - 1) / obj.PointersPerByte
|
|
for i := int32(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) {
|
|
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); i, length = i+1, 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) {
|
|
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 := int64(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 := int64(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 := int64(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) {
|
|
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() {
|
|
if Debug['v'] != 0 {
|
|
fmt.Fprintf(&Bso, "%5.2f dodata\n", obj.Cputime())
|
|
}
|
|
Bflush(&Bso)
|
|
|
|
var last *LSym
|
|
datap = nil
|
|
|
|
for s := Ctxt.Allsym; s != nil; s = s.Allsym {
|
|
if !s.Reachable || s.Special != 0 {
|
|
continue
|
|
}
|
|
if obj.STEXT < s.Type && s.Type < obj.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 == obj.Hdarwin {
|
|
machosymorder()
|
|
}
|
|
dynreloc()
|
|
|
|
/* some symbols may no longer belong in datap (Mach-O) */
|
|
var l **LSym
|
|
var s *LSym
|
|
for l = &datap; ; {
|
|
s = *l
|
|
if s == nil {
|
|
break
|
|
}
|
|
|
|
if s.Type <= obj.STEXT || obj.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 < obj.SELFSECT; s = s.Next {
|
|
}
|
|
|
|
/* writable ELF sections */
|
|
datsize := int64(0)
|
|
|
|
var sect *Section
|
|
for ; s != nil && s.Type < obj.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 = obj.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 == obj.SELFGOT {
|
|
sect := addsection(&Segdata, ".got", 06)
|
|
sect.Align = maxalign(s, obj.SELFGOT)
|
|
datsize = Rnd(datsize, int64(sect.Align))
|
|
sect.Vaddr = uint64(datsize)
|
|
var toc *LSym
|
|
for ; s != nil && s.Type == obj.SELFGOT; s = s.Next {
|
|
datsize = aligndatsize(datsize, s)
|
|
s.Sect = sect
|
|
s.Type = obj.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, obj.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 < obj.SINITARR; s = s.Next {
|
|
datsize = aligndatsize(datsize, s)
|
|
s.Sect = sect
|
|
s.Type = obj.SDATA
|
|
s.Value = int64(uint64(datsize) - sect.Vaddr)
|
|
growdatsize(&datsize, s)
|
|
}
|
|
|
|
sect.Length = uint64(datsize) - sect.Vaddr
|
|
|
|
hasinitarr := Linkshared
|
|
|
|
/* shared library initializer */
|
|
switch Buildmode {
|
|
case BuildmodeCArchive, BuildmodeCShared, BuildmodeShared:
|
|
hasinitarr = true
|
|
}
|
|
|
|
if hasinitarr {
|
|
sect := addsection(&Segdata, ".init_array", 06)
|
|
sect.Align = maxalign(s, obj.SINITARR)
|
|
datsize = Rnd(datsize, int64(sect.Align))
|
|
sect.Vaddr = uint64(datsize)
|
|
for ; s != nil && s.Type == obj.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, obj.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)
|
|
var gen ProgGen
|
|
proggeninit(&gen, gcdata)
|
|
for ; s != nil && s.Type < obj.SBSS; s = s.Next {
|
|
if s.Type == obj.SINITARR {
|
|
Ctxt.Cursym = s
|
|
Diag("unexpected symbol type %d", s.Type)
|
|
}
|
|
|
|
s.Sect = sect
|
|
s.Type = obj.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, obj.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 < obj.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, obj.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 == obj.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 == obj.STLSBSS && HEADTYPE != obj.Hopenbsd {
|
|
sect := addsection(&Segdata, ".tbss", 06)
|
|
sect.Align = int32(Thearch.Ptrsize)
|
|
sect.Vaddr = 0
|
|
datsize = 0
|
|
for ; s != nil && s.Type == obj.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 == obj.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.
|
|
*/
|
|
var segro *Segment
|
|
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 < obj.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 = obj.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, obj.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 < obj.STYPELINK; s = s.Next {
|
|
datsize = aligndatsize(datsize, s)
|
|
s.Sect = sect
|
|
s.Type = obj.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, obj.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 == obj.STYPELINK; s = s.Next {
|
|
datsize = aligndatsize(datsize, s)
|
|
s.Sect = sect
|
|
s.Type = obj.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, obj.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 < obj.SPCLNTAB; s = s.Next {
|
|
datsize = aligndatsize(datsize, s)
|
|
s.Sect = sect
|
|
s.Type = obj.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, obj.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 < obj.SELFROSECT; s = s.Next {
|
|
datsize = aligndatsize(datsize, s)
|
|
s.Sect = sect
|
|
s.Type = obj.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 < obj.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 = obj.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 := int32(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 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&obj.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() {
|
|
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 == obj.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 == obj.Hwindows {
|
|
Segdata.Fileoff = Segtext.Fileoff + uint64(Rnd(int64(Segtext.Length), PEFILEALIGN))
|
|
}
|
|
if HEADTYPE == obj.Hplan9 {
|
|
Segdata.Fileoff = Segtext.Fileoff + Segtext.Filelen
|
|
}
|
|
var data *Section
|
|
var noptr *Section
|
|
var bss *Section
|
|
var noptrbss *Section
|
|
var vlen int64
|
|
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
|
|
var rodata *Section
|
|
if Segrodata.Sect != nil {
|
|
rodata = Segrodata.Sect
|
|
} else {
|
|
rodata = text.Next
|
|
}
|
|
typelink := rodata.Next
|
|
symtab := typelink.Next
|
|
pclntab := symtab.Next
|
|
|
|
var sub *LSym
|
|
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", obj.STEXT, int64(text.Vaddr))
|
|
xdefine("runtime.etext", obj.STEXT, int64(text.Vaddr+text.Length))
|
|
xdefine("runtime.rodata", obj.SRODATA, int64(rodata.Vaddr))
|
|
xdefine("runtime.erodata", obj.SRODATA, int64(rodata.Vaddr+rodata.Length))
|
|
xdefine("runtime.typelink", obj.SRODATA, int64(typelink.Vaddr))
|
|
xdefine("runtime.etypelink", obj.SRODATA, int64(typelink.Vaddr+typelink.Length))
|
|
|
|
sym := Linklookup(Ctxt, "runtime.gcdata", 0)
|
|
sym.Local = true
|
|
xdefine("runtime.egcdata", obj.SRODATA, Symaddr(sym)+sym.Size)
|
|
Linklookup(Ctxt, "runtime.egcdata", 0).Sect = sym.Sect
|
|
|
|
sym = Linklookup(Ctxt, "runtime.gcbss", 0)
|
|
sym.Local = true
|
|
xdefine("runtime.egcbss", obj.SRODATA, Symaddr(sym)+sym.Size)
|
|
Linklookup(Ctxt, "runtime.egcbss", 0).Sect = sym.Sect
|
|
|
|
xdefine("runtime.symtab", obj.SRODATA, int64(symtab.Vaddr))
|
|
xdefine("runtime.esymtab", obj.SRODATA, int64(symtab.Vaddr+symtab.Length))
|
|
xdefine("runtime.pclntab", obj.SRODATA, int64(pclntab.Vaddr))
|
|
xdefine("runtime.epclntab", obj.SRODATA, int64(pclntab.Vaddr+pclntab.Length))
|
|
xdefine("runtime.noptrdata", obj.SNOPTRDATA, int64(noptr.Vaddr))
|
|
xdefine("runtime.enoptrdata", obj.SNOPTRDATA, int64(noptr.Vaddr+noptr.Length))
|
|
xdefine("runtime.bss", obj.SBSS, int64(bss.Vaddr))
|
|
xdefine("runtime.ebss", obj.SBSS, int64(bss.Vaddr+bss.Length))
|
|
xdefine("runtime.data", obj.SDATA, int64(data.Vaddr))
|
|
xdefine("runtime.edata", obj.SDATA, int64(data.Vaddr+data.Length))
|
|
xdefine("runtime.noptrbss", obj.SNOPTRBSS, int64(noptrbss.Vaddr))
|
|
xdefine("runtime.enoptrbss", obj.SNOPTRBSS, int64(noptrbss.Vaddr+noptrbss.Length))
|
|
xdefine("runtime.end", obj.SBSS, int64(Segdata.Vaddr+Segdata.Length))
|
|
}
|